Comparar commits
596 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 9ede9571bc | |||
| dbc21befec | |||
| ca0f0357d7 | |||
| be1c1f8719 | |||
| e95c60b4ec | |||
| 847dd6d93b | |||
| 7f62720350 | |||
| 8af0a59c52 | |||
| 31a909828d | |||
| 78562dcf15 | |||
| 4869fad7f6 | |||
| 77da136d17 | |||
| 67b1d0d22a | |||
| 23fcb59c5d | |||
| 16ef30dc82 | |||
| e3a4f450ad | |||
| 847578398d | |||
| 63dc8863e3 | |||
| 58d09ec5a0 | |||
| bb5d70fa2e | |||
| 4a904673e5 | |||
| 4c1d47779b | |||
| a0adc32e04 | |||
| 2434db4dc2 | |||
| e2ee99556d | |||
| cececc2297 | |||
| f0825ca526 | |||
| 57c0261f96 | |||
| 6891922e07 | |||
| e03a7c336d | |||
| 49a32dd2dc | |||
| 94d4ad618c | |||
| 9b0f8ccee0 | |||
| fa963f0583 | |||
| d36c4f0ec9 | |||
| e6e72d99f3 | |||
| 9ce22fd6d3 | |||
| 5166ca274c | |||
| 6f1dd702b0 | |||
| 1d4f447975 | |||
| 4998fdda85 | |||
| aa52428c26 | |||
| 9c1127dd1b | |||
| 91d4ef2653 | |||
| 80cc881912 | |||
| 867aaa8f95 | |||
| 28a1dc58e7 | |||
| ee7ef0f893 | |||
| eaf60a00b3 | |||
| e01c19e6b7 | |||
| 930e479cdb | |||
| 46c6d63dcd | |||
| 1b502c6349 | |||
| 07eb01016d | |||
| 99d6955644 | |||
| 3f05ff9b14 | |||
| 9caf3ca349 | |||
| 25a1b77048 | |||
| 866c0c0668 | |||
| 6094efde76 | |||
| b4099ba66f | |||
| 10b560b5a7 | |||
| 305c331777 | |||
| 0b5cad27d2 | |||
| 7d967e71f4 | |||
| d36a0aa437 | |||
| 8316da3338 | |||
| a0b200565c | |||
| 8f4de0b568 | |||
| 45cd3e9335 | |||
| c58b30bba9 | |||
| 0093f39102 | |||
| b1f041fa6e | |||
| ca11661f6d | |||
| bc4ceb189c | |||
| 98694e5407 | |||
| 86ae743e9d | |||
| dddd515c35 | |||
| 85d46004fd | |||
| 5cb6aa0538 | |||
| d228cfe1ee | |||
| a8f7c5201b | |||
| e15b96691e | |||
| 3053c4a585 | |||
| 776d58bf21 | |||
| 1b5762274c | |||
| 1f20dbf16d | |||
| 9fe507d675 | |||
| a190a069c1 | |||
| c697e58a71 | |||
| 1f36d0ca02 | |||
| 235a1e46ab | |||
| 1197dc1979 | |||
| 94e94506b5 | |||
| 163c800d55 | |||
| 3f5a6184b6 | |||
| 77819616a8 | |||
| 3749726742 | |||
| d22d6e9af2 | |||
| 7ad6f0b18b | |||
| cdfd27be3e | |||
| 4e554a4941 | |||
| 3db5cad927 | |||
| 1a21600bde | |||
| 3c3811c527 | |||
| 16c0ef935f | |||
| 6cc897ac67 | |||
| 80782feb24 | |||
| 3cfe50585a | |||
| 4c336ed0ee | |||
| c1cf8936eb | |||
| d131b3e39c | |||
| 043ce8f972 | |||
| e4cac4de70 | |||
| c5cc0706e0 | |||
| 45ddece673 | |||
| 2901a04843 | |||
| 0259536c5d | |||
| 48692e5127 | |||
| 19dc6b3523 | |||
| d1496a0634 | |||
| d8c4fa902d | |||
| f1e768767e | |||
| c03e9ad815 | |||
| ebe3d0d885 | |||
| 46f73d037f | |||
| e7e0f081f4 | |||
| dde46ca931 | |||
| 65746521a6 | |||
| 5352f7322c | |||
| f395905d4e | |||
| f84635766c | |||
| 413078a493 | |||
| d3c9b6e547 | |||
| 078fffa7c1 | |||
| 8472ac4fc8 | |||
| 1bdfb004ef | |||
| e42079c762 | |||
| a0ceb78627 | |||
| eb985a9880 | |||
| d0eb26c35f | |||
| 1489488159 | |||
| a0a90ca26a | |||
| 21f6676094 | |||
| ad9721a893 | |||
| 12ae7abc38 | |||
| ee0814313f | |||
| a3c9e01595 | |||
| 2e801fbf87 | |||
| 40d7fb3c45 | |||
| 14ad277012 | |||
| 6a9f29024a | |||
| 3ce6176313 | |||
| 7d7ed79fde | |||
| 8aa32fc8d1 | |||
| f8ed6eadc8 | |||
| 0c2c739741 | |||
| 79c6badce8 | |||
| a48ef934eb | |||
| 85d5968318 | |||
| 9b4dd602be | |||
| 62d84e5d4b | |||
| 61ed5da1dc | |||
| 32e25d4bb2 | |||
| aae9614839 | |||
| 7fa4121227 | |||
| 1d783826a2 | |||
| bdb6e193de | |||
| 3f75512de2 | |||
| 5910b05344 | |||
| 7f039b3383 | |||
| ca35ced587 | |||
| 734ef19f48 | |||
| ef9ce1bf70 | |||
| c754b73b71 | |||
| 21565332a4 | |||
| 8871d9cd2d | |||
| 64f0fcc839 | |||
| 581d12b04f | |||
| 05a9ace3e6 | |||
| 03eac362f6 | |||
| 8a05b0f51d | |||
| 4069d23d86 | |||
| 78b48345ac | |||
| 139e2f6de0 | |||
| ed86b4a478 | |||
| f2352131cc | |||
| 56df435fb8 | |||
| 595ff19b5b | |||
| 33eb0bae8f | |||
| fde1560377 | |||
| a640d07599 | |||
| 45b65cd3aa | |||
| f217278001 | |||
| 3320176a0a | |||
| 754b5a6004 | |||
| 79b84b2433 | |||
| 17be036ff8 | |||
| 23deec833f | |||
| 6a8d0bef4a | |||
| d5ce1a0312 | |||
| 74decbd18a | |||
| 085806f97f | |||
| ca8be83903 | |||
| da713da311 | |||
| 7a6c75a83e | |||
| 3e7a517c25 | |||
| e623ef4232 | |||
| 18399aa264 | |||
| 2a5f393712 | |||
| 27f779ec03 | |||
| 5d717eb7bd | |||
| 0b457fd80a | |||
| dd9aa2d02f | |||
| 87b530140b | |||
| 2f46fee1ca | |||
| fbb48e7807 | |||
| 01c141eec6 | |||
| 7d9d0c715c | |||
| a9887b5007 | |||
| c4dec72dcd | |||
| d093d5cc06 | |||
| 27c9e54538 | |||
| 3bbe6ee98c | |||
| 524d8e8e21 | |||
| c31211dc21 | |||
| 6c1b63d352 | |||
| 6fa7da79eb | |||
| c2371f3054 | |||
| 2349627e3c | |||
| 90d92a4c92 | |||
| 66171e0301 | |||
| cdc7f70b22 | |||
| e225dbe93d | |||
| 7dc18765ad | |||
| c1d379fd6c | |||
| a754ac4da0 | |||
| 21560df2f0 | |||
| f0aa408b70 | |||
| 161c9a62b5 | |||
| 2c5bbcbc22 | |||
| eee72f7664 | |||
| 073d398e6f | |||
| 2310e263a7 | |||
| de7b212d99 | |||
| 75873ef6b3 | |||
| 73855a49fc | |||
| cae055fd3f | |||
| 2aad31c4dc | |||
| 93052ad611 | |||
| 67733b8b05 | |||
| 21543569ef | |||
| 7371ebbf20 | |||
| 73470cc294 | |||
| 1e60b5fa3b | |||
| 172ecbd897 | |||
| 289cc24b56 | |||
| a20483ccdb | |||
| b2ceaf3b8b | |||
| 1c3c508985 | |||
| 645f4ad907 | |||
| 05dd6f8f17 | |||
| b19390b519 | |||
| a3f339e0c3 | |||
| 42040e14b3 | |||
| 91640f0886 | |||
| 0377d64788 | |||
| cd37caae96 | |||
| aab63c26e7 | |||
| 6da6101a52 | |||
| 87b33648dc | |||
| d0b380e535 | |||
| 99480901e2 | |||
| 6852720408 | |||
| f9498732a5 | |||
| e4aa82fda1 | |||
| bc1a743b2f | |||
| 3fe22aa5c8 | |||
| fe035d4d7c | |||
| e8bfb7ca09 | |||
| 4bfd48b983 | |||
| 5a1fadf7ce | |||
| 819ac9ea68 | |||
| 6384841134 | |||
| a0fbec29c3 | |||
| 238fca2004 | |||
| 014beda455 | |||
| 777df644ce | |||
| f3ea3a3395 | |||
| b82fdace61 | |||
| 7195102a04 | |||
| 5ec6a4a189 | |||
| e330b1a2e8 | |||
| 5334433bc2 | |||
| 366a12903a | |||
| 063cb04fb5 | |||
| d9c2f07fbe | |||
| 5c1fa8e53d | |||
| a1f3540cb4 | |||
| 2eeb399cf1 | |||
| 71e8e865f2 | |||
| 9f080be6e1 | |||
| 56f66f8578 | |||
| 0b1dc704ea | |||
| 578d823118 | |||
| 0196f2a2eb | |||
| 1e68a7266f | |||
| 093143f7a9 | |||
| 4eeef9cfbd | |||
| ce3ec75c55 | |||
| bb09de9703 | |||
| e64ba18fe3 | |||
| 3908f81fc6 | |||
| fac46a295c | |||
| b01470a738 | |||
| f3be613662 | |||
| ede29d99c1 | |||
| d2369e94c8 | |||
| dbe3399016 | |||
| 3952423d99 | |||
| ffdcecc0f2 | |||
| 0069eb4d0d | |||
| 5fa55026d5 | |||
| 6d04d57e74 | |||
| 3f0dca5a40 | |||
| 8b14a66e2c | |||
| 1607411df1 | |||
| dade9f6309 | |||
| 568aa1d396 | |||
| 9febe179fa | |||
| c9e68ab044 | |||
| 285186567a | |||
| 1c136f16e3 | |||
| 534a2d4565 | |||
| 9e814de969 | |||
| 7202ba274a | |||
| 46e85fac87 | |||
| d1f1b494cf | |||
| f11803df60 | |||
| f094a86ae7 | |||
| a6c791ce39 | |||
| dfa870f514 | |||
| a98377b899 | |||
| c3de3d8eea | |||
| 2d15f5e49a | |||
| 56386cb06a | |||
| 986a7ad5c3 | |||
| 9a01b5a6bf | |||
| ccafda6f7f | |||
| 33538a5ed7 | |||
| aae85cd7c1 | |||
| ce098e587f | |||
| 233d819e04 | |||
| fbbf3d177a | |||
| c33bd34996 | |||
| 34cdb23d71 | |||
| fcedcd117d | |||
| 3d7de21d6c | |||
| 2278ee742a | |||
| 55e90f8ae1 | |||
| 2ca738453b | |||
| 31a9bb83cf | |||
| 1e4504e7f3 | |||
| bf05ddb958 | |||
| 756c2be64a | |||
| 6fdd4f775b | |||
| 625fcaffc8 | |||
| 955d379e0e | |||
| 0d71f20073 | |||
| 42c40e8c7a | |||
| 89212e599f | |||
| 95e4ac903c | |||
| 311155ac0d | |||
| aaa82e23da | |||
| 8e46bc5241 | |||
| 37a5a6f501 | |||
| 83f14c137c | |||
| b0b458b1f6 | |||
| f4de124aa6 | |||
| 4b0eaf05a5 | |||
| 03e8bc6f19 | |||
| 13186fcf7a | |||
| fea0f1f90a | |||
| b449bb4444 | |||
| fe6b40fc5d | |||
| 82e3935ae3 | |||
| 32a3b6302c | |||
| 6157fcaf73 | |||
| ab74d8be38 | |||
| 886a2aa867 | |||
| aeaa76a9e1 | |||
| 6a7bcb6f52 | |||
| ee1ec4670d | |||
| 7e0af4c575 | |||
| f55a200591 | |||
| 89d8eac091 | |||
| 5011b6e78f | |||
| d6e67c5b32 | |||
| de547e20c2 | |||
| 2b79b19330 | |||
| 2b234545b5 | |||
| a66543048b | |||
| ca96aa2804 | |||
| 316571308a | |||
| b323d9ce18 | |||
| 0e1c757cd0 | |||
| 084bbb1578 | |||
| 7c348ee478 | |||
| a0c6a94409 | |||
| 80cdf61fa4 | |||
| 29c3fadb6f | |||
| 1ae3806c69 | |||
| 98b509441c | |||
| 02f40688e2 | |||
| cb8e378af6 | |||
| babc4732b8 | |||
| dcccde8f3f | |||
| 8ed4923e58 | |||
| 529c829438 | |||
| 809a02ca10 | |||
| b9902cb6f2 | |||
| de4d3dbbe9 | |||
| 3fe88c4df1 | |||
| 20811a9f52 | |||
| b137f1a3e3 | |||
| 0b12f01206 | |||
| 1d7b4c5f9a | |||
| e2d4b58d5f | |||
| 1c8df2c0b5 | |||
| 9067c65a41 | |||
| 1d4f2fba8b | |||
| 12dd412439 | |||
| 19d680544b | |||
| a45dd3fe37 | |||
| f6c8a435ae | |||
| 93bfe0edf6 | |||
| d3fed57cb3 | |||
| 8372adb38a | |||
| e4b3d3a83c | |||
| 11f1ef9d8b | |||
| 7d87ae00ff | |||
| 3ec2378242 | |||
| 28943a35da | |||
| 7b43c8a860 | |||
| 8b17b7eca9 | |||
| 6432cda691 | |||
| cf0bdb9c94 | |||
| 34f1472653 | |||
| a475e27cd4 | |||
| abc1f23516 | |||
| dc7e7f9ed0 | |||
| 5fdec4dc7b | |||
| 618d281d6c | |||
| 055ec8cb9c | |||
| df3fe90c89 | |||
| 21e0e95a7a | |||
| 6caed6e918 | |||
| 7dd84636ba | |||
| b12954760d | |||
| 4bb21fd9ec | |||
| 11787e5a5d | |||
| 276e63611a | |||
| 957374eb40 | |||
| fe9f1373c1 | |||
| 6290c19264 | |||
| 913bb82d6e | |||
| ffc936ca4d | |||
| 1808e5f991 | |||
| 62feefd28d | |||
| ada992be4d | |||
| f8933cfeab | |||
| 0878d7ab6a | |||
| bd8e19bce7 | |||
| 4852ba6d95 | |||
| caffcafe2e | |||
| c7a1205ca6 | |||
| 2c4f94c319 | |||
| 941fc97e79 | |||
| 8788b2a51c | |||
| ca8ae9ad61 | |||
| 2fb00af255 | |||
| f6ce0f038f | |||
| 94a8d16664 | |||
| fc0a46d6b2 | |||
| fd443a8b68 | |||
| c43f277c5b | |||
| dd0938dca6 | |||
| e90f19da97 | |||
| c3aea1d149 | |||
| 569c3116a8 | |||
| 8bdc1d2418 | |||
| fa1600c53d | |||
| 6a1e83205f | |||
| 33891b51f2 | |||
| 1ef821f4e7 | |||
| ed030a54c3 | |||
| 6bb3a69410 | |||
| 53f5e9fbc6 | |||
| 1dac1f375c | |||
| cfab5c619d | |||
| b6afc415f8 | |||
| 5454e93168 | |||
| 389b2bd8d6 | |||
| 8ea011597c | |||
| 21060ae85a | |||
| fe0cc7d273 | |||
| f67e9b6e03 | |||
| 64f2cdb795 | |||
| a996597d49 | |||
| 52680bd63f | |||
| b8f0f1c683 | |||
| 0840de95dc | |||
| fe1b2c6d9d | |||
| 7b307a17ea | |||
| 097571a83d | |||
| 0673ce8e71 | |||
| 967b022fa6 | |||
| e330b8940a | |||
| 526e5311b9 | |||
| 3720f0fb8f | |||
| 46b108f1cc | |||
| aa59002922 | |||
| 6af125bd33 | |||
| d5d2ae63fe | |||
| 08f774e57a | |||
| 513a964732 | |||
| 8f9f5ed0ed | |||
| 001fe5931e | |||
| c7541f89da | |||
| cb0f3ce2ec | |||
| d71e58ec33 | |||
| 5aacccb03b | |||
| 8f63e40ba0 | |||
| 6c09a42545 | |||
| 1cb1387abd | |||
| fe426000d0 | |||
| 3617a61ea2 | |||
| c7b5753814 | |||
| 4ce23e4b51 | |||
| c8aeb8ec2d | |||
| 0d75bcae48 | |||
| 81c56ca4f1 | |||
| fb02917adf | |||
| 8c8f1bc048 | |||
| 94bc4ce737 | |||
| 9914085ead | |||
| b801c3ce56 | |||
| e31dbfd7d6 | |||
| d4b5303983 | |||
| ad0d0473b3 | |||
| 0db23c3e96 | |||
| 0b78450917 | |||
| 4b15b98bdd | |||
| 572157124e | |||
| d0e1d32ae9 | |||
| 33cb42a3f7 | |||
| 055109a708 | |||
| 3539288a1e | |||
| 88d80918a7 | |||
| 0897007662 | |||
| 1c4e38c867 | |||
| 78617e31bc | |||
| a377a49004 | |||
| 5756ec45ba | |||
| 23c8db09b7 | |||
| 9f8a8139e0 | |||
| d793d114d4 | |||
| 9b3edc89a6 | |||
| 58b2a4f98e | |||
| f356190b42 | |||
| 08a81b61a3 | |||
| bff47e10f4 | |||
| 6dbe86b063 | |||
| 21edc61384 | |||
| ef0022f4da | |||
| cbee5efee6 | |||
| 55da08c69e | |||
| df7c3d066a | |||
| ba627c005e | |||
| b3efe00757 | |||
| 5f8e757f57 | |||
| 5f6f09fc45 | |||
| f9e7d64131 | |||
| eefeb2ab2a | |||
| ee896846bb | |||
| ddd560b785 | |||
| 59d8beb935 | |||
| c4de03d5c4 | |||
| 53f7592815 | |||
| 86a1f43196 | |||
| 177f3b9d53 | |||
| affcb5ff4a | |||
| a168692eea | |||
| 19e6c632ea | |||
| 0d014c6257 | |||
| 11f3686832 |
+25
-49
@@ -1,67 +1,43 @@
|
||||
# :rotating_light: Contributing to Atom :rotating_light:
|
||||
# :tada: Contributing to Atom :tada:
|
||||
|
||||
These are just guidelines, not rules, use your best judgement and feel free
|
||||
to propose changes to this document in a pull request.
|
||||
|
||||
## Issues
|
||||
* Include screenshots and animated GIFs whenever possible, they are immensely
|
||||
helpful
|
||||
helpful.
|
||||
* Include the behavior you expected to happen and other places you've seen
|
||||
that behavior such as Emacs, vi, Xcode, etc.
|
||||
* Check the Console app for stack traces to include if reporting a crash
|
||||
* Check the Dev tools (`alt-cmd-i`) for errors and stack traces to include
|
||||
* Check the Console app for stack traces to include if reporting a crash.
|
||||
* Check the Dev tools (`alt-cmd-i`) for errors and stack traces to include.
|
||||
|
||||
## Code
|
||||
## Pull Requests
|
||||
* Include screenshots and animated GIFs whenever possible.
|
||||
* Follow the [JavaScript](https://github.com/styleguide/javascript) and
|
||||
[CSS](https://github.com/styleguide/css) styleguides
|
||||
* Include thoughtfully worded [Jasmine](http://pivotal.github.com/jasmine/)
|
||||
specs
|
||||
* Add 3rd-party packages as a `package.json` dependency
|
||||
* Commit messages are in the present tense
|
||||
* Commit messages that improve the format of the code start with :lipstick:
|
||||
* Commit messages that improve the performance start with :racehorse:
|
||||
* Commit messages that remove memory leaks start with :non-potable_water:
|
||||
* Commit messages that improve documentation start with :memo:
|
||||
* Files end with a newline
|
||||
* Avoid placing files in `vendor`. 3rd-party packages should be added as a
|
||||
`package.json` dependency.
|
||||
* Files end with a newline.
|
||||
* Requires should be in the following order:
|
||||
* Built in Node Modules (such as `path`)
|
||||
* Built in Atom and Atom Shell Modules (such as `atom`, `shell`)
|
||||
* Local Modules (using relative paths)
|
||||
* Class variables and methods should be in the following order:
|
||||
* Class variables (variables starting with a `@`)
|
||||
* Class methods (methods starting with a `@`)
|
||||
* Instance variables
|
||||
* Instance methods
|
||||
* Beware of platform differences
|
||||
* The home directory is `process.env.USERPROFILE` on Windows, while on OS X
|
||||
and Linux it's `process.env.HOME`
|
||||
* Path separator is `\` on Windows, and is `/` on OS X and Linux, so use
|
||||
`path.join` to concatenate filenames
|
||||
* Use `require('atom').fs.getHomeDirectory()` to get the home directory.
|
||||
* Use `path.join()` to concatenate filenames.
|
||||
* Temporary directory is not `/tmp` on Windows, use `os.tmpdir()` when
|
||||
possible
|
||||
|
||||
## Philosophy
|
||||
|
||||
### Write Beautiful Code
|
||||
Once you get something working, take the time to consider whether you can achieve it in a more elegant way. We're planning on open-sourcing Atom, so let's put our best foot forward.
|
||||
|
||||
### When in doubt, pair-up
|
||||
Pairing can be an effective and fun way to pass on culture, knowledge, and taste. If you can find the time, we encourage you to work synchronously with other community members of all experience levels to help the knowledge-mulching process. It doesn't have to be all the time; a little pairing goes a long way.
|
||||
|
||||
### Write tests, and write them first
|
||||
The test suite keeps protects our codebase from the ravages of entropy, but it only works when we have thorough coverage. Before you write implementation code, write a failing test proving that it's needed.
|
||||
|
||||
### Leave the test suite better than you found it
|
||||
Consider how the specs you are adding fit into the spec-file as a whole. Is this the right place for your spec? Does the spec need to be reorganized now that you're adding this extra dimension? Specs are only as useful as the next person's ability to understand them.
|
||||
|
||||
### Solve today's problem
|
||||
Avoid adding flexibility that isn't needed *today*. Nothing is ever set in stone, and we can always go back and add flexibility later. Adding it early just means we have to pay for complexity that we might not end up using.
|
||||
|
||||
### Favor clarity over brevity or cleverness.
|
||||
Three lines that someone else can read are better than one line that's tricky.
|
||||
|
||||
### Don't be defensive
|
||||
Only catch exceptions that are truly exceptional. Assume that components we control will honor their contracts. If they don't, the solution is to find and fix the problem in code rather than cluttering the code with attempts to foresee all potential issues at runtime.
|
||||
|
||||
### Don't be afraid to add classes and methods
|
||||
Code rarely suffers from too many methods and classes, and often suffers from too few. Err on the side of numerous short, well-named methods. Pull out classes with well-defined roles.
|
||||
|
||||
### Rip shit out
|
||||
Don't be afraid to delete code. Don't be afraid to rewrite something that needs to be refreshed. If it's in version control, we can always resurrect it.
|
||||
|
||||
### Maintain a consistent level of abstraction
|
||||
Every line in a method should read at the same basic level of abstraction. If there's a section of a method that goes into a lot more detail than the rest of the method, consider extracting a new method and giving it a clear name.
|
||||
## Git Commit Messages
|
||||
* Use the present tense
|
||||
* Reference issues and pull requests liberally
|
||||
* Consider starting the commit message with an applicable emoji:
|
||||
* :lipstick: when improving the format/structure of the code
|
||||
* :racehorse: when improving performance
|
||||
* :non-potable_water: when plugging memory leaks
|
||||
* :memo: when writing docs
|
||||
|
||||
+16
-3
@@ -8,6 +8,10 @@ _ = require 'underscore-plus'
|
||||
packageJson = require './package.json'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
if not grunt.option('verbose')
|
||||
grunt.log.writeln = (args...) -> grunt.log
|
||||
grunt.log.write = (args...) -> grunt.log
|
||||
|
||||
[major, minor, patch] = packageJson.version.split('.')
|
||||
if process.platform is 'win32'
|
||||
appName = 'Atom'
|
||||
@@ -16,6 +20,7 @@ module.exports = (grunt) ->
|
||||
buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'atom-build')
|
||||
shellAppDir = path.join(buildDir, appName)
|
||||
appDir = path.join(shellAppDir, 'resources', 'app')
|
||||
atomShellDownloadDir = path.join(os.tmpdir(), 'atom-cached-atom-shells')
|
||||
else
|
||||
appName = 'Atom.app'
|
||||
tmpDir = '/tmp'
|
||||
@@ -24,6 +29,7 @@ module.exports = (grunt) ->
|
||||
shellAppDir = path.join(buildDir, appName)
|
||||
contentsDir = path.join(shellAppDir, 'Contents')
|
||||
appDir = path.join(contentsDir, 'Resources', 'app')
|
||||
atomShellDownloadDir = '/tmp/atom-cached-atom-shells'
|
||||
|
||||
installDir = path.join(installRoot, appName)
|
||||
|
||||
@@ -164,6 +170,12 @@ module.exports = (grunt) ->
|
||||
_.extend(context, parsed.attributes)
|
||||
parsed.body
|
||||
|
||||
'download-atom-shell':
|
||||
version: packageJson.atomShellVersion
|
||||
outputDir: 'atom-shell'
|
||||
downloadDir: atomShellDownloadDir
|
||||
rebuild: true # rebuild native modules after atom-shell is updated
|
||||
|
||||
shell:
|
||||
'kill-atom':
|
||||
command: 'pkill -9 Atom'
|
||||
@@ -179,13 +191,14 @@ module.exports = (grunt) ->
|
||||
grunt.loadNpmTasks('grunt-contrib-coffee')
|
||||
grunt.loadNpmTasks('grunt-contrib-less')
|
||||
grunt.loadNpmTasks('grunt-markdown')
|
||||
grunt.loadNpmTasks('grunt-download-atom-shell')
|
||||
grunt.loadNpmTasks('grunt-shell')
|
||||
grunt.loadTasks('tasks')
|
||||
|
||||
grunt.registerTask('compile', ['coffee', 'prebuild-less', 'cson'])
|
||||
grunt.registerTask('lint', ['coffeelint', 'csslint', 'lesslint'])
|
||||
grunt.registerTask('test', ['shell:kill-atom', 'run-specs'])
|
||||
grunt.registerTask('ci', ['update-atom-shell', 'build', 'set-development-version', 'lint', 'test'])
|
||||
grunt.registerTask('deploy', ['partial-clean', 'update-atom-shell', 'build', 'codesign'])
|
||||
grunt.registerTask('ci', ['download-atom-shell', 'build', 'set-development-version', 'lint', 'test'])
|
||||
grunt.registerTask('deploy', ['partial-clean', 'download-atom-shell', 'build', 'codesign'])
|
||||
grunt.registerTask('docs', ['markdown:guides', 'build-docs'])
|
||||
grunt.registerTask('default', ['update-atom-shell', 'build', 'set-development-version', 'install'])
|
||||
grunt.registerTask('default', ['download-atom-shell', 'build', 'set-development-version', 'install'])
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
Copyright 2013 GitHub Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+6
-19
@@ -1,30 +1,17 @@
|
||||
# Atom — Futuristic Text Editing
|
||||
# Atom — The hackable, ~~collaborative~~ editor of tomorrow!
|
||||
|
||||

|
||||

|
||||
|
||||
Check out our [guides](https://www.atom.io/docs/latest/) and [API documentation](https://www.atom.io/docs/api/v34.0.0/api/)
|
||||
Check out our [guides and API documentation](https://www.atom.io/docs/latest/).
|
||||
|
||||
## Installing
|
||||
|
||||
Download the latest Atom release from [speakeasy](https://speakeasy.githubapp.com/apps/27).
|
||||
Download the latest [Atom release](https://github.com/atom/atom/releases/latest).
|
||||
|
||||
It will automatically update when a new release is available.
|
||||
Atom will automatically update when a new release is available.
|
||||
|
||||
## Building
|
||||
|
||||
### Requirements
|
||||
|
||||
* Mountain Lion
|
||||
* Looking for Windows support? Read [here][building].
|
||||
* Boxen (Obviously Atom won't release with this requirement)
|
||||
|
||||
### Installation
|
||||
|
||||
1. `gh-setup atom`
|
||||
|
||||
2. `cd ~/github/atom`
|
||||
|
||||
3. `script/build`
|
||||
|
||||
Follow the instructions in the [build docs][building].
|
||||
|
||||
[building]: https://github.com/atom/atom/blob/master/docs/building-atom.md
|
||||
|
||||
@@ -73,8 +73,8 @@ window.clickEvent = (properties={}) ->
|
||||
|
||||
window.mouseEvent = (type, properties) ->
|
||||
if properties.point
|
||||
{point, editor} = properties
|
||||
{top, left} = @pagePixelPositionForPoint(editor, point)
|
||||
{point, editorView} = properties
|
||||
{top, left} = @pagePixelPositionForPoint(editorView, point)
|
||||
properties.pageX = left + 1
|
||||
properties.pageY = top + 1
|
||||
properties.originalEvent ?= {detail: 1}
|
||||
@@ -86,14 +86,14 @@ window.mousedownEvent = (properties={}) ->
|
||||
window.mousemoveEvent = (properties={}) ->
|
||||
window.mouseEvent('mousemove', properties)
|
||||
|
||||
window.pagePixelPositionForPoint = (editor, point) ->
|
||||
window.pagePixelPositionForPoint = (editorView, point) ->
|
||||
point = Point.fromObject point
|
||||
top = editor.lines.offset().top + point.row * editor.lineHeight
|
||||
left = editor.lines.offset().left + point.column * editor.charWidth - editor.lines.scrollLeft()
|
||||
top = editorView.lines.offset().top + point.row * editorView.lineHeight
|
||||
left = editorView.lines.offset().left + point.column * editorView.charWidth - editorView.lines.scrollLeft()
|
||||
{ top, left }
|
||||
|
||||
window.setEditorWidthInChars = (editor, widthInChars, charWidth=editor.charWidth) ->
|
||||
editor.width(charWidth * widthInChars + editor.lines.position().left)
|
||||
window.seteditorViewWidthInChars = (editorView, widthInChars, charWidth=editorView.charWidth) ->
|
||||
editorView.width(charWidth * widthInChars + editorView.lines.position().left)
|
||||
|
||||
$.fn.resultOfTrigger = (type) ->
|
||||
event = $.Event(type)
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
require './benchmark-helper'
|
||||
{$, _, RootView} = require 'atom'
|
||||
{$, _, WorkspaceView} = require 'atom'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
|
||||
describe "editor.", ->
|
||||
editor = null
|
||||
describe "editorView.", ->
|
||||
editorView = null
|
||||
|
||||
beforeEach ->
|
||||
window.rootViewParentSelector = '#jasmine-content'
|
||||
window.rootView = new RootView
|
||||
window.rootView.attachToDom()
|
||||
atom.workspaceViewParentSelector = '#jasmine-content'
|
||||
atom.workspaceView = new WorkspaceView
|
||||
atom.workspaceView.attachToDom()
|
||||
|
||||
rootView.width(1024)
|
||||
rootView.height(768)
|
||||
rootView.openSync() # open blank editor
|
||||
editor = rootView.getActiveView()
|
||||
atom.workspaceView.width(1024)
|
||||
atom.workspaceView.height(768)
|
||||
atom.workspaceView.openSync()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
|
||||
afterEach ->
|
||||
if editor.pendingDisplayUpdate
|
||||
if editorView.pendingDisplayUpdate
|
||||
waitsFor "editor to finish rendering", (done) ->
|
||||
editor.on 'editor:display-updated', done
|
||||
editorView.on 'editor:display-updated', done
|
||||
|
||||
describe "keymap.", ->
|
||||
event = null
|
||||
|
||||
beforeEach ->
|
||||
event = keydownEvent('x', target: editor.hiddenInput[0])
|
||||
event = keydownEvent('x', target: editorView.hiddenInput[0])
|
||||
|
||||
benchmark "keydown-event-with-no-binding", 10, ->
|
||||
keymap.handleKeyEvent(event)
|
||||
@@ -35,90 +35,90 @@ describe "editor.", ->
|
||||
|
||||
describe "empty-file.", ->
|
||||
benchmark "insert-delete", ->
|
||||
editor.insertText('x')
|
||||
editor.backspace()
|
||||
editorView.insertText('x')
|
||||
editorView.backspace()
|
||||
|
||||
describe "300-line-file.", ->
|
||||
beforeEach ->
|
||||
rootView.openSync('medium.coffee')
|
||||
atom.workspaceView.openSync('medium.coffee')
|
||||
|
||||
describe "at-begining.", ->
|
||||
benchmark "insert-delete", ->
|
||||
editor.insertText('x')
|
||||
editor.backspace()
|
||||
editorView.insertText('x')
|
||||
editorView.backspace()
|
||||
|
||||
benchmark "insert-delete-rehighlight", ->
|
||||
editor.insertText('"')
|
||||
editor.backspace()
|
||||
editorView.insertText('"')
|
||||
editorView.backspace()
|
||||
|
||||
describe "at-end.", ->
|
||||
beforeEach ->
|
||||
editor.moveCursorToBottom()
|
||||
editorView.moveCursorToBottom()
|
||||
|
||||
benchmark "insert-delete", ->
|
||||
editor.insertText('"')
|
||||
editor.backspace()
|
||||
editorView.insertText('"')
|
||||
editorView.backspace()
|
||||
|
||||
describe "empty-vs-set-innerHTML.", ->
|
||||
[firstRow, lastRow] = []
|
||||
beforeEach ->
|
||||
firstRow = editor.getFirstVisibleScreenRow()
|
||||
lastRow = editor.getLastVisibleScreenRow()
|
||||
firstRow = editorView.getFirstVisibleScreenRow()
|
||||
lastRow = editorView.getLastVisibleScreenRow()
|
||||
|
||||
benchmark "build-gutter-html.", 1000, ->
|
||||
editor.gutter.renderLineNumbers(null, firstRow, lastRow)
|
||||
editorView.gutter.renderLineNumbers(null, firstRow, lastRow)
|
||||
|
||||
benchmark "set-innerHTML.", 1000, ->
|
||||
editor.gutter.renderLineNumbers(null, firstRow, lastRow)
|
||||
editor.gutter.lineNumbers[0].innerHtml = ''
|
||||
editorView.gutter.renderLineNumbers(null, firstRow, lastRow)
|
||||
editorView.gutter.lineNumbers[0].innerHtml = ''
|
||||
|
||||
benchmark "empty.", 1000, ->
|
||||
editor.gutter.renderLineNumbers(null, firstRow, lastRow)
|
||||
editor.gutter.lineNumbers.empty()
|
||||
editorView.gutter.renderLineNumbers(null, firstRow, lastRow)
|
||||
editorView.gutter.lineNumbers.empty()
|
||||
|
||||
describe "positionLeftForLineAndColumn.", ->
|
||||
line = null
|
||||
beforeEach ->
|
||||
editor.scrollTop(2000)
|
||||
editor.resetDisplay()
|
||||
line = editor.lineElementForScreenRow(106)[0]
|
||||
editorView.scrollTop(2000)
|
||||
editorView.resetDisplay()
|
||||
line = editorView.lineElementForScreenRow(106)[0]
|
||||
|
||||
describe "one-line.", ->
|
||||
beforeEach ->
|
||||
editor.clearCharacterWidthCache()
|
||||
editorView.clearCharacterWidthCache()
|
||||
|
||||
benchmark "uncached", 5000, ->
|
||||
editor.positionLeftForLineAndColumn(line, 106, 82)
|
||||
editor.clearCharacterWidthCache()
|
||||
editorView.positionLeftForLineAndColumn(line, 106, 82)
|
||||
editorView.clearCharacterWidthCache()
|
||||
|
||||
benchmark "cached", 5000, ->
|
||||
editor.positionLeftForLineAndColumn(line, 106, 82)
|
||||
editorView.positionLeftForLineAndColumn(line, 106, 82)
|
||||
|
||||
describe "multiple-lines.", ->
|
||||
[firstRow, lastRow] = []
|
||||
beforeEach ->
|
||||
firstRow = editor.getFirstVisibleScreenRow()
|
||||
lastRow = editor.getLastVisibleScreenRow()
|
||||
firstRow = editorView.getFirstVisibleScreenRow()
|
||||
lastRow = editorView.getLastVisibleScreenRow()
|
||||
|
||||
benchmark "cache-entire-visible-area", 100, ->
|
||||
for i in [firstRow..lastRow]
|
||||
line = editor.lineElementForScreenRow(i)[0]
|
||||
editor.positionLeftForLineAndColumn(line, i, Math.max(0, editor.lineLengthForBufferRow(i)))
|
||||
line = editorView.lineElementForScreenRow(i)[0]
|
||||
editorView.positionLeftForLineAndColumn(line, i, Math.max(0, editorView.lineLengthForBufferRow(i)))
|
||||
|
||||
describe "text-rendering.", ->
|
||||
beforeEach ->
|
||||
editor.scrollTop(2000)
|
||||
editorView.scrollTop(2000)
|
||||
|
||||
benchmark "resetDisplay", 50, ->
|
||||
editor.resetDisplay()
|
||||
editorView.resetDisplay()
|
||||
|
||||
benchmark "htmlForScreenRows", 1000, ->
|
||||
lastRow = editor.getLastScreenRow()
|
||||
editor.htmlForScreenRows(0, lastRow)
|
||||
lastRow = editorView.getLastScreenRow()
|
||||
editorView.htmlForScreenRows(0, lastRow)
|
||||
|
||||
benchmark "htmlForScreenRows.htmlParsing", 50, ->
|
||||
lastRow = editor.getLastScreenRow()
|
||||
html = editor.htmlForScreenRows(0, lastRow)
|
||||
lastRow = editorView.getLastScreenRow()
|
||||
html = editorView.htmlForScreenRows(0, lastRow)
|
||||
|
||||
div = document.createElement('div')
|
||||
div.innerHTML = html
|
||||
@@ -126,44 +126,44 @@ describe "editor.", ->
|
||||
describe "gutter-api.", ->
|
||||
describe "getLineNumberElementsForClass.", ->
|
||||
beforeEach ->
|
||||
editor.gutter.addClassToLine(20, 'omgwow')
|
||||
editor.gutter.addClassToLine(40, 'omgwow')
|
||||
editorView.gutter.addClassToLine(20, 'omgwow')
|
||||
editorView.gutter.addClassToLine(40, 'omgwow')
|
||||
|
||||
benchmark "DOM", 20000, ->
|
||||
editor.gutter.getLineNumberElementsForClass('omgwow')
|
||||
editorView.gutter.getLineNumberElementsForClass('omgwow')
|
||||
|
||||
benchmark "getLineNumberElement.DOM", 20000, ->
|
||||
editor.gutter.getLineNumberElement(12)
|
||||
editorView.gutter.getLineNumberElement(12)
|
||||
|
||||
benchmark "toggle-class", 2000, ->
|
||||
editor.gutter.addClassToLine(40, 'omgwow')
|
||||
editor.gutter.removeClassFromLine(40, 'omgwow')
|
||||
editorView.gutter.addClassToLine(40, 'omgwow')
|
||||
editorView.gutter.removeClassFromLine(40, 'omgwow')
|
||||
|
||||
describe "find-then-unset.", ->
|
||||
classes = ['one', 'two', 'three', 'four']
|
||||
|
||||
benchmark "single-class", 200, ->
|
||||
editor.gutter.addClassToLine(30, 'omgwow')
|
||||
editor.gutter.addClassToLine(40, 'omgwow')
|
||||
editor.gutter.removeClassFromAllLines('omgwow')
|
||||
editorView.gutter.addClassToLine(30, 'omgwow')
|
||||
editorView.gutter.addClassToLine(40, 'omgwow')
|
||||
editorView.gutter.removeClassFromAllLines('omgwow')
|
||||
|
||||
benchmark "multiple-class", 200, ->
|
||||
editor.gutter.addClassToLine(30, 'one')
|
||||
editor.gutter.addClassToLine(30, 'two')
|
||||
editorView.gutter.addClassToLine(30, 'one')
|
||||
editorView.gutter.addClassToLine(30, 'two')
|
||||
|
||||
editor.gutter.addClassToLine(40, 'two')
|
||||
editor.gutter.addClassToLine(40, 'three')
|
||||
editor.gutter.addClassToLine(40, 'four')
|
||||
editorView.gutter.addClassToLine(40, 'two')
|
||||
editorView.gutter.addClassToLine(40, 'three')
|
||||
editorView.gutter.addClassToLine(40, 'four')
|
||||
|
||||
for klass in classes
|
||||
editor.gutter.removeClassFromAllLines(klass)
|
||||
editorView.gutter.removeClassFromAllLines(klass)
|
||||
|
||||
describe "line-htmlification.", ->
|
||||
div = null
|
||||
html = null
|
||||
beforeEach ->
|
||||
lastRow = editor.getLastScreenRow()
|
||||
html = editor.htmlForScreenRows(0, lastRow)
|
||||
lastRow = editorView.getLastScreenRow()
|
||||
html = editorView.htmlForScreenRows(0, lastRow)
|
||||
div = document.createElement('div')
|
||||
|
||||
benchmark "setInnerHTML", 1, ->
|
||||
@@ -171,47 +171,47 @@ describe "editor.", ->
|
||||
|
||||
describe "9000-line-file.", ->
|
||||
benchmark "opening.", 5, ->
|
||||
rootView.openSync('huge.js')
|
||||
atom.workspaceView.openSync('huge.js')
|
||||
|
||||
describe "after-opening.", ->
|
||||
beforeEach ->
|
||||
rootView.openSync('huge.js')
|
||||
atom.workspaceView.openSync('huge.js')
|
||||
|
||||
benchmark "moving-to-eof.", 1, ->
|
||||
editor.moveCursorToBottom()
|
||||
editorView.moveCursorToBottom()
|
||||
|
||||
describe "on-first-line.", ->
|
||||
benchmark "inserting-newline", 5, ->
|
||||
editor.insertNewline()
|
||||
editorView.insertNewline()
|
||||
|
||||
describe "on-last-visible-line.", ->
|
||||
beforeEach ->
|
||||
editor.setCursorScreenPosition([editor.getLastVisibleScreenRow(), 0])
|
||||
editorView.setCursorScreenPosition([editorView.getLastVisibleScreenRow(), 0])
|
||||
|
||||
benchmark "move-down-and-scroll", 300, ->
|
||||
editor.trigger 'move-down'
|
||||
editorView.trigger 'move-down'
|
||||
|
||||
describe "at-eof.", ->
|
||||
endPosition = null
|
||||
|
||||
beforeEach ->
|
||||
editor.moveCursorToBottom()
|
||||
endPosition = editor.getCursorScreenPosition()
|
||||
editorView.moveCursorToBottom()
|
||||
endPosition = editorView.getCursorScreenPosition()
|
||||
|
||||
benchmark "move-to-beginning-of-word", ->
|
||||
editor.moveCursorToBeginningOfWord()
|
||||
editor.setCursorScreenPosition(endPosition)
|
||||
editorView.moveCursorToBeginningOfWord()
|
||||
editorView.setCursorScreenPosition(endPosition)
|
||||
|
||||
benchmark "insert", ->
|
||||
editor.insertText('x')
|
||||
editorView.insertText('x')
|
||||
|
||||
describe "TokenizedBuffer.", ->
|
||||
describe "coffee-script-grammar.", ->
|
||||
[languageMode, buffer] = []
|
||||
|
||||
beforeEach ->
|
||||
editSession = benchmarkFixturesProject.openSync('medium.coffee')
|
||||
{ languageMode, buffer } = editSession
|
||||
editor = benchmarkFixturesProject.openSync('medium.coffee')
|
||||
{ languageMode, buffer } = editor
|
||||
|
||||
benchmark "construction", 20, ->
|
||||
new TokenizedBuffer(buffer, { languageMode, tabLength: 2})
|
||||
|
||||
@@ -6,17 +6,17 @@ atom][download].
|
||||
|
||||
## OSX
|
||||
|
||||
* Use Mountain Lion
|
||||
* Use OS X 10.8 or later
|
||||
* Install the latest node 0.10.x release (32bit preferable)
|
||||
* Clone [atom][atom-git] to `~/github/atom`
|
||||
* Run `~/github/atom/script/bootstrap`
|
||||
* Run `~/github/atom/script/build`
|
||||
|
||||
## Windows
|
||||
|
||||
* Install [Visual C++ 2010 Express][win-vs2010]
|
||||
* Install the [latest 32bit Node 0.10.x][win-node]
|
||||
* Install the [latest Python 2.7.x][win-python]
|
||||
* Install [Github for Windows][win-github]
|
||||
* Install [GitHub for Windows][win-github]
|
||||
* Clone [atom/atom][atom-git] to `C:\Users\<user>\github\atom\`
|
||||
* Add `C:\Python27;C:\Program Files\nodejs;C:\Users\<user>\github\atom\node_modules\`
|
||||
to your PATH
|
||||
@@ -24,9 +24,9 @@ atom][download].
|
||||
find-generic-password -ws 'GitHub API Token'` on OSX to get your
|
||||
credentials).
|
||||
* Use the Windows GitHub shell and cd into `C:\Users\<user>\github\atom`
|
||||
* Run `node script/bootstrap`
|
||||
* Run `script\bootstrap`
|
||||
|
||||
[download]: http://www.atom.io
|
||||
[download]: https://github.com/atom/atom/releases/latest
|
||||
[win-node]: http://nodejs.org/download/
|
||||
[win-python]: http://www.python.org/download/
|
||||
[win-github]: http://windows.github.com/
|
||||
|
||||
@@ -67,11 +67,11 @@ object.
|
||||
|
||||
Your package's top-level module should implement the following methods:
|
||||
|
||||
- `activate(rootView, state)`: This **required** method is called when your
|
||||
package is loaded. It is always passed the window's global `rootView`, and is
|
||||
sometimes passed state data if the window has been reloaded and your module
|
||||
implements the `serialize` method. Use this to do initialization work when your
|
||||
package is started (like setting up DOM elements or binding events).
|
||||
- `activate(state)`: This **required** method is called when your
|
||||
package is activated. It is passed the state data from the last time the window
|
||||
was serialized if your module implements the `serialize()` method. Use this to
|
||||
do initialization work when your package is started (like setting up DOM
|
||||
elements or binding events).
|
||||
|
||||
- `serialize()`: This **optional** method is called when the window is shutting
|
||||
down, allowing you to return JSON to represent the state of your component. When
|
||||
@@ -104,7 +104,7 @@ module.exports = require "./lib/my-package"
|
||||
`my-package/my-package.coffee` might start:
|
||||
```coffeescript
|
||||
module.exports =
|
||||
activate: (rootView, state) -> # ...
|
||||
activate: (state) -> # ...
|
||||
deactivate: -> # ...
|
||||
serialize: -> # ...
|
||||
```
|
||||
@@ -126,7 +126,7 @@ is recommended).
|
||||
Ideally, you won't need much in the way of styling. We've provided a standard
|
||||
set of components which define both the colors and UI elements for any package
|
||||
that fits into Atom seamlessly. You can view all of Atom's UI components by opening
|
||||
the styleguide: open the command palette (`cmd-p`) and search for _styleguide_,
|
||||
the styleguide: open the command palette (`cmd-shift-P`) and search for _styleguide_,
|
||||
or just type `cmd-ctrl-G`.
|
||||
|
||||
If you _do_ need special styling, try to keep only structural styles in the package
|
||||
@@ -206,7 +206,7 @@ specific parts of the interface, like adding a file in the tree-view:
|
||||
'context-menu':
|
||||
'.tree-view':
|
||||
'Add file': 'tree-view:add-file'
|
||||
'#root-view':
|
||||
'.workspace':
|
||||
'Inspect Element': 'core:inspect'
|
||||
```
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ target elements which are outside of the editor.
|
||||
|
||||
Let's create your first theme.
|
||||
|
||||
To get started, hit `cmd-p`, and start typing "Generate Theme" to generate
|
||||
To get started, hit `cmd-shift-P`, and start typing "Generate Theme" to generate
|
||||
a package. Select "Generate Theme," and you'll be asked for a theme name. Let's
|
||||
call ours _motif_.
|
||||
|
||||
@@ -114,7 +114,7 @@ If you are creating an interface theme, you'll want a way to see how your theme
|
||||
changes affect all the components in the system. The [styleguide] is a page with
|
||||
every component Atom supports rendered.
|
||||
|
||||
To open the styleguide, open the command palette (`cmd-p`) and search for
|
||||
To open the styleguide, open the command palette (`cmd-shift-P`) and search for
|
||||
_styleguide_, or use the shortcut `cmd-ctrl-shift-g`.
|
||||
|
||||
![styleguide-img]
|
||||
|
||||
+38
-39
@@ -6,13 +6,12 @@ productive as quickly as possible. There are also guides which cover
|
||||
|
||||
## The Command Palette
|
||||
|
||||
If there's one key-command you must remember in Atom, it should be `cmd-p`. You
|
||||
can always hit `cmd-p` to bring up a list of commands that are relevant to the
|
||||
currently focused interface element. If there is a key binding for a given
|
||||
command, it is also displayed. This is a great way to explore the system and get
|
||||
to know the key commands interactively. If you'd like to learn about adding or
|
||||
changing a binding for a command, refer to the [key bindings][key-bindings]
|
||||
section below.
|
||||
If there's one key-command you remember in Atom, it should be `cmd-shift-P`. You
|
||||
can always press `cmd-shift-P` to bring up a list of commands (and key bindings)
|
||||
that are relevant to the currently focused interface element. This is a great
|
||||
way to explore the system and learn key bindings interactively. For information
|
||||
about adding or changing a key binding refer to the [customizing key
|
||||
bindings][key-bindings] section.
|
||||
|
||||
![Command Palette]
|
||||
|
||||
@@ -20,24 +19,24 @@ section below.
|
||||
|
||||
### Working With Files
|
||||
|
||||
Atom windows are scoped to the directory in which they're opened from. So if you
|
||||
launch Atom from the command line, everything will be relative to the current
|
||||
directory. This means that the tree view on the left will only show files
|
||||
contained within that directory.
|
||||
Atom windows are scoped to the directory they're opened from. If you launch Atom
|
||||
from the command line everything will be relative to the current directory. This
|
||||
means that the tree view on the left will only show files contained within that
|
||||
directory.
|
||||
|
||||
This can be a useful way to organize multiple projects, as each project will be
|
||||
contained within it's own window and it's state will be unique to that window.
|
||||
contained within its own window.
|
||||
|
||||
#### Finding Files
|
||||
|
||||
The fastest way to find a file in your project is to use the fuzzy finder. Just
|
||||
hit `cmd-t` and start typing the name of the file you're looking for. If you
|
||||
already have the file open as a tab and want to jump to it, hit `cmd-b` to bring
|
||||
up a searchable list of open buffers.
|
||||
The fastest way to find a file is to use the fuzzy finder. Press `cmd-t` and
|
||||
begin typing the name of the file you're looking for. If you are looking for a
|
||||
file that is already open press `cmd-b` to bring up a searchable list of open
|
||||
files.
|
||||
|
||||
You can also use the tree view to navigate to a file. To open or move focus to
|
||||
the tree view, hit `cmd-\`. You can then navigate to a file and select it with
|
||||
`return`.
|
||||
the tree view, press `cmd-\`. You can then navigate to a file using the arrow
|
||||
keys and select it with `return`.
|
||||
|
||||
#### Adding, Moving, Deleting Files
|
||||
|
||||
@@ -46,50 +45,50 @@ select a directory in the tree view and press `a`. Then type the name of the
|
||||
file. Any intermediate directories you type will be created automatically if
|
||||
needed.
|
||||
|
||||
To move or rename a file or directory, select it in the tree view and hit `m`.
|
||||
To delete a file, select it in the tree view and hit `delete`.
|
||||
To move or rename a file or directory, select it in the tree view and press `m`.
|
||||
|
||||
To delete a file, select it in the tree view and press `delete`.
|
||||
|
||||
### Searching
|
||||
|
||||
#### Find and Replace
|
||||
|
||||
To search within a buffer use `cmd-f`. To search the entire project use
|
||||
`cmd-shift-f`. To find and replace within the current buffer use `cmd-alt-f`.
|
||||
`cmd-shift-f`.
|
||||
|
||||
#### Navigating By Symbols
|
||||
|
||||
If you want to jump to a method, the `cmd-j` binding opens a list of all symbols
|
||||
in the current file. `cmd-.` jumps to the tag for the word currently under the
|
||||
cursor.
|
||||
If you want to jump to a method press `cmd-r`. It opens a list of all symbols
|
||||
in the current file.
|
||||
|
||||
To search for symbols across your project use `cmd-shift-j`, but you'll need to
|
||||
make sure you have a tags file generated for the project Also, if you're editing
|
||||
CoffeeScript, it's a good idea to update your `~/.ctags` file to understand the
|
||||
language. Here is [a good example][ctags].
|
||||
To search for symbols across your project use `cmd-shift-r`, but you'll need to
|
||||
make sure you have a ctags installed and a tags file generated for your project.
|
||||
Also, if you're editing CoffeeScript, it's a good idea to update your `~/.ctags`
|
||||
file to understand the language. Here is [a good example][ctags].
|
||||
|
||||
### Split Panes
|
||||
|
||||
You can split any editor pane horizontally or vertically by using `ctrl-w s` or
|
||||
`ctrl-w v`. Once you have a split pane, you can move focus between them with
|
||||
`ctrl-tab` or `ctrl-w w`. To close a pane, close all tabs inside it.
|
||||
You can split any editor pane horizontally or vertically by using `cmd-k right` or
|
||||
`cmd-k down`. Once you have a split pane, you can move focus between them with
|
||||
`cmd-k cmd-right` or `cmd-k cmd-down`. To close a pane, close all tabs inside it.
|
||||
|
||||
### Folding
|
||||
|
||||
You can fold everything with `ctrl-{` and unfold everything with
|
||||
`ctrl-}`. Or, you can fold / unfold by a single level with `ctrl-[` and
|
||||
`ctrl-]`.
|
||||
You can fold everything with `alt-cmd-{` and unfold everything with
|
||||
`alt-cmd-}`. Or, you can fold / unfold by a single level with `alt-cmd-[` and
|
||||
`alt-cmd-]`.
|
||||
|
||||
### Soft-Wrap
|
||||
|
||||
If you want to toggle soft wrap, trigger the command from the command palette.
|
||||
Hit `cmd-p` to open the palette, then type "wrap" to find the correct
|
||||
Press `cmd-shift-P` to open the palette, then type "wrap" to find the correct
|
||||
command.
|
||||
|
||||
## Configuration
|
||||
|
||||
If you press `cmd-,`, a configuration panel will appear in the currently focused
|
||||
pane. This serves as the primary interface for adjusting settings, installing
|
||||
packages and changing themes.
|
||||
Press `cmd-,` to display the a settings pane. This serves as the primary
|
||||
interface for adjusting config settings, installing packages and changing
|
||||
themes.
|
||||
|
||||
For more advanced configuration see the [customization guide][customization].
|
||||
|
||||
@@ -97,6 +96,6 @@ For more advanced configuration see the [customization guide][customization].
|
||||
[theming]: creating-a-theme.md
|
||||
[extending]: creating-a-package.md
|
||||
[customization]: customizing-atom.md
|
||||
[key-bindings]: #customizing-key-bindings
|
||||
[key-bindings]: customizing-atom.md#customizing-key-bindings
|
||||
[command palette]: https://f.cloud.github.com/assets/1424/1091618/ee7c3554-166a-11e3-9955-aaa61bb5509c.png
|
||||
[ctags]: https://github.com/kevinsawicki/dotfiles/blob/master/.ctags
|
||||
|
||||
@@ -23,7 +23,7 @@ Note that the last example describes multiple keystrokes in succession:
|
||||
|
||||
A semantic event is the name of the custom event that will be triggered on the
|
||||
target of the keydown event when a key binding matches. You can use the command
|
||||
palette (bound to `cmd-p`), to get a list of relevant events and their bindings
|
||||
palette (bound to `cmd-shift-P`), to get a list of relevant events and their bindings
|
||||
in any focused context in Atom.
|
||||
|
||||
### Rules for Mapping A Keydown Event to A Semantic Event
|
||||
|
||||
@@ -8,11 +8,11 @@ view objects inherit from the jQuery prototype, and wrap DOM nodes
|
||||
View objects are actually jQuery wrappers around DOM fragments, supporting all
|
||||
the typical jQuery traversal and manipulation methods. In addition, view objects
|
||||
have methods that are view-specific. For example, you could call both general
|
||||
and view-specific on the global `rootView` instance:
|
||||
and view-specific on the global `atom.workspaceView` instance:
|
||||
|
||||
```coffeescript
|
||||
rootView.find('.editor.active') # standard jQuery method
|
||||
rootView.getActiveEditor() # view-specific method
|
||||
atom.workspaceView.find('.editor.active') # standard jQuery method
|
||||
atom.workspaceView.getActiveEditor() # view-specific method
|
||||
```
|
||||
|
||||
If you retrieve a jQuery wrapper for an element associated with a view, use the
|
||||
@@ -20,7 +20,7 @@ If you retrieve a jQuery wrapper for an element associated with a view, use the
|
||||
|
||||
```coffeescript
|
||||
# this is a plain jQuery object; you can't call view-specific methods
|
||||
editorElement = rootView.find('.editor.active')
|
||||
editorElement = atom.workspaceView.find('.editor.active')
|
||||
|
||||
# get the view object by calling `.view()` to call view-specific methods
|
||||
editorView = editorElement.view()
|
||||
@@ -29,18 +29,18 @@ editorView.setCursorBufferPosition([1, 2])
|
||||
|
||||
Refer to the [SpacePen] documentation for more details.
|
||||
|
||||
### RootView
|
||||
### WorkspaceView
|
||||
|
||||
The root of Atom's view hierarchy is a global called `rootView`, which is a
|
||||
singleton instance of the `RootView` view class. The root view fills the entire
|
||||
The root of Atom's view hierarchy is a global called `atom.workspaceView`, which is a
|
||||
singleton instance of the `WorkspaceView` view class. The root view fills the entire
|
||||
window, and contains every other view. If you open Atom's inspector with
|
||||
`alt-cmd-i`, you can see the internal structure of `RootView`:
|
||||
`alt-cmd-i`, you can see the internal structure of `WorkspaceView`:
|
||||
|
||||
![RootView in the inspector][rootview-inspector]
|
||||
![WorkspaceView in the inspector][workspaceview-inspector]
|
||||
|
||||
#### Panes
|
||||
|
||||
The `RootView` contains a `#horizontal` and a `#vertical` axis surrounding
|
||||
The `WorkspaceView` contains a `#horizontal` and a `#vertical` axis surrounding
|
||||
`#panes`. Elements in the horizontal axis will tile across the window
|
||||
horizontally, appearing to have a vertical orientation. Items in the vertical
|
||||
axis will tile across the window vertically, appearing to have a horizontal
|
||||
@@ -55,11 +55,11 @@ outlets as follows:
|
||||
|
||||
```coffeescript
|
||||
# place a view to the left of the panes (or use .append() to place it to the right)
|
||||
rootView.horizontal.prepend(new MyView)
|
||||
atom.workspaceView.horizontal.prepend(new MyView)
|
||||
|
||||
# place a view below the panes (or use .prepend() to place it above)
|
||||
rootView.vertical.append(new MyOtherView)
|
||||
atom.workspaceView.vertical.append(new MyOtherView)
|
||||
```
|
||||
|
||||
[spacepen]: http://github.com/nathansobo/space-pen
|
||||
[rootview-inspector]: https://f.cloud.github.com/assets/1424/1091631/1932c2d6-166b-11e3-8adf-9690fe82d3b8.png
|
||||
[workspaceView-inspector]: https://f.cloud.github.com/assets/1424/1091631/1932c2d6-166b-11e3-8adf-9690fe82d3b8.png
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
Let's take a look at creating your first package.
|
||||
|
||||
To get started, hit `cmd-p`, and start typing "Generate Package" to generate
|
||||
To get started, hit `cmd-shift-P`, and start typing "Generate Package" to generate
|
||||
a package. Once you select the "Generate Package" command, it'll ask you for a
|
||||
name for your new package. Let's call ours _changer_.
|
||||
|
||||
Atom will pop open a new window, showing the _changer_ package with a default set of
|
||||
folders and files created for us. Hit `cmd-p` and start typing "Changer." You'll
|
||||
folders and files created for us. Hit `cmd-shift-P` and start typing "Changer." You'll
|
||||
see a new `Changer:Toggle` command which, if selected, pops up a greeting. So far,
|
||||
so good!
|
||||
|
||||
@@ -44,12 +44,12 @@ you can map to `body` if you want to scope to anywhere in Atom, or just `.editor
|
||||
for the editor portion.
|
||||
|
||||
To bind keybindings to a command, we'll need to do a bit of association in our
|
||||
CoffeeScript code using the `rootView.command` method. This method takes a command
|
||||
CoffeeScript code using the `atom.workspaceView.command` method. This method takes a command
|
||||
name and executes a callback function. Open up _lib/changer-view.coffee_, and
|
||||
change `rootView.command "changer:toggle"` to look like this:
|
||||
change `atom.workspaceView.command "changer:toggle"` to look like this:
|
||||
|
||||
```coffeescript
|
||||
rootView.command "changer:magic", => @magic()
|
||||
atom.workspaceView.command "changer:magic", => @magic()
|
||||
```
|
||||
|
||||
It's common practice to namespace your commands with your package name, separated
|
||||
@@ -180,7 +180,7 @@ ul.modified-files-list {
|
||||
We'll add one more line to the end of the `magic` method to make this pane appear:
|
||||
|
||||
```coffeescript
|
||||
rootView.vertical.append(this)
|
||||
atom.workspaceView.vertical.append(this)
|
||||
```
|
||||
|
||||
If you refresh Atom and hit the key command, you'll see a box appear right underneath
|
||||
@@ -188,21 +188,21 @@ the editor:
|
||||
|
||||
![Changer_Panel_Append]
|
||||
|
||||
As you might have guessed, `rootView.vertical.append` tells Atom to append `this`
|
||||
As you might have guessed, `atom.workspaceView.vertical.append` tells Atom to append `this`
|
||||
item (_i.e._, whatever is defined by`@content`) _vertically_ to the editor. If
|
||||
we had called `rootView.horizontal.append`, the pane would be attached to the
|
||||
we had called `atom.workspaceView.horizontal.append`, the pane would be attached to the
|
||||
right-hand side of the editor.
|
||||
|
||||
Before we populate this panel for real, let's apply some logic to toggle the pane
|
||||
off and on, just like we did with the tree view. Replace the `rootView.vertical.append`
|
||||
off and on, just like we did with the tree view. Replace the `atom.workspaceView.vertical.append`
|
||||
call with this code:
|
||||
|
||||
```coffeescript
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
rootView.vertical.children().last().remove()
|
||||
atom.workspaceView.vertical.children().last().remove()
|
||||
else
|
||||
rootView.vertical.append(this)
|
||||
atom.workspaceView.vertical.append(this)
|
||||
```
|
||||
|
||||
There are about a hundred different ways to toggle a pane on and off, and this
|
||||
@@ -245,7 +245,7 @@ $('ol.entries li.file.status-modified span.name').each (i, el) ->
|
||||
parents.each (i, el) ->
|
||||
filePath.unshift($(el).find('div.header span.name').eq(0).text())
|
||||
|
||||
modifiedFilePath = path.join(project.rootDirectory.path, filePath.join(path.sep))
|
||||
modifiedFilePath = path.join(atom.project.rootDirectory.path, filePath.join(path.sep))
|
||||
modifiedFiles.push modifiedFilePath
|
||||
```
|
||||
|
||||
@@ -261,13 +261,13 @@ appending it to `modifiedFilesList`:
|
||||
```coffeescript
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
rootView.vertical.children().last().remove()
|
||||
atom.workspaceView.vertical.children().last().remove()
|
||||
else
|
||||
for file in modifiedFiles
|
||||
stat = fs.lstatSync(file)
|
||||
mtime = stat.mtime
|
||||
@modifiedFilesList.append("<li>#{file} - Modified at #{mtime}")
|
||||
rootView.vertical.append(this)
|
||||
atom.workspaceView.vertical.append(this)
|
||||
```
|
||||
|
||||
When you toggle the modified files list, your pane is now populated with the
|
||||
@@ -283,13 +283,13 @@ this demonstration, we'll just clear the `modifiedFilesList` each time it's clos
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
@modifiedFilesList.empty() # added this to clear the list on close
|
||||
rootView.vertical.children().last().remove()
|
||||
atom.workspaceView.vertical.children().last().remove()
|
||||
else
|
||||
for file in modifiedFiles
|
||||
stat = fs.lstatSync(file)
|
||||
mtime = stat.mtime
|
||||
@modifiedFilesList.append("<li>#{file} - Modified at #{mtime}")
|
||||
rootView.vertical.append(this)
|
||||
atom.workspaceView.vertical.append(this)
|
||||
```
|
||||
|
||||
## Coloring UI Elements
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{Document, Point, Range, Site} = require 'telepath'
|
||||
{Document, Point, Range} = require 'telepath'
|
||||
|
||||
module.exports =
|
||||
_: require 'underscore-plus'
|
||||
@@ -11,7 +11,6 @@ module.exports =
|
||||
Git: require '../src/git'
|
||||
Point: Point
|
||||
Range: Range
|
||||
Site: Site
|
||||
|
||||
# The following classes can't be used from a Task handler and should therefore
|
||||
# only be exported when not running as a child node process
|
||||
@@ -21,9 +20,8 @@ unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
|
||||
module.exports.$ = $
|
||||
module.exports.$$ = $$
|
||||
module.exports.$$$ = $$$
|
||||
module.exports.Editor = require '../src/editor'
|
||||
module.exports.pathForRepositoryUrl = require('../src/project').pathForRepositoryUrl
|
||||
module.exports.RootView = require '../src/root-view'
|
||||
module.exports.EditorView = require '../src/editor-view'
|
||||
module.exports.WorkspaceView = require '../src/workspace-view'
|
||||
module.exports.SelectList = require '../src/select-list'
|
||||
module.exports.ScrollView = require '../src/scroll-view'
|
||||
module.exports.Task = require '../src/task'
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
'.editor':
|
||||
# Platform Bindings
|
||||
'alt-left': 'editor:move-to-previous-word-boundary'
|
||||
'alt-right': 'editor:move-to-next-word-boundary'
|
||||
'alt-shift-left': 'editor:select-to-previous-word-boundary'
|
||||
'alt-shift-right': 'editor:select-to-next-word-boundary'
|
||||
'home': 'editor:move-to-first-character-of-line'
|
||||
'end': 'editor:move-to-end-of-line'
|
||||
'shift-home': 'editor:select-to-first-character-of-line'
|
||||
'shift-end': 'editor:select-to-end-of-line'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-t': 'editor:transpose'
|
||||
|
||||
'.editor:not(.mini)':
|
||||
# Atom Specific
|
||||
'ctrl-C': 'editor:copy-path'
|
||||
|
||||
# Sublime Parity
|
||||
'tab': 'editor:indent'
|
||||
'enter': 'editor:newline'
|
||||
'shift-tab': 'editor:outdent-selected-rows'
|
||||
'ctrl-K': 'editor:delete-line'
|
||||
'ctrl-shift-up': 'editor:add-selection-above'
|
||||
'ctrl-shift-down': 'editor:add-selection-below'
|
||||
|
||||
'.tool-panel':
|
||||
'escape': 'core:close'
|
||||
|
||||
'.tool-panel.panel-left, .tool-panel.panel-right':
|
||||
'escape': 'tool-panel:unfocus'
|
||||
|
||||
'.editor !important, .editor.mini !important':
|
||||
'escape': 'editor:consolidate-selections'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
'body .native-key-bindings':
|
||||
'tab': 'core:focus-next'
|
||||
'shift-tab': 'core:focus-previous'
|
||||
'enter': 'native!'
|
||||
'backspace': 'native!'
|
||||
'shift-backspace': 'native!'
|
||||
'delete': 'native!'
|
||||
'left': 'native!'
|
||||
'right': 'native!'
|
||||
'shift-left': 'native!'
|
||||
'shift-right': 'native!'
|
||||
'alt-left': 'native!'
|
||||
'alt-right': 'native!'
|
||||
'alt-shift-left': 'native!'
|
||||
'alt-shift-right': 'native!'
|
||||
'ctrl-b': 'native!'
|
||||
'ctrl-f': 'native!'
|
||||
'ctrl-F': 'native!'
|
||||
'ctrl-B': 'native!'
|
||||
'ctrl-h': 'native!'
|
||||
'ctrl-d': 'native!'
|
||||
@@ -0,0 +1,154 @@
|
||||
'.platform-darwin':
|
||||
# Apple specific
|
||||
'cmd-q': 'application:quit'
|
||||
'cmd-h': 'application:hide'
|
||||
'cmd-H': 'application:hide-other-applications'
|
||||
'cmd-m': 'application:minimize'
|
||||
'alt-cmd-ctrl-m': 'application:zoom'
|
||||
|
||||
'ctrl-p': 'core:move-up'
|
||||
'ctrl-n': 'core:move-down'
|
||||
'ctrl-b': 'core:move-left'
|
||||
'ctrl-f': 'core:move-right'
|
||||
'ctrl-P': 'core:select-up'
|
||||
'ctrl-N': 'core:select-down'
|
||||
'ctrl-F': 'core:select-right'
|
||||
'ctrl-B': 'core:select-left'
|
||||
'ctrl-h': 'core:backspace'
|
||||
'ctrl-d': 'core:delete'
|
||||
|
||||
# Atom Specific
|
||||
'cmd-O': 'application:open-dev'
|
||||
'cmd-alt-ctrl-s': 'application:run-all-specs'
|
||||
'enter': 'core:confirm'
|
||||
'escape': 'core:cancel'
|
||||
'up': 'core:move-up'
|
||||
'down': 'core:move-down'
|
||||
'left': 'core:move-left'
|
||||
'right': 'core:move-right'
|
||||
'ctrl-alt-cmd-r': 'window:reload'
|
||||
'alt-cmd-i': 'window:toggle-dev-tools'
|
||||
'cmd-alt-ctrl-p': 'window:run-package-specs'
|
||||
|
||||
# Sublime Parity
|
||||
'cmd-,': 'application:show-settings'
|
||||
'cmd-N': 'application:new-window'
|
||||
'cmd-W': 'window:close'
|
||||
'cmd-o': 'application:open'
|
||||
'cmd-T': 'pane:reopen-closed-item'
|
||||
'cmd-n': 'application:new-file'
|
||||
'cmd-s': 'core:save'
|
||||
'cmd-S': 'core:save-as'
|
||||
'cmd-alt-s': 'window:save-all'
|
||||
'cmd-w': 'core:close'
|
||||
'cmd-ctrl-f': 'window:toggle-full-screen'
|
||||
'cmd-z': 'core:undo'
|
||||
'cmd-Z': 'core:redo'
|
||||
'cmd-y': 'core:redo'
|
||||
'cmd-x': 'core:cut'
|
||||
'cmd-c': 'core:copy'
|
||||
'cmd-v': 'core:paste'
|
||||
'shift-up': 'core:select-up'
|
||||
'shift-down': 'core:select-down'
|
||||
'shift-left': 'core:select-left'
|
||||
'shift-right': 'core:select-right'
|
||||
'delete': 'core:delete'
|
||||
'pageup': 'core:page-up'
|
||||
'pagedown': 'core:page-down'
|
||||
'backspace': 'core:backspace'
|
||||
'shift-backspace': 'core:backspace'
|
||||
'cmd-up': 'core:move-to-top'
|
||||
'cmd-down': 'core:move-to-bottom'
|
||||
'cmd-shift-up': 'core:select-to-top'
|
||||
'cmd-shift-down': 'core:select-to-bottom'
|
||||
'cmd-{': 'pane:show-previous-item'
|
||||
'cmd-}': 'pane:show-next-item'
|
||||
'cmd-alt-left': 'pane:show-previous-item'
|
||||
'cmd-alt-right': 'pane:show-next-item'
|
||||
'cmd-=': 'window:increase-font-size'
|
||||
'cmd-+': 'window:increase-font-size'
|
||||
'cmd--': 'window:decrease-font-size'
|
||||
|
||||
'cmd-k up': 'pane:split-up' # Atom Specific
|
||||
'cmd-k down': 'pane:split-down' # Atom Specific
|
||||
'cmd-k left': 'pane:split-left' # Atom Specific
|
||||
'cmd-k right': 'pane:split-right' # Atom Specific
|
||||
'cmd-k cmd-w': 'pane:close' # Atom Specific
|
||||
'cmd-k alt-cmd-w': 'pane:close-other-items' # Atom Specific
|
||||
'cmd-k cmd-left': 'window:focus-previous-pane'
|
||||
'cmd-k cmd-right': 'window:focus-next-pane'
|
||||
'cmd-1': 'pane:show-item-1'
|
||||
'cmd-2': 'pane:show-item-2'
|
||||
'cmd-3': 'pane:show-item-3'
|
||||
'cmd-4': 'pane:show-item-4'
|
||||
'cmd-5': 'pane:show-item-5'
|
||||
'cmd-6': 'pane:show-item-6'
|
||||
'cmd-7': 'pane:show-item-7'
|
||||
'cmd-8': 'pane:show-item-8'
|
||||
'cmd-9': 'pane:show-item-9'
|
||||
|
||||
'.platform-darwin .editor':
|
||||
# Apple Specific
|
||||
'cmd-backspace': 'editor:backspace-to-beginning-of-line'
|
||||
'cmd-delete': 'editor:backspace-to-beginning-of-line'
|
||||
'ctrl-A': 'editor:select-to-first-character-of-line'
|
||||
'ctrl-E': 'editor:select-to-end-of-line'
|
||||
'cmd-left': 'editor:move-to-first-character-of-line'
|
||||
'cmd-right': 'editor:move-to-end-of-line'
|
||||
'cmd-shift-left': 'editor:select-to-first-character-of-line'
|
||||
'cmd-shift-right': 'editor:select-to-end-of-line'
|
||||
'alt-backspace': 'editor:backspace-to-beginning-of-word'
|
||||
'alt-delete': 'editor:delete-to-end-of-word'
|
||||
'ctrl-a': 'editor:move-to-first-character-of-line'
|
||||
'ctrl-e': 'editor:move-to-end-of-line'
|
||||
'ctrl-k': 'editor:cut-to-end-of-line'
|
||||
|
||||
# Atom Specific
|
||||
'ctrl-W': 'editor:select-word'
|
||||
|
||||
# Sublime Parity
|
||||
'cmd-a': 'core:select-all'
|
||||
'cmd-alt-p': 'editor:log-cursor-scope'
|
||||
'cmd-k cmd-u': 'editor:upper-case'
|
||||
'cmd-k cmd-l': 'editor:lower-case'
|
||||
|
||||
'body.platform-darwin .editor:not(.mini)':
|
||||
# Atom specific
|
||||
'alt-cmd-z': 'editor:checkout-head-revision'
|
||||
'cmd-<': 'editor:scroll-to-cursor'
|
||||
'alt-cmd-ctrl-f': 'editor:fold-selection'
|
||||
'cmd-=': 'editor:auto-indent'
|
||||
|
||||
# Sublime Parity
|
||||
'cmd-enter': 'editor:newline-below'
|
||||
'cmd-shift-enter': 'editor:newline-above'
|
||||
'cmd-]': 'editor:indent-selected-rows'
|
||||
'cmd-[': 'editor:outdent-selected-rows'
|
||||
'ctrl-cmd-up': 'editor:move-line-up'
|
||||
'ctrl-cmd-down': 'editor:move-line-down'
|
||||
'cmd-/': 'editor:toggle-line-comments'
|
||||
'cmd-j': 'editor:join-line'
|
||||
'cmd-D': 'editor:duplicate-line'
|
||||
|
||||
'cmd-alt-[': 'editor:fold-current-row'
|
||||
'cmd-alt-]': 'editor:unfold-current-row'
|
||||
'cmd-alt-{': 'editor:fold-all' # Atom Specific
|
||||
'cmd-alt-}': 'editor:unfold-all' # Atom Specific
|
||||
'cmd-k cmd-0': 'editor:unfold-all'
|
||||
'cmd-k cmd-1': 'editor:fold-at-indent-level-1'
|
||||
'cmd-k cmd-2': 'editor:fold-at-indent-level-2'
|
||||
'cmd-k cmd-3': 'editor:fold-at-indent-level-3'
|
||||
'cmd-k cmd-4': 'editor:fold-at-indent-level-4'
|
||||
'cmd-k cmd-5': 'editor:fold-at-indent-level-5'
|
||||
'cmd-k cmd-6': 'editor:fold-at-indent-level-6'
|
||||
'cmd-k cmd-7': 'editor:fold-at-indent-level-7'
|
||||
'cmd-k cmd-8': 'editor:fold-at-indent-level-8'
|
||||
'cmd-k cmd-9': 'editor:fold-at-indent-level-9'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
'body.platform-darwin .native-key-bindings':
|
||||
'cmd-z': 'native!'
|
||||
'cmd-Z': 'native!'
|
||||
'cmd-x': 'native!'
|
||||
'cmd-c': 'native!'
|
||||
'cmd-v': 'native!'
|
||||
@@ -1,194 +0,0 @@
|
||||
'body':
|
||||
# Apple specific
|
||||
'meta-q': 'application:quit'
|
||||
'meta-h': 'application:hide'
|
||||
'meta-H': 'application:hide-other-applications'
|
||||
'meta-m': 'application:minimize'
|
||||
'alt-meta-ctrl-m': 'application:zoom'
|
||||
|
||||
'ctrl-p': 'core:move-up'
|
||||
'ctrl-n': 'core:move-down'
|
||||
'ctrl-b': 'core:move-left'
|
||||
'ctrl-f': 'core:move-right'
|
||||
'ctrl-P': 'core:select-up'
|
||||
'ctrl-N': 'core:select-down'
|
||||
'ctrl-F': 'core:select-right'
|
||||
'ctrl-B': 'core:select-left'
|
||||
'ctrl-h': 'core:backspace'
|
||||
'ctrl-d': 'core:delete'
|
||||
|
||||
# Atom Specific
|
||||
'meta-O': 'application:open-dev'
|
||||
'meta-alt-ctrl-s': 'application:run-all-specs'
|
||||
'enter': 'core:confirm'
|
||||
'escape': 'core:cancel'
|
||||
'up': 'core:move-up'
|
||||
'down': 'core:move-down'
|
||||
'left': 'core:move-left'
|
||||
'right': 'core:move-right'
|
||||
'ctrl-alt-meta-r': 'window:reload'
|
||||
'alt-meta-i': 'window:toggle-dev-tools'
|
||||
'meta-alt-ctrl-p': 'window:run-package-specs'
|
||||
|
||||
# Sublime Parity
|
||||
'meta-,': 'application:show-settings'
|
||||
'meta-N': 'application:new-window'
|
||||
'meta-W': 'window:close'
|
||||
'meta-o': 'application:open'
|
||||
'meta-T': 'pane:reopen-closed-item'
|
||||
'meta-n': 'application:new-file'
|
||||
'meta-s': 'core:save'
|
||||
'meta-S': 'core:save-as'
|
||||
'meta-alt-s': 'window:save-all'
|
||||
'meta-w': 'core:close'
|
||||
'meta-ctrl-f': 'window:toggle-full-screen'
|
||||
'meta-z': 'core:undo'
|
||||
'meta-Z': 'core:redo'
|
||||
'meta-y': 'core:redo'
|
||||
'meta-x': 'core:cut'
|
||||
'meta-c': 'core:copy'
|
||||
'meta-v': 'core:paste'
|
||||
'shift-up': 'core:select-up'
|
||||
'shift-down': 'core:select-down'
|
||||
'shift-left': 'core:select-left'
|
||||
'shift-right': 'core:select-right'
|
||||
'delete': 'core:delete'
|
||||
'pageup': 'core:page-up'
|
||||
'pagedown': 'core:page-down'
|
||||
'backspace': 'core:backspace'
|
||||
'shift-backspace': 'core:backspace'
|
||||
'meta-up': 'core:move-to-top'
|
||||
'meta-down': 'core:move-to-bottom'
|
||||
'meta-shift-up': 'core:select-to-top'
|
||||
'meta-shift-down': 'core:select-to-bottom'
|
||||
'meta-{': 'pane:show-previous-item'
|
||||
'meta-}': 'pane:show-next-item'
|
||||
'meta-alt-left': 'pane:show-previous-item'
|
||||
'meta-alt-right': 'pane:show-next-item'
|
||||
'meta-=': 'window:increase-font-size'
|
||||
'meta-+': 'window:increase-font-size'
|
||||
'meta--': 'window:decrease-font-size'
|
||||
|
||||
'meta-k up': 'pane:split-up' # Atom Specific
|
||||
'meta-k down': 'pane:split-down' # Atom Specific
|
||||
'meta-k left': 'pane:split-left' # Atom Specific
|
||||
'meta-k right': 'pane:split-right' # Atom Specific
|
||||
'meta-k meta-w': 'pane:close' # Atom Specific
|
||||
'meta-k alt-meta-w': 'pane:close-other-items' # Atom Specific
|
||||
'meta-k meta-left': 'window:focus-previous-pane'
|
||||
'meta-k meta-right': 'window:focus-next-pane'
|
||||
'meta-1': 'pane:show-item-1'
|
||||
'meta-2': 'pane:show-item-2'
|
||||
'meta-3': 'pane:show-item-3'
|
||||
'meta-4': 'pane:show-item-4'
|
||||
'meta-5': 'pane:show-item-5'
|
||||
'meta-6': 'pane:show-item-6'
|
||||
'meta-7': 'pane:show-item-7'
|
||||
'meta-8': 'pane:show-item-8'
|
||||
'meta-9': 'pane:show-item-9'
|
||||
|
||||
'.editor':
|
||||
# Apple Specific
|
||||
'alt-left': 'editor:move-to-previous-word-boundary'
|
||||
'alt-right': 'editor:move-to-next-word-boundary'
|
||||
'alt-shift-left': 'editor:select-to-previous-word-boundary'
|
||||
'alt-shift-right': 'editor:select-to-next-word-boundary'
|
||||
'meta-backspace': 'editor:backspace-to-beginning-of-line'
|
||||
'meta-delete': 'editor:backspace-to-beginning-of-line'
|
||||
'ctrl-A': 'editor:select-to-first-character-of-line'
|
||||
'ctrl-E': 'editor:select-to-end-of-line'
|
||||
'meta-left': 'editor:move-to-first-character-of-line'
|
||||
'meta-right': 'editor:move-to-end-of-line'
|
||||
'meta-shift-left': 'editor:select-to-first-character-of-line'
|
||||
'meta-shift-right': 'editor:select-to-end-of-line'
|
||||
'home': 'editor:move-to-first-character-of-line'
|
||||
'end': 'editor:move-to-end-of-line'
|
||||
'shift-home': 'editor:select-to-first-character-of-line'
|
||||
'shift-end': 'editor:select-to-end-of-line'
|
||||
'alt-backspace': 'editor:backspace-to-beginning-of-word'
|
||||
'alt-delete': 'editor:delete-to-end-of-word'
|
||||
'ctrl-a': 'editor:move-to-first-character-of-line'
|
||||
'ctrl-e': 'editor:move-to-end-of-line'
|
||||
'ctrl-k': 'editor:cut-to-end-of-line'
|
||||
|
||||
# Atom Specific
|
||||
'ctrl-W': 'editor:select-word'
|
||||
|
||||
# Sublime Parity
|
||||
'meta-a': 'core:select-all'
|
||||
'meta-alt-p': 'editor:log-cursor-scope'
|
||||
'ctrl-t': 'editor:transpose'
|
||||
'meta-k meta-u': 'editor:upper-case'
|
||||
'meta-k meta-l': 'editor:lower-case'
|
||||
|
||||
'.editor:not(.mini)':
|
||||
# Atom Specific
|
||||
'alt-meta-z': 'editor:checkout-head-revision'
|
||||
'meta-<': 'editor:scroll-to-cursor'
|
||||
'ctrl-C': 'editor:copy-path'
|
||||
'alt-meta-ctrl-f': 'editor:fold-selection'
|
||||
'meta-=': 'editor:auto-indent'
|
||||
|
||||
# Sublime Parity
|
||||
'tab': 'editor:indent'
|
||||
'enter': 'editor:newline'
|
||||
'meta-enter': 'editor:newline-below'
|
||||
'meta-shift-enter': 'editor:newline-above'
|
||||
'meta-]': 'editor:indent-selected-rows'
|
||||
'meta-[': 'editor:outdent-selected-rows'
|
||||
'shift-tab': 'editor:outdent-selected-rows'
|
||||
'ctrl-meta-up': 'editor:move-line-up'
|
||||
'ctrl-meta-down': 'editor:move-line-down'
|
||||
'meta-/': 'editor:toggle-line-comments'
|
||||
'meta-j': 'editor:join-line'
|
||||
'meta-D': 'editor:duplicate-line'
|
||||
'ctrl-K': 'editor:delete-line'
|
||||
'ctrl-shift-up': 'editor:add-selection-above'
|
||||
'ctrl-shift-down': 'editor:add-selection-below'
|
||||
|
||||
'meta-alt-[': 'editor:fold-current-row'
|
||||
'meta-alt-]': 'editor:unfold-current-row'
|
||||
'meta-alt-{': 'editor:fold-all' # Atom Specific
|
||||
'meta-alt-}': 'editor:unfold-all' # Atom Specific
|
||||
'meta-k meta-0': 'editor:unfold-all'
|
||||
'meta-k meta-1': 'editor:fold-at-indent-level-1'
|
||||
'meta-k meta-2': 'editor:fold-at-indent-level-2'
|
||||
'meta-k meta-3': 'editor:fold-at-indent-level-3'
|
||||
'meta-k meta-4': 'editor:fold-at-indent-level-4'
|
||||
'meta-k meta-5': 'editor:fold-at-indent-level-5'
|
||||
'meta-k meta-6': 'editor:fold-at-indent-level-6'
|
||||
'meta-k meta-7': 'editor:fold-at-indent-level-7'
|
||||
'meta-k meta-8': 'editor:fold-at-indent-level-8'
|
||||
'meta-k meta-9': 'editor:fold-at-indent-level-9'
|
||||
|
||||
'.tool-panel':
|
||||
'escape': 'core:close'
|
||||
|
||||
'.tool-panel.panel-left, .tool-panel.panel-right':
|
||||
'escape': 'tool-panel:unfocus'
|
||||
|
||||
'.editor !important, .editor.mini !important':
|
||||
'escape': 'editor:consolidate-selections'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
'input:not(.hidden-input), .native-key-bindings':
|
||||
'tab': 'core:focus-next'
|
||||
'shift-tab': 'core:focus-previous'
|
||||
'left': 'native!'
|
||||
'right': 'native!'
|
||||
'shift-left': 'native!'
|
||||
'shift-right': 'native!'
|
||||
'backspace': 'native!'
|
||||
'shift-backspace': 'native!'
|
||||
'delete': 'native!'
|
||||
'meta-z': 'native!'
|
||||
'meta-Z': 'native!'
|
||||
'meta-x': 'native!'
|
||||
'meta-c': 'native!'
|
||||
'meta-v': 'native!'
|
||||
'ctrl-b': 'native!'
|
||||
'ctrl-f': 'native!'
|
||||
'ctrl-F': 'native!'
|
||||
'ctrl-B': 'native!'
|
||||
'ctrl-h': 'native!'
|
||||
'ctrl-d': 'native!'
|
||||
@@ -0,0 +1,102 @@
|
||||
'.platform-win32':
|
||||
# Atom Specific
|
||||
'enter': 'core:confirm'
|
||||
'escape': 'core:cancel'
|
||||
'up': 'core:move-up'
|
||||
'down': 'core:move-down'
|
||||
'left': 'core:move-left'
|
||||
'right': 'core:move-right'
|
||||
'ctrl-alt-r': 'window:reload'
|
||||
'ctrl-alt-i': 'window:toggle-dev-tools'
|
||||
'ctrl-alt-p': 'window:run-package-specs'
|
||||
'ctrl-alt-s': 'application:run-all-specs'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-N': 'application:new-window'
|
||||
'ctrl-W': 'window:close'
|
||||
'ctrl-o': 'application:open'
|
||||
'ctrl-T': 'pane:reopen-closed-item'
|
||||
'ctrl-n': 'application:new-file'
|
||||
'ctrl-s': 'core:save'
|
||||
'ctrl-S': 'core:save-as'
|
||||
'ctrl-w': 'core:close'
|
||||
'ctrl-z': 'core:undo'
|
||||
'ctrl-y': 'core:redo'
|
||||
'ctrl-x': 'core:cut'
|
||||
'ctrl-c': 'core:copy'
|
||||
'ctrl-v': 'core:paste'
|
||||
'shift-up': 'core:select-up'
|
||||
'shift-down': 'core:select-down'
|
||||
'shift-left': 'core:select-left'
|
||||
'shift-right': 'core:select-right'
|
||||
'delete': 'core:delete'
|
||||
'pageup': 'core:page-up'
|
||||
'pagedown': 'core:page-down'
|
||||
'backspace': 'core:backspace'
|
||||
'ctrl-tab': 'pane:show-next-item'
|
||||
'ctrl-shift-tab': 'pane:show-previous-item'
|
||||
'ctrl-shift-up': 'core:move-up'
|
||||
'ctrl-shift-down': 'core:move-down'
|
||||
'ctrl-=': 'window:increase-font-size'
|
||||
'ctrl-+': 'window:increase-font-size'
|
||||
'ctrl--': 'window:decrease-font-size'
|
||||
|
||||
'ctrl-k up': 'pane:split-up' # Atom Specific
|
||||
'ctrl-k down': 'pane:split-down' # Atom Specific
|
||||
'ctrl-k left': 'pane:split-left' # Atom Specific
|
||||
'ctrl-k right': 'pane:split-right' # Atom Specific
|
||||
'ctrl-k ctrl-w': 'pane:close' # Atom Specific
|
||||
'ctrl-k alt-ctrl-w': 'pane:close-other-items' # Atom Specific
|
||||
'ctrl-k ctrl-left': 'window:focus-previous-pane'
|
||||
'ctrl-k ctrl-right': 'window:focus-next-pane'
|
||||
|
||||
'.platform-win32 .editor':
|
||||
# Windows specific
|
||||
'ctrl-delete': 'editor:backspace-to-beginning-of-word'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-a': 'core:select-all'
|
||||
'ctrl-alt-p': 'editor:log-cursor-scope'
|
||||
'ctrl-k ctrl-u': 'editor:upper-case'
|
||||
'ctrl-k ctrl-l': 'editor:lower-case'
|
||||
|
||||
'.platform-win32 .editor:not(.mini)':
|
||||
# Atom specific
|
||||
'alt-ctrl-z': 'editor:checkout-head-revision'
|
||||
'ctrl-<': 'editor:scroll-to-cursor'
|
||||
'alt-ctrl-f': 'editor:fold-selection'
|
||||
'ctrl-=': 'editor:auto-indent'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-enter': 'editor:newline-below'
|
||||
'ctrl-shift-enter': 'editor:newline-above'
|
||||
'ctrl-]': 'editor:indent-selected-rows'
|
||||
'ctrl-[': 'editor:outdent-selected-rows'
|
||||
'ctrl-up': 'editor:move-line-up'
|
||||
'ctrl-down': 'editor:move-line-down'
|
||||
'ctrl-/': 'editor:toggle-line-comments'
|
||||
'ctrl-j': 'editor:join-line'
|
||||
'ctrl-D': 'editor:duplicate-line'
|
||||
|
||||
'ctrl-alt-[': 'editor:fold-current-row'
|
||||
'ctrl-alt-]': 'editor:unfold-current-row'
|
||||
'ctrl-alt-{': 'editor:fold-all' # Atom Specific
|
||||
'ctrl-alt-}': 'editor:unfold-all' # Atom Specific
|
||||
'ctrl-k ctrl-0': 'editor:unfold-all'
|
||||
'ctrl-k ctrl-1': 'editor:fold-at-indent-level-1'
|
||||
'ctrl-k ctrl-2': 'editor:fold-at-indent-level-2'
|
||||
'ctrl-k ctrl-3': 'editor:fold-at-indent-level-3'
|
||||
'ctrl-k ctrl-4': 'editor:fold-at-indent-level-4'
|
||||
'ctrl-k ctrl-5': 'editor:fold-at-indent-level-5'
|
||||
'ctrl-k ctrl-6': 'editor:fold-at-indent-level-6'
|
||||
'ctrl-k ctrl-7': 'editor:fold-at-indent-level-7'
|
||||
'ctrl-k ctrl-8': 'editor:fold-at-indent-level-8'
|
||||
'ctrl-k ctrl-9': 'editor:fold-at-indent-level-9'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
'.platform-win32 input:not(.hidden-input), .platform-win32 .native-key-bindings':
|
||||
'ctrl-z': 'native!'
|
||||
'ctrl-Z': 'native!'
|
||||
'ctrl-x': 'native!'
|
||||
'ctrl-c': 'native!'
|
||||
'ctrl-v': 'native!'
|
||||
@@ -0,0 +1,171 @@
|
||||
'menu': [
|
||||
{
|
||||
label: '&File'
|
||||
submenu: [
|
||||
{ label: 'New &Window', command: 'application:new-window' }
|
||||
{ label: '&New File', command: 'application:new-file' }
|
||||
{ label: '&Open...', command: 'application:open' }
|
||||
{ label: 'Reopen Last &Item', command: 'pane:reopen-closed-item' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Preferences...', command: 'application:show-settings' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Save', command: 'core:save' }
|
||||
{ label: 'Save &As...', command: 'core:save-as' }
|
||||
{ label: 'Save A&ll', command: 'window:save-all' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Close Buffer', command: 'core:close' }
|
||||
{ label: 'Close All &Buffers', command: 'pane:close' }
|
||||
{ label: 'Clos&e Window', command: 'window:close' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'E&xit', command: 'application:quit' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Edit'
|
||||
submenu: [
|
||||
{ label: '&Undo', command: 'core:undo' }
|
||||
{ label: '&Redo', command: 'core:redo' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Cut', command: 'core:cut' }
|
||||
{ label: 'C&opy', command: 'core:copy' }
|
||||
{ label: 'Copy Pat&h', command: 'editor:copy-path' }
|
||||
{ label: '&Paste', command: 'core:paste' }
|
||||
{ label: 'Select &All', command: 'core:select-all' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Toggle Comments', command: 'editor:toggle-line-comments' }
|
||||
{
|
||||
label: 'Lines',
|
||||
submenu: [
|
||||
{ label: '&Indent', command: 'editor:indent-selected-rows' }
|
||||
{ label: '&Outdent', command: 'editor:outdent-selected-rows' }
|
||||
{ label: '&Auto Indent', command: 'editor:auto-indent' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Move Line &Up', command: 'editor:move-line-up' }
|
||||
{ label: 'Move Line &Down', command: 'editor:move-line-down' }
|
||||
{ label: 'Du&plicate Line', command: 'editor:duplicate-line' }
|
||||
{ label: 'D&elete Line', command: 'editor:delete-line' }
|
||||
{ label: '&Join Lines', command: 'editor:join-line' }
|
||||
]
|
||||
}
|
||||
{
|
||||
label: 'Text',
|
||||
submenu: [
|
||||
{ label: '&Upper Case', command: 'editor:upper-case' }
|
||||
{ label: '&Lower Case', command: 'editor:lower-case' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Delete to End of &Word', command: 'editor:delete-to-end-of-word' }
|
||||
{ label: '&Delete Line', command: 'editor:delete-line' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Transpose', command: 'editor:transpose' }
|
||||
]
|
||||
}
|
||||
{
|
||||
label: 'Folding',
|
||||
submenu: [
|
||||
{ label: '&Fold', command: 'editor:fold-current-row' }
|
||||
{ label: '&Unfold', command: 'editor:unfold-current-row' }
|
||||
{ label: 'Unfold &All', command: 'editor:unfold-all' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Fol&d All', command: 'editor:fold-all' }
|
||||
{ label: 'Fold Level 1', command: 'editor:fold-at-indent-level-1' }
|
||||
{ label: 'Fold Level 2', command: 'editor:fold-at-indent-level-2' }
|
||||
{ label: 'Fold Level 3', command: 'editor:fold-at-indent-level-3' }
|
||||
{ label: 'Fold Level 4', command: 'editor:fold-at-indent-level-4' }
|
||||
{ label: 'Fold Level 5', command: 'editor:fold-at-indent-level-5' }
|
||||
{ label: 'Fold Level 6', command: 'editor:fold-at-indent-level-6' }
|
||||
{ label: 'Fold Level 7', command: 'editor:fold-at-indent-level-7' }
|
||||
{ label: 'Fold Level 8', command: 'editor:fold-at-indent-level-8' }
|
||||
{ label: 'Fold Level 9', command: 'editor:fold-at-indent-level-9' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: '&View'
|
||||
submenu: [
|
||||
{ label: '&Reload', command: 'window:reload' }
|
||||
{ label: 'Toggle &Full Screen', command: 'window:toggle-full-screen' }
|
||||
{
|
||||
label: 'Developer'
|
||||
submenu: [
|
||||
{ label: 'Open In &Dev Mode...', command: 'application:open-dev' }
|
||||
{ label: 'Run &Atom Specs', command: 'application:run-all-specs' }
|
||||
{ label: 'Run Package &Specs', command: 'window:run-package-specs' }
|
||||
{ label: 'Toggle Developer &Tools', command: 'window:toggle-dev-tools' }
|
||||
]
|
||||
}
|
||||
{ type: 'separator' }
|
||||
{ label: 'Toggle Soft &Wrap', command: 'editor:toggle-soft-wrap' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Selection'
|
||||
submenu: [
|
||||
{ label: 'Add Selection &Above', command: 'editor:add-selection-above' }
|
||||
{ label: 'Add Selection &Below', command: 'editor:add-selection-below' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Select to &Top', command: 'core:select-to-top' }
|
||||
{ label: 'Select to Botto&m', command: 'core:select-to-bottom' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Select &Line', command: 'editor:select-line' }
|
||||
{ label: 'Select &Word', command: 'editor:select-word' }
|
||||
{ label: 'Select to Beginning of W&ord', command: 'editor:select-to-beginning-of-word' }
|
||||
{ label: 'Select to Beginning of L&ine', command: 'editor:select-to-beginning-of-line' }
|
||||
{ label: 'Select to First &Character of Line', command: 'editor:select-to-first-character-of-line' }
|
||||
{ label: 'Select to End of Wor&d', command: 'editor:select-to-end-of-word' }
|
||||
{ label: 'Select to End of Lin&e', command: 'editor:select-to-end-of-line' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Movement'
|
||||
submenu: [
|
||||
{ label: 'Move to &Top', command: 'core:move-to-top' }
|
||||
{ label: 'Move to &Bottom', command: 'core:move-to-bottom' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Move to Beginning of &Line', command: 'editor:move-to-beginning-of-line' }
|
||||
{ label: 'Move to &First Character of Line', command: 'editor:move-to-first-character-of-line' }
|
||||
{ label: 'Move to &End of Line', command: 'editor:move-to-end-of-line' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Move to Beginning of &Word', command: 'editor:move-to-beginning-of-word' }
|
||||
{ label: 'Move to End of Wor&d', command: 'editor:move-to-end-of-word' }
|
||||
{ label: 'Move to &Next Word', command: 'editor:move-to-next-word-boundary' }
|
||||
{ label: 'Move to &Previous Word', command: 'editor:move-to-previous-word-boundary' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: 'F&ind'
|
||||
submenu: []
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Packages'
|
||||
submenu: []
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Window'
|
||||
submenu: [
|
||||
{ label: 'Mi&nimize', command: 'application:minimize' }
|
||||
{ label: 'Ma&ximize', command: 'application:zoom' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Bring &All to Front', command: 'application:bring-all-windows-to-front' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Help'
|
||||
submenu: [
|
||||
{ label: '&About Atom...', command: 'application:about' }
|
||||
{ label: "VERSION", enabled: false }
|
||||
{ label: "Install &update", command: 'application:install-update', visible: false }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Documentation', command: 'application:open-documentation' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
}
|
||||
]
|
||||
+71
-63
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"version": "35.0.0",
|
||||
"version": "0.41.0",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -9,35 +9,41 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/atom/atom/issues"
|
||||
},
|
||||
"atomShellVersion": "0.6.9",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "Apache",
|
||||
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
|
||||
}
|
||||
],
|
||||
"atomShellVersion": "0.7.4",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"bootstrap": "git://github.com/twbs/bootstrap.git#v3.0.0",
|
||||
"clear-cut": "0.1.0",
|
||||
"bootstrap": "git://github.com/benogle/bootstrap.git",
|
||||
"clear-cut": "0.2.0",
|
||||
"coffee-script": "1.6.3",
|
||||
"coffeestack": "0.6.0",
|
||||
"emissary": "0.9.0",
|
||||
"emissary": "0.19.0",
|
||||
"first-mate": "0.5.0",
|
||||
"fs-plus": "0.7.0",
|
||||
"fs-plus": "0.10.0",
|
||||
"fuzzaldrin": "0.1.0",
|
||||
"git-utils": "0.29.0",
|
||||
"guid": "0.0.10",
|
||||
"jasmine-focused": "~0.15.0",
|
||||
"mkdirp": "0.3.5",
|
||||
"less-cache": "0.9.0",
|
||||
"less-cache": "0.10.0",
|
||||
"nslog": "0.1.0",
|
||||
"oniguruma": "0.24.0",
|
||||
"optimist": "0.4.0",
|
||||
"pathwatcher": "0.9.0",
|
||||
"pathwatcher": "0.10.0",
|
||||
"pegjs": "0.7.0",
|
||||
"q": "0.9.7",
|
||||
"scandal": "0.6.4",
|
||||
"scandal": "0.8.0",
|
||||
"season": "0.14.0",
|
||||
"semver": "1.1.4",
|
||||
"space-pen": "2.0.0",
|
||||
"telepath": "0.23.0",
|
||||
"space-pen": "2.0.1",
|
||||
"telepath": "0.45.1",
|
||||
"temp": "0.5.0",
|
||||
"underscore-plus": "0.2.0"
|
||||
"underscore-plus": "0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"biscotto": "0.0.17",
|
||||
@@ -52,7 +58,6 @@
|
||||
"grunt-contrib-coffee": "~0.7.0",
|
||||
"grunt-contrib-less": "~0.8.0",
|
||||
"walkdir": "0.0.7",
|
||||
"ws": "0.4.27",
|
||||
"js-yaml": "~2.1.0",
|
||||
"grunt-markdown": "~0.4.0",
|
||||
"json-front-matter": "~0.1.3",
|
||||
@@ -61,60 +66,62 @@
|
||||
"jasmine-tagged": "0.2.0",
|
||||
"request": "~2.27.0",
|
||||
"unzip": "~0.1.9",
|
||||
"rcedit": "~0.1.1"
|
||||
"rcedit": "~0.1.2",
|
||||
"rimraf": "~2.2.2"
|
||||
},
|
||||
"packageDependencies" : {
|
||||
"atom-light-ui": "0.6.0",
|
||||
"atom-light-syntax": "0.6.0",
|
||||
"atom-dark-ui": "0.6.0",
|
||||
"packageDependencies": {
|
||||
"atom-dark-syntax": "0.6.0",
|
||||
"base16-tomorrow-dark-theme": "0.5.0",
|
||||
"atom-dark-ui": "0.11.0",
|
||||
"atom-light-syntax": "0.6.0",
|
||||
"atom-light-ui": "0.13.0",
|
||||
"base16-tomorrow-dark-theme": "0.6.0",
|
||||
"solarized-dark-syntax": "0.4.0",
|
||||
|
||||
"archive-view": "0.11.0",
|
||||
"autocomplete": "0.12.0",
|
||||
"autoflow": "0.5.0",
|
||||
"autosave": "0.6.0",
|
||||
"bookmarks": "0.10.0",
|
||||
"bracket-matcher": "0.9.0",
|
||||
"command-logger": "0.6.0",
|
||||
"command-palette": "0.7.0",
|
||||
"dev-live-reload": "0.14.0",
|
||||
"editor-stats": "0.5.0",
|
||||
"exception-reporting": "0.5.0",
|
||||
"find-and-replace": "0.38.0",
|
||||
"fuzzy-finder": "0.19.0",
|
||||
"gists": "0.6.0",
|
||||
"git-diff": "0.13.0",
|
||||
"github-sign-in": "0.9.0",
|
||||
"go-to-line": "0.8.0",
|
||||
"grammar-selector": "0.8.0",
|
||||
"image-view": "0.7.0",
|
||||
"link": "0.7.0",
|
||||
"markdown-preview": "0.15.0",
|
||||
"metrics": "0.8.0",
|
||||
"package-generator": "0.17.0",
|
||||
"release-notes": "0.11.0",
|
||||
"settings-view": "0.37.0",
|
||||
"snippets": "0.13.0",
|
||||
"spell-check": "0.11.0",
|
||||
"status-bar": "0.15.1",
|
||||
"styleguide": "0.9.0",
|
||||
"symbols-view": "0.18.0",
|
||||
"tabs": "0.8.0",
|
||||
"terminal": "0.15.0",
|
||||
"timecop": "0.9.0",
|
||||
"to-the-hubs": "0.8.0",
|
||||
"tree-view": "0.28.0",
|
||||
"visual-bell": "0.3.0",
|
||||
"whitespace": "0.8.0",
|
||||
"wrap-guide": "0.5.0",
|
||||
|
||||
"archive-view": "0.16.0",
|
||||
"autocomplete": "0.18.0",
|
||||
"autoflow": "0.11.0",
|
||||
"autosave": "0.9.0",
|
||||
"bookmarks": "0.15.0",
|
||||
"bracket-matcher": "0.15.0",
|
||||
"command-logger": "0.8.0",
|
||||
"command-palette": "0.11.0",
|
||||
"dev-live-reload": "0.18.0",
|
||||
"editor-stats": "0.8.0",
|
||||
"exception-reporting": "0.8.0",
|
||||
"feedback": "0.12.0",
|
||||
"find-and-replace": "0.55.0",
|
||||
"fuzzy-finder": "0.27.0",
|
||||
"gists": "0.12.0",
|
||||
"git-diff": "0.19.0",
|
||||
"github-sign-in": "0.14.0",
|
||||
"go-to-line": "0.12.0",
|
||||
"grammar-selector": "0.13.0",
|
||||
"image-view": "0.10.0",
|
||||
"keybinding-resolver": "0.6.0",
|
||||
"link": "0.11.0",
|
||||
"markdown-preview": "0.22.0",
|
||||
"metrics": "0.12.0",
|
||||
"package-generator": "0.23.0",
|
||||
"release-notes": "0.14.0",
|
||||
"settings-view": "0.49.0",
|
||||
"snippets": "0.17.0",
|
||||
"spell-check": "0.17.0",
|
||||
"status-bar": "0.23.0",
|
||||
"styleguide": "0.17.0",
|
||||
"symbols-view": "0.27.0",
|
||||
"tabs": "0.15.0",
|
||||
"terminal": "0.23.0",
|
||||
"timecop": "0.11.0",
|
||||
"to-the-hubs": "0.15.0",
|
||||
"tree-view": "0.42.0",
|
||||
"visual-bell": "0.6.0",
|
||||
"welcome": "0.3.0",
|
||||
"whitespace": "0.10.0",
|
||||
"wrap-guide": "0.8.0",
|
||||
"language-c": "0.2.0",
|
||||
"language-clojure": "0.1.0",
|
||||
"language-coffee-script": "0.3.0",
|
||||
"language-css": "0.2.0",
|
||||
"language-gfm": "0.8.0",
|
||||
"language-gfm": "0.9.0",
|
||||
"language-git": "0.3.0",
|
||||
"language-go": "0.2.0",
|
||||
"language-html": "0.2.0",
|
||||
@@ -132,7 +139,7 @@
|
||||
"language-property-list": "0.2.0",
|
||||
"language-puppet": "0.2.0",
|
||||
"language-python": "0.2.0",
|
||||
"language-ruby": "0.4.0",
|
||||
"language-ruby": "0.5.0",
|
||||
"language-ruby-on-rails": "0.3.0",
|
||||
"language-sass": "0.3.0",
|
||||
"language-shellscript": "0.2.0",
|
||||
@@ -140,9 +147,10 @@
|
||||
"language-sql": "0.2.0",
|
||||
"language-text": "0.2.0",
|
||||
"language-todo": "0.2.0",
|
||||
"language-toml": "0.6.0",
|
||||
"language-toml": "0.7.0",
|
||||
"language-xml": "0.2.0",
|
||||
"language-yaml": "0.1.0"
|
||||
"language-yaml": "0.1.0",
|
||||
"grunt-download-atom-shell": "0.1.1"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
Arquivo executável
BIN
Arquivo binário não exibido.
|
Depois Largura: | Altura: | Tamanho: 52 KiB |
Arquivo binário não exibido.
+16
-5
@@ -2,12 +2,23 @@
|
||||
var safeExec = require('./utils/child-process-wrapper.js').safeExec;
|
||||
var path = require('path');
|
||||
|
||||
// OAuth token for atom-bot
|
||||
// TODO Remove once all repositories are public
|
||||
if (!process.env.ATOM_ACCESS_TOKEN)
|
||||
process.env.ATOM_ACCESS_TOKEN = '362295be4c5258d3f7b967bbabae662a455ca2a7';
|
||||
|
||||
// Executes an array of commands one by one.
|
||||
function executeCommands(commands, done, index) {
|
||||
index = (index == undefined ? 0 : index);
|
||||
if (index < commands.length)
|
||||
safeExec(commands[index], executeCommands.bind(this, commands, done, index + 1));
|
||||
else
|
||||
if (index < commands.length) {
|
||||
var command = commands[index];
|
||||
var options = null;
|
||||
if (typeof command !== 'string') {
|
||||
options = command.options;
|
||||
command = command.command;
|
||||
}
|
||||
safeExec(command, options, executeCommands.bind(this, commands, done, index + 1));
|
||||
} else
|
||||
done(null);
|
||||
}
|
||||
|
||||
@@ -21,8 +32,8 @@ var echoNewLine = process.platform == 'win32' ? 'echo.' : 'echo';
|
||||
var commands = [
|
||||
'git submodule --quiet sync',
|
||||
'git submodule --quiet update --recursive --init',
|
||||
joinCommands('cd vendor/apm', 'npm install --silent .'),
|
||||
'npm install --silent vendor/apm',
|
||||
{command: joinCommands('cd vendor/apm', 'npm install --silent .'), options: {ignoreStdout: true}},
|
||||
{command: 'npm install --silent vendor/apm', options: {ignoreStdout: true}},
|
||||
echoNewLine,
|
||||
'node node_modules/atom-package-manager/bin/apm clean',
|
||||
'node node_modules/atom-package-manager/bin/apm install --silent',
|
||||
|
||||
@@ -10,7 +10,7 @@ exports.safeExec = function(command, options, callback) {
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
// This needed to be increase for `apm test` runs that generate tons of failures
|
||||
// This needed to be increased for `apm test` runs that generate many failures
|
||||
// The default is 200KB.
|
||||
options.maxBuffer = 1024 * 1024;
|
||||
|
||||
@@ -21,7 +21,8 @@ exports.safeExec = function(command, options, callback) {
|
||||
callback(null);
|
||||
});
|
||||
child.stderr.pipe(process.stderr);
|
||||
child.stdout.pipe(process.stdout);
|
||||
if (!options.ignoreStdout)
|
||||
child.stdout.pipe(process.stdout);
|
||||
}
|
||||
|
||||
// Same with safeExec but call child_process.spawn instead.
|
||||
|
||||
@@ -15,14 +15,14 @@ describe "AtomPackage", ->
|
||||
describe "when the theme contains a single style file", ->
|
||||
it "loads and applies css", ->
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "1234px"
|
||||
themePath = project.resolve('packages/theme-with-index-css')
|
||||
themePath = atom.project.resolve('packages/theme-with-index-css')
|
||||
theme = Package.load(themePath)
|
||||
theme.activate()
|
||||
expect($(".editor").css("padding-top")).toBe "1234px"
|
||||
|
||||
it "parses, loads and applies less", ->
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "1234px"
|
||||
themePath = project.resolve('packages/theme-with-index-less')
|
||||
themePath = atom.project.resolve('packages/theme-with-index-less')
|
||||
theme = Package.load(themePath)
|
||||
theme.activate()
|
||||
expect($(".editor").css("padding-top")).toBe "4321px"
|
||||
@@ -33,7 +33,7 @@ describe "AtomPackage", ->
|
||||
expect($(".editor").css("padding-right")).not.toBe("102px")
|
||||
expect($(".editor").css("padding-bottom")).not.toBe("103px")
|
||||
|
||||
themePath = project.resolve('packages/theme-with-package-file')
|
||||
themePath = atom.project.resolve('packages/theme-with-package-file')
|
||||
theme = Package.load(themePath)
|
||||
theme.activate()
|
||||
expect($(".editor").css("padding-top")).toBe("101px")
|
||||
@@ -46,7 +46,7 @@ describe "AtomPackage", ->
|
||||
expect($(".editor").css("padding-right")).not.toBe "20px"
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "30px"
|
||||
|
||||
themePath = project.resolve('packages/theme-without-package-file')
|
||||
themePath = atom.project.resolve('packages/theme-without-package-file')
|
||||
theme = Package.load(themePath)
|
||||
theme.activate()
|
||||
expect($(".editor").css("padding-top")).toBe "10px"
|
||||
@@ -55,7 +55,7 @@ describe "AtomPackage", ->
|
||||
|
||||
describe "reloading a theme", ->
|
||||
beforeEach ->
|
||||
themePath = project.resolve('packages/theme-with-package-file')
|
||||
themePath = atom.project.resolve('packages/theme-with-package-file')
|
||||
theme = Package.load(themePath)
|
||||
theme.activate()
|
||||
|
||||
@@ -66,7 +66,7 @@ describe "AtomPackage", ->
|
||||
|
||||
describe "events", ->
|
||||
beforeEach ->
|
||||
themePath = project.resolve('packages/theme-with-package-file')
|
||||
themePath = atom.project.resolve('packages/theme-with-package-file')
|
||||
theme = Package.load(themePath)
|
||||
theme.activate()
|
||||
|
||||
|
||||
+118
-117
@@ -1,20 +1,20 @@
|
||||
{$, $$, fs, RootView} = require 'atom'
|
||||
{$, $$, fs, WorkspaceView} = require 'atom'
|
||||
Exec = require('child_process').exec
|
||||
path = require 'path'
|
||||
ThemeManager = require '../src/theme-manager'
|
||||
|
||||
describe "the `atom` global", ->
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
atom.workspaceView = new WorkspaceView
|
||||
|
||||
describe "package lifecycle methods", ->
|
||||
describe ".loadPackage(name)", ->
|
||||
describe "when the package has deferred deserializers", ->
|
||||
it "requires the package's main module if one of its deferred deserializers is referenced", ->
|
||||
pack = atom.loadPackage('package-with-activation-events')
|
||||
pack = atom.packages.loadPackage('package-with-activation-events')
|
||||
spyOn(pack, 'activateStylesheets').andCallThrough()
|
||||
expect(pack.mainModule).toBeNull()
|
||||
object = deserialize({deserializer: 'Foo', data: 5})
|
||||
object = atom.deserializers.deserialize({deserializer: 'Foo', data: 5})
|
||||
expect(pack.mainModule).toBeDefined()
|
||||
expect(object.constructor.name).toBe 'Foo'
|
||||
expect(object.data).toBe 5
|
||||
@@ -22,35 +22,35 @@ describe "the `atom` global", ->
|
||||
|
||||
it "continues if the package has an invalid package.json", ->
|
||||
spyOn(console, 'warn')
|
||||
config.set("core.disabledPackages", [])
|
||||
expect(-> atom.loadPackage("package-with-broken-package-json")).not.toThrow()
|
||||
atom.config.set("core.disabledPackages", [])
|
||||
expect(-> atom.packages.loadPackage("package-with-broken-package-json")).not.toThrow()
|
||||
|
||||
it "continues if the package has an invalid keymap", ->
|
||||
config.set("core.disabledPackages", [])
|
||||
expect(-> atom.loadPackage("package-with-broken-keymap")).not.toThrow()
|
||||
atom.config.set("core.disabledPackages", [])
|
||||
expect(-> atom.packages.loadPackage("package-with-broken-keymap")).not.toThrow()
|
||||
|
||||
describe ".unloadPackage(name)", ->
|
||||
describe "when the package is active", ->
|
||||
it "throws an error", ->
|
||||
pack = atom.activatePackage('package-with-main')
|
||||
expect(atom.isPackageLoaded(pack.name)).toBeTruthy()
|
||||
expect(atom.isPackageActive(pack.name)).toBeTruthy()
|
||||
expect( -> atom.unloadPackage(pack.name)).toThrow()
|
||||
expect(atom.isPackageLoaded(pack.name)).toBeTruthy()
|
||||
expect(atom.isPackageActive(pack.name)).toBeTruthy()
|
||||
pack = atom.packages.activatePackage('package-with-main')
|
||||
expect(atom.packages.isPackageLoaded(pack.name)).toBeTruthy()
|
||||
expect(atom.packages.isPackageActive(pack.name)).toBeTruthy()
|
||||
expect( -> atom.packages.unloadPackage(pack.name)).toThrow()
|
||||
expect(atom.packages.isPackageLoaded(pack.name)).toBeTruthy()
|
||||
expect(atom.packages.isPackageActive(pack.name)).toBeTruthy()
|
||||
|
||||
describe "when the package is not loaded", ->
|
||||
it "throws an error", ->
|
||||
expect(atom.isPackageLoaded('unloaded')).toBeFalsy()
|
||||
expect( -> atom.unloadPackage('unloaded')).toThrow()
|
||||
expect(atom.isPackageLoaded('unloaded')).toBeFalsy()
|
||||
expect(atom.packages.isPackageLoaded('unloaded')).toBeFalsy()
|
||||
expect( -> atom.packages.unloadPackage('unloaded')).toThrow()
|
||||
expect(atom.packages.isPackageLoaded('unloaded')).toBeFalsy()
|
||||
|
||||
describe "when the package is loaded", ->
|
||||
it "no longers reports it as being loaded", ->
|
||||
pack = atom.loadPackage('package-with-main')
|
||||
expect(atom.isPackageLoaded(pack.name)).toBeTruthy()
|
||||
atom.unloadPackage(pack.name)
|
||||
expect(atom.isPackageLoaded(pack.name)).toBeFalsy()
|
||||
pack = atom.packages.loadPackage('package-with-main')
|
||||
expect(atom.packages.isPackageLoaded(pack.name)).toBeTruthy()
|
||||
atom.packages.unloadPackage(pack.name)
|
||||
expect(atom.packages.isPackageLoaded(pack.name)).toBeFalsy()
|
||||
|
||||
describe ".activatePackage(id)", ->
|
||||
describe "atom packages", ->
|
||||
@@ -59,7 +59,7 @@ describe "the `atom` global", ->
|
||||
it "requires the module at the specified path", ->
|
||||
mainModule = require('./fixtures/packages/package-with-main/main-module')
|
||||
spyOn(mainModule, 'activate')
|
||||
pack = atom.activatePackage('package-with-main')
|
||||
pack = atom.packages.activatePackage('package-with-main')
|
||||
expect(mainModule.activate).toHaveBeenCalled()
|
||||
expect(pack.mainModule).toBe mainModule
|
||||
|
||||
@@ -67,15 +67,15 @@ describe "the `atom` global", ->
|
||||
it "requires index.coffee", ->
|
||||
indexModule = require('./fixtures/packages/package-with-index/index')
|
||||
spyOn(indexModule, 'activate')
|
||||
pack = atom.activatePackage('package-with-index')
|
||||
pack = atom.packages.activatePackage('package-with-index')
|
||||
expect(indexModule.activate).toHaveBeenCalled()
|
||||
expect(pack.mainModule).toBe indexModule
|
||||
|
||||
it "assigns config defaults from the module", ->
|
||||
expect(config.get('package-with-config-defaults.numbers.one')).toBeUndefined()
|
||||
atom.activatePackage('package-with-config-defaults')
|
||||
expect(config.get('package-with-config-defaults.numbers.one')).toBe 1
|
||||
expect(config.get('package-with-config-defaults.numbers.two')).toBe 2
|
||||
expect(atom.config.get('package-with-config-defaults.numbers.one')).toBeUndefined()
|
||||
atom.packages.activatePackage('package-with-config-defaults')
|
||||
expect(atom.config.get('package-with-config-defaults.numbers.one')).toBe 1
|
||||
expect(atom.config.get('package-with-config-defaults.numbers.two')).toBe 2
|
||||
|
||||
describe "when the package metadata includes activation events", ->
|
||||
[mainModule, pack] = []
|
||||
@@ -85,24 +85,24 @@ describe "the `atom` global", ->
|
||||
spyOn(mainModule, 'activate').andCallThrough()
|
||||
AtomPackage = require '../src/atom-package'
|
||||
spyOn(AtomPackage.prototype, 'requireMainModule').andCallThrough()
|
||||
pack = atom.activatePackage('package-with-activation-events')
|
||||
pack = atom.packages.activatePackage('package-with-activation-events')
|
||||
|
||||
it "defers requiring/activating the main module until an activation event bubbles to the root view", ->
|
||||
expect(pack.requireMainModule).not.toHaveBeenCalled()
|
||||
expect(mainModule.activate).not.toHaveBeenCalled()
|
||||
rootView.trigger 'activation-event'
|
||||
atom.workspaceView.trigger 'activation-event'
|
||||
expect(mainModule.activate).toHaveBeenCalled()
|
||||
|
||||
it "triggers the activation event on all handlers registered during activation", ->
|
||||
rootView.openSync()
|
||||
editor = rootView.getActiveView()
|
||||
atom.workspaceView.openSync()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
eventHandler = jasmine.createSpy("activation-event")
|
||||
editor.command 'activation-event', eventHandler
|
||||
editor.trigger 'activation-event'
|
||||
editorView.command 'activation-event', eventHandler
|
||||
editorView.trigger 'activation-event'
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
expect(mainModule.activationEventCallCount).toBe 1
|
||||
expect(eventHandler.callCount).toBe 1
|
||||
editor.trigger 'activation-event'
|
||||
editorView.trigger 'activation-event'
|
||||
expect(mainModule.activationEventCallCount).toBe 2
|
||||
expect(eventHandler.callCount).toBe 2
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
@@ -111,23 +111,23 @@ describe "the `atom` global", ->
|
||||
it "does not throw an exception", ->
|
||||
spyOn(console, "error")
|
||||
spyOn(console, "warn").andCallThrough()
|
||||
expect(-> atom.activatePackage('package-without-module')).not.toThrow()
|
||||
expect(-> atom.packages.activatePackage('package-without-module')).not.toThrow()
|
||||
expect(console.error).not.toHaveBeenCalled()
|
||||
expect(console.warn).not.toHaveBeenCalled()
|
||||
|
||||
it "passes the activate method the package's previously serialized state if it exists", ->
|
||||
pack = atom.activatePackage("package-with-serialization")
|
||||
pack = atom.packages.activatePackage("package-with-serialization")
|
||||
expect(pack.mainModule.someNumber).not.toBe 77
|
||||
pack.mainModule.someNumber = 77
|
||||
atom.deactivatePackage("package-with-serialization")
|
||||
atom.packages.deactivatePackage("package-with-serialization")
|
||||
spyOn(pack.mainModule, 'activate').andCallThrough()
|
||||
atom.activatePackage("package-with-serialization")
|
||||
atom.packages.activatePackage("package-with-serialization")
|
||||
expect(pack.mainModule.activate).toHaveBeenCalledWith({someNumber: 77})
|
||||
|
||||
it "logs warning instead of throwing an exception if the package fails to load", ->
|
||||
config.set("core.disabledPackages", [])
|
||||
atom.config.set("core.disabledPackages", [])
|
||||
spyOn(console, "warn")
|
||||
expect(-> atom.activatePackage("package-that-throws-an-exception")).not.toThrow()
|
||||
expect(-> atom.packages.activatePackage("package-that-throws-an-exception")).not.toThrow()
|
||||
expect(console.warn).toHaveBeenCalled()
|
||||
|
||||
describe "keymap loading", ->
|
||||
@@ -137,28 +137,28 @@ describe "the `atom` global", ->
|
||||
element2 = $$ -> @div class: 'test-2'
|
||||
element3 = $$ -> @div class: 'test-3'
|
||||
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-z']).toBeUndefined()
|
||||
expect(keymap.bindingsForElement(element2)['ctrl-z']).toBeUndefined()
|
||||
expect(keymap.bindingsForElement(element3)['ctrl-z']).toBeUndefined()
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)).toHaveLength 0
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element2)).toHaveLength 0
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element3)).toHaveLength 0
|
||||
|
||||
atom.activatePackage("package-with-keymaps")
|
||||
atom.packages.activatePackage("package-with-keymaps")
|
||||
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-z']).toBe "test-1"
|
||||
expect(keymap.bindingsForElement(element2)['ctrl-z']).toBe "test-2"
|
||||
expect(keymap.bindingsForElement(element3)['ctrl-z']).toBeUndefined()
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)[0].command).toBe "test-1"
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element2)[0].command).toBe "test-2"
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element3)).toHaveLength 0
|
||||
|
||||
describe "when the metadata contains a 'keymaps' manifest", ->
|
||||
it "loads only the keymaps specified by the manifest, in the specified order", ->
|
||||
element1 = $$ -> @div class: 'test-1'
|
||||
element3 = $$ -> @div class: 'test-3'
|
||||
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-z']).toBeUndefined()
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)).toHaveLength 0
|
||||
|
||||
atom.activatePackage("package-with-keymaps-manifest")
|
||||
atom.packages.activatePackage("package-with-keymaps-manifest")
|
||||
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-z']).toBe 'keymap-1'
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-n']).toBe 'keymap-2'
|
||||
expect(keymap.bindingsForElement(element3)['ctrl-y']).toBeUndefined()
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)[0].command).toBe 'keymap-1'
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-n', element1)[0].command).toBe 'keymap-2'
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-y', element3)).toHaveLength 0
|
||||
|
||||
describe "menu loading", ->
|
||||
beforeEach ->
|
||||
@@ -171,7 +171,7 @@ describe "the `atom` global", ->
|
||||
|
||||
expect(atom.contextMenu.definitionsForElement(element)).toEqual []
|
||||
|
||||
atom.activatePackage("package-with-menus")
|
||||
atom.packages.activatePackage("package-with-menus")
|
||||
|
||||
expect(atom.menu.template.length).toBe 2
|
||||
expect(atom.menu.template[0].label).toBe "Second to Last"
|
||||
@@ -186,7 +186,7 @@ describe "the `atom` global", ->
|
||||
|
||||
expect(atom.contextMenu.definitionsForElement(element)).toEqual []
|
||||
|
||||
atom.activatePackage("package-with-menus-manifest")
|
||||
atom.packages.activatePackage("package-with-menus-manifest")
|
||||
|
||||
expect(atom.menu.template[0].label).toBe "Second to Last"
|
||||
expect(atom.menu.template[1].label).toBe "Last"
|
||||
@@ -209,7 +209,7 @@ describe "the `atom` global", ->
|
||||
expect(atom.themes.stylesheetElementForId(two)).not.toExist()
|
||||
expect(atom.themes.stylesheetElementForId(three)).not.toExist()
|
||||
|
||||
atom.activatePackage("package-with-stylesheets-manifest")
|
||||
atom.packages.activatePackage("package-with-stylesheets-manifest")
|
||||
|
||||
expect(atom.themes.stylesheetElementForId(one)).toExist()
|
||||
expect(atom.themes.stylesheetElementForId(two)).toExist()
|
||||
@@ -231,7 +231,7 @@ describe "the `atom` global", ->
|
||||
expect(atom.themes.stylesheetElementForId(two)).not.toExist()
|
||||
expect(atom.themes.stylesheetElementForId(three)).not.toExist()
|
||||
|
||||
atom.activatePackage("package-with-stylesheets")
|
||||
atom.packages.activatePackage("package-with-stylesheets")
|
||||
expect(atom.themes.stylesheetElementForId(one)).toExist()
|
||||
expect(atom.themes.stylesheetElementForId(two)).toExist()
|
||||
expect(atom.themes.stylesheetElementForId(three)).toExist()
|
||||
@@ -239,90 +239,91 @@ describe "the `atom` global", ->
|
||||
|
||||
describe "grammar loading", ->
|
||||
it "loads the package's grammars", ->
|
||||
atom.activatePackage('package-with-grammars')
|
||||
expect(syntax.selectGrammar('a.alot').name).toBe 'Alot'
|
||||
expect(syntax.selectGrammar('a.alittle').name).toBe 'Alittle'
|
||||
atom.packages.activatePackage('package-with-grammars')
|
||||
expect(atom.syntax.selectGrammar('a.alot').name).toBe 'Alot'
|
||||
expect(atom.syntax.selectGrammar('a.alittle').name).toBe 'Alittle'
|
||||
|
||||
describe "scoped-property loading", ->
|
||||
it "loads the scoped properties", ->
|
||||
atom.activatePackage("package-with-scoped-properties")
|
||||
expect(syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a'
|
||||
atom.packages.activatePackage("package-with-scoped-properties")
|
||||
expect(atom.syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a'
|
||||
|
||||
describe "textmate packages", ->
|
||||
it "loads the package's grammars", ->
|
||||
expect(syntax.selectGrammar("file.rb").name).toBe "Null Grammar"
|
||||
atom.activatePackage('language-ruby', sync: true)
|
||||
expect(syntax.selectGrammar("file.rb").name).toBe "Ruby"
|
||||
expect(atom.syntax.selectGrammar("file.rb").name).toBe "Null Grammar"
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
expect(atom.syntax.selectGrammar("file.rb").name).toBe "Ruby"
|
||||
|
||||
it "translates the package's scoped properties to Atom terms", ->
|
||||
expect(syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined()
|
||||
atom.activatePackage('language-ruby', sync: true)
|
||||
expect(syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBe '# '
|
||||
expect(atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined()
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
expect(atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBe '# '
|
||||
|
||||
describe "when the package has no grammars but does have preferences", ->
|
||||
it "loads the package's preferences as scoped properties", ->
|
||||
jasmine.unspy(window, 'setTimeout')
|
||||
spyOn(syntax, 'addProperties').andCallThrough()
|
||||
spyOn(atom.syntax, 'addProperties').andCallThrough()
|
||||
|
||||
atom.activatePackage('package-with-preferences-tmbundle')
|
||||
atom.packages.activatePackage('package-with-preferences-tmbundle')
|
||||
|
||||
waitsFor ->
|
||||
syntax.addProperties.callCount > 0
|
||||
atom.syntax.addProperties.callCount > 0
|
||||
|
||||
runs ->
|
||||
expect(syntax.getProperty(['.source.pref'], 'editor.increaseIndentPattern')).toBe '^abc$'
|
||||
expect(atom.syntax.getProperty(['.source.pref'], 'editor.increaseIndentPattern')).toBe '^abc$'
|
||||
|
||||
describe ".deactivatePackage(id)", ->
|
||||
describe "atom packages", ->
|
||||
it "calls `deactivate` on the package's main module if activate was successful", ->
|
||||
pack = atom.activatePackage("package-with-deactivate")
|
||||
expect(atom.isPackageActive("package-with-deactivate")).toBeTruthy()
|
||||
pack = atom.packages.activatePackage("package-with-deactivate")
|
||||
expect(atom.packages.isPackageActive("package-with-deactivate")).toBeTruthy()
|
||||
spyOn(pack.mainModule, 'deactivate').andCallThrough()
|
||||
|
||||
atom.deactivatePackage("package-with-deactivate")
|
||||
atom.packages.deactivatePackage("package-with-deactivate")
|
||||
expect(pack.mainModule.deactivate).toHaveBeenCalled()
|
||||
expect(atom.isPackageActive("package-with-module")).toBeFalsy()
|
||||
expect(atom.packages.isPackageActive("package-with-module")).toBeFalsy()
|
||||
|
||||
spyOn(console, 'warn')
|
||||
badPack = atom.activatePackage("package-that-throws-on-activate")
|
||||
expect(atom.isPackageActive("package-that-throws-on-activate")).toBeTruthy()
|
||||
badPack = atom.packages.activatePackage("package-that-throws-on-activate")
|
||||
expect(atom.packages.isPackageActive("package-that-throws-on-activate")).toBeTruthy()
|
||||
spyOn(badPack.mainModule, 'deactivate').andCallThrough()
|
||||
|
||||
atom.deactivatePackage("package-that-throws-on-activate")
|
||||
atom.packages.deactivatePackage("package-that-throws-on-activate")
|
||||
expect(badPack.mainModule.deactivate).not.toHaveBeenCalled()
|
||||
expect(atom.isPackageActive("package-that-throws-on-activate")).toBeFalsy()
|
||||
expect(atom.packages.isPackageActive("package-that-throws-on-activate")).toBeFalsy()
|
||||
|
||||
it "does not serialize packages that have not been activated called on their main module", ->
|
||||
spyOn(console, 'warn')
|
||||
badPack = atom.activatePackage("package-that-throws-on-activate")
|
||||
badPack = atom.packages.activatePackage("package-that-throws-on-activate")
|
||||
spyOn(badPack.mainModule, 'serialize').andCallThrough()
|
||||
|
||||
atom.deactivatePackage("package-that-throws-on-activate")
|
||||
atom.packages.deactivatePackage("package-that-throws-on-activate")
|
||||
expect(badPack.mainModule.serialize).not.toHaveBeenCalled()
|
||||
|
||||
it "absorbs exceptions that are thrown by the package module's serialize methods", ->
|
||||
spyOn(console, 'error')
|
||||
atom.activatePackage('package-with-serialize-error', immediate: true)
|
||||
atom.activatePackage('package-with-serialization', immediate: true)
|
||||
atom.deactivatePackages()
|
||||
atom.packages.activatePackage('package-with-serialize-error', immediate: true)
|
||||
atom.packages.activatePackage('package-with-serialization', immediate: true)
|
||||
atom.packages.deactivatePackages()
|
||||
expect(atom.packages.packageStates['package-with-serialize-error']).toBeUndefined()
|
||||
expect(atom.packages.packageStates['package-with-serialization']).toEqual someNumber: 1
|
||||
expect(console.error).toHaveBeenCalled()
|
||||
|
||||
it "removes the package's grammars", ->
|
||||
atom.activatePackage('package-with-grammars')
|
||||
atom.deactivatePackage('package-with-grammars')
|
||||
expect(syntax.selectGrammar('a.alot').name).toBe 'Null Grammar'
|
||||
expect(syntax.selectGrammar('a.alittle').name).toBe 'Null Grammar'
|
||||
atom.packages.activatePackage('package-with-grammars')
|
||||
atom.packages.deactivatePackage('package-with-grammars')
|
||||
expect(atom.syntax.selectGrammar('a.alot').name).toBe 'Null Grammar'
|
||||
expect(atom.syntax.selectGrammar('a.alittle').name).toBe 'Null Grammar'
|
||||
|
||||
it "removes the package's keymaps", ->
|
||||
atom.activatePackage('package-with-keymaps')
|
||||
atom.deactivatePackage('package-with-keymaps')
|
||||
expect(keymap.bindingsForElement($$ -> @div class: 'test-1')['ctrl-z']).toBeUndefined()
|
||||
expect(keymap.bindingsForElement($$ -> @div class: 'test-2')['ctrl-z']).toBeUndefined()
|
||||
atom.packages.activatePackage('package-with-keymaps')
|
||||
atom.packages.deactivatePackage('package-with-keymaps')
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', $$ -> @div class: 'test-1')).toHaveLength 0
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', $$ -> @div class: 'test-2')).toHaveLength 0
|
||||
|
||||
it "removes the package's stylesheets", ->
|
||||
atom.activatePackage('package-with-stylesheets')
|
||||
atom.deactivatePackage('package-with-stylesheets')
|
||||
atom.packages.activatePackage('package-with-stylesheets')
|
||||
atom.packages.deactivatePackage('package-with-stylesheets')
|
||||
one = require.resolve("./fixtures/packages/package-with-stylesheets-manifest/stylesheets/1.css")
|
||||
two = require.resolve("./fixtures/packages/package-with-stylesheets-manifest/stylesheets/2.less")
|
||||
three = require.resolve("./fixtures/packages/package-with-stylesheets-manifest/stylesheets/3.css")
|
||||
@@ -331,23 +332,23 @@ describe "the `atom` global", ->
|
||||
expect(atom.themes.stylesheetElementForId(three)).not.toExist()
|
||||
|
||||
it "removes the package's scoped-properties", ->
|
||||
atom.activatePackage("package-with-scoped-properties")
|
||||
expect(syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a'
|
||||
atom.deactivatePackage("package-with-scoped-properties")
|
||||
expect(syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBeUndefined()
|
||||
atom.packages.activatePackage("package-with-scoped-properties")
|
||||
expect(atom.syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a'
|
||||
atom.packages.deactivatePackage("package-with-scoped-properties")
|
||||
expect(atom.syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBeUndefined()
|
||||
|
||||
describe "textmate packages", ->
|
||||
it "removes the package's grammars", ->
|
||||
expect(syntax.selectGrammar("file.rb").name).toBe "Null Grammar"
|
||||
atom.activatePackage('language-ruby', sync: true)
|
||||
expect(syntax.selectGrammar("file.rb").name).toBe "Ruby"
|
||||
atom.deactivatePackage('language-ruby')
|
||||
expect(syntax.selectGrammar("file.rb").name).toBe "Null Grammar"
|
||||
expect(atom.syntax.selectGrammar("file.rb").name).toBe "Null Grammar"
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
expect(atom.syntax.selectGrammar("file.rb").name).toBe "Ruby"
|
||||
atom.packages.deactivatePackage('language-ruby')
|
||||
expect(atom.syntax.selectGrammar("file.rb").name).toBe "Null Grammar"
|
||||
|
||||
it "removes the package's scoped properties", ->
|
||||
atom.activatePackage('language-ruby', sync: true)
|
||||
atom.deactivatePackage('language-ruby')
|
||||
expect(syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined()
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
atom.packages.deactivatePackage('language-ruby')
|
||||
expect(atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined()
|
||||
|
||||
describe ".activate()", ->
|
||||
packageActivator = null
|
||||
@@ -387,7 +388,7 @@ describe "the `atom` global", ->
|
||||
packageName = 'package-with-main'
|
||||
atom.config.pushAtKeyPath('core.disabledPackages', packageName)
|
||||
atom.packages.observeDisabledPackages()
|
||||
expect(config.get('core.disabledPackages')).toContain packageName
|
||||
expect(atom.config.get('core.disabledPackages')).toContain packageName
|
||||
|
||||
pack = atom.packages.enablePackage(packageName)
|
||||
|
||||
@@ -395,19 +396,19 @@ describe "the `atom` global", ->
|
||||
activatedPackages = atom.packages.getActivePackages()
|
||||
expect(loadedPackages).toContain(pack)
|
||||
expect(activatedPackages).toContain(pack)
|
||||
expect(config.get('core.disabledPackages')).not.toContain packageName
|
||||
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
|
||||
|
||||
it ".disablePackage() disables an enabled package", ->
|
||||
packageName = 'package-with-main'
|
||||
atom.packages.activatePackage(packageName)
|
||||
atom.packages.observeDisabledPackages()
|
||||
expect(config.get('core.disabledPackages')).not.toContain packageName
|
||||
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
|
||||
|
||||
pack = atom.packages.disablePackage(packageName)
|
||||
|
||||
activatedPackages = atom.packages.getActivePackages()
|
||||
expect(activatedPackages).not.toContain(pack)
|
||||
expect(config.get('core.disabledPackages')).toContain packageName
|
||||
expect(atom.config.get('core.disabledPackages')).toContain packageName
|
||||
|
||||
describe "with themes", ->
|
||||
beforeEach ->
|
||||
@@ -420,20 +421,20 @@ describe "the `atom` global", ->
|
||||
it ".enablePackage() and .disablePackage() enables and disables a theme", ->
|
||||
packageName = 'theme-with-package-file'
|
||||
|
||||
expect(config.get('core.themes')).not.toContain packageName
|
||||
expect(config.get('core.disabledPackages')).not.toContain packageName
|
||||
expect(atom.config.get('core.themes')).not.toContain packageName
|
||||
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
|
||||
|
||||
# enabling of theme
|
||||
pack = atom.packages.enablePackage(packageName)
|
||||
activatedPackages = atom.packages.getActivePackages()
|
||||
expect(activatedPackages).toContain(pack)
|
||||
expect(config.get('core.themes')).toContain packageName
|
||||
expect(config.get('core.disabledPackages')).not.toContain packageName
|
||||
expect(atom.config.get('core.themes')).toContain packageName
|
||||
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
|
||||
|
||||
# disabling of theme
|
||||
pack = atom.packages.disablePackage(packageName)
|
||||
activatedPackages = atom.packages.getActivePackages()
|
||||
expect(activatedPackages).not.toContain(pack)
|
||||
expect(config.get('core.themes')).not.toContain packageName
|
||||
expect(config.get('core.themes')).not.toContain packageName
|
||||
expect(config.get('core.disabledPackages')).not.toContain packageName
|
||||
expect(atom.config.get('core.themes')).not.toContain packageName
|
||||
expect(atom.config.get('core.themes')).not.toContain packageName
|
||||
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
|
||||
|
||||
+137
-113
@@ -8,176 +8,200 @@ describe "Config", ->
|
||||
|
||||
describe ".get(keyPath)", ->
|
||||
it "allows a key path's value to be read", ->
|
||||
expect(config.set("foo.bar.baz", 42)).toBe 42
|
||||
expect(config.get("foo.bar.baz")).toBe 42
|
||||
expect(config.get("bogus.key.path")).toBeUndefined()
|
||||
expect(atom.config.set("foo.bar.baz", 42)).toBe 42
|
||||
expect(atom.config.get("foo.bar.baz")).toBe 42
|
||||
expect(atom.config.get("bogus.key.path")).toBeUndefined()
|
||||
|
||||
it "returns a deep clone of the key path's value", ->
|
||||
config.set('value', array: [1, b: 2, 3])
|
||||
retrievedValue = config.get('value')
|
||||
atom.config.set('value', array: [1, b: 2, 3])
|
||||
retrievedValue = atom.config.get('value')
|
||||
retrievedValue.array[0] = 4
|
||||
retrievedValue.array[1].b = 2.1
|
||||
expect(config.get('value')).toEqual(array: [1, b: 2, 3])
|
||||
expect(atom.config.get('value')).toEqual(array: [1, b: 2, 3])
|
||||
|
||||
describe ".set(keyPath, value)", ->
|
||||
it "allows a key path's value to be written", ->
|
||||
expect(config.set("foo.bar.baz", 42)).toBe 42
|
||||
expect(config.get("foo.bar.baz")).toBe 42
|
||||
expect(atom.config.set("foo.bar.baz", 42)).toBe 42
|
||||
expect(atom.config.get("foo.bar.baz")).toBe 42
|
||||
|
||||
it "updates observers and saves when a key path is set", ->
|
||||
observeHandler = jasmine.createSpy "observeHandler"
|
||||
config.observe "foo.bar.baz", observeHandler
|
||||
atom.config.observe "foo.bar.baz", observeHandler
|
||||
observeHandler.reset()
|
||||
|
||||
config.set("foo.bar.baz", 42)
|
||||
atom.config.set("foo.bar.baz", 42)
|
||||
|
||||
expect(config.save).toHaveBeenCalled()
|
||||
expect(atom.config.save).toHaveBeenCalled()
|
||||
expect(observeHandler).toHaveBeenCalledWith 42, {previous: undefined}
|
||||
|
||||
describe "when the value equals the default value", ->
|
||||
it "does not store the value", ->
|
||||
config.setDefaults("foo", same: 1, changes: 1)
|
||||
expect(config.settings.foo).toBeUndefined()
|
||||
config.set('foo.same', 1)
|
||||
config.set('foo.changes', 2)
|
||||
expect(config.settings.foo).toEqual {changes: 2}
|
||||
atom.config.setDefaults("foo", same: 1, changes: 1)
|
||||
expect(atom.config.settings.foo).toBeUndefined()
|
||||
atom.config.set('foo.same', 1)
|
||||
atom.config.set('foo.changes', 2)
|
||||
expect(atom.config.settings.foo).toEqual {changes: 2}
|
||||
|
||||
config.set('foo.changes', 1)
|
||||
expect(config.settings.foo).toEqual {}
|
||||
atom.config.set('foo.changes', 1)
|
||||
expect(atom.config.settings.foo).toEqual {}
|
||||
|
||||
describe ".toggle(keyPath)", ->
|
||||
it "negates the boolean value of the current key path value", ->
|
||||
atom.config.set('foo.a', 1)
|
||||
atom.config.toggle('foo.a')
|
||||
expect(atom.config.get('foo.a')).toBe false
|
||||
|
||||
atom.config.set('foo.a', '')
|
||||
atom.config.toggle('foo.a')
|
||||
expect(atom.config.get('foo.a')).toBe true
|
||||
|
||||
atom.config.set('foo.a', null)
|
||||
atom.config.toggle('foo.a')
|
||||
expect(atom.config.get('foo.a')).toBe true
|
||||
|
||||
atom.config.set('foo.a', true)
|
||||
atom.config.toggle('foo.a')
|
||||
expect(atom.config.get('foo.a')).toBe false
|
||||
|
||||
describe ".pushAtKeyPath(keyPath, value)", ->
|
||||
it "pushes the given value to the array at the key path and updates observers", ->
|
||||
config.set("foo.bar.baz", ["a"])
|
||||
atom.config.set("foo.bar.baz", ["a"])
|
||||
observeHandler = jasmine.createSpy "observeHandler"
|
||||
config.observe "foo.bar.baz", observeHandler
|
||||
atom.config.observe "foo.bar.baz", observeHandler
|
||||
observeHandler.reset()
|
||||
|
||||
expect(config.pushAtKeyPath("foo.bar.baz", "b")).toBe 2
|
||||
expect(config.get("foo.bar.baz")).toEqual ["a", "b"]
|
||||
expect(observeHandler).toHaveBeenCalledWith config.get("foo.bar.baz"), {previous: ['a']}
|
||||
expect(atom.config.pushAtKeyPath("foo.bar.baz", "b")).toBe 2
|
||||
expect(atom.config.get("foo.bar.baz")).toEqual ["a", "b"]
|
||||
expect(observeHandler).toHaveBeenCalledWith atom.config.get("foo.bar.baz"), {previous: ['a']}
|
||||
|
||||
describe ".unshiftAtKeyPath(keyPath, value)", ->
|
||||
it "unshifts the given value to the array at the key path and updates observers", ->
|
||||
config.set("foo.bar.baz", ["b"])
|
||||
atom.config.set("foo.bar.baz", ["b"])
|
||||
observeHandler = jasmine.createSpy "observeHandler"
|
||||
config.observe "foo.bar.baz", observeHandler
|
||||
atom.config.observe "foo.bar.baz", observeHandler
|
||||
observeHandler.reset()
|
||||
|
||||
expect(config.unshiftAtKeyPath("foo.bar.baz", "a")).toBe 2
|
||||
expect(config.get("foo.bar.baz")).toEqual ["a", "b"]
|
||||
expect(observeHandler).toHaveBeenCalledWith config.get("foo.bar.baz"), {previous: ['b']}
|
||||
expect(atom.config.unshiftAtKeyPath("foo.bar.baz", "a")).toBe 2
|
||||
expect(atom.config.get("foo.bar.baz")).toEqual ["a", "b"]
|
||||
expect(observeHandler).toHaveBeenCalledWith atom.config.get("foo.bar.baz"), {previous: ['b']}
|
||||
|
||||
describe ".removeAtKeyPath(keyPath, value)", ->
|
||||
it "removes the given value from the array at the key path and updates observers", ->
|
||||
config.set("foo.bar.baz", ["a", "b", "c"])
|
||||
atom.config.set("foo.bar.baz", ["a", "b", "c"])
|
||||
observeHandler = jasmine.createSpy "observeHandler"
|
||||
config.observe "foo.bar.baz", observeHandler
|
||||
atom.config.observe "foo.bar.baz", observeHandler
|
||||
observeHandler.reset()
|
||||
|
||||
expect(config.removeAtKeyPath("foo.bar.baz", "b")).toEqual ["a", "c"]
|
||||
expect(config.get("foo.bar.baz")).toEqual ["a", "c"]
|
||||
expect(observeHandler).toHaveBeenCalledWith config.get("foo.bar.baz"), {previous: ['a', 'b', 'c']}
|
||||
expect(atom.config.removeAtKeyPath("foo.bar.baz", "b")).toEqual ["a", "c"]
|
||||
expect(atom.config.get("foo.bar.baz")).toEqual ["a", "c"]
|
||||
expect(observeHandler).toHaveBeenCalledWith atom.config.get("foo.bar.baz"), {previous: ['a', 'b', 'c']}
|
||||
|
||||
describe ".getPositiveInt(keyPath, defaultValue)", ->
|
||||
it "returns the proper current or default value", ->
|
||||
config.set('editor.preferredLineLength', 0)
|
||||
expect(config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
config.set('editor.preferredLineLength', -1234)
|
||||
expect(config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
config.set('editor.preferredLineLength', 'abcd')
|
||||
expect(config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
config.set('editor.preferredLineLength', null)
|
||||
expect(config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
atom.config.set('editor.preferredLineLength', 0)
|
||||
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
atom.config.set('editor.preferredLineLength', -1234)
|
||||
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
atom.config.set('editor.preferredLineLength', 'abcd')
|
||||
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
atom.config.set('editor.preferredLineLength', null)
|
||||
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
|
||||
describe ".save()", ->
|
||||
nodeFs = require 'fs'
|
||||
|
||||
beforeEach ->
|
||||
spyOn(nodeFs, 'writeFileSync')
|
||||
jasmine.unspy config, 'save'
|
||||
jasmine.unspy atom.config, 'save'
|
||||
|
||||
describe "when ~/.atom/config.json exists", ->
|
||||
it "writes any non-default properties to ~/.atom/config.json", ->
|
||||
config.configFilePath = path.join(config.configDirPath, "config.json")
|
||||
config.set("a.b.c", 1)
|
||||
config.set("a.b.d", 2)
|
||||
config.set("x.y.z", 3)
|
||||
config.setDefaults("a.b", e: 4, f: 5)
|
||||
atom.config.configFilePath = path.join(atom.config.configDirPath, "atom.config.json")
|
||||
atom.config.set("a.b.c", 1)
|
||||
atom.config.set("a.b.d", 2)
|
||||
atom.config.set("x.y.z", 3)
|
||||
atom.config.setDefaults("a.b", e: 4, f: 5)
|
||||
|
||||
nodeFs.writeFileSync.reset()
|
||||
config.save()
|
||||
atom.config.save()
|
||||
|
||||
expect(nodeFs.writeFileSync.argsForCall[0][0]).toBe(path.join(config.configDirPath, "config.json"))
|
||||
expect(nodeFs.writeFileSync.argsForCall[0][0]).toBe(path.join(atom.config.configDirPath, "atom.config.json"))
|
||||
writtenConfig = JSON.parse(nodeFs.writeFileSync.argsForCall[0][1])
|
||||
expect(writtenConfig).toEqual config.settings
|
||||
expect(writtenConfig).toEqual atom.config.settings
|
||||
|
||||
describe "when ~/.atom/config.json doesn't exist", ->
|
||||
it "writes any non-default properties to ~/.atom/config.cson", ->
|
||||
config.configFilePath = path.join(config.configDirPath, "config.cson")
|
||||
config.set("a.b.c", 1)
|
||||
config.set("a.b.d", 2)
|
||||
config.set("x.y.z", 3)
|
||||
config.setDefaults("a.b", e: 4, f: 5)
|
||||
atom.config.configFilePath = path.join(atom.config.configDirPath, "atom.config.cson")
|
||||
atom.config.set("a.b.c", 1)
|
||||
atom.config.set("a.b.d", 2)
|
||||
atom.config.set("x.y.z", 3)
|
||||
atom.config.setDefaults("a.b", e: 4, f: 5)
|
||||
|
||||
nodeFs.writeFileSync.reset()
|
||||
config.save()
|
||||
atom.config.save()
|
||||
|
||||
expect(nodeFs.writeFileSync.argsForCall[0][0]).toBe(path.join(config.configDirPath, "config.cson"))
|
||||
expect(nodeFs.writeFileSync.argsForCall[0][0]).toBe(path.join(atom.config.configDirPath, "atom.config.cson"))
|
||||
CoffeeScript = require 'coffee-script'
|
||||
writtenConfig = CoffeeScript.eval(nodeFs.writeFileSync.argsForCall[0][1], bare: true)
|
||||
expect(writtenConfig).toEqual config.settings
|
||||
expect(writtenConfig).toEqual atom.config.settings
|
||||
|
||||
describe ".setDefaults(keyPath, defaults)", ->
|
||||
it "assigns any previously-unassigned keys to the object at the key path", ->
|
||||
config.set("foo.bar.baz", a: 1)
|
||||
config.setDefaults("foo.bar.baz", a: 2, b: 3, c: 4)
|
||||
expect(config.get("foo.bar.baz.a")).toBe 1
|
||||
expect(config.get("foo.bar.baz.b")).toBe 3
|
||||
expect(config.get("foo.bar.baz.c")).toBe 4
|
||||
atom.config.set("foo.bar.baz", a: 1)
|
||||
atom.config.setDefaults("foo.bar.baz", a: 2, b: 3, c: 4)
|
||||
expect(atom.config.get("foo.bar.baz.a")).toBe 1
|
||||
expect(atom.config.get("foo.bar.baz.b")).toBe 3
|
||||
expect(atom.config.get("foo.bar.baz.c")).toBe 4
|
||||
|
||||
config.setDefaults("foo.quux", x: 0, y: 1)
|
||||
expect(config.get("foo.quux.x")).toBe 0
|
||||
expect(config.get("foo.quux.y")).toBe 1
|
||||
atom.config.setDefaults("foo.quux", x: 0, y: 1)
|
||||
expect(atom.config.get("foo.quux.x")).toBe 0
|
||||
expect(atom.config.get("foo.quux.y")).toBe 1
|
||||
|
||||
describe ".observe(keyPath)", ->
|
||||
observeHandler = null
|
||||
[observeHandler, observeSubscription] = []
|
||||
|
||||
beforeEach ->
|
||||
observeHandler = jasmine.createSpy("observeHandler")
|
||||
config.set("foo.bar.baz", "value 1")
|
||||
config.observe "foo.bar.baz", observeHandler
|
||||
atom.config.set("foo.bar.baz", "value 1")
|
||||
observeSubscription = atom.config.observe "foo.bar.baz", observeHandler
|
||||
|
||||
it "fires the given callback with the current value at the keypath", ->
|
||||
expect(observeHandler).toHaveBeenCalledWith("value 1")
|
||||
|
||||
it "fires the callback every time the observed value changes", ->
|
||||
observeHandler.reset() # clear the initial call
|
||||
config.set('foo.bar.baz', "value 2")
|
||||
atom.config.set('foo.bar.baz', "value 2")
|
||||
expect(observeHandler).toHaveBeenCalledWith("value 2", {previous: 'value 1'})
|
||||
observeHandler.reset()
|
||||
|
||||
config.set('foo.bar.baz', "value 1")
|
||||
atom.config.set('foo.bar.baz', "value 1")
|
||||
expect(observeHandler).toHaveBeenCalledWith("value 1", {previous: 'value 2'})
|
||||
|
||||
it "fires the callback when the observed value is deleted", ->
|
||||
observeHandler.reset() # clear the initial call
|
||||
config.set('foo.bar.baz', undefined)
|
||||
atom.config.set('foo.bar.baz', undefined)
|
||||
expect(observeHandler).toHaveBeenCalledWith(undefined, {previous: 'value 1'})
|
||||
|
||||
it "fires the callback when the full key path goes into and out of existence", ->
|
||||
observeHandler.reset() # clear the initial call
|
||||
config.set("foo.bar", undefined)
|
||||
atom.config.set("foo.bar", undefined)
|
||||
|
||||
expect(observeHandler).toHaveBeenCalledWith(undefined, {previous: 'value 1'})
|
||||
observeHandler.reset()
|
||||
|
||||
config.set("foo.bar.baz", "i'm back")
|
||||
atom.config.set("foo.bar.baz", "i'm back")
|
||||
expect(observeHandler).toHaveBeenCalledWith("i'm back", {previous: undefined})
|
||||
|
||||
it "does not fire the callback once the observe subscription is off'ed", ->
|
||||
observeHandler.reset() # clear the initial call
|
||||
observeSubscription.off()
|
||||
atom.config.set('foo.bar.baz', "value 2")
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe ".initializeConfigDirectory()", ->
|
||||
beforeEach ->
|
||||
config.configDirPath = dotAtomPath
|
||||
expect(fs.existsSync(config.configDirPath)).toBeFalsy()
|
||||
atom.config.configDirPath = dotAtomPath
|
||||
expect(fs.existsSync(atom.config.configDirPath)).toBeFalsy()
|
||||
|
||||
afterEach ->
|
||||
fs.removeSync(dotAtomPath) if fs.existsSync(dotAtomPath)
|
||||
@@ -186,94 +210,94 @@ describe "Config", ->
|
||||
it "copies the contents of dot-atom to ~/.atom", ->
|
||||
initializationDone = false
|
||||
jasmine.unspy(window, "setTimeout")
|
||||
config.initializeConfigDirectory ->
|
||||
atom.config.initializeConfigDirectory ->
|
||||
initializationDone = true
|
||||
|
||||
waitsFor -> initializationDone
|
||||
|
||||
runs ->
|
||||
expect(fs.existsSync(config.configDirPath)).toBeTruthy()
|
||||
expect(fs.existsSync(path.join(config.configDirPath, 'packages'))).toBeTruthy()
|
||||
expect(fs.existsSync(path.join(config.configDirPath, 'snippets'))).toBeTruthy()
|
||||
expect(fs.isFileSync(path.join(config.configDirPath, 'config.cson'))).toBeTruthy()
|
||||
expect(fs.existsSync(atom.config.configDirPath)).toBeTruthy()
|
||||
expect(fs.existsSync(path.join(atom.config.configDirPath, 'packages'))).toBeTruthy()
|
||||
expect(fs.existsSync(path.join(atom.config.configDirPath, 'snippets'))).toBeTruthy()
|
||||
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'config.cson'))).toBeTruthy()
|
||||
|
||||
describe ".loadUserConfig()", ->
|
||||
beforeEach ->
|
||||
config.configDirPath = dotAtomPath
|
||||
config.configFilePath = path.join(config.configDirPath, "config.cson")
|
||||
expect(fs.existsSync(config.configDirPath)).toBeFalsy()
|
||||
atom.config.configDirPath = dotAtomPath
|
||||
atom.config.configFilePath = path.join(atom.config.configDirPath, "atom.config.cson")
|
||||
expect(fs.existsSync(atom.config.configDirPath)).toBeFalsy()
|
||||
|
||||
afterEach ->
|
||||
fs.removeSync(dotAtomPath) if fs.existsSync(dotAtomPath)
|
||||
|
||||
describe "when the config file contains valid cson", ->
|
||||
beforeEach ->
|
||||
fs.writeFileSync(config.configFilePath, "foo: bar: 'baz'")
|
||||
config.loadUserConfig()
|
||||
fs.writeFileSync(atom.config.configFilePath, "foo: bar: 'baz'")
|
||||
atom.config.loadUserConfig()
|
||||
|
||||
it "updates the config data based on the file contents", ->
|
||||
expect(config.get("foo.bar")).toBe 'baz'
|
||||
expect(atom.config.get("foo.bar")).toBe 'baz'
|
||||
|
||||
describe "when the config file contains invalid cson", ->
|
||||
beforeEach ->
|
||||
spyOn(console, 'error')
|
||||
fs.writeFileSync(config.configFilePath, "{{{{{")
|
||||
fs.writeFileSync(atom.config.configFilePath, "{{{{{")
|
||||
|
||||
it "logs an error to the console and does not overwrite the config file on a subsequent save", ->
|
||||
config.loadUserConfig()
|
||||
atom.config.loadUserConfig()
|
||||
expect(console.error).toHaveBeenCalled()
|
||||
config.set("hair", "blonde") # trigger a save
|
||||
expect(config.save).not.toHaveBeenCalled()
|
||||
atom.config.set("hair", "blonde") # trigger a save
|
||||
expect(atom.config.save).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the config file does not exist", ->
|
||||
it "creates it with an empty object", ->
|
||||
fs.makeTreeSync(config.configDirPath)
|
||||
config.loadUserConfig()
|
||||
expect(fs.existsSync(config.configFilePath)).toBe true
|
||||
expect(CSON.readFileSync(config.configFilePath)).toEqual {}
|
||||
fs.makeTreeSync(atom.config.configDirPath)
|
||||
atom.config.loadUserConfig()
|
||||
expect(fs.existsSync(atom.config.configFilePath)).toBe true
|
||||
expect(CSON.readFileSync(atom.config.configFilePath)).toEqual {}
|
||||
|
||||
describe ".observeUserConfig()", ->
|
||||
updatedHandler = null
|
||||
|
||||
beforeEach ->
|
||||
config.configDirPath = dotAtomPath
|
||||
config.configFilePath = path.join(config.configDirPath, "config.cson")
|
||||
expect(fs.existsSync(config.configDirPath)).toBeFalsy()
|
||||
fs.writeFileSync(config.configFilePath, "foo: bar: 'baz'")
|
||||
config.loadUserConfig()
|
||||
config.observeUserConfig()
|
||||
atom.config.configDirPath = dotAtomPath
|
||||
atom.config.configFilePath = path.join(atom.config.configDirPath, "atom.config.cson")
|
||||
expect(fs.existsSync(atom.config.configDirPath)).toBeFalsy()
|
||||
fs.writeFileSync(atom.config.configFilePath, "foo: bar: 'baz'")
|
||||
atom.config.loadUserConfig()
|
||||
atom.config.observeUserConfig()
|
||||
updatedHandler = jasmine.createSpy("updatedHandler")
|
||||
config.on 'updated', updatedHandler
|
||||
atom.config.on 'updated', updatedHandler
|
||||
|
||||
afterEach ->
|
||||
config.unobserveUserConfig()
|
||||
atom.config.unobserveUserConfig()
|
||||
fs.removeSync(dotAtomPath) if fs.existsSync(dotAtomPath)
|
||||
|
||||
describe "when the config file changes to contain valid cson", ->
|
||||
it "updates the config data", ->
|
||||
fs.writeFileSync(config.configFilePath, "foo: { bar: 'quux', baz: 'bar'}")
|
||||
fs.writeFileSync(atom.config.configFilePath, "foo: { bar: 'quux', baz: 'bar'}")
|
||||
waitsFor 'update event', -> updatedHandler.callCount > 0
|
||||
runs ->
|
||||
expect(config.get('foo.bar')).toBe 'quux'
|
||||
expect(config.get('foo.baz')).toBe 'bar'
|
||||
expect(atom.config.get('foo.bar')).toBe 'quux'
|
||||
expect(atom.config.get('foo.baz')).toBe 'bar'
|
||||
|
||||
describe "when the config file changes to contain invalid cson", ->
|
||||
beforeEach ->
|
||||
spyOn(console, 'error')
|
||||
fs.writeFileSync(config.configFilePath, "}}}")
|
||||
fs.writeFileSync(atom.config.configFilePath, "}}}")
|
||||
waitsFor "error to be logged", -> console.error.callCount > 0
|
||||
|
||||
it "logs a warning and does not update config data", ->
|
||||
expect(updatedHandler.callCount).toBe 0
|
||||
expect(config.get('foo.bar')).toBe 'baz'
|
||||
config.set("hair", "blonde") # trigger a save
|
||||
expect(config.save).not.toHaveBeenCalled()
|
||||
expect(atom.config.get('foo.bar')).toBe 'baz'
|
||||
atom.config.set("hair", "blonde") # trigger a save
|
||||
expect(atom.config.save).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the config file subsequently changes again to contain valid cson", ->
|
||||
beforeEach ->
|
||||
fs.writeFileSync(config.configFilePath, "foo: bar: 'baz'")
|
||||
fs.writeFileSync(atom.config.configFilePath, "foo: bar: 'baz'")
|
||||
waitsFor 'update event', -> updatedHandler.callCount > 0
|
||||
|
||||
it "updates the config data and resumes saving", ->
|
||||
config.set("hair", "blonde")
|
||||
expect(config.save).toHaveBeenCalled()
|
||||
atom.config.set("hair", "blonde")
|
||||
expect(atom.config.save).toHaveBeenCalled()
|
||||
|
||||
@@ -5,8 +5,8 @@ describe "DisplayBuffer", ->
|
||||
[displayBuffer, buffer, changeHandler, tabLength] = []
|
||||
beforeEach ->
|
||||
tabLength = 2
|
||||
atom.activatePackage('language-javascript', sync: true)
|
||||
buffer = project.bufferForPathSync('sample.js')
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength})
|
||||
changeHandler = jasmine.createSpy 'changeHandler'
|
||||
displayBuffer.on 'changed', changeHandler
|
||||
@@ -20,7 +20,7 @@ describe "DisplayBuffer", ->
|
||||
displayBuffer.setTabLength(4)
|
||||
displayBuffer.setEditorWidthInChars(64)
|
||||
displayBuffer.createFold(2, 4)
|
||||
displayBuffer2 = deserialize(displayBuffer.serialize())
|
||||
displayBuffer2 = atom.deserializers.deserialize(displayBuffer.serialize())
|
||||
expect(displayBuffer2.id).toBe displayBuffer.id
|
||||
expect(displayBuffer2.buffer).toBe displayBuffer.buffer
|
||||
expect(displayBuffer2.tokenizedBuffer.buffer).toBe displayBuffer.tokenizedBuffer.buffer
|
||||
@@ -66,14 +66,14 @@ describe "DisplayBuffer", ->
|
||||
describe "rendering of soft-wrapped lines", ->
|
||||
describe "when editor.softWrapAtPreferredLineLength is set", ->
|
||||
it "uses the preferred line length as the soft wrap column when it is less than the configured soft wrap column", ->
|
||||
config.set('editor.preferredLineLength', 100)
|
||||
config.set('editor.softWrapAtPreferredLineLength', true)
|
||||
atom.config.set('editor.preferredLineLength', 100)
|
||||
atom.config.set('editor.softWrapAtPreferredLineLength', true)
|
||||
expect(displayBuffer.lineForRow(10).text).toBe ' return '
|
||||
|
||||
config.set('editor.preferredLineLength', 5)
|
||||
atom.config.set('editor.preferredLineLength', 5)
|
||||
expect(displayBuffer.lineForRow(10).text).toBe 'funct'
|
||||
|
||||
config.set('editor.softWrapAtPreferredLineLength', false)
|
||||
atom.config.set('editor.softWrapAtPreferredLineLength', false)
|
||||
expect(displayBuffer.lineForRow(10).text).toBe ' return '
|
||||
|
||||
describe "when the line is shorter than the max line length", ->
|
||||
@@ -154,7 +154,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe "when a newline is inserted, deleted, and re-inserted at the end of a wrapped line (regression)", ->
|
||||
it "correctly renders the original wrapped line", ->
|
||||
buffer = project.buildBufferSync(null, '')
|
||||
buffer = atom.project.buildBufferSync(null, '')
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength, editorWidthInChars: 30, softWrap: true})
|
||||
|
||||
buffer.insert([0, 0], "the quick brown fox jumps over the lazy dog.")
|
||||
@@ -206,7 +206,7 @@ describe "DisplayBuffer", ->
|
||||
beforeEach ->
|
||||
displayBuffer.destroy()
|
||||
buffer.release()
|
||||
buffer = project.bufferForPathSync('two-hundred.txt')
|
||||
buffer = atom.project.bufferForPathSync('two-hundred.txt')
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength})
|
||||
displayBuffer.on 'changed', changeHandler
|
||||
|
||||
@@ -600,7 +600,7 @@ describe "DisplayBuffer", ->
|
||||
it "unsubscribes all display buffer markers from their underlying buffer marker (regression)", ->
|
||||
marker = displayBuffer.markBufferPosition([12, 2])
|
||||
displayBuffer.destroy()
|
||||
expect(marker.bufferMarker.subscriptionCount()).toBe 0
|
||||
expect(marker.bufferMarker.getSubscriptionCount()).toBe 0
|
||||
expect( -> buffer.insert([12, 2], '\n')).not.toThrow()
|
||||
|
||||
describe "markers", ->
|
||||
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+2613
-2763
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -1,5 +1,5 @@
|
||||
class Foo
|
||||
registerDeserializer(this)
|
||||
atom.deserializers.add(this)
|
||||
@deserialize: ({data}) -> new Foo(data)
|
||||
constructor: (@data) ->
|
||||
|
||||
@@ -9,5 +9,5 @@ module.exports =
|
||||
|
||||
activate: ->
|
||||
@activateCallCount++
|
||||
rootView.getActiveView()?.command 'activation-event', =>
|
||||
atom.workspaceView.getActiveView()?.command 'activation-event', =>
|
||||
@activationEventCallCount++
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# This package loads async, otherwise it would log errors when it
|
||||
# is automatically serialized when rootView is deactivatated
|
||||
# is automatically serialized when workspaceView is deactivatated
|
||||
|
||||
'main': 'index.coffee'
|
||||
'activationEvents': ['activation-event']
|
||||
|
||||
+24
-23
@@ -182,10 +182,10 @@ describe "Git", ->
|
||||
|
||||
beforeEach ->
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'working-dir'))
|
||||
modifiedPath = project.resolve('git/working-dir/file.txt')
|
||||
modifiedPath = atom.project.resolve('git/working-dir/file.txt')
|
||||
originalModifiedPathText = fs.readFileSync(modifiedPath, 'utf8')
|
||||
newPath = project.resolve('git/working-dir/untracked.txt')
|
||||
cleanPath = project.resolve('git/working-dir/other.txt')
|
||||
newPath = atom.project.resolve('git/working-dir/untracked.txt')
|
||||
cleanPath = atom.project.resolve('git/working-dir/other.txt')
|
||||
fs.writeFileSync(newPath, '')
|
||||
|
||||
afterEach ->
|
||||
@@ -208,44 +208,44 @@ describe "Git", ->
|
||||
expect(repo.isStatusModified(statuses[modifiedPath])).toBeTruthy()
|
||||
|
||||
describe "buffer events", ->
|
||||
[originalContent, editSession] = []
|
||||
[originalContent, editor] = []
|
||||
|
||||
beforeEach ->
|
||||
editSession = project.openSync('sample.js')
|
||||
originalContent = editSession.getText()
|
||||
editor = atom.project.openSync('sample.js')
|
||||
originalContent = editor.getText()
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(editSession.getPath(), originalContent)
|
||||
fs.writeFileSync(editor.getPath(), originalContent)
|
||||
|
||||
it "emits a status-changed event when a buffer is saved", ->
|
||||
editSession.insertNewline()
|
||||
editor.insertNewline()
|
||||
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
project.getRepo().on 'status-changed', statusHandler
|
||||
editSession.save()
|
||||
atom.project.getRepo().on 'status-changed', statusHandler
|
||||
editor.save()
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler).toHaveBeenCalledWith editSession.getPath(), 256
|
||||
expect(statusHandler).toHaveBeenCalledWith editor.getPath(), 256
|
||||
|
||||
it "emits a status-changed event when a buffer is reloaded", ->
|
||||
fs.writeFileSync(editSession.getPath(), 'changed')
|
||||
fs.writeFileSync(editor.getPath(), 'changed')
|
||||
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
project.getRepo().on 'status-changed', statusHandler
|
||||
editSession.getBuffer().reload()
|
||||
atom.project.getRepo().on 'status-changed', statusHandler
|
||||
editor.getBuffer().reload()
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler).toHaveBeenCalledWith editSession.getPath(), 256
|
||||
editSession.getBuffer().reload()
|
||||
expect(statusHandler).toHaveBeenCalledWith editor.getPath(), 256
|
||||
editor.getBuffer().reload()
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
|
||||
it "emits a status-changed event when a buffer's path changes", ->
|
||||
fs.writeFileSync(editSession.getPath(), 'changed')
|
||||
fs.writeFileSync(editor.getPath(), 'changed')
|
||||
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
project.getRepo().on 'status-changed', statusHandler
|
||||
editSession.getBuffer().trigger 'path-changed'
|
||||
atom.project.getRepo().on 'status-changed', statusHandler
|
||||
editor.getBuffer().emit 'path-changed'
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler).toHaveBeenCalledWith editSession.getPath(), 256
|
||||
editSession.getBuffer().trigger 'path-changed'
|
||||
expect(statusHandler).toHaveBeenCalledWith editor.getPath(), 256
|
||||
editor.getBuffer().emit 'path-changed'
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
|
||||
describe "when a project is deserialized", ->
|
||||
@@ -256,8 +256,9 @@ describe "Git", ->
|
||||
project2?.destroy()
|
||||
|
||||
it "subscribes to all the serialized buffers in the project", ->
|
||||
project.openSync('sample.js')
|
||||
project2 = deserialize(project.serialize())
|
||||
atom.project.openSync('sample.js')
|
||||
#TODO Replace with atom.replicate().project when Atom is a telepath model
|
||||
project2 = atom.replicate().get('project')
|
||||
buffer = project2.getBuffers()[0]
|
||||
|
||||
waitsFor ->
|
||||
|
||||
+77
-112
@@ -1,14 +1,15 @@
|
||||
path = require 'path'
|
||||
|
||||
Keymap = require '../src/keymap'
|
||||
{$, $$, RootView} = require 'atom'
|
||||
{$, $$, WorkspaceView} = require 'atom'
|
||||
|
||||
describe "Keymap", ->
|
||||
fragment = null
|
||||
keymap = null
|
||||
resourcePath = atom.getLoadSettings().resourcePath
|
||||
|
||||
beforeEach ->
|
||||
keymap = new Keymap
|
||||
keymap = new Keymap({configDirPath: atom.getConfigDirPath(), resourcePath})
|
||||
fragment = $ """
|
||||
<div class="command-mode">
|
||||
<div class="child-node">
|
||||
@@ -20,25 +21,19 @@ describe "Keymap", ->
|
||||
describe ".handleKeyEvent(event)", ->
|
||||
deleteCharHandler = null
|
||||
insertCharHandler = null
|
||||
commandZHandler = null
|
||||
|
||||
beforeEach ->
|
||||
keymap.bindKeys '.command-mode', 'x': 'deleteChar'
|
||||
keymap.bindKeys '.insert-mode', 'x': 'insertChar'
|
||||
keymap.bindKeys 'name', '.command-mode', 'x': 'deleteChar'
|
||||
keymap.bindKeys 'name', '.insert-mode', 'x': 'insertChar'
|
||||
keymap.bindKeys 'name', '.command-mode', 'cmd-z': 'commandZPressed'
|
||||
|
||||
deleteCharHandler = jasmine.createSpy('deleteCharHandler')
|
||||
insertCharHandler = jasmine.createSpy('insertCharHandler')
|
||||
commandZHandler = jasmine.createSpy('commandZHandler')
|
||||
fragment.on 'deleteChar', deleteCharHandler
|
||||
fragment.on 'insertChar', insertCharHandler
|
||||
|
||||
it "adds a 'keystrokes' string to the event object", ->
|
||||
event = keydownEvent('x', altKey: true, metaKey: true)
|
||||
keymap.handleKeyEvent(event)
|
||||
expect(event.keystrokes).toBe 'alt-meta-x'
|
||||
|
||||
event = keydownEvent(',', metaKey: true)
|
||||
event.which = 188
|
||||
keymap.handleKeyEvent(event)
|
||||
expect(event.keystrokes).toBe 'meta-,'
|
||||
fragment.on 'commandZPressed', commandZHandler
|
||||
|
||||
describe "when no binding matches the event's keystroke", ->
|
||||
it "does not return false so the event continues to propagate", ->
|
||||
@@ -46,10 +41,11 @@ describe "Keymap", ->
|
||||
|
||||
describe "when a non-English keyboard language is used", ->
|
||||
it "uses the physical character pressed instead of the character it maps to in the current language", ->
|
||||
event = keydownEvent('U+03B6', metaKey: true) # This is the 'z' key using the Greek keyboard layout
|
||||
event.which = 122
|
||||
keymap.handleKeyEvent(event)
|
||||
expect(event.keystrokes).toBe 'meta-z'
|
||||
event = keydownEvent('U+03B6', metaKey: true, which: 122, target: fragment[0]) # This is the 'z' key using the Greek keyboard layout
|
||||
result = keymap.handleKeyEvent(event)
|
||||
|
||||
expect(result).toBe(false)
|
||||
expect(commandZHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when at least one binding fully matches the event's keystroke", ->
|
||||
describe "when the event's target node matches a selector with a matching binding", ->
|
||||
@@ -66,9 +62,6 @@ describe "Keymap", ->
|
||||
keymap.handleKeyEvent(event)
|
||||
expect(deleteCharHandler).not.toHaveBeenCalled()
|
||||
expect(insertCharHandler).toHaveBeenCalled()
|
||||
commandEvent = insertCharHandler.argsForCall[0][0]
|
||||
expect(commandEvent.keyEvent).toBe event
|
||||
expect(event.keystrokes).toBe 'x'
|
||||
|
||||
describe "when the event's target node *descends* from a selector with a matching binding", ->
|
||||
it "triggers the command event associated with that binding on the target node and returns false", ->
|
||||
@@ -87,7 +80,7 @@ describe "Keymap", ->
|
||||
|
||||
describe "when the event's target node descends from multiple nodes that match selectors with a binding", ->
|
||||
beforeEach ->
|
||||
keymap.bindKeys '.child-node', 'x': 'foo'
|
||||
keymap.bindKeys 'name', '.child-node', 'x': 'foo'
|
||||
|
||||
it "only triggers bindings on selectors associated with the closest ancestor node", ->
|
||||
fooHandler = jasmine.createSpy 'fooHandler'
|
||||
@@ -124,10 +117,10 @@ describe "Keymap", ->
|
||||
describe "when the event bubbles to a node that matches multiple selectors", ->
|
||||
describe "when the matching selectors differ in specificity", ->
|
||||
it "triggers the binding for the most specific selector", ->
|
||||
keymap.bindKeys 'div .child-node', 'x': 'foo'
|
||||
keymap.bindKeys '.command-mode .child-node !important', 'x': 'baz'
|
||||
keymap.bindKeys '.command-mode .child-node', 'x': 'quux'
|
||||
keymap.bindKeys '.child-node', 'x': 'bar'
|
||||
keymap.bindKeys 'name', 'div .child-node', 'x': 'foo'
|
||||
keymap.bindKeys 'name', '.command-mode .child-node !important', 'x': 'baz'
|
||||
keymap.bindKeys 'name', '.command-mode .child-node', 'x': 'quux'
|
||||
keymap.bindKeys 'name', '.child-node', 'x': 'bar'
|
||||
|
||||
fooHandler = jasmine.createSpy 'fooHandler'
|
||||
barHandler = jasmine.createSpy 'barHandler'
|
||||
@@ -145,8 +138,8 @@ describe "Keymap", ->
|
||||
|
||||
describe "when the matching selectors have the same specificity", ->
|
||||
it "triggers the bindings for the most recently declared selector", ->
|
||||
keymap.bindKeys '.child-node', 'x': 'foo', 'y': 'baz'
|
||||
keymap.bindKeys '.child-node', 'x': 'bar'
|
||||
keymap.bindKeys 'name', '.child-node', 'x': 'foo', 'y': 'baz'
|
||||
keymap.bindKeys 'name', '.child-node', 'x': 'bar'
|
||||
|
||||
fooHandler = jasmine.createSpy 'fooHandler'
|
||||
barHandler = jasmine.createSpy 'barHandler'
|
||||
@@ -165,11 +158,12 @@ describe "Keymap", ->
|
||||
expect(bazHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when the event's target is the document body", ->
|
||||
it "triggers the mapped event on the rootView", ->
|
||||
window.rootView = new RootView
|
||||
keymap.bindKeys 'body', 'x': 'foo'
|
||||
it "triggers the mapped event on the workspaceView", ->
|
||||
atom.workspaceView = new WorkspaceView
|
||||
atom.workspaceView.attachToDom()
|
||||
keymap.bindKeys 'name', 'body', 'x': 'foo'
|
||||
fooHandler = jasmine.createSpy("fooHandler")
|
||||
rootView.on 'foo', fooHandler
|
||||
atom.workspaceView.on 'foo', fooHandler
|
||||
|
||||
result = keymap.handleKeyEvent(keydownEvent('x', target: document.body))
|
||||
expect(result).toBe(false)
|
||||
@@ -179,7 +173,7 @@ describe "Keymap", ->
|
||||
|
||||
describe "when the event matches a 'native!' binding", ->
|
||||
it "returns true, allowing the browser's native key handling to process the event", ->
|
||||
keymap.bindKeys '.grandchild-node', 'x': 'native!'
|
||||
keymap.bindKeys 'name', '.grandchild-node', 'x': 'native!'
|
||||
nativeHandler = jasmine.createSpy("nativeHandler")
|
||||
fragment.on 'native!', nativeHandler
|
||||
expect(keymap.handleKeyEvent(keydownEvent('x', target: fragment.find('.grandchild-node')[0]))).toBe true
|
||||
@@ -189,7 +183,7 @@ describe "Keymap", ->
|
||||
[quitHandler, closeOtherWindowsHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
keymap.bindKeys "*",
|
||||
keymap.bindKeys 'name', "*",
|
||||
'ctrl-x ctrl-c': 'quit'
|
||||
'ctrl-x 1': 'close-other-windows'
|
||||
|
||||
@@ -219,7 +213,7 @@ describe "Keymap", ->
|
||||
expect(closeOtherWindowsHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when a second keystroke added to the first doesn't match any bindings", ->
|
||||
it "clears the queued keystrokes without triggering any events", ->
|
||||
it "clears the queued keystroke without triggering any events", ->
|
||||
expect(keymap.handleKeyEvent(keydownEvent('x', target: fragment[0], ctrlKey: true))).toBe false
|
||||
expect(keymap.handleKeyEvent(keydownEvent('c', target: fragment[0]))).toBe false
|
||||
expect(quitHandler).not.toHaveBeenCalled()
|
||||
@@ -229,7 +223,7 @@ describe "Keymap", ->
|
||||
|
||||
describe "when the event's target node descends from multiple nodes that match selectors with a partial binding match", ->
|
||||
it "allows any of the bindings to be triggered upon a second keystroke, favoring the most specific selector", ->
|
||||
keymap.bindKeys ".grandchild-node", 'ctrl-x ctrl-c': 'more-specific-quit'
|
||||
keymap.bindKeys 'name', ".grandchild-node", 'ctrl-x ctrl-c': 'more-specific-quit'
|
||||
grandchildNode = fragment.find('.grandchild-node')[0]
|
||||
moreSpecificQuitHandler = jasmine.createSpy('moreSpecificQuitHandler')
|
||||
fragment.on 'more-specific-quit', moreSpecificQuitHandler
|
||||
@@ -253,39 +247,17 @@ describe "Keymap", ->
|
||||
describe "when there is a complete binding with a more specific selector", ->
|
||||
it "favors the more specific complete match", ->
|
||||
|
||||
describe "when a tab keystroke does not match any bindings", ->
|
||||
it "returns false to prevent the browser from transferring focus", ->
|
||||
expect(keymap.handleKeyEvent(keydownEvent('U+0009', target: fragment[0]))).toBe false
|
||||
|
||||
describe ".keystrokesByCommandForSelector(selector)", ->
|
||||
it "returns a hash of all commands and their keybindings", ->
|
||||
keymap.bindKeys 'body', 'a': 'letter'
|
||||
keymap.bindKeys '.editor', 'b': 'letter'
|
||||
keymap.bindKeys '.editor', '1': 'number'
|
||||
keymap.bindKeys '.editor', 'meta-alt-1': 'number-with-modifiers'
|
||||
|
||||
expect(keymap.keystrokesByCommandForSelector()).toEqual
|
||||
'letter': ['b', 'a']
|
||||
'number': ['1']
|
||||
'number-with-modifiers': ['alt-meta-1']
|
||||
|
||||
expect(keymap.keystrokesByCommandForSelector('.editor')).toEqual
|
||||
'letter': ['b']
|
||||
'number': ['1']
|
||||
'number-with-modifiers': ['alt-meta-1']
|
||||
|
||||
|
||||
describe ".bindKeys(selector, bindings)", ->
|
||||
describe ".bindKeys(name, selector, bindings)", ->
|
||||
it "normalizes the key patterns in the hash to put the modifiers in alphabetical order", ->
|
||||
fooHandler = jasmine.createSpy('fooHandler')
|
||||
fragment.on 'foo', fooHandler
|
||||
keymap.bindKeys '*', 'ctrl-alt-delete': 'foo'
|
||||
keymap.bindKeys 'name', '*', 'ctrl-alt-delete': 'foo'
|
||||
result = keymap.handleKeyEvent(keydownEvent('delete', ctrlKey: true, altKey: true, target: fragment[0]))
|
||||
expect(result).toBe(false)
|
||||
expect(fooHandler).toHaveBeenCalled()
|
||||
|
||||
fooHandler.reset()
|
||||
keymap.bindKeys '*', 'ctrl-alt--': 'foo'
|
||||
keymap.bindKeys 'name', '*', 'ctrl-alt--': 'foo'
|
||||
result = keymap.handleKeyEvent(keydownEvent('-', ctrlKey: true, altKey: true, target: fragment[0]))
|
||||
expect(result).toBe(false)
|
||||
expect(fooHandler).toHaveBeenCalled()
|
||||
@@ -298,15 +270,17 @@ describe "Keymap", ->
|
||||
'.brown':
|
||||
'ctrl-h': 'harvest'
|
||||
|
||||
expect(keymap.bindingsForElement($$ -> @div class: 'green')).toEqual { 'ctrl-c': 'cultivate' }
|
||||
expect(keymap.bindingsForElement($$ -> @div class: 'brown')).toEqual { 'ctrl-h': 'harvest' }
|
||||
keymap.add 'medical',
|
||||
'.green':
|
||||
'ctrl-v': 'vomit'
|
||||
|
||||
expect(keymap.keyBindingsMatchingElement($$ -> @div class: 'green')).toHaveLength 2
|
||||
expect(keymap.keyBindingsMatchingElement($$ -> @div class: 'brown')).toHaveLength 1
|
||||
|
||||
keymap.remove('nature')
|
||||
|
||||
expect(keymap.bindingsForElement($$ -> @div class: 'green')).toEqual {}
|
||||
expect(keymap.bindingsForElement($$ -> @div class: 'brown')).toEqual {}
|
||||
expect(keymap.bindingSetsByFirstKeystroke['ctrl-c']).toEqual []
|
||||
expect(keymap.bindingSetsByFirstKeystroke['ctrl-h']).toEqual []
|
||||
expect(keymap.keyBindingsMatchingElement($$ -> @div class: 'green')).toHaveLength 1
|
||||
expect(keymap.keyBindingsMatchingElement($$ -> @div class: 'brown')).toEqual []
|
||||
|
||||
describe ".keystrokeStringForEvent(event)", ->
|
||||
describe "when no modifiers are pressed", ->
|
||||
@@ -317,12 +291,12 @@ describe "Keymap", ->
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('left'))).toBe 'left'
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('\b'))).toBe 'backspace'
|
||||
|
||||
describe "when ctrl, alt or meta is pressed with a non-modifier key", ->
|
||||
describe "when ctrl, alt or command is pressed with a non-modifier key", ->
|
||||
it "returns a string that identifies the key pressed", ->
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('a', altKey: true))).toBe 'alt-a'
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('[', metaKey: true))).toBe 'meta-['
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('[', metaKey: true))).toBe 'cmd-['
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('*', ctrlKey: true))).toBe 'ctrl-*'
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('left', ctrlKey: true, metaKey: true, altKey: true))).toBe 'alt-ctrl-meta-left'
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('left', ctrlKey: true, metaKey: true, altKey: true))).toBe 'alt-cmd-ctrl-left'
|
||||
|
||||
describe "when shift is pressed when a non-modifer key", ->
|
||||
it "returns a string that identifies the key pressed", ->
|
||||
@@ -331,54 +305,45 @@ describe "Keymap", ->
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('left', shiftKey: true))).toBe 'shift-left'
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('Left', shiftKey: true))).toBe 'shift-left'
|
||||
|
||||
describe ".bindingsForElement(element)", ->
|
||||
describe ".keyBindingsMatchingElement(element)", ->
|
||||
it "returns the matching bindings for the element", ->
|
||||
keymap.bindKeys '.command-mode', 'c': 'c'
|
||||
keymap.bindKeys '.grandchild-node', 'g': 'g'
|
||||
keymap.bindKeys 'name', '.command-mode', 'c': 'c'
|
||||
keymap.bindKeys 'name', '.grandchild-node', 'g': 'g'
|
||||
|
||||
bindings = keymap.bindingsForElement(fragment.find('.grandchild-node'))
|
||||
expect(Object.keys(bindings).length).toBe 2
|
||||
expect(bindings['c']).toEqual "c"
|
||||
expect(bindings['g']).toEqual "g"
|
||||
bindings = keymap.keyBindingsMatchingElement(fragment.find('.grandchild-node'))
|
||||
expect(bindings).toHaveLength 2
|
||||
expect(bindings[0].command).toEqual "g"
|
||||
expect(bindings[1].command).toEqual "c"
|
||||
|
||||
describe "when multiple bindings match a keystroke", ->
|
||||
it "only returns bindings that match the most specific selector", ->
|
||||
keymap.bindKeys '.command-mode', 'g': 'command-mode'
|
||||
keymap.bindKeys '.command-mode .grandchild-node', 'g': 'command-and-grandchild-node'
|
||||
keymap.bindKeys '.grandchild-node', 'g': 'grandchild-node'
|
||||
keymap.bindKeys 'name', '.command-mode', 'g': 'cmd-mode'
|
||||
keymap.bindKeys 'name', '.command-mode .grandchild-node', 'g': 'cmd-and-grandchild-node'
|
||||
keymap.bindKeys 'name', '.grandchild-node', 'g': 'grandchild-node'
|
||||
|
||||
bindings = keymap.bindingsForElement(fragment.find('.grandchild-node'))
|
||||
expect(Object.keys(bindings).length).toBe 1
|
||||
expect(bindings['g']).toEqual "command-and-grandchild-node"
|
||||
bindings = keymap.keyBindingsMatchingElement(fragment.find('.grandchild-node'))
|
||||
expect(bindings).toHaveLength 3
|
||||
expect(bindings[0].command).toEqual "cmd-and-grandchild-node"
|
||||
|
||||
describe ".getAllKeyMappings", ->
|
||||
it "returns the all bindings", ->
|
||||
keymap.bindKeys path.join('~', '.atom', 'packages', 'dummy', 'keymaps', 'a.cson'), '.command-mode', 'k': 'c'
|
||||
describe ".keyBindingsForCommandMatchingElement(element)", ->
|
||||
beforeEach ->
|
||||
keymap.add 'nature',
|
||||
'.green':
|
||||
'ctrl-c': 'cultivate'
|
||||
'.green-2':
|
||||
'ctrl-o': 'cultivate'
|
||||
'.brown':
|
||||
'ctrl-h': 'harvest'
|
||||
'.blue':
|
||||
'ctrl-c': 'fly'
|
||||
|
||||
mappings = keymap.getAllKeyMappings()
|
||||
expect(mappings.length).toBe 1
|
||||
expect(mappings[0].source).toEqual 'dummy'
|
||||
expect(mappings[0].keystrokes).toEqual 'k'
|
||||
expect(mappings[0].command).toEqual 'c'
|
||||
expect(mappings[0].selector).toEqual '.command-mode'
|
||||
it "finds a keymap for an element", ->
|
||||
el = $$ -> @div class: 'green'
|
||||
bindings = keymap.keyBindingsForCommandMatchingElement('cultivate', el)
|
||||
expect(bindings).toHaveLength 1
|
||||
expect(bindings[0].keystroke).toEqual "ctrl-c"
|
||||
|
||||
describe ".determineSource", ->
|
||||
describe "for a package", ->
|
||||
it "returns '<package-name>'", ->
|
||||
expect(keymap.determineSource(path.join('~', '.atom', 'packages', 'dummy', 'keymaps', 'a.cson'))).toEqual 'dummy'
|
||||
|
||||
describe "for a linked package", ->
|
||||
it "returns '<package-name>'", ->
|
||||
expect(keymap.determineSource(path.join('Users', 'john', 'github', 'dummy', 'keymaps', 'a.cson'))).toEqual 'dummy'
|
||||
|
||||
describe "for a user defined keymap", ->
|
||||
it "returns 'User'", ->
|
||||
expect(keymap.determineSource(path.join('~', '.atom', 'keymaps', 'a.cson'))).toEqual 'User'
|
||||
|
||||
describe "for a core keymap", ->
|
||||
it "returns 'Core'", ->
|
||||
expect(keymap.determineSource(path.join('Applications', 'Atom.app', '..', 'node_modules', 'dummy', 'keymaps', 'a.cson'))).toEqual 'Core'
|
||||
|
||||
describe "for a linked core keymap", ->
|
||||
it "returns 'Core'", ->
|
||||
expect(keymap.determineSource(path.join('Users', 'john', 'github', 'atom', 'keymaps', 'a.cson'))).toEqual 'Core'
|
||||
it "no keymap an element without that map", ->
|
||||
el = $$ -> @div class: 'brown'
|
||||
bindings = keymap.keyBindingsForCommandMatchingElement('cultivate', el)
|
||||
expect(bindings).toHaveLength 0
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
describe "LanguageMode", ->
|
||||
[editSession, buffer, languageMode] = []
|
||||
[editor, buffer, languageMode] = []
|
||||
|
||||
afterEach ->
|
||||
editSession.destroy()
|
||||
editor.destroy()
|
||||
|
||||
describe "javascript", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-javascript', sync: true)
|
||||
editSession = project.openSync('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
editor = atom.project.openSync('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
describe ".minIndentLevelForRowRange(startRow, endRow)", ->
|
||||
it "returns the minimum indent level for the given row range", ->
|
||||
@@ -109,9 +109,9 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "coffeescript", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-coffee-script', sync: true)
|
||||
editSession = project.openSync('coffee.coffee', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
atom.packages.activatePackage('language-coffee-script', sync: true)
|
||||
editor = atom.project.openSync('coffee.coffee', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
describe ".toggleLineCommentsForBufferRows(start, end)", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
@@ -156,9 +156,9 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "css", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-css', sync: true)
|
||||
editSession = project.openSync('css.css', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
atom.packages.activatePackage('language-css', sync: true)
|
||||
editor = atom.project.openSync('css.css', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
describe ".toggleLineCommentsForBufferRows(start, end)", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
@@ -197,10 +197,10 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "less", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-less', sync: true)
|
||||
atom.activatePackage('language-css', sync: true)
|
||||
editSession = project.openSync('sample.less', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
atom.packages.activatePackage('language-less', sync: true)
|
||||
atom.packages.activatePackage('language-css', sync: true)
|
||||
editor = atom.project.openSync('sample.less', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
describe "when commenting lines", ->
|
||||
it "only uses the `commentEnd` pattern if it comes from the same grammar as the `commentStart`", ->
|
||||
@@ -209,68 +209,68 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "folding", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-javascript', sync: true)
|
||||
editSession = project.openSync('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
editor = atom.project.openSync('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
it "maintains cursor buffer position when a folding/unfolding", ->
|
||||
editSession.setCursorBufferPosition([5,5])
|
||||
editor.setCursorBufferPosition([5,5])
|
||||
languageMode.foldAll()
|
||||
expect(editSession.getCursorBufferPosition()).toEqual([5,5])
|
||||
expect(editor.getCursorBufferPosition()).toEqual([5,5])
|
||||
|
||||
describe ".unfoldAll()", ->
|
||||
it "unfolds every folded line", ->
|
||||
initialScreenLineCount = editSession.getScreenLineCount()
|
||||
initialScreenLineCount = editor.getScreenLineCount()
|
||||
languageMode.foldBufferRow(0)
|
||||
languageMode.foldBufferRow(1)
|
||||
expect(editSession.getScreenLineCount()).toBeLessThan initialScreenLineCount
|
||||
expect(editor.getScreenLineCount()).toBeLessThan initialScreenLineCount
|
||||
languageMode.unfoldAll()
|
||||
expect(editSession.getScreenLineCount()).toBe initialScreenLineCount
|
||||
expect(editor.getScreenLineCount()).toBe initialScreenLineCount
|
||||
|
||||
describe ".foldAll()", ->
|
||||
it "folds every foldable line", ->
|
||||
languageMode.foldAll()
|
||||
|
||||
fold1 = editSession.lineForScreenRow(0).fold
|
||||
fold1 = editor.lineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 12]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(1).fold
|
||||
fold2 = editor.lineForScreenRow(1).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 9]
|
||||
fold2.destroy()
|
||||
|
||||
fold3 = editSession.lineForScreenRow(4).fold
|
||||
fold3 = editor.lineForScreenRow(4).fold
|
||||
expect([fold3.getStartRow(), fold3.getEndRow()]).toEqual [4, 7]
|
||||
|
||||
describe ".foldBufferRow(bufferRow)", ->
|
||||
describe "when bufferRow can be folded", ->
|
||||
it "creates a fold based on the syntactic region starting at the given row", ->
|
||||
languageMode.foldBufferRow(1)
|
||||
fold = editSession.lineForScreenRow(1).fold
|
||||
fold = editor.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 9
|
||||
|
||||
describe "when bufferRow can't be folded", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the given buffer row (and folds it)", ->
|
||||
languageMode.foldBufferRow(8)
|
||||
fold = editSession.lineForScreenRow(1).fold
|
||||
fold = editor.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 9
|
||||
|
||||
describe "when the bufferRow is already folded", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", ->
|
||||
languageMode.foldBufferRow(2)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeDefined()
|
||||
expect(editSession.lineForScreenRow(0).fold).not.toBeDefined()
|
||||
expect(editor.lineForScreenRow(1).fold).toBeDefined()
|
||||
expect(editor.lineForScreenRow(0).fold).not.toBeDefined()
|
||||
|
||||
languageMode.foldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(0).fold).toBeDefined()
|
||||
expect(editor.lineForScreenRow(0).fold).toBeDefined()
|
||||
|
||||
describe "when the bufferRow is in a multi-line comment", ->
|
||||
it "searches upward and downward for surrounding comment lines and folds them as a single fold", ->
|
||||
buffer.insert([1,0], " //this is a comment\n // and\n //more docs\n\n//second comment")
|
||||
languageMode.foldBufferRow(1)
|
||||
fold = editSession.lineForScreenRow(1).fold
|
||||
fold = editor.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 3
|
||||
|
||||
@@ -278,7 +278,7 @@ describe "LanguageMode", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", ->
|
||||
buffer.insert([1,0], " //this is a single line comment\n")
|
||||
languageMode.foldBufferRow(1)
|
||||
fold = editSession.lineForScreenRow(0).fold
|
||||
fold = editor.lineForScreenRow(0).fold
|
||||
expect(fold.getStartRow()).toBe 0
|
||||
expect(fold.getEndRow()).toBe 13
|
||||
|
||||
@@ -286,77 +286,77 @@ describe "LanguageMode", ->
|
||||
describe "when bufferRow can be unfolded", ->
|
||||
it "destroys a fold based on the syntactic region starting at the given row", ->
|
||||
languageMode.foldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeDefined()
|
||||
expect(editor.lineForScreenRow(1).fold).toBeDefined()
|
||||
|
||||
languageMode.unfoldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
expect(editor.lineForScreenRow(1).fold).toBeUndefined()
|
||||
|
||||
describe "when bufferRow can't be unfolded", ->
|
||||
it "does not throw an error", ->
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
expect(editor.lineForScreenRow(1).fold).toBeUndefined()
|
||||
languageMode.unfoldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
expect(editor.lineForScreenRow(1).fold).toBeUndefined()
|
||||
|
||||
describe "folding with comments", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-javascript', sync: true)
|
||||
editSession = project.openSync('sample-with-comments.js', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
editor = atom.project.openSync('sample-with-comments.js', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
describe ".unfoldAll()", ->
|
||||
it "unfolds every folded line", ->
|
||||
initialScreenLineCount = editSession.getScreenLineCount()
|
||||
initialScreenLineCount = editor.getScreenLineCount()
|
||||
languageMode.foldBufferRow(0)
|
||||
languageMode.foldBufferRow(5)
|
||||
expect(editSession.getScreenLineCount()).toBeLessThan initialScreenLineCount
|
||||
expect(editor.getScreenLineCount()).toBeLessThan initialScreenLineCount
|
||||
languageMode.unfoldAll()
|
||||
expect(editSession.getScreenLineCount()).toBe initialScreenLineCount
|
||||
expect(editor.getScreenLineCount()).toBe initialScreenLineCount
|
||||
|
||||
describe ".foldAll()", ->
|
||||
it "folds every foldable line", ->
|
||||
languageMode.foldAll()
|
||||
|
||||
fold1 = editSession.lineForScreenRow(0).fold
|
||||
fold1 = editor.lineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(1).fold
|
||||
fold2 = editor.lineForScreenRow(1).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 4]
|
||||
|
||||
fold3 = editSession.lineForScreenRow(2).fold.destroy()
|
||||
fold3 = editor.lineForScreenRow(2).fold.destroy()
|
||||
|
||||
fold4 = editSession.lineForScreenRow(3).fold
|
||||
fold4 = editor.lineForScreenRow(3).fold
|
||||
expect([fold4.getStartRow(), fold4.getEndRow()]).toEqual [6, 8]
|
||||
|
||||
describe ".foldAllAtIndentLevel()", ->
|
||||
it "folds every foldable range at a given indentLevel", ->
|
||||
languageMode.foldAllAtIndentLevel(2)
|
||||
|
||||
fold1 = editSession.lineForScreenRow(6).fold
|
||||
fold1 = editor.lineForScreenRow(6).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [6, 8]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(11).fold
|
||||
fold2 = editor.lineForScreenRow(11).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [11, 14]
|
||||
fold2.destroy()
|
||||
|
||||
it "does not fold anything but the indentLevel", ->
|
||||
languageMode.foldAllAtIndentLevel(0)
|
||||
|
||||
fold1 = editSession.lineForScreenRow(0).fold
|
||||
fold1 = editor.lineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(5).fold
|
||||
fold2 = editor.lineForScreenRow(5).fold
|
||||
expect(fold2).toBeFalsy()
|
||||
|
||||
describe "css", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-source', sync: true)
|
||||
atom.activatePackage('language-css', sync: true)
|
||||
editSession = project.openSync('css.css', autoIndent: true)
|
||||
atom.packages.activatePackage('language-source', sync: true)
|
||||
atom.packages.activatePackage('language-css', sync: true)
|
||||
editor = atom.project.openSync('css.css', autoIndent: true)
|
||||
|
||||
describe "suggestedIndentForBufferRow", ->
|
||||
it "does not return negative values (regression)", ->
|
||||
editSession.setText('.test {\npadding: 0;\n}')
|
||||
expect(editSession.suggestedIndentForBufferRow(2)).toBe 0
|
||||
editor.setText('.test {\npadding: 0;\n}')
|
||||
expect(editor.suggestedIndentForBufferRow(2)).toBe 0
|
||||
|
||||
@@ -9,7 +9,7 @@ describe "PaneContainer", ->
|
||||
|
||||
beforeEach ->
|
||||
class TestView extends View
|
||||
registerDeserializer(this)
|
||||
atom.deserializers.add(this)
|
||||
@deserialize: ({name}) -> new TestView(name)
|
||||
@content: -> @div tabindex: -1
|
||||
initialize: (@name) -> @text(@name)
|
||||
@@ -25,7 +25,7 @@ describe "PaneContainer", ->
|
||||
pane3 = pane2.splitDown(new TestView('3'))
|
||||
|
||||
afterEach ->
|
||||
unregisterDeserializer(TestView)
|
||||
atom.deserializers.remove(TestView)
|
||||
|
||||
describe ".focusNextPane()", ->
|
||||
it "focuses the pane following the focused pane or the first pane if no pane has focus", ->
|
||||
@@ -84,7 +84,7 @@ describe "PaneContainer", ->
|
||||
expect(panes).toEqual [pane4]
|
||||
|
||||
panes = []
|
||||
subscription.cancel()
|
||||
subscription.off()
|
||||
pane4.splitDown()
|
||||
expect(panes).toEqual []
|
||||
|
||||
@@ -111,11 +111,11 @@ describe "PaneContainer", ->
|
||||
|
||||
describe "when the last-closed pane item is an edit session", ->
|
||||
it "reopens the edit session (regression)", ->
|
||||
editSession = project.openSync('sample.js')
|
||||
pane3.showItem(editSession)
|
||||
pane3.destroyItem(editSession)
|
||||
editor = atom.project.openSync('sample.js')
|
||||
pane3.showItem(editor)
|
||||
pane3.destroyItem(editor)
|
||||
expect(container.reopenItem()).toBeTruthy()
|
||||
expect(pane3.activeItem.getPath()).toBe editSession.getPath()
|
||||
expect(pane3.activeItem.getPath()).toBe editor.getPath()
|
||||
expect(container.reopenItem()).toBeFalsy()
|
||||
|
||||
describe "when there is no active pane", ->
|
||||
@@ -169,28 +169,28 @@ describe "PaneContainer", ->
|
||||
it "returns true after modified files are saved", ->
|
||||
pane1.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
pane2.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
spyOn(atom, "confirmSync").andReturn(0)
|
||||
spyOn(atom, "confirm").andReturn(0)
|
||||
|
||||
saved = container.confirmClose()
|
||||
|
||||
runs ->
|
||||
expect(saved).toBeTruthy()
|
||||
expect(atom.confirmSync).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "returns false if the user cancels saving", ->
|
||||
pane1.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
pane2.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
spyOn(atom, "confirmSync").andReturn(1)
|
||||
spyOn(atom, "confirm").andReturn(1)
|
||||
|
||||
saved = container.confirmClose()
|
||||
|
||||
runs ->
|
||||
expect(saved).toBeFalsy()
|
||||
expect(atom.confirmSync).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
describe "serialization", ->
|
||||
it "can be serialized and deserialized, and correctly adjusts dimensions of deserialized panes after attach", ->
|
||||
newContainer = deserialize(container.serialize())
|
||||
newContainer = atom.deserializers.deserialize(container.serialize())
|
||||
expect(newContainer.find('.row > :contains(1)')).toExist()
|
||||
expect(newContainer.find('.row > .column > :contains(2)')).toExist()
|
||||
expect(newContainer.find('.row > .column > :contains(3)')).toExist()
|
||||
@@ -202,7 +202,7 @@ describe "PaneContainer", ->
|
||||
xit "removes empty panes on deserialization", ->
|
||||
# only deserialize pane 1's view successfully
|
||||
TestView.deserialize = ({name}) -> new TestView(name) if name is '1'
|
||||
newContainer = deserialize(container.serialize())
|
||||
newContainer = atom.deserializers.deserialize(container.serialize())
|
||||
expect(newContainer.find('.row, .column')).not.toExist()
|
||||
expect(newContainer.find('> :contains(1)')).toExist()
|
||||
|
||||
|
||||
+122
-122
@@ -5,7 +5,7 @@ path = require 'path'
|
||||
temp = require 'temp'
|
||||
|
||||
describe "Pane", ->
|
||||
[container, view1, view2, editSession1, editSession2, pane] = []
|
||||
[container, view1, view2, editor1, editor2, pane] = []
|
||||
|
||||
class TestView extends View
|
||||
@deserialize: ({id, text}) -> new TestView({id, text})
|
||||
@@ -16,17 +16,17 @@ describe "Pane", ->
|
||||
isEqual: (other) -> @id == other.id and @text == other.text
|
||||
|
||||
beforeEach ->
|
||||
registerDeserializer(TestView)
|
||||
atom.deserializers.add(TestView)
|
||||
container = new PaneContainer
|
||||
view1 = new TestView(id: 'view-1', text: 'View 1')
|
||||
view2 = new TestView(id: 'view-2', text: 'View 2')
|
||||
editSession1 = project.openSync('sample.js')
|
||||
editSession2 = project.openSync('sample.txt')
|
||||
pane = new Pane(view1, editSession1, view2, editSession2)
|
||||
editor1 = atom.project.openSync('sample.js')
|
||||
editor2 = atom.project.openSync('sample.txt')
|
||||
pane = new Pane(view1, editor1, view2, editor2)
|
||||
container.setRoot(pane)
|
||||
|
||||
afterEach ->
|
||||
unregisterDeserializer(TestView)
|
||||
atom.deserializers.remove(TestView)
|
||||
|
||||
describe ".initialize(items...)", ->
|
||||
it "displays the first item in the pane", ->
|
||||
@@ -52,9 +52,9 @@ describe "Pane", ->
|
||||
expect(itemChangedHandler.argsForCall[0][1]).toBe view2
|
||||
itemChangedHandler.reset()
|
||||
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editor1)
|
||||
expect(itemChangedHandler).toHaveBeenCalled()
|
||||
expect(itemChangedHandler.argsForCall[0][1]).toBe editSession1
|
||||
expect(itemChangedHandler.argsForCall[0][1]).toBe editor1
|
||||
itemChangedHandler.reset()
|
||||
|
||||
describe "if the pane's active view is focused before calling showItem", ->
|
||||
@@ -70,12 +70,12 @@ describe "Pane", ->
|
||||
view3 = null
|
||||
beforeEach ->
|
||||
view3 = new TestView(id: 'view-3', text: "View 3")
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editor1)
|
||||
expect(pane.getActiveItemIndex()).toBe 1
|
||||
|
||||
it "adds it to the items list after the active item", ->
|
||||
pane.showItem(view3)
|
||||
expect(pane.getItems()).toEqual [view1, editSession1, view3, view2, editSession2]
|
||||
expect(pane.getItems()).toEqual [view1, editor1, view3, view2, editor2]
|
||||
expect(pane.activeItem).toBe view3
|
||||
expect(pane.getActiveItemIndex()).toBe 2
|
||||
|
||||
@@ -89,19 +89,19 @@ describe "Pane", ->
|
||||
describe "when showing a model item", ->
|
||||
describe "when no view has yet been appended for that item", ->
|
||||
it "appends and shows a view to display the item based on its `.getViewClass` method", ->
|
||||
pane.showItem(editSession1)
|
||||
editor = pane.activeView
|
||||
expect(editor.css('display')).not.toBe 'none'
|
||||
expect(editor.activeEditSession).toBe editSession1
|
||||
pane.showItem(editor1)
|
||||
editorView = pane.activeView
|
||||
expect(editorView.css('display')).not.toBe 'none'
|
||||
expect(editorView.editor).toBe editor1
|
||||
|
||||
describe "when a valid view has already been appended for another item", ->
|
||||
it "multiple views are created for multiple items", ->
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editSession2)
|
||||
pane.showItem(editor1)
|
||||
pane.showItem(editor2)
|
||||
expect(pane.itemViews.find('.editor').length).toBe 2
|
||||
editor = pane.activeView
|
||||
expect(editor.css('display')).not.toBe 'none'
|
||||
expect(editor.activeEditSession).toBe editSession2
|
||||
editorView = pane.activeView
|
||||
expect(editorView.css('display')).not.toBe 'none'
|
||||
expect(editorView.editor).toBe editor2
|
||||
|
||||
it "creates a new view with the item", ->
|
||||
initialViewCount = pane.itemViews.find('.test-view').length
|
||||
@@ -141,77 +141,77 @@ describe "Pane", ->
|
||||
describe ".destroyItem(item)", ->
|
||||
describe "if the item is not modified", ->
|
||||
it "removes the item and tries to call destroy on it", ->
|
||||
pane.destroyItem(editSession2)
|
||||
expect(pane.getItems().indexOf(editSession2)).toBe -1
|
||||
expect(editSession2.destroyed).toBeTruthy()
|
||||
pane.destroyItem(editor2)
|
||||
expect(pane.getItems().indexOf(editor2)).toBe -1
|
||||
expect(editor2.destroyed).toBeTruthy()
|
||||
|
||||
describe "if the item is modified", ->
|
||||
beforeEach ->
|
||||
spyOn(editSession2, 'save')
|
||||
spyOn(editSession2, 'saveAs')
|
||||
spyOn(editor2, 'save')
|
||||
spyOn(editor2, 'saveAs')
|
||||
|
||||
editSession2.insertText('a')
|
||||
expect(editSession2.isModified()).toBeTruthy()
|
||||
editor2.insertText('a')
|
||||
expect(editor2.isModified()).toBeTruthy()
|
||||
|
||||
describe "if the [Save] option is selected", ->
|
||||
describe "when the item has a uri", ->
|
||||
it "saves the item before removing and destroying it", ->
|
||||
spyOn(atom, 'confirmSync').andReturn(0)
|
||||
pane.destroyItem(editSession2)
|
||||
spyOn(atom, 'confirm').andReturn(0)
|
||||
pane.destroyItem(editor2)
|
||||
|
||||
expect(editSession2.save).toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editSession2)).toBe -1
|
||||
expect(editSession2.destroyed).toBeTruthy()
|
||||
expect(editor2.save).toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editor2)).toBe -1
|
||||
expect(editor2.destroyed).toBeTruthy()
|
||||
|
||||
describe "when the item has no uri", ->
|
||||
it "presents a save-as dialog, then saves the item with the given uri before removing and destroying it", ->
|
||||
editSession2.buffer.setPath(undefined)
|
||||
editor2.buffer.setPath(undefined)
|
||||
|
||||
spyOn(atom, 'showSaveDialogSync').andReturn("/selected/path")
|
||||
spyOn(atom, 'confirmSync').andReturn(0)
|
||||
pane.destroyItem(editSession2)
|
||||
spyOn(atom, 'confirm').andReturn(0)
|
||||
pane.destroyItem(editor2)
|
||||
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||
|
||||
expect(editSession2.saveAs).toHaveBeenCalledWith("/selected/path")
|
||||
expect(pane.getItems().indexOf(editSession2)).toBe -1
|
||||
expect(editSession2.destroyed).toBeTruthy()
|
||||
expect(editor2.saveAs).toHaveBeenCalledWith("/selected/path")
|
||||
expect(pane.getItems().indexOf(editor2)).toBe -1
|
||||
expect(editor2.destroyed).toBeTruthy()
|
||||
|
||||
describe "if the [Don't Save] option is selected", ->
|
||||
it "removes and destroys the item without saving it", ->
|
||||
spyOn(atom, 'confirmSync').andReturn(2)
|
||||
pane.destroyItem(editSession2)
|
||||
spyOn(atom, 'confirm').andReturn(2)
|
||||
pane.destroyItem(editor2)
|
||||
|
||||
expect(editSession2.save).not.toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editSession2)).toBe -1
|
||||
expect(editSession2.destroyed).toBeTruthy()
|
||||
expect(editor2.save).not.toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editor2)).toBe -1
|
||||
expect(editor2.destroyed).toBeTruthy()
|
||||
|
||||
describe "if the [Cancel] option is selected", ->
|
||||
it "does not save, remove, or destroy the item", ->
|
||||
spyOn(atom, 'confirmSync').andReturn(1)
|
||||
pane.destroyItem(editSession2)
|
||||
spyOn(atom, 'confirm').andReturn(1)
|
||||
pane.destroyItem(editor2)
|
||||
|
||||
expect(editSession2.save).not.toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editSession2)).not.toBe -1
|
||||
expect(editSession2.destroyed).toBeFalsy()
|
||||
expect(editor2.save).not.toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editor2)).not.toBe -1
|
||||
expect(editor2.destroyed).toBeFalsy()
|
||||
|
||||
describe ".removeItem(item)", ->
|
||||
it "removes the item from the items list and shows the next item if it was showing", ->
|
||||
pane.removeItem(view1)
|
||||
expect(pane.getItems()).toEqual [editSession1, view2, editSession2]
|
||||
expect(pane.activeItem).toBe editSession1
|
||||
expect(pane.getItems()).toEqual [editor1, view2, editor2]
|
||||
expect(pane.activeItem).toBe editor1
|
||||
|
||||
pane.showItem(editSession2)
|
||||
pane.removeItem(editSession2)
|
||||
expect(pane.getItems()).toEqual [editSession1, view2]
|
||||
expect(pane.activeItem).toBe editSession1
|
||||
pane.showItem(editor2)
|
||||
pane.removeItem(editor2)
|
||||
expect(pane.getItems()).toEqual [editor1, view2]
|
||||
expect(pane.activeItem).toBe editor1
|
||||
|
||||
it "triggers 'pane:item-removed' with the item and its former index", ->
|
||||
itemRemovedHandler = jasmine.createSpy("itemRemovedHandler")
|
||||
pane.on 'pane:item-removed', itemRemovedHandler
|
||||
pane.removeItem(editSession1)
|
||||
pane.removeItem(editor1)
|
||||
expect(itemRemovedHandler).toHaveBeenCalled()
|
||||
expect(itemRemovedHandler.argsForCall[0][1..2]).toEqual [editSession1, 1]
|
||||
expect(itemRemovedHandler.argsForCall[0][1..2]).toEqual [editor1, 1]
|
||||
|
||||
describe "when removing the last item", ->
|
||||
it "removes the pane", ->
|
||||
@@ -236,11 +236,11 @@ describe "Pane", ->
|
||||
|
||||
describe "when the item is a model", ->
|
||||
it "removes the associated view only when all items that require it have been removed", ->
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editSession2)
|
||||
pane.removeItem(editSession2)
|
||||
pane.showItem(editor1)
|
||||
pane.showItem(editor2)
|
||||
pane.removeItem(editor2)
|
||||
expect(pane.itemViews.find('.editor')).toExist()
|
||||
pane.removeItem(editSession1)
|
||||
pane.removeItem(editor1)
|
||||
expect(pane.itemViews.find('.editor')).not.toExist()
|
||||
|
||||
describe ".moveItem(item, index)", ->
|
||||
@@ -249,21 +249,21 @@ describe "Pane", ->
|
||||
pane.on 'pane:item-moved', itemMovedHandler
|
||||
|
||||
pane.moveItem(view1, 2)
|
||||
expect(pane.getItems()).toEqual [editSession1, view2, view1, editSession2]
|
||||
expect(pane.getItems()).toEqual [editor1, view2, view1, editor2]
|
||||
expect(itemMovedHandler).toHaveBeenCalled()
|
||||
expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [view1, 2]
|
||||
itemMovedHandler.reset()
|
||||
|
||||
pane.moveItem(editSession1, 3)
|
||||
expect(pane.getItems()).toEqual [view2, view1, editSession2, editSession1]
|
||||
pane.moveItem(editor1, 3)
|
||||
expect(pane.getItems()).toEqual [view2, view1, editor2, editor1]
|
||||
expect(itemMovedHandler).toHaveBeenCalled()
|
||||
expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [editSession1, 3]
|
||||
expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [editor1, 3]
|
||||
itemMovedHandler.reset()
|
||||
|
||||
pane.moveItem(editSession1, 1)
|
||||
expect(pane.getItems()).toEqual [view2, editSession1, view1, editSession2]
|
||||
pane.moveItem(editor1, 1)
|
||||
expect(pane.getItems()).toEqual [view2, editor1, view1, editor2]
|
||||
expect(itemMovedHandler).toHaveBeenCalled()
|
||||
expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [editSession1, 1]
|
||||
expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [editor1, 1]
|
||||
itemMovedHandler.reset()
|
||||
|
||||
describe ".moveItemToPane(item, pane, index)", ->
|
||||
@@ -275,21 +275,21 @@ describe "Pane", ->
|
||||
|
||||
it "moves the item to the given pane at the given index", ->
|
||||
pane.moveItemToPane(view1, pane2, 1)
|
||||
expect(pane.getItems()).toEqual [editSession1, view2, editSession2]
|
||||
expect(pane.getItems()).toEqual [editor1, view2, editor2]
|
||||
expect(pane2.getItems()).toEqual [view3, view1]
|
||||
|
||||
describe "when it is the last item on the source pane", ->
|
||||
it "removes the source pane, but does not destroy the item", ->
|
||||
pane.removeItem(view1)
|
||||
pane.removeItem(view2)
|
||||
pane.removeItem(editSession2)
|
||||
pane.removeItem(editor2)
|
||||
|
||||
expect(pane.getItems()).toEqual [editSession1]
|
||||
pane.moveItemToPane(editSession1, pane2, 1)
|
||||
expect(pane.getItems()).toEqual [editor1]
|
||||
pane.moveItemToPane(editor1, pane2, 1)
|
||||
|
||||
expect(pane.hasParent()).toBeFalsy()
|
||||
expect(pane2.getItems()).toEqual [view3, editSession1]
|
||||
expect(editSession1.destroyed).toBeFalsy()
|
||||
expect(pane2.getItems()).toEqual [view3, editor1]
|
||||
expect(editor1.destroyed).toBeFalsy()
|
||||
|
||||
describe "when the item is a jQuery object", ->
|
||||
it "preserves data by detaching instead of removing", ->
|
||||
@@ -303,37 +303,37 @@ describe "Pane", ->
|
||||
containerCloseHandler = jasmine.createSpy("containerCloseHandler")
|
||||
container.on 'core:close', containerCloseHandler
|
||||
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editor1)
|
||||
initialItemCount = pane.getItems().length
|
||||
pane.trigger 'core:close'
|
||||
expect(pane.getItems().length).toBe initialItemCount - 1
|
||||
expect(editSession1.destroyed).toBeTruthy()
|
||||
expect(editor1.destroyed).toBeTruthy()
|
||||
|
||||
expect(containerCloseHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe "pane:close", ->
|
||||
it "destroys all items and removes the pane", ->
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editor1)
|
||||
pane.trigger 'pane:close'
|
||||
expect(pane.hasParent()).toBeFalsy()
|
||||
expect(editSession2.destroyed).toBeTruthy()
|
||||
expect(editSession1.destroyed).toBeTruthy()
|
||||
expect(editor2.destroyed).toBeTruthy()
|
||||
expect(editor1.destroyed).toBeTruthy()
|
||||
|
||||
describe "pane:close-other-items", ->
|
||||
it "destroys all items except the current", ->
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editor1)
|
||||
pane.trigger 'pane:close-other-items'
|
||||
expect(editSession2.destroyed).toBeTruthy()
|
||||
expect(pane.getItems()).toEqual [editSession1]
|
||||
expect(editor2.destroyed).toBeTruthy()
|
||||
expect(pane.getItems()).toEqual [editor1]
|
||||
|
||||
describe "core:save", ->
|
||||
describe "when the current item has a uri", ->
|
||||
describe "when the current item has a save method", ->
|
||||
it "saves the current item", ->
|
||||
spyOn(editSession2, 'save')
|
||||
pane.showItem(editSession2)
|
||||
spyOn(editor2, 'save')
|
||||
pane.showItem(editor2)
|
||||
pane.trigger 'core:save'
|
||||
expect(editSession2.save).toHaveBeenCalled()
|
||||
expect(editor2.save).toHaveBeenCalled()
|
||||
|
||||
describe "when the current item has no save method", ->
|
||||
it "does nothing", ->
|
||||
@@ -347,14 +347,14 @@ describe "Pane", ->
|
||||
|
||||
describe "when the current item has a saveAs method", ->
|
||||
it "opens a save dialog and saves the current item as the selected path", ->
|
||||
spyOn(editSession2, 'saveAs')
|
||||
editSession2.buffer.setPath(undefined)
|
||||
pane.showItem(editSession2)
|
||||
spyOn(editor2, 'saveAs')
|
||||
editor2.buffer.setPath(undefined)
|
||||
pane.showItem(editor2)
|
||||
|
||||
pane.trigger 'core:save'
|
||||
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||
expect(editSession2.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
expect(editor2.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
|
||||
describe "when the current item has no saveAs method", ->
|
||||
it "does nothing", ->
|
||||
@@ -368,13 +368,13 @@ describe "Pane", ->
|
||||
|
||||
describe "when the current item has a saveAs method", ->
|
||||
it "opens the save dialog and calls saveAs on the item with the selected path", ->
|
||||
spyOn(editSession2, 'saveAs')
|
||||
pane.showItem(editSession2)
|
||||
spyOn(editor2, 'saveAs')
|
||||
pane.showItem(editor2)
|
||||
|
||||
pane.trigger 'core:save-as'
|
||||
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalledWith(path.dirname(editSession2.getPath()))
|
||||
expect(editSession2.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalledWith(path.dirname(editor2.getPath()))
|
||||
expect(editor2.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
|
||||
describe "when the current item does not have a saveAs method", ->
|
||||
it "does nothing", ->
|
||||
@@ -386,11 +386,11 @@ describe "Pane", ->
|
||||
it "advances forward/backward through the pane's items, looping around at either end", ->
|
||||
expect(pane.activeItem).toBe view1
|
||||
pane.trigger 'pane:show-previous-item'
|
||||
expect(pane.activeItem).toBe editSession2
|
||||
expect(pane.activeItem).toBe editor2
|
||||
pane.trigger 'pane:show-previous-item'
|
||||
expect(pane.activeItem).toBe view2
|
||||
pane.trigger 'pane:show-next-item'
|
||||
expect(pane.activeItem).toBe editSession2
|
||||
expect(pane.activeItem).toBe editor2
|
||||
pane.trigger 'pane:show-next-item'
|
||||
expect(pane.activeItem).toBe view1
|
||||
|
||||
@@ -424,8 +424,8 @@ describe "Pane", ->
|
||||
describe ".remove()", ->
|
||||
it "destroys all the pane's items", ->
|
||||
pane.remove()
|
||||
expect(editSession1.destroyed).toBeTruthy()
|
||||
expect(editSession2.destroyed).toBeTruthy()
|
||||
expect(editor1.destroyed).toBeTruthy()
|
||||
expect(editor2.destroyed).toBeTruthy()
|
||||
|
||||
it "triggers a 'pane:removed' event with the pane", ->
|
||||
removedHandler = jasmine.createSpy("removedHandler")
|
||||
@@ -438,7 +438,7 @@ describe "Pane", ->
|
||||
[paneToLeft, paneToRight] = []
|
||||
|
||||
beforeEach ->
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editor1)
|
||||
paneToLeft = pane.splitLeft(pane.copyActiveItem())
|
||||
paneToRight = pane.splitRight(pane.copyActiveItem())
|
||||
container.attachToDom()
|
||||
@@ -475,24 +475,24 @@ describe "Pane", ->
|
||||
describe "when it is the last pane", ->
|
||||
beforeEach ->
|
||||
expect(container.getPanes().length).toBe 1
|
||||
window.rootView = focus: jasmine.createSpy("rootView.focus")
|
||||
atom.workspaceView = focus: jasmine.createSpy("workspaceView.focus")
|
||||
|
||||
describe "when the removed pane is focused", ->
|
||||
it "calls focus on rootView so we don't lose focus", ->
|
||||
it "calls focus on workspaceView so we don't lose focus", ->
|
||||
container.attachToDom()
|
||||
pane.focus()
|
||||
pane.remove()
|
||||
expect(rootView.focus).toHaveBeenCalled()
|
||||
expect(atom.workspaceView.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the removed pane is not focused", ->
|
||||
it "does not call focus on root view", ->
|
||||
expect(pane).not.toMatchSelector ':has(:focus)'
|
||||
pane.remove()
|
||||
expect(rootView.focus).not.toHaveBeenCalled()
|
||||
expect(atom.workspaceView.focus).not.toHaveBeenCalled()
|
||||
|
||||
describe ".getNextPane()", ->
|
||||
it "returns the next pane if one exists, wrapping around from the last pane to the first", ->
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editor1)
|
||||
expect(pane.getNextPane()).toBeUndefined
|
||||
pane2 = pane.splitRight(pane.copyActiveItem())
|
||||
expect(pane.getNextPane()).toBe pane2
|
||||
@@ -538,7 +538,7 @@ describe "Pane", ->
|
||||
[pane1, view3, view4] = []
|
||||
beforeEach ->
|
||||
pane1 = pane
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editor1)
|
||||
view3 = new TestView(id: 'view-3', text: 'View 3')
|
||||
view4 = new TestView(id: 'view-4', text: 'View 4')
|
||||
|
||||
@@ -547,8 +547,8 @@ describe "Pane", ->
|
||||
# creates the new pane with a copy of the active item if none are given
|
||||
pane2 = pane1.splitRight(pane1.copyActiveItem())
|
||||
expect(container.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
expect(pane2.items).toEqual [editSession1]
|
||||
expect(pane2.activeItem).not.toBe editSession1 # it's a copy
|
||||
expect(pane2.items).toEqual [editor1]
|
||||
expect(pane2.activeItem).not.toBe editor1 # it's a copy
|
||||
|
||||
pane3 = pane2.splitRight(view3, view4)
|
||||
expect(pane3.getItems()).toEqual [view3, view4]
|
||||
@@ -571,8 +571,8 @@ describe "Pane", ->
|
||||
# creates the new pane with a copy of the active item if none are given
|
||||
pane2 = pane.splitLeft(pane1.copyActiveItem())
|
||||
expect(container.find('.row .pane').toArray()).toEqual [pane2[0], pane[0]]
|
||||
expect(pane2.items).toEqual [editSession1]
|
||||
expect(pane2.activeItem).not.toBe editSession1 # it's a copy
|
||||
expect(pane2.items).toEqual [editor1]
|
||||
expect(pane2.activeItem).not.toBe editor1 # it's a copy
|
||||
|
||||
pane3 = pane2.splitLeft(view3, view4)
|
||||
expect(pane3.getItems()).toEqual [view3, view4]
|
||||
@@ -583,8 +583,8 @@ describe "Pane", ->
|
||||
# creates the new pane with a copy of the active item if none are given
|
||||
pane2 = pane.splitDown(pane1.copyActiveItem())
|
||||
expect(container.find('.column .pane').toArray()).toEqual [pane[0], pane2[0]]
|
||||
expect(pane2.items).toEqual [editSession1]
|
||||
expect(pane2.activeItem).not.toBe editSession1 # it's a copy
|
||||
expect(pane2.items).toEqual [editor1]
|
||||
expect(pane2.activeItem).not.toBe editor1 # it's a copy
|
||||
|
||||
pane3 = pane2.splitDown(view3, view4)
|
||||
expect(pane3.getItems()).toEqual [view3, view4]
|
||||
@@ -595,8 +595,8 @@ describe "Pane", ->
|
||||
# creates the new pane with a copy of the active item if none are given
|
||||
pane2 = pane.splitUp(pane1.copyActiveItem())
|
||||
expect(container.find('.column .pane').toArray()).toEqual [pane2[0], pane[0]]
|
||||
expect(pane2.items).toEqual [editSession1]
|
||||
expect(pane2.activeItem).not.toBe editSession1 # it's a copy
|
||||
expect(pane2.items).toEqual [editor1]
|
||||
expect(pane2.activeItem).not.toBe editor1 # it's a copy
|
||||
|
||||
pane3 = pane2.splitUp(view3, view4)
|
||||
expect(pane3.getItems()).toEqual [view3, view4]
|
||||
@@ -673,18 +673,18 @@ describe "Pane", ->
|
||||
|
||||
describe ".itemForUri(uri)", ->
|
||||
it "returns the item for which a call to .getUri() returns the given uri", ->
|
||||
expect(pane.itemForUri(editSession1.getUri())).toBe editSession1
|
||||
expect(pane.itemForUri(editSession2.getUri())).toBe editSession2
|
||||
expect(pane.itemForUri(editor1.getUri())).toBe editor1
|
||||
expect(pane.itemForUri(editor2.getUri())).toBe editor2
|
||||
|
||||
describe "serialization", ->
|
||||
it "can serialize and deserialize the pane and all its items", ->
|
||||
newPane = deserialize(pane.serialize())
|
||||
expect(newPane.getItems()).toEqual [view1, editSession1, view2, editSession2]
|
||||
newPane = atom.deserializers.deserialize(pane.serialize())
|
||||
expect(newPane.getItems()).toEqual [view1, editor1, view2, editor2]
|
||||
|
||||
it "restores the active item on deserialization", ->
|
||||
pane.showItem(editSession2)
|
||||
newPane = deserialize(pane.serialize())
|
||||
expect(newPane.activeItem).toEqual editSession2
|
||||
pane.showItem(editor2)
|
||||
newPane = atom.deserializers.deserialize(pane.serialize())
|
||||
expect(newPane.activeItem).toEqual editor2
|
||||
|
||||
it "does not show items that cannot be deserialized", ->
|
||||
spyOn(console, 'warn')
|
||||
@@ -693,18 +693,18 @@ describe "Pane", ->
|
||||
paneState = pane.serialize()
|
||||
paneState.get('items').set(pane.items.indexOf(view2), {deserializer: 'Bogus'}) # nuke serialized state of active item
|
||||
|
||||
newPane = deserialize(paneState)
|
||||
newPane = atom.deserializers.deserialize(paneState)
|
||||
expect(newPane.activeItem).toEqual pane.items[0]
|
||||
expect(newPane.items.length).toBe pane.items.length - 1
|
||||
|
||||
it "focuses the pane after attach only if had focus when serialized", ->
|
||||
reloadContainer = ->
|
||||
projectState = project.serialize()
|
||||
projectReplica = atom.replicate().get('project')
|
||||
containerState = container.serialize()
|
||||
container.remove()
|
||||
project.destroy()
|
||||
window.project = deserialize(projectState)
|
||||
container = deserialize(containerState)
|
||||
atom.project = projectReplica
|
||||
atom.project.destroy()
|
||||
container = atom.deserializers.deserialize(containerState)
|
||||
pane = container.getRoot()
|
||||
container.attachToDom()
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
describe "Pasteboard", ->
|
||||
describe "write(text, metadata) and read()", ->
|
||||
it "writes and reads text to/from the native pasteboard", ->
|
||||
expect(pasteboard.read()).toEqual ['initial pasteboard content']
|
||||
pasteboard.write('next')
|
||||
expect(pasteboard.read()[0]).toBe 'next'
|
||||
expect(atom.pasteboard.read()).toEqual ['initial pasteboard content']
|
||||
atom.pasteboard.write('next')
|
||||
expect(atom.pasteboard.read()[0]).toBe 'next'
|
||||
|
||||
it "returns metadata if the item on the native pasteboard matches the last written item", ->
|
||||
pasteboard.write('next', {meta: 'data'})
|
||||
expect(pasteboard.read()).toEqual ['next', {meta: 'data'}]
|
||||
atom.pasteboard.write('next', {meta: 'data'})
|
||||
expect(atom.pasteboard.read()).toEqual ['next', {meta: 'data'}]
|
||||
|
||||
+265
-149
@@ -8,7 +8,7 @@ BufferedProcess = require '../src/buffered-process'
|
||||
|
||||
describe "Project", ->
|
||||
beforeEach ->
|
||||
project.setPath(project.resolve('dir'))
|
||||
atom.project.setPath(atom.project.resolve('dir'))
|
||||
|
||||
describe "serialization", ->
|
||||
deserializedProject = null
|
||||
@@ -17,203 +17,216 @@ describe "Project", ->
|
||||
deserializedProject?.destroy()
|
||||
|
||||
it "destroys unretained buffers and does not include them in the serialized state", ->
|
||||
project.bufferForPathSync('a')
|
||||
expect(project.getBuffers().length).toBe 1
|
||||
deserializedProject = deserialize(project.serialize())
|
||||
atom.project.bufferForPathSync('a')
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
|
||||
atom.project.getState().serializeForPersistence()
|
||||
deserializedProject = atom.replicate().get('project')
|
||||
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
expect(atom.project.getBuffers().length).toBe 0
|
||||
|
||||
it "listens for destroyed events on deserialized buffers and removes them when they are destroyed", ->
|
||||
atom.project.openSync('a')
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
atom.project.getState().serializeForPersistence()
|
||||
deserializedProject = atom.replicate().get('project')
|
||||
|
||||
expect(deserializedProject.getBuffers().length).toBe 1
|
||||
deserializedProject.getBuffers()[0].destroy()
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
expect(project.getBuffers().length).toBe 0
|
||||
|
||||
describe "when an edit session is destroyed", ->
|
||||
it "removes edit session and calls destroy on buffer (if buffer is not referenced by other edit sessions)", ->
|
||||
editSession = project.openSync("a")
|
||||
anotherEditSession = project.openSync("a")
|
||||
editor = atom.project.openSync("a")
|
||||
anotherEditor = atom.project.openSync("a")
|
||||
|
||||
expect(project.editSessions.length).toBe 2
|
||||
expect(editSession.buffer).toBe anotherEditSession.buffer
|
||||
expect(atom.project.editors.length).toBe 2
|
||||
expect(editor.buffer).toBe anotherEditor.buffer
|
||||
|
||||
editSession.destroy()
|
||||
expect(project.editSessions.length).toBe 1
|
||||
editor.destroy()
|
||||
expect(atom.project.editors.length).toBe 1
|
||||
|
||||
anotherEditSession.destroy()
|
||||
expect(project.editSessions.length).toBe 0
|
||||
anotherEditor.destroy()
|
||||
expect(atom.project.editors.length).toBe 0
|
||||
|
||||
describe "when an edit session is saved and the project has no path", ->
|
||||
it "sets the project's path to the saved file's parent directory", ->
|
||||
tempFile = temp.openSync().path
|
||||
project.setPath(undefined)
|
||||
expect(project.getPath()).toBeUndefined()
|
||||
editSession = project.openSync()
|
||||
editSession.saveAs(tempFile)
|
||||
expect(project.getPath()).toBe path.dirname(tempFile)
|
||||
atom.project.setPath(undefined)
|
||||
expect(atom.project.getPath()).toBeUndefined()
|
||||
editor = atom.project.openSync()
|
||||
editor.saveAs(tempFile)
|
||||
expect(atom.project.getPath()).toBe path.dirname(tempFile)
|
||||
|
||||
describe "when an edit session is deserialized", ->
|
||||
it "emits an 'edit-session-created' event and stores the edit session", ->
|
||||
handler = jasmine.createSpy('editSessionCreatedHandler')
|
||||
project.on 'edit-session-created', handler
|
||||
it "emits an 'editor-created' event and stores the edit session", ->
|
||||
handler = jasmine.createSpy('editorCreatedHandler')
|
||||
atom.project.on 'editor-created', handler
|
||||
|
||||
editSession1 = project.openSync("a")
|
||||
editor1 = atom.project.openSync("a")
|
||||
expect(handler.callCount).toBe 1
|
||||
expect(project.getEditSessions().length).toBe 1
|
||||
expect(project.getEditSessions()[0]).toBe editSession1
|
||||
expect(atom.project.getEditors().length).toBe 1
|
||||
expect(atom.project.getEditors()[0]).toBe editor1
|
||||
|
||||
editSession2 = deserialize(editSession1.serialize())
|
||||
editor2 = atom.deserializers.deserialize(editor1.serialize())
|
||||
expect(handler.callCount).toBe 2
|
||||
expect(project.getEditSessions().length).toBe 2
|
||||
expect(project.getEditSessions()[0]).toBe editSession1
|
||||
expect(project.getEditSessions()[1]).toBe editSession2
|
||||
expect(atom.project.getEditors().length).toBe 2
|
||||
expect(atom.project.getEditors()[0]).toBe editor1
|
||||
expect(atom.project.getEditors()[1]).toBe editor2
|
||||
|
||||
describe "when an edit session is copied", ->
|
||||
it "emits an 'edit-session-created' event and stores the edit session", ->
|
||||
handler = jasmine.createSpy('editSessionCreatedHandler')
|
||||
project.on 'edit-session-created', handler
|
||||
it "emits an 'editor-created' event and stores the edit session", ->
|
||||
handler = jasmine.createSpy('editorCreatedHandler')
|
||||
atom.project.on 'editor-created', handler
|
||||
|
||||
editSession1 = project.openSync("a")
|
||||
editor1 = atom.project.openSync("a")
|
||||
expect(handler.callCount).toBe 1
|
||||
expect(project.getEditSessions().length).toBe 1
|
||||
expect(project.getEditSessions()[0]).toBe editSession1
|
||||
expect(atom.project.getEditors().length).toBe 1
|
||||
expect(atom.project.getEditors()[0]).toBe editor1
|
||||
|
||||
editSession2 = editSession1.copy()
|
||||
editor2 = editor1.copy()
|
||||
expect(handler.callCount).toBe 2
|
||||
expect(project.getEditSessions().length).toBe 2
|
||||
expect(project.getEditSessions()[0]).toBe editSession1
|
||||
expect(project.getEditSessions()[1]).toBe editSession2
|
||||
expect(atom.project.getEditors().length).toBe 2
|
||||
expect(atom.project.getEditors()[0]).toBe editor1
|
||||
expect(atom.project.getEditors()[1]).toBe editor2
|
||||
|
||||
describe ".openSync(path)", ->
|
||||
[fooOpener, barOpener, absolutePath, newBufferHandler, newEditSessionHandler] = []
|
||||
[fooOpener, barOpener, absolutePath, newBufferHandler, newEditorHandler] = []
|
||||
beforeEach ->
|
||||
absolutePath = require.resolve('./fixtures/dir/a')
|
||||
newBufferHandler = jasmine.createSpy('newBufferHandler')
|
||||
project.on 'buffer-created', newBufferHandler
|
||||
newEditSessionHandler = jasmine.createSpy('newEditSessionHandler')
|
||||
project.on 'edit-session-created', newEditSessionHandler
|
||||
atom.project.on 'buffer-created', newBufferHandler
|
||||
newEditorHandler = jasmine.createSpy('newEditorHandler')
|
||||
atom.project.on 'editor-created', newEditorHandler
|
||||
|
||||
fooOpener = (pathToOpen, options) -> { foo: pathToOpen, options } if pathToOpen?.match(/\.foo/)
|
||||
barOpener = (pathToOpen) -> { bar: pathToOpen } if pathToOpen?.match(/^bar:\/\//)
|
||||
project.registerOpener(fooOpener)
|
||||
project.registerOpener(barOpener)
|
||||
atom.project.registerOpener(fooOpener)
|
||||
atom.project.registerOpener(barOpener)
|
||||
|
||||
afterEach ->
|
||||
project.unregisterOpener(fooOpener)
|
||||
project.unregisterOpener(barOpener)
|
||||
atom.project.unregisterOpener(fooOpener)
|
||||
atom.project.unregisterOpener(barOpener)
|
||||
|
||||
describe "when passed a path that doesn't match a custom opener", ->
|
||||
describe "when given an absolute path that hasn't been opened previously", ->
|
||||
it "returns a new edit session for the given path and emits 'buffer-created' and 'edit-session-created' events", ->
|
||||
editSession = project.openSync(absolutePath)
|
||||
expect(editSession.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editSession.buffer
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
|
||||
it "returns a new edit session for the given path and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = atom.project.openSync(absolutePath)
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when given a relative path that hasn't been opened previously", ->
|
||||
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'edit-session-created' events", ->
|
||||
editSession = project.openSync('a')
|
||||
expect(editSession.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editSession.buffer
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
|
||||
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = atom.project.openSync('a')
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed the path to a buffer that has already been opened", ->
|
||||
it "returns a new edit session containing previously opened buffer and emits a 'edit-session-created' event", ->
|
||||
editSession = project.openSync(absolutePath)
|
||||
it "returns a new edit session containing previously opened buffer and emits a 'editor-created' event", ->
|
||||
editor = atom.project.openSync(absolutePath)
|
||||
newBufferHandler.reset()
|
||||
expect(project.openSync(absolutePath).buffer).toBe editSession.buffer
|
||||
expect(project.openSync('a').buffer).toBe editSession.buffer
|
||||
expect(atom.project.openSync(absolutePath).buffer).toBe editor.buffer
|
||||
expect(atom.project.openSync('a').buffer).toBe editor.buffer
|
||||
expect(newBufferHandler).not.toHaveBeenCalled()
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when not passed a path", ->
|
||||
it "returns a new edit session and emits 'buffer-created' and 'edit-session-created' events", ->
|
||||
editSession = project.openSync()
|
||||
expect(editSession.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editSession.buffer)
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
|
||||
it "returns a new edit session and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = atom.project.openSync()
|
||||
expect(editor.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer)
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed a path that matches a custom opener", ->
|
||||
it "returns the resource returned by the custom opener", ->
|
||||
pathToOpen = project.resolve('a.foo')
|
||||
expect(project.openSync(pathToOpen, hey: "there")).toEqual { foo: pathToOpen, options: {hey: "there"} }
|
||||
expect(project.openSync("bar://baz")).toEqual { bar: "bar://baz" }
|
||||
pathToOpen = atom.project.resolve('a.foo')
|
||||
expect(atom.project.openSync(pathToOpen, hey: "there")).toEqual { foo: pathToOpen, options: {hey: "there"} }
|
||||
expect(atom.project.openSync("bar://baz")).toEqual { bar: "bar://baz" }
|
||||
|
||||
describe ".open(path)", ->
|
||||
[fooOpener, barOpener, absolutePath, newBufferHandler, newEditSessionHandler] = []
|
||||
[fooOpener, barOpener, absolutePath, newBufferHandler, newEditorHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
absolutePath = require.resolve('./fixtures/dir/a')
|
||||
newBufferHandler = jasmine.createSpy('newBufferHandler')
|
||||
project.on 'buffer-created', newBufferHandler
|
||||
newEditSessionHandler = jasmine.createSpy('newEditSessionHandler')
|
||||
project.on 'edit-session-created', newEditSessionHandler
|
||||
atom.project.on 'buffer-created', newBufferHandler
|
||||
newEditorHandler = jasmine.createSpy('newEditorHandler')
|
||||
atom.project.on 'editor-created', newEditorHandler
|
||||
|
||||
fooOpener = (pathToOpen, options) -> { foo: pathToOpen, options } if pathToOpen?.match(/\.foo/)
|
||||
barOpener = (pathToOpen) -> { bar: pathToOpen } if pathToOpen?.match(/^bar:\/\//)
|
||||
project.registerOpener(fooOpener)
|
||||
project.registerOpener(barOpener)
|
||||
atom.project.registerOpener(fooOpener)
|
||||
atom.project.registerOpener(barOpener)
|
||||
|
||||
afterEach ->
|
||||
project.unregisterOpener(fooOpener)
|
||||
project.unregisterOpener(barOpener)
|
||||
atom.project.unregisterOpener(fooOpener)
|
||||
atom.project.unregisterOpener(barOpener)
|
||||
|
||||
describe "when passed a path that doesn't match a custom opener", ->
|
||||
describe "when given an absolute path that isn't currently open", ->
|
||||
it "returns a new edit session for the given path and emits 'buffer-created' and 'edit-session-created' events", ->
|
||||
editSession = null
|
||||
it "returns a new edit session for the given path and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
project.open(absolutePath).then (o) -> editSession = o
|
||||
atom.project.open(absolutePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editSession.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editSession.buffer
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when given a relative path that isn't currently opened", ->
|
||||
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'edit-session-created' events", ->
|
||||
editSession = null
|
||||
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
project.open(absolutePath).then (o) -> editSession = o
|
||||
atom.project.open(absolutePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editSession.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editSession.buffer
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed the path to a buffer that is currently opened", ->
|
||||
it "returns a new edit session containing currently opened buffer and emits a 'edit-session-created' event", ->
|
||||
editSession = null
|
||||
it "returns a new edit session containing currently opened buffer and emits a 'editor-created' event", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
project.open(absolutePath).then (o) -> editSession = o
|
||||
atom.project.open(absolutePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
newBufferHandler.reset()
|
||||
expect(project.openSync(absolutePath).buffer).toBe editSession.buffer
|
||||
expect(project.openSync('a').buffer).toBe editSession.buffer
|
||||
expect(atom.project.openSync(absolutePath).buffer).toBe editor.buffer
|
||||
expect(atom.project.openSync('a').buffer).toBe editor.buffer
|
||||
expect(newBufferHandler).not.toHaveBeenCalled()
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when not passed a path", ->
|
||||
it "returns a new edit session and emits 'buffer-created' and 'edit-session-created' events", ->
|
||||
editSession = null
|
||||
it "returns a new edit session and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
project.open().then (o) -> editSession = o
|
||||
atom.project.open().then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editSession.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editSession.buffer)
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
|
||||
expect(editor.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer)
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed a path that matches a custom opener", ->
|
||||
it "returns the resource returned by the custom opener", ->
|
||||
waitsForPromise ->
|
||||
pathToOpen = project.resolve('a.foo')
|
||||
project.open(pathToOpen, hey: "there").then (item) ->
|
||||
pathToOpen = atom.project.resolve('a.foo')
|
||||
atom.project.open(pathToOpen, hey: "there").then (item) ->
|
||||
expect(item).toEqual { foo: pathToOpen, options: {hey: "there"} }
|
||||
|
||||
waitsForPromise ->
|
||||
project.open("bar://baz").then (item) ->
|
||||
atom.project.open("bar://baz").then (item) ->
|
||||
expect(item).toEqual { bar: "bar://baz" }
|
||||
|
||||
it "returns number of read bytes as progress indicator", ->
|
||||
filePath = project.resolve 'a'
|
||||
filePath = atom.project.resolve 'a'
|
||||
totalBytes = 0
|
||||
promise = project.open(filePath)
|
||||
promise = atom.project.open(filePath)
|
||||
promise.progress (bytesRead) -> totalBytes = bytesRead
|
||||
|
||||
waitsForPromise ->
|
||||
@@ -225,22 +238,22 @@ describe "Project", ->
|
||||
describe ".bufferForPathSync(path)", ->
|
||||
describe "when opening a previously opened path", ->
|
||||
it "does not create a new buffer", ->
|
||||
buffer = project.bufferForPathSync("a").retain()
|
||||
expect(project.bufferForPathSync("a")).toBe buffer
|
||||
buffer = atom.project.bufferForPathSync("a").retain()
|
||||
expect(atom.project.bufferForPathSync("a")).toBe buffer
|
||||
|
||||
alternativeBuffer = project.bufferForPathSync("b").retain().release()
|
||||
alternativeBuffer = atom.project.bufferForPathSync("b").retain().release()
|
||||
expect(alternativeBuffer).not.toBe buffer
|
||||
buffer.release()
|
||||
|
||||
it "creates a new buffer if the previous buffer was destroyed", ->
|
||||
buffer = project.bufferForPathSync("a").retain().release()
|
||||
expect(project.bufferForPathSync("a").retain().release()).not.toBe buffer
|
||||
buffer = atom.project.bufferForPathSync("a").retain().release()
|
||||
expect(atom.project.bufferForPathSync("a").retain().release()).not.toBe buffer
|
||||
|
||||
describe ".bufferForPath(path)", ->
|
||||
[buffer] = []
|
||||
beforeEach ->
|
||||
waitsForPromise ->
|
||||
project.bufferForPath("a").then (o) ->
|
||||
atom.project.bufferForPath("a").then (o) ->
|
||||
buffer = o
|
||||
buffer.retain()
|
||||
|
||||
@@ -250,63 +263,137 @@ describe "Project", ->
|
||||
describe "when opening a previously opened path", ->
|
||||
it "does not create a new buffer", ->
|
||||
waitsForPromise ->
|
||||
project.bufferForPath("a").then (anotherBuffer) ->
|
||||
atom.project.bufferForPath("a").then (anotherBuffer) ->
|
||||
expect(anotherBuffer).toBe buffer
|
||||
|
||||
waitsForPromise ->
|
||||
project.bufferForPath("b").then (anotherBuffer) ->
|
||||
atom.project.bufferForPath("b").then (anotherBuffer) ->
|
||||
expect(anotherBuffer).not.toBe buffer
|
||||
|
||||
it "creates a new buffer if the previous buffer was destroyed", ->
|
||||
buffer.release()
|
||||
|
||||
waitsForPromise ->
|
||||
project.bufferForPath("b").then (anotherBuffer) ->
|
||||
atom.project.bufferForPath("b").then (anotherBuffer) ->
|
||||
expect(anotherBuffer).not.toBe buffer
|
||||
|
||||
describe ".resolve(uri)", ->
|
||||
describe "when passed an absolute or relative path", ->
|
||||
it "returns an absolute path based on the project's root", ->
|
||||
it "returns an absolute path based on the atom.project's root", ->
|
||||
absolutePath = require.resolve('./fixtures/dir/a')
|
||||
expect(project.resolve('a')).toBe absolutePath
|
||||
expect(project.resolve(absolutePath + '/../a')).toBe absolutePath
|
||||
expect(project.resolve('a/../a')).toBe absolutePath
|
||||
expect(atom.project.resolve('a')).toBe absolutePath
|
||||
expect(atom.project.resolve(absolutePath + '/../a')).toBe absolutePath
|
||||
expect(atom.project.resolve('a/../a')).toBe absolutePath
|
||||
|
||||
describe "when passed a uri with a scheme", ->
|
||||
it "does not modify uris that begin with a scheme", ->
|
||||
expect(project.resolve('http://zombo.com')).toBe 'http://zombo.com'
|
||||
expect(atom.project.resolve('http://zombo.com')).toBe 'http://zombo.com'
|
||||
|
||||
describe ".setPath(path)", ->
|
||||
describe "when path is a file", ->
|
||||
it "sets its path to the files parent directory and updates the root directory", ->
|
||||
project.setPath(require.resolve('./fixtures/dir/a'))
|
||||
expect(project.getPath()).toEqual path.dirname(require.resolve('./fixtures/dir/a'))
|
||||
expect(project.getRootDirectory().path).toEqual path.dirname(require.resolve('./fixtures/dir/a'))
|
||||
atom.project.setPath(require.resolve('./fixtures/dir/a'))
|
||||
expect(atom.project.getPath()).toEqual path.dirname(require.resolve('./fixtures/dir/a'))
|
||||
expect(atom.project.getRootDirectory().path).toEqual path.dirname(require.resolve('./fixtures/dir/a'))
|
||||
|
||||
describe "when path is a directory", ->
|
||||
it "sets its path to the directory and updates the root directory", ->
|
||||
directory = fs.absolute(path.join(__dirname, 'fixtures', 'dir', 'a-dir'))
|
||||
project.setPath(directory)
|
||||
expect(project.getPath()).toEqual directory
|
||||
expect(project.getRootDirectory().path).toEqual directory
|
||||
atom.project.setPath(directory)
|
||||
expect(atom.project.getPath()).toEqual directory
|
||||
expect(atom.project.getRootDirectory().path).toEqual directory
|
||||
|
||||
describe "when path is null", ->
|
||||
it "sets its path and root directory to null", ->
|
||||
project.setPath(null)
|
||||
expect(project.getPath()?).toBeFalsy()
|
||||
expect(project.getRootDirectory()?).toBeFalsy()
|
||||
atom.project.setPath(null)
|
||||
expect(atom.project.getPath()?).toBeFalsy()
|
||||
expect(atom.project.getRootDirectory()?).toBeFalsy()
|
||||
|
||||
describe ".replace()", ->
|
||||
[filePath, commentFilePath, sampleContent, sampleCommentContent] = []
|
||||
|
||||
beforeEach ->
|
||||
atom.project.setPath(atom.project.resolve('../'))
|
||||
|
||||
filePath = atom.project.resolve('sample.js')
|
||||
commentFilePath = atom.project.resolve('sample-with-comments.js')
|
||||
sampleContent = fs.readFileSync(filePath).toString()
|
||||
sampleCommentContent = fs.readFileSync(commentFilePath).toString()
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(filePath, sampleContent)
|
||||
fs.writeFileSync(commentFilePath, sampleCommentContent)
|
||||
|
||||
describe "when called with unopened files", ->
|
||||
it "replaces properly", ->
|
||||
results = []
|
||||
waitsForPromise ->
|
||||
atom.project.replace /items/gi, 'items', [filePath], (result) ->
|
||||
results.push(result)
|
||||
|
||||
runs ->
|
||||
expect(results).toHaveLength 1
|
||||
expect(results[0].filePath).toBe filePath
|
||||
expect(results[0].replacements).toBe 6
|
||||
|
||||
describe "when a buffer is already open", ->
|
||||
it "replaces properly and saves when not modified", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
expect(editor.isModified()).toBeFalsy()
|
||||
|
||||
results = []
|
||||
waitsForPromise ->
|
||||
atom.project.replace /items/gi, 'items', [filePath], (result) ->
|
||||
results.push(result)
|
||||
|
||||
runs ->
|
||||
expect(results).toHaveLength 1
|
||||
expect(results[0].filePath).toBe filePath
|
||||
expect(results[0].replacements).toBe 6
|
||||
|
||||
expect(editor.isModified()).toBeFalsy()
|
||||
|
||||
it "does not replace when the path is not specified", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor = atom.project.openSync('sample-with-comments.js')
|
||||
|
||||
results = []
|
||||
waitsForPromise ->
|
||||
atom.project.replace /items/gi, 'items', [commentFilePath], (result) ->
|
||||
results.push(result)
|
||||
|
||||
runs ->
|
||||
expect(results).toHaveLength 1
|
||||
expect(results[0].filePath).toBe commentFilePath
|
||||
|
||||
it "does NOT save when modified", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor.buffer.change([[0,0],[0,0]], 'omg')
|
||||
expect(editor.isModified()).toBeTruthy()
|
||||
|
||||
results = []
|
||||
waitsForPromise ->
|
||||
atom.project.replace /items/gi, 'okthen', [filePath], (result) ->
|
||||
results.push(result)
|
||||
|
||||
runs ->
|
||||
expect(results).toHaveLength 1
|
||||
expect(results[0].filePath).toBe filePath
|
||||
expect(results[0].replacements).toBe 6
|
||||
|
||||
expect(editor.isModified()).toBeTruthy()
|
||||
|
||||
describe ".scan(options, callback)", ->
|
||||
describe "when called with a regex", ->
|
||||
it "calls the callback with all regex results in all files in the project", ->
|
||||
results = []
|
||||
waitsForPromise ->
|
||||
project.scan /(a)+/, (result) ->
|
||||
atom.project.scan /(a)+/, (result) ->
|
||||
results.push(result)
|
||||
|
||||
runs ->
|
||||
expect(results).toHaveLength(3)
|
||||
expect(results[0].filePath).toBe project.resolve('a')
|
||||
expect(results[0].filePath).toBe atom.project.resolve('a')
|
||||
expect(results[0].matches).toHaveLength(3)
|
||||
expect(results[0].matches[0]).toEqual
|
||||
matchText: 'aaa'
|
||||
@@ -317,13 +404,13 @@ describe "Project", ->
|
||||
it "works with with escaped literals (like $ and ^)", ->
|
||||
results = []
|
||||
waitsForPromise ->
|
||||
project.scan /\$\w+/, (result) -> results.push(result)
|
||||
atom.project.scan /\$\w+/, (result) -> results.push(result)
|
||||
|
||||
runs ->
|
||||
expect(results.length).toBe 1
|
||||
|
||||
{filePath, matches} = results[0]
|
||||
expect(filePath).toBe project.resolve('a')
|
||||
expect(filePath).toBe atom.project.resolve('a')
|
||||
expect(matches).toHaveLength 1
|
||||
expect(matches[0]).toEqual
|
||||
matchText: '$bill'
|
||||
@@ -332,11 +419,11 @@ describe "Project", ->
|
||||
range: [[2, 6], [2, 11]]
|
||||
|
||||
it "works on evil filenames", ->
|
||||
project.setPath(path.join(__dirname, 'fixtures', 'evil-files'))
|
||||
atom.project.setPath(path.join(__dirname, 'fixtures', 'evil-files'))
|
||||
paths = []
|
||||
matches = []
|
||||
waitsForPromise ->
|
||||
project.scan /evil/, (result) ->
|
||||
atom.project.scan /evil/, (result) ->
|
||||
paths.push(result.filePath)
|
||||
matches = matches.concat(result.matches)
|
||||
|
||||
@@ -359,7 +446,7 @@ describe "Project", ->
|
||||
it "ignores case if the regex includes the `i` flag", ->
|
||||
results = []
|
||||
waitsForPromise ->
|
||||
project.scan /DOLLAR/i, (result) -> results.push(result)
|
||||
atom.project.scan /DOLLAR/i, (result) -> results.push(result)
|
||||
|
||||
runs ->
|
||||
expect(results).toHaveLength 1
|
||||
@@ -387,11 +474,11 @@ describe "Project", ->
|
||||
fs.removeSync(projectPath) if fs.existsSync(projectPath)
|
||||
|
||||
it "excludes ignored files", ->
|
||||
project.setPath(projectPath)
|
||||
config.set('core.excludeVcsIgnoredPaths', true)
|
||||
atom.project.setPath(projectPath)
|
||||
atom.config.set('core.excludeVcsIgnoredPaths', true)
|
||||
resultHandler = jasmine.createSpy("result found")
|
||||
waitsForPromise ->
|
||||
project.scan /match/, (results) ->
|
||||
atom.project.scan /match/, (results) ->
|
||||
resultHandler()
|
||||
|
||||
runs ->
|
||||
@@ -399,14 +486,14 @@ describe "Project", ->
|
||||
|
||||
it "includes only files when a directory filter is specified", ->
|
||||
projectPath = path.join(path.join(__dirname, 'fixtures', 'dir'))
|
||||
project.setPath(projectPath)
|
||||
atom.project.setPath(projectPath)
|
||||
|
||||
filePath = path.join(projectPath, 'a-dir', 'oh-git')
|
||||
|
||||
paths = []
|
||||
matches = []
|
||||
waitsForPromise ->
|
||||
project.scan /aaa/, paths: ["a-dir#{path.sep}"], (result) ->
|
||||
atom.project.scan /aaa/, paths: ["a-dir#{path.sep}"], (result) ->
|
||||
paths.push(result.filePath)
|
||||
matches = matches.concat(result.matches)
|
||||
|
||||
@@ -419,11 +506,11 @@ describe "Project", ->
|
||||
projectPath = temp.mkdirSync()
|
||||
filePath = path.join(projectPath, '.text')
|
||||
fs.writeFileSync(filePath, 'match this')
|
||||
project.setPath(projectPath)
|
||||
atom.project.setPath(projectPath)
|
||||
paths = []
|
||||
matches = []
|
||||
waitsForPromise ->
|
||||
project.scan /match this/, (result) ->
|
||||
atom.project.scan /match this/, (result) ->
|
||||
paths.push(result.filePath)
|
||||
matches = matches.concat(result.matches)
|
||||
|
||||
@@ -434,27 +521,56 @@ describe "Project", ->
|
||||
|
||||
it "excludes values in core.ignoredNames", ->
|
||||
projectPath = path.join(__dirname, 'fixtures', 'git', 'working-dir')
|
||||
ignoredNames = config.get("core.ignoredNames")
|
||||
ignoredNames = atom.config.get("core.ignoredNames")
|
||||
ignoredNames.push("a")
|
||||
config.set("core.ignoredNames", ignoredNames)
|
||||
atom.config.set("core.ignoredNames", ignoredNames)
|
||||
|
||||
resultHandler = jasmine.createSpy("result found")
|
||||
waitsForPromise ->
|
||||
project.scan /dollar/, (results) ->
|
||||
atom.project.scan /dollar/, (results) ->
|
||||
resultHandler()
|
||||
|
||||
runs ->
|
||||
expect(resultHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "scans buffer contents if the buffer is modified", ->
|
||||
editSession = project.openSync("a")
|
||||
editSession.setText("Elephant")
|
||||
editor = atom.project.openSync("a")
|
||||
editor.setText("Elephant")
|
||||
results = []
|
||||
waitsForPromise ->
|
||||
project.scan /a|Elephant/, (result) -> results.push result
|
||||
atom.project.scan /a|Elephant/, (result) -> results.push result
|
||||
|
||||
runs ->
|
||||
expect(results).toHaveLength 3
|
||||
resultForA = _.find results, ({filePath}) -> path.basename(filePath) == 'a'
|
||||
expect(resultForA.matches).toHaveLength 1
|
||||
expect(resultForA.matches[0].matchText).toBe 'Elephant'
|
||||
|
||||
describe ".eachBuffer(callback)", ->
|
||||
beforeEach ->
|
||||
atom.project.bufferForPathSync('a')
|
||||
|
||||
it "invokes the callback for existing buffer", ->
|
||||
count = 0
|
||||
count = 0
|
||||
callbackBuffer = null
|
||||
callback = (buffer) ->
|
||||
callbackBuffer = buffer
|
||||
count++
|
||||
atom.project.eachBuffer(callback)
|
||||
expect(count).toBe 1
|
||||
expect(callbackBuffer).toBe atom.project.getBuffers()[0]
|
||||
|
||||
it "invokes the callback for new buffers", ->
|
||||
count = 0
|
||||
callbackBuffer = null
|
||||
callback = (buffer) ->
|
||||
callbackBuffer = buffer
|
||||
count++
|
||||
|
||||
atom.project.eachBuffer(callback)
|
||||
count = 0
|
||||
callbackBuffer = null
|
||||
atom.project.bufferForPathSync(require.resolve('./fixtures/sample.txt'))
|
||||
expect(count).toBe 1
|
||||
expect(callbackBuffer).toBe atom.project.getBuffers()[1]
|
||||
|
||||
@@ -1,556 +0,0 @@
|
||||
{$, $$, fs, RootView, View} = require 'atom'
|
||||
Q = require 'q'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
Pane = require '../src/pane'
|
||||
|
||||
describe "RootView", ->
|
||||
pathToOpen = null
|
||||
|
||||
beforeEach ->
|
||||
project.setPath(project.resolve('dir'))
|
||||
pathToOpen = project.resolve('a')
|
||||
window.rootView = new RootView
|
||||
rootView.enableKeymap()
|
||||
rootView.openSync(pathToOpen)
|
||||
rootView.focus()
|
||||
|
||||
describe "@deserialize()", ->
|
||||
viewState = null
|
||||
|
||||
refreshRootViewAndProject = ->
|
||||
rootViewState = rootView.serialize()
|
||||
projectState = project.serialize()
|
||||
rootView.remove()
|
||||
project.destroy()
|
||||
window.project = deserialize(projectState)
|
||||
window.rootView = deserialize(rootViewState)
|
||||
rootView.attachToDom()
|
||||
|
||||
describe "when the serialized RootView has an unsaved buffer", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
rootView.attachToDom()
|
||||
rootView.openSync()
|
||||
editor1 = rootView.getActiveView()
|
||||
buffer = editor1.getBuffer()
|
||||
editor1.splitRight()
|
||||
expect(rootView.getActiveView()).toBe rootView.getEditors()[2]
|
||||
|
||||
refreshRootViewAndProject()
|
||||
|
||||
expect(rootView.getEditors().length).toBe 2
|
||||
expect(rootView.getActiveView()).toBe rootView.getEditors()[1]
|
||||
expect(rootView.title).toBe "untitled - #{project.getPath()}"
|
||||
|
||||
describe "when there are open editors", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
rootView.attachToDom()
|
||||
pane1 = rootView.getActivePane()
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitRight()
|
||||
pane4 = pane2.splitDown()
|
||||
pane2.showItem(project.openSync('b'))
|
||||
pane3.showItem(project.openSync('../sample.js'))
|
||||
pane3.activeItem.setCursorScreenPosition([2, 4])
|
||||
pane4.showItem(project.openSync('../sample.txt'))
|
||||
pane4.activeItem.setCursorScreenPosition([0, 2])
|
||||
pane2.focus()
|
||||
|
||||
refreshRootViewAndProject()
|
||||
|
||||
expect(rootView.getEditors().length).toBe 4
|
||||
editor1 = rootView.panes.find('.row > .pane .editor:eq(0)').view()
|
||||
editor3 = rootView.panes.find('.row > .pane .editor:eq(1)').view()
|
||||
editor2 = rootView.panes.find('.row > .column > .pane .editor:eq(0)').view()
|
||||
editor4 = rootView.panes.find('.row > .column > .pane .editor:eq(1)').view()
|
||||
|
||||
expect(editor1.getPath()).toBe project.resolve('a')
|
||||
expect(editor2.getPath()).toBe project.resolve('b')
|
||||
expect(editor3.getPath()).toBe project.resolve('../sample.js')
|
||||
expect(editor3.getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(editor4.getPath()).toBe project.resolve('../sample.txt')
|
||||
expect(editor4.getCursorScreenPosition()).toEqual [0, 2]
|
||||
|
||||
# ensure adjust pane dimensions is called
|
||||
expect(editor1.width()).toBeGreaterThan 0
|
||||
expect(editor2.width()).toBeGreaterThan 0
|
||||
expect(editor3.width()).toBeGreaterThan 0
|
||||
expect(editor4.width()).toBeGreaterThan 0
|
||||
|
||||
# ensure correct editor is focused again
|
||||
expect(editor2.isFocused).toBeTruthy()
|
||||
expect(editor1.isFocused).toBeFalsy()
|
||||
expect(editor3.isFocused).toBeFalsy()
|
||||
expect(editor4.isFocused).toBeFalsy()
|
||||
|
||||
expect(rootView.title).toBe "#{path.basename(editor2.getPath())} - #{project.getPath()}"
|
||||
|
||||
describe "where there are no open editors", ->
|
||||
it "constructs the view with no open editors", ->
|
||||
rootView.getActivePane().remove()
|
||||
expect(rootView.getEditors().length).toBe 0
|
||||
refreshRootViewAndProject()
|
||||
expect(rootView.getEditors().length).toBe 0
|
||||
|
||||
describe "focus", ->
|
||||
beforeEach ->
|
||||
rootView.attachToDom()
|
||||
|
||||
describe "when there is an active view", ->
|
||||
it "hands off focus to the active view", ->
|
||||
editor = rootView.getActiveView()
|
||||
editor.isFocused = false
|
||||
rootView.focus()
|
||||
expect(editor.isFocused).toBeTruthy()
|
||||
|
||||
describe "when there is no active view", ->
|
||||
beforeEach ->
|
||||
rootView.getActivePane().remove()
|
||||
expect(rootView.getActiveView()).toBeUndefined()
|
||||
rootView.attachToDom()
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
describe "when are visible focusable elements (with a -1 tabindex)", ->
|
||||
it "passes focus to the first focusable element", ->
|
||||
focusable1 = $$ -> @div "One", id: 'one', tabindex: -1
|
||||
focusable2 = $$ -> @div "Two", id: 'two', tabindex: -1
|
||||
rootView.horizontal.append(focusable1, focusable2)
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
rootView.focus()
|
||||
expect(document.activeElement).toBe focusable1[0]
|
||||
|
||||
describe "when there are no visible focusable elements", ->
|
||||
it "surrenders focus to the body", ->
|
||||
focusable = $$ -> @div "One", id: 'one', tabindex: -1
|
||||
rootView.horizontal.append(focusable)
|
||||
focusable.hide()
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
rootView.focus()
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
describe "keymap wiring", ->
|
||||
commandHandler = null
|
||||
beforeEach ->
|
||||
commandHandler = jasmine.createSpy('commandHandler')
|
||||
rootView.on('foo-command', commandHandler)
|
||||
|
||||
window.keymap.bindKeys('*', 'x': 'foo-command')
|
||||
|
||||
describe "when a keydown event is triggered in the RootView", ->
|
||||
it "triggers matching keybindings for that event", ->
|
||||
event = keydownEvent 'x', target: rootView[0]
|
||||
|
||||
rootView.trigger(event)
|
||||
expect(commandHandler).toHaveBeenCalled()
|
||||
|
||||
describe "window title", ->
|
||||
describe "when the project has no path", ->
|
||||
it "sets the title to 'untitled'", ->
|
||||
project.setPath(undefined)
|
||||
expect(rootView.title).toBe 'untitled'
|
||||
|
||||
describe "when the project has a path", ->
|
||||
beforeEach ->
|
||||
rootView.openSync('b')
|
||||
|
||||
describe "when there is an active pane item", ->
|
||||
it "sets the title to the pane item's title plus the project path", ->
|
||||
item = rootView.getActivePaneItem()
|
||||
expect(rootView.title).toBe "#{item.getTitle()} - #{project.getPath()}"
|
||||
|
||||
describe "when the title of the active pane item changes", ->
|
||||
it "updates the window title based on the item's new title", ->
|
||||
editSession = rootView.getActivePaneItem()
|
||||
editSession.buffer.setPath(path.join(temp.dir, 'hi'))
|
||||
expect(rootView.title).toBe "#{editSession.getTitle()} - #{project.getPath()}"
|
||||
|
||||
describe "when the active pane's item changes", ->
|
||||
it "updates the title to the new item's title plus the project path", ->
|
||||
rootView.getActivePane().showNextItem()
|
||||
item = rootView.getActivePaneItem()
|
||||
expect(rootView.title).toBe "#{item.getTitle()} - #{project.getPath()}"
|
||||
|
||||
describe "when the last pane item is removed", ->
|
||||
it "updates the title to contain the project's path", ->
|
||||
rootView.getActivePane().remove()
|
||||
expect(rootView.getActivePaneItem()).toBeUndefined()
|
||||
expect(rootView.title).toBe project.getPath()
|
||||
|
||||
describe "when an inactive pane's item changes", ->
|
||||
it "does not update the title", ->
|
||||
pane = rootView.getActivePane()
|
||||
pane.splitRight()
|
||||
initialTitle = rootView.title
|
||||
pane.showNextItem()
|
||||
expect(rootView.title).toBe initialTitle
|
||||
|
||||
describe "when the root view is deserialized", ->
|
||||
it "updates the title to contain the project's path", ->
|
||||
rootView2 = atom.deserializers.deserialize(rootView.serialize())
|
||||
item = rootView.getActivePaneItem()
|
||||
expect(rootView2.title).toBe "#{item.getTitle()} - #{project.getPath()}"
|
||||
rootView2.remove()
|
||||
|
||||
describe "font size adjustment", ->
|
||||
it "increases/decreases font size when increase/decrease-font-size events are triggered", ->
|
||||
fontSizeBefore = config.get('editor.fontSize')
|
||||
rootView.trigger 'window:increase-font-size'
|
||||
expect(config.get('editor.fontSize')).toBe fontSizeBefore + 1
|
||||
rootView.trigger 'window:increase-font-size'
|
||||
expect(config.get('editor.fontSize')).toBe fontSizeBefore + 2
|
||||
rootView.trigger 'window:decrease-font-size'
|
||||
expect(config.get('editor.fontSize')).toBe fontSizeBefore + 1
|
||||
rootView.trigger 'window:decrease-font-size'
|
||||
expect(config.get('editor.fontSize')).toBe fontSizeBefore
|
||||
|
||||
it "does not allow the font size to be less than 1", ->
|
||||
config.set("editor.fontSize", 1)
|
||||
rootView.trigger 'window:decrease-font-size'
|
||||
expect(config.get('editor.fontSize')).toBe 1
|
||||
|
||||
describe ".openSync(filePath, options)", ->
|
||||
describe "when there is no active pane", ->
|
||||
beforeEach ->
|
||||
spyOn(Pane.prototype, 'focus')
|
||||
rootView.getActivePane().remove()
|
||||
expect(rootView.getActivePane()).toBeUndefined()
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "creates a empty edit session as an item on a new pane, and focuses the pane", ->
|
||||
editSession = rootView.openSync()
|
||||
expect(rootView.getActivePane().activeItem).toBe editSession
|
||||
expect(editSession.getPath()).toBeUndefined()
|
||||
expect(rootView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
it "can create multiple empty edit sessions as an item on a new pane", ->
|
||||
editSession = rootView.openSync()
|
||||
editSession2 = rootView.openSync()
|
||||
expect(rootView.getActivePane().getItems().length).toBe 2
|
||||
expect(editSession).not.toBe editSession2
|
||||
|
||||
describe "when called with a path", ->
|
||||
it "creates an edit session for the given path as an item on a new pane, and focuses the pane", ->
|
||||
editSession = rootView.openSync('b')
|
||||
expect(rootView.getActivePane().activeItem).toBe editSession
|
||||
expect(editSession.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
expect(rootView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the changeFocus option is false", ->
|
||||
it "does not focus the new pane", ->
|
||||
editSession = rootView.openSync('b', changeFocus: false)
|
||||
expect(rootView.getActivePane().focus).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the split option is 'right'", ->
|
||||
it "creates a new pane and opens the file in said pane", ->
|
||||
editSession = rootView.openSync('b', split: 'right')
|
||||
expect(rootView.getActivePane().activeItem).toBe editSession
|
||||
expect(editSession.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
|
||||
describe "when there is an active pane", ->
|
||||
[activePane, initialItemCount] = []
|
||||
beforeEach ->
|
||||
activePane = rootView.getActivePane()
|
||||
spyOn(activePane, 'focus')
|
||||
initialItemCount = activePane.getItems().length
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "opens an edit session with an empty buffer as an item in the active pane and focuses it", ->
|
||||
editSession = rootView.openSync()
|
||||
expect(activePane.getItems().length).toBe initialItemCount + 1
|
||||
expect(activePane.activeItem).toBe editSession
|
||||
expect(editSession.getPath()).toBeUndefined()
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a path", ->
|
||||
describe "when the active pane already has an edit session item for the path being opened", ->
|
||||
it "shows the existing edit session in the pane", ->
|
||||
previousEditSession = activePane.activeItem
|
||||
|
||||
editSession = rootView.openSync('b')
|
||||
expect(activePane.activeItem).toBe editSession
|
||||
expect(editSession).not.toBe previousEditSession
|
||||
|
||||
editSession = rootView.openSync(previousEditSession.getPath())
|
||||
expect(editSession).toBe previousEditSession
|
||||
expect(activePane.activeItem).toBe editSession
|
||||
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the active pane does not have an edit session item for the path being opened", ->
|
||||
it "creates a new edit session for the given path in the active editor", ->
|
||||
editSession = rootView.openSync('b')
|
||||
expect(activePane.items.length).toBe 2
|
||||
expect(activePane.activeItem).toBe editSession
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the changeFocus option is false", ->
|
||||
it "does not focus the active pane", ->
|
||||
editSession = rootView.openSync('b', changeFocus: false)
|
||||
expect(activePane.focus).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the split option is 'right'", ->
|
||||
it "creates a new pane and opens the file in said pane", ->
|
||||
pane1 = rootView.getActivePane()
|
||||
|
||||
editSession = rootView.openSync('b', split: 'right')
|
||||
pane2 = rootView.getActivePane()
|
||||
expect(pane2[0]).not.toBe pane1[0]
|
||||
expect(editSession.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
|
||||
expect(rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
editSession = rootView.openSync('file1', split: 'right')
|
||||
pane3 = rootView.getActivePane()
|
||||
expect(pane3[0]).toBe pane2[0]
|
||||
expect(editSession.getPath()).toBe require.resolve('./fixtures/dir/file1')
|
||||
|
||||
expect(rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
describe ".openSingletonSync(filePath, options)", ->
|
||||
describe "when there is an active pane", ->
|
||||
[pane1] = []
|
||||
beforeEach ->
|
||||
spyOn(Pane.prototype, 'focus').andCallFake -> @makeActive()
|
||||
pane1 = rootView.getActivePane()
|
||||
|
||||
it "creates a new pane and reuses the file when already open", ->
|
||||
rootView.openSingletonSync('b', split: 'right')
|
||||
pane2 = rootView.getActivePane()
|
||||
expect(pane2[0]).not.toBe pane1[0]
|
||||
expect(pane1.itemForUri('b')).toBeFalsy()
|
||||
expect(pane2.itemForUri('b')).not.toBeFalsy()
|
||||
expect(rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
pane1.focus()
|
||||
expect(rootView.getActivePane()[0]).toBe pane1[0]
|
||||
|
||||
rootView.openSingletonSync('b', split: 'right')
|
||||
pane3 = rootView.getActivePane()
|
||||
expect(pane3[0]).toBe pane2[0]
|
||||
expect(pane1.itemForUri('b')).toBeFalsy()
|
||||
expect(pane2.itemForUri('b')).not.toBeFalsy()
|
||||
expect(rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
it "handles split: left by opening to the left pane when necessary", ->
|
||||
rootView.openSingletonSync('b', split: 'right')
|
||||
pane2 = rootView.getActivePane()
|
||||
expect(pane2[0]).not.toBe pane1[0]
|
||||
|
||||
rootView.openSingletonSync('file1', split: 'left')
|
||||
|
||||
activePane = rootView.getActivePane()
|
||||
expect(activePane[0]).toBe pane1[0]
|
||||
|
||||
expect(pane1.itemForUri('file1')).toBeTruthy()
|
||||
expect(pane2.itemForUri('file1')).toBeFalsy()
|
||||
expect(rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
pane2.focus()
|
||||
expect(rootView.getActivePane()[0]).toBe pane2[0]
|
||||
|
||||
rootView.openSingletonSync('file1', split: 'left')
|
||||
activePane = rootView.getActivePane()
|
||||
expect(activePane[0]).toBe pane1[0]
|
||||
expect(rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
it "reuses the file when already open", ->
|
||||
rootView.openSync('b')
|
||||
rootView.openSingletonSync('b', split: 'right')
|
||||
expect(rootView.panes.find('.pane').toArray()).toEqual [pane1[0]]
|
||||
|
||||
describe ".open(filePath)", ->
|
||||
beforeEach ->
|
||||
spyOn(Pane.prototype, 'focus')
|
||||
|
||||
describe "when there is no active pane", ->
|
||||
beforeEach ->
|
||||
rootView.getActivePane().remove()
|
||||
expect(rootView.getActivePane()).toBeUndefined()
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "creates a empty edit session as an item on a new pane, and focuses the pane", ->
|
||||
editSession = null
|
||||
|
||||
waitsForPromise ->
|
||||
rootView.open().then (o) -> editSession = o
|
||||
|
||||
runs ->
|
||||
expect(rootView.getActivePane().activeItem).toBe editSession
|
||||
expect(editSession.getPath()).toBeUndefined()
|
||||
expect(rootView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
it "can create multiple empty edit sessions as items on a pane", ->
|
||||
editSession1 = null
|
||||
editSession2 = null
|
||||
|
||||
waitsForPromise ->
|
||||
rootView.open()
|
||||
.then (o) ->
|
||||
editSession1 = o
|
||||
rootView.open()
|
||||
.then (o) ->
|
||||
editSession2 = o
|
||||
|
||||
runs ->
|
||||
expect(rootView.getActivePane().getItems().length).toBe 2
|
||||
expect(editSession1).not.toBe editSession2
|
||||
|
||||
describe "when called with a path", ->
|
||||
it "creates an edit session for the given path as an item on a new pane, and focuses the pane", ->
|
||||
editSession = null
|
||||
waitsForPromise ->
|
||||
rootView.open('b').then (o) -> editSession = o
|
||||
|
||||
runs ->
|
||||
expect(rootView.getActivePane().activeItem).toBe editSession
|
||||
expect(editSession.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
expect(rootView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
describe "when there is an active pane", ->
|
||||
[activePane] = []
|
||||
|
||||
beforeEach ->
|
||||
activePane = rootView.getActivePane()
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "opens an edit session with an empty buffer as an item in the active pane and focuses it", ->
|
||||
editSession = null
|
||||
|
||||
waitsForPromise ->
|
||||
rootView.open().then (o) -> editSession = o
|
||||
|
||||
runs ->
|
||||
expect(activePane.getItems().length).toBe 2
|
||||
expect(activePane.activeItem).toBe editSession
|
||||
expect(editSession.getPath()).toBeUndefined()
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a path", ->
|
||||
describe "when the active pane already has an item for the given path", ->
|
||||
it "shows the existing edit session in the pane", ->
|
||||
previousEditSession = activePane.activeItem
|
||||
|
||||
editSession = null
|
||||
waitsForPromise ->
|
||||
rootView.open('b').then (o) -> editSession = o
|
||||
|
||||
runs ->
|
||||
expect(activePane.activeItem).toBe editSession
|
||||
expect(editSession).not.toBe previousEditSession
|
||||
|
||||
waitsForPromise ->
|
||||
rootView.open(previousEditSession.getPath()).then (o) -> editSession = o
|
||||
|
||||
runs ->
|
||||
expect(editSession).toBe previousEditSession
|
||||
expect(activePane.activeItem).toBe editSession
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the active pane does not have an existing item for the given path", ->
|
||||
it "creates a new edit session for the given path in the active pane", ->
|
||||
editSession = null
|
||||
|
||||
waitsForPromise ->
|
||||
rootView.open('b').then (o) -> editSession = o
|
||||
|
||||
runs ->
|
||||
expect(activePane.activeItem).toBe editSession
|
||||
expect(activePane.getItems().length).toBe 2
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "window:toggle-invisibles event", ->
|
||||
it "shows/hides invisibles in all open and future editors", ->
|
||||
rootView.height(200)
|
||||
rootView.attachToDom()
|
||||
rightEditor = rootView.getActiveView()
|
||||
rightEditor.setText(" \t ")
|
||||
leftEditor = rightEditor.splitLeft()
|
||||
expect(rightEditor.find(".line:first").text()).toBe " "
|
||||
expect(leftEditor.find(".line:first").text()).toBe " "
|
||||
|
||||
withInvisiblesShowing = "#{rightEditor.invisibles.space}#{rightEditor.invisibles.tab} #{rightEditor.invisibles.space}#{rightEditor.invisibles.eol}"
|
||||
|
||||
rootView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
expect(leftEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
lowerLeftEditor = leftEditor.splitDown()
|
||||
expect(lowerLeftEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
rootView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditor.find(".line:first").text()).toBe " "
|
||||
expect(leftEditor.find(".line:first").text()).toBe " "
|
||||
|
||||
lowerRightEditor = rightEditor.splitDown()
|
||||
expect(lowerRightEditor.find(".line:first").text()).toBe " "
|
||||
|
||||
describe ".eachEditor(callback)", ->
|
||||
beforeEach ->
|
||||
rootView.attachToDom()
|
||||
|
||||
it "invokes the callback for existing editor", ->
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
callback = (editor) ->
|
||||
callbackEditor = editor
|
||||
count++
|
||||
rootView.eachEditor(callback)
|
||||
expect(count).toBe 1
|
||||
expect(callbackEditor).toBe rootView.getActiveView()
|
||||
|
||||
it "invokes the callback for new editor", ->
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
callback = (editor) ->
|
||||
callbackEditor = editor
|
||||
count++
|
||||
|
||||
rootView.eachEditor(callback)
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
rootView.getActiveView().splitRight()
|
||||
expect(count).toBe 1
|
||||
expect(callbackEditor).toBe rootView.getActiveView()
|
||||
|
||||
it "returns a subscription that can be disabled", ->
|
||||
count = 0
|
||||
callback = (editor) -> count++
|
||||
|
||||
subscription = rootView.eachEditor(callback)
|
||||
expect(count).toBe 1
|
||||
rootView.getActiveView().splitRight()
|
||||
expect(count).toBe 2
|
||||
subscription.off()
|
||||
rootView.getActiveView().splitRight()
|
||||
expect(count).toBe 2
|
||||
|
||||
describe ".eachBuffer(callback)", ->
|
||||
beforeEach ->
|
||||
rootView.attachToDom()
|
||||
|
||||
it "invokes the callback for existing buffer", ->
|
||||
count = 0
|
||||
count = 0
|
||||
callbackBuffer = null
|
||||
callback = (buffer) ->
|
||||
callbackBuffer = buffer
|
||||
count++
|
||||
rootView.eachBuffer(callback)
|
||||
expect(count).toBe 1
|
||||
expect(callbackBuffer).toBe rootView.getActiveView().getBuffer()
|
||||
|
||||
it "invokes the callback for new buffer", ->
|
||||
count = 0
|
||||
callbackBuffer = null
|
||||
callback = (buffer) ->
|
||||
callbackBuffer = buffer
|
||||
count++
|
||||
|
||||
rootView.eachBuffer(callback)
|
||||
count = 0
|
||||
callbackBuffer = null
|
||||
rootView.openSync(require.resolve('./fixtures/sample.txt'))
|
||||
expect(count).toBe 1
|
||||
expect(callbackBuffer).toBe rootView.getActiveView().getBuffer()
|
||||
@@ -1,12 +1,12 @@
|
||||
EditSession = require '../src/edit-session'
|
||||
Editor = require '../src/editor'
|
||||
|
||||
describe "Selection", ->
|
||||
[buffer, editSession, selection] = []
|
||||
[buffer, editor, selection] = []
|
||||
|
||||
beforeEach ->
|
||||
buffer = project.buildBufferSync('sample.js')
|
||||
editSession = new EditSession(buffer: buffer, tabLength: 2)
|
||||
selection = editSession.getSelection()
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
editor = new Editor(buffer: buffer, tabLength: 2)
|
||||
selection = editor.getSelection()
|
||||
|
||||
afterEach ->
|
||||
buffer.destroy()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{View, $$} = require 'atom'
|
||||
{View, $, $$} = require 'atom'
|
||||
|
||||
describe "SpacePen extensions", ->
|
||||
class TestView extends View
|
||||
@@ -23,21 +23,21 @@ describe "SpacePen extensions", ->
|
||||
expect(observeHandler).toHaveBeenCalledWith(undefined)
|
||||
observeHandler.reset()
|
||||
|
||||
config.set("foo.bar", "hello")
|
||||
atom.config.set("foo.bar", "hello")
|
||||
|
||||
expect(observeHandler).toHaveBeenCalledWith("hello", previous: undefined)
|
||||
observeHandler.reset()
|
||||
|
||||
view.unobserveConfig()
|
||||
|
||||
config.set("foo.bar", "goodbye")
|
||||
atom.config.set("foo.bar", "goodbye")
|
||||
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "unobserves when the view is removed", ->
|
||||
observeHandler.reset()
|
||||
parent.remove()
|
||||
config.set("foo.bar", "hello")
|
||||
atom.config.set("foo.bar", "hello")
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe "View.subscribe(eventEmitter, eventName, callback)", ->
|
||||
@@ -51,3 +51,17 @@ describe "SpacePen extensions", ->
|
||||
it "subscribes to the given event emitter and unsubscribes when unsubscribe is called", ->
|
||||
emitter.trigger "foo"
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
|
||||
describe "tooltips", ->
|
||||
describe "replaceModifiers", ->
|
||||
replaceModifiers = $.fn.setTooltip.replaceModifiers
|
||||
|
||||
it "replaces single keystroke", ->
|
||||
expect(replaceModifiers('cmd-O')).toEqual '⌘⇧O'
|
||||
expect(replaceModifiers('cmd-shift-up')).toEqual '⌘⇧↑'
|
||||
expect(replaceModifiers('cmd-option-down')).toEqual '⌘⌥↓'
|
||||
expect(replaceModifiers('cmd-option-left')).toEqual '⌘⌥←'
|
||||
expect(replaceModifiers('cmd-option-right')).toEqual '⌘⌥→'
|
||||
|
||||
it "replaces multiple keystroke", ->
|
||||
expect(replaceModifiers('cmd-o ctrl-2')).toEqual '⌘O ⌃2'
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Start the crash reporter before anything else.
|
||||
require('crash-reporter').start(productName: 'Atom', companyName: 'GitHub')
|
||||
|
||||
try
|
||||
require '../src/window'
|
||||
Atom = require '../src/atom'
|
||||
|
||||
+33
-45
@@ -1,15 +1,15 @@
|
||||
require '../src/window'
|
||||
window.setUpEnvironment('spec')
|
||||
window.restoreDimensions()
|
||||
atom.setUpEnvironment('spec')
|
||||
atom.restoreDimensions()
|
||||
|
||||
require '../vendor/jasmine-jquery'
|
||||
path = require 'path'
|
||||
{_, $, File, RootView, fs} = require 'atom'
|
||||
{_, $, File, WorkspaceView, fs} = require 'atom'
|
||||
Keymap = require '../src/keymap'
|
||||
Config = require '../src/config'
|
||||
{Point} = require 'telepath'
|
||||
Project = require '../src/project'
|
||||
Editor = require '../src/editor'
|
||||
EditorView = require '../src/editor-view'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
pathwatcher = require 'pathwatcher'
|
||||
platform = require './spec-helper-platform'
|
||||
@@ -23,7 +23,7 @@ atom.themes.requireStylesheet '../static/jasmine'
|
||||
fixturePackagesPath = path.resolve(__dirname, './fixtures/packages')
|
||||
atom.packages.packageDirPaths.unshift(fixturePackagesPath)
|
||||
atom.keymap.loadBundledKeymaps()
|
||||
[bindingSetsToRestore, bindingSetsByFirstKeystrokeToRestore] = []
|
||||
keyBindingsToRestore = atom.keymap.getKeyBindings()
|
||||
|
||||
$(window).on 'core:close', -> window.close()
|
||||
$(window).on 'unload', ->
|
||||
@@ -39,7 +39,9 @@ specPackageName = null
|
||||
specPackagePath = null
|
||||
specProjectPath = null
|
||||
|
||||
if specDirectory = atom.getLoadSettings().specDirectory
|
||||
{specDirectory, resourcePath} = atom.getLoadSettings()
|
||||
|
||||
if specDirectory
|
||||
specPackagePath = path.resolve(specDirectory, '..')
|
||||
try
|
||||
specPackageName = fs.readObjectSync(path.join(specPackagePath, 'package.json'))?.name
|
||||
@@ -47,11 +49,9 @@ if specDirectory = atom.getLoadSettings().specDirectory
|
||||
|
||||
beforeEach ->
|
||||
$.fx.off = true
|
||||
if specProjectPath
|
||||
atom.project = new Project(specProjectPath)
|
||||
else
|
||||
atom.project = new Project(path.join(@specDirectory, 'fixtures'))
|
||||
window.project = atom.project
|
||||
projectPath = specProjectPath ? path.join(@specDirectory, 'fixtures')
|
||||
atom.project = atom.getWindowState().set('project', new Project(path: projectPath))
|
||||
atom.keymap.keyBindings = _.clone(keyBindingsToRestore)
|
||||
|
||||
window.resetTimeouts()
|
||||
atom.packages.packageStates = {}
|
||||
@@ -66,20 +66,15 @@ beforeEach ->
|
||||
resolvePackagePath(packageName)
|
||||
resolvePackagePath = _.bind(spy.originalValue, atom.packages)
|
||||
|
||||
# used to reset keymap after each spec
|
||||
bindingSetsToRestore = _.clone(keymap.bindingSets)
|
||||
bindingSetsByFirstKeystrokeToRestore = _.clone(keymap.bindingSetsByFirstKeystroke)
|
||||
|
||||
# prevent specs from modifying Atom's menus
|
||||
spyOn(atom.menu, 'sendToBrowserProcess')
|
||||
|
||||
# reset config before each spec; don't load or save from/to `config.json`
|
||||
config = new Config
|
||||
resourcePath: window.resourcePath
|
||||
configDirPath: atom.getConfigDirPath()
|
||||
config.packageDirPaths.unshift(fixturePackagesPath)
|
||||
config = new Config({resourcePath, configDirPath: atom.getConfigDirPath()})
|
||||
spyOn(config, 'load')
|
||||
spyOn(config, 'save')
|
||||
config.setDefaults('core', WorkspaceView.configDefaults)
|
||||
config.setDefaults('editor', EditorView.configDefaults)
|
||||
config.set "editor.fontFamily", "Courier"
|
||||
config.set "editor.fontSize", 16
|
||||
config.set "editor.autoIndent", false
|
||||
@@ -87,11 +82,10 @@ beforeEach ->
|
||||
"package-with-broken-package-json", "package-with-broken-keymap"]
|
||||
config.save.reset()
|
||||
atom.config = config
|
||||
window.config = config
|
||||
|
||||
# make editor display updates synchronous
|
||||
spyOn(Editor.prototype, 'requestDisplayUpdate').andCallFake -> @updateDisplay()
|
||||
spyOn(RootView.prototype, 'setTitle').andCallFake (@title) ->
|
||||
spyOn(EditorView.prototype, 'requestDisplayUpdate').andCallFake -> @updateDisplay()
|
||||
spyOn(WorkspaceView.prototype, 'setTitle').andCallFake (@title) ->
|
||||
spyOn(window, "setTimeout").andCallFake window.fakeSetTimeout
|
||||
spyOn(window, "clearTimeout").andCallFake window.fakeClearTimeout
|
||||
spyOn(File.prototype, "detectResurrectionAfterDelay").andCallFake -> @detectResurrection()
|
||||
@@ -107,26 +101,20 @@ beforeEach ->
|
||||
addCustomMatchers(this)
|
||||
|
||||
afterEach ->
|
||||
keymap.bindingSets = bindingSetsToRestore
|
||||
keymap.bindingSetsByFirstKeystroke = bindingSetsByFirstKeystrokeToRestore
|
||||
atom.deactivatePackages()
|
||||
atom.packages.deactivatePackages()
|
||||
atom.menu.template = []
|
||||
|
||||
window.rootView?.remove?()
|
||||
atom.rootView?.remove?() if atom.rootView isnt window.rootView
|
||||
window.rootView = null
|
||||
atom.rootView = null
|
||||
atom.workspaceView?.remove?()
|
||||
atom.workspaceView = null
|
||||
|
||||
window.project?.destroy?()
|
||||
atom.project?.destroy?() if atom.project isnt window.project
|
||||
window.project = null
|
||||
atom.project?.destroy?()
|
||||
atom.project = null
|
||||
|
||||
$('#jasmine-content').empty() unless window.debugContent
|
||||
delete atom.windowState
|
||||
jasmine.unspy(atom, 'saveWindowState')
|
||||
ensureNoPathSubscriptions()
|
||||
syntax.off()
|
||||
atom.syntax.off()
|
||||
waits(0) # yield to ui thread to make screen update more frequently
|
||||
|
||||
ensureNoPathSubscriptions = ->
|
||||
@@ -180,8 +168,8 @@ window.keydownEvent = (key, properties={}) ->
|
||||
|
||||
window.mouseEvent = (type, properties) ->
|
||||
if properties.point
|
||||
{point, editor} = properties
|
||||
{top, left} = @pagePixelPositionForPoint(editor, point)
|
||||
{point, editorView} = properties
|
||||
{top, left} = @pagePixelPositionForPoint(editorView, point)
|
||||
properties.pageX = left + 1
|
||||
properties.pageY = top + 1
|
||||
properties.originalEvent ?= {detail: 1}
|
||||
@@ -242,22 +230,22 @@ window.advanceClock = (delta=1) ->
|
||||
|
||||
callback() for callback in callbacks
|
||||
|
||||
window.pagePixelPositionForPoint = (editor, point) ->
|
||||
window.pagePixelPositionForPoint = (editorView, point) ->
|
||||
point = Point.fromObject point
|
||||
top = editor.renderedLines.offset().top + point.row * editor.lineHeight
|
||||
left = editor.renderedLines.offset().left + point.column * editor.charWidth - editor.renderedLines.scrollLeft()
|
||||
top = editorView.renderedLines.offset().top + point.row * editorView.lineHeight
|
||||
left = editorView.renderedLines.offset().left + point.column * editorView.charWidth - editorView.renderedLines.scrollLeft()
|
||||
{ top, left }
|
||||
|
||||
window.tokensText = (tokens) ->
|
||||
_.pluck(tokens, 'value').join('')
|
||||
|
||||
window.setEditorWidthInChars = (editor, widthInChars, charWidth=editor.charWidth) ->
|
||||
editor.width(charWidth * widthInChars + editor.gutter.outerWidth())
|
||||
$(window).trigger 'resize' # update width of editor's on-screen lines
|
||||
window.setEditorWidthInChars = (editorView, widthInChars, charWidth=editorView.charWidth) ->
|
||||
editorView.width(charWidth * widthInChars + editorView.gutter.outerWidth())
|
||||
$(window).trigger 'resize' # update width of editor view's on-screen lines
|
||||
|
||||
window.setEditorHeightInLines = (editor, heightInChars, charHeight=editor.lineHeight) ->
|
||||
editor.height(charHeight * heightInChars + editor.renderedLines.position().top)
|
||||
$(window).trigger 'resize' # update editor's on-screen lines
|
||||
window.setEditorHeightInLines = (editorView, heightInChars, charHeight=editorView.lineHeight) ->
|
||||
editorView.height(charHeight * heightInChars + editorView.renderedLines.position().top)
|
||||
$(window).trigger 'resize' # update editor view's on-screen lines
|
||||
|
||||
$.fn.resultOfTrigger = (type) ->
|
||||
event = $.Event(type)
|
||||
@@ -265,7 +253,7 @@ $.fn.resultOfTrigger = (type) ->
|
||||
event.result
|
||||
|
||||
$.fn.enableKeymap = ->
|
||||
@on 'keydown', (e) => window.keymap.handleKeyEvent(e)
|
||||
@on 'keydown', (e) => atom.keymap.handleKeyEvent(e)
|
||||
|
||||
$.fn.attachToDom = ->
|
||||
@appendTo($('#jasmine-content'))
|
||||
|
||||
+45
-44
@@ -1,54 +1,55 @@
|
||||
measure 'spec suite require time', ->
|
||||
{_, fs, Git} = require 'atom'
|
||||
path = require 'path'
|
||||
require './spec-helper'
|
||||
{_, fs, Git} = require 'atom'
|
||||
path = require 'path'
|
||||
require './spec-helper'
|
||||
|
||||
requireSpecs = (specDirectory, specType) ->
|
||||
for specFilePath in fs.listTreeSync(specDirectory) when /-spec\.coffee$/.test specFilePath
|
||||
require specFilePath
|
||||
requireSpecs = (specDirectory, specType) ->
|
||||
for specFilePath in fs.listTreeSync(specDirectory) when /-spec\.coffee$/.test specFilePath
|
||||
require specFilePath
|
||||
|
||||
# Set spec directory on spec for setting up the project in spec-helper
|
||||
setSpecDirectory(specDirectory)
|
||||
# Set spec directory on spec for setting up the project in spec-helper
|
||||
setSpecDirectory(specDirectory)
|
||||
|
||||
setSpecField = (name, value) ->
|
||||
specs = jasmine.getEnv().currentRunner().specs()
|
||||
return if specs.length is 0
|
||||
for index in [specs.length-1..0]
|
||||
break if specs[index][name]?
|
||||
specs[index][name] = value
|
||||
setSpecField = (name, value) ->
|
||||
specs = jasmine.getEnv().currentRunner().specs()
|
||||
return if specs.length is 0
|
||||
for index in [specs.length-1..0]
|
||||
break if specs[index][name]?
|
||||
specs[index][name] = value
|
||||
|
||||
setSpecType = (specType) ->
|
||||
setSpecField('specType', specType)
|
||||
setSpecType = (specType) ->
|
||||
setSpecField('specType', specType)
|
||||
|
||||
setSpecDirectory = (specDirectory) ->
|
||||
setSpecField('specDirectory', specDirectory)
|
||||
setSpecDirectory = (specDirectory) ->
|
||||
setSpecField('specDirectory', specDirectory)
|
||||
|
||||
runAllSpecs = ->
|
||||
# Only run core specs when resource path is the Atom repository
|
||||
if Git.exists(window.resourcePath)
|
||||
requireSpecs(path.join(window.resourcePath, 'spec'))
|
||||
setSpecType('core')
|
||||
runAllSpecs = ->
|
||||
{resourcePath} = atom.getLoadSettings()
|
||||
# Only run core specs when resource path is the Atom repository
|
||||
if Git.exists(resourcePath)
|
||||
requireSpecs(path.join(resourcePath, 'spec'))
|
||||
setSpecType('core')
|
||||
|
||||
fixturesPackagesPath = path.join(__dirname, 'fixtures', 'packages')
|
||||
packagePaths = atom.getAvailablePackageNames().map (packageName) -> atom.resolvePackagePath(packageName)
|
||||
packagePaths = _.groupBy packagePaths, (packagePath) ->
|
||||
if packagePath.indexOf("#{fixturesPackagesPath}#{path.sep}") is 0
|
||||
'fixtures'
|
||||
else if packagePath.indexOf("#{window.resourcePath}#{path.sep}") is 0
|
||||
'bundled'
|
||||
else
|
||||
'user'
|
||||
fixturesPackagesPath = path.join(__dirname, 'fixtures', 'packages')
|
||||
packagePaths = atom.packages.getAvailablePackageNames().map (packageName) ->
|
||||
atom.packages.resolvePackagePath(packageName)
|
||||
packagePaths = _.groupBy packagePaths, (packagePath) ->
|
||||
if packagePath.indexOf("#{fixturesPackagesPath}#{path.sep}") is 0
|
||||
'fixtures'
|
||||
else if packagePath.indexOf("#{resourcePath}#{path.sep}") is 0
|
||||
'bundled'
|
||||
else
|
||||
'user'
|
||||
|
||||
# Run bundled package specs
|
||||
requireSpecs(path.join(packagePath, 'spec')) for packagePath in packagePaths.bundled ? []
|
||||
setSpecType('bundled')
|
||||
# Run bundled package specs
|
||||
requireSpecs(path.join(packagePath, 'spec')) for packagePath in packagePaths.bundled ? []
|
||||
setSpecType('bundled')
|
||||
|
||||
# Run user package specs
|
||||
requireSpecs(path.join(packagePath, 'spec')) for packagePath in packagePaths.user ? []
|
||||
setSpecType('user')
|
||||
# Run user package specs
|
||||
requireSpecs(path.join(packagePath, 'spec')) for packagePath in packagePaths.user ? []
|
||||
setSpecType('user')
|
||||
|
||||
if specDirectory = atom.getLoadSettings().specDirectory
|
||||
requireSpecs(specDirectory)
|
||||
setSpecType('user')
|
||||
else
|
||||
runAllSpecs()
|
||||
if specDirectory = atom.getLoadSettings().specDirectory
|
||||
requireSpecs(specDirectory)
|
||||
setSpecType('user')
|
||||
else
|
||||
runAllSpecs()
|
||||
|
||||
+50
-50
@@ -5,60 +5,60 @@ TextMateGrammar = require '../src/text-mate-grammar'
|
||||
|
||||
describe "the `syntax` global", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-text', sync: true)
|
||||
atom.activatePackage('language-javascript', sync: true)
|
||||
atom.activatePackage('language-coffee-script', sync: true)
|
||||
atom.activatePackage('language-ruby', sync: true)
|
||||
atom.packages.activatePackage('language-text', sync: true)
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
atom.packages.activatePackage('language-coffee-script', sync: true)
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
|
||||
describe "serialization", ->
|
||||
it "remembers grammar overrides by path", ->
|
||||
filePath = '/foo/bar/file.js'
|
||||
expect(syntax.selectGrammar(filePath).name).not.toBe 'Ruby'
|
||||
syntax.setGrammarOverrideForPath(filePath, 'source.ruby')
|
||||
syntax2 = deserialize(syntax.serialize())
|
||||
syntax2.addGrammar(grammar) for grammar in syntax.grammars when grammar isnt syntax.nullGrammar
|
||||
expect(atom.syntax.selectGrammar(filePath).name).not.toBe 'Ruby'
|
||||
atom.syntax.setGrammarOverrideForPath(filePath, 'source.ruby')
|
||||
syntax2 = atom.deserializers.deserialize(atom.syntax.serialize())
|
||||
syntax2.addGrammar(grammar) for grammar in atom.syntax.grammars when grammar isnt atom.syntax.nullGrammar
|
||||
expect(syntax2.selectGrammar(filePath).name).toBe 'Ruby'
|
||||
|
||||
describe ".selectGrammar(filePath)", ->
|
||||
it "can use the filePath to load the correct grammar based on the grammar's filetype", ->
|
||||
atom.activatePackage('language-git', sync: true)
|
||||
atom.packages.activatePackage('language-git', sync: true)
|
||||
|
||||
expect(syntax.selectGrammar("file.js").name).toBe "JavaScript" # based on extension (.js)
|
||||
expect(syntax.selectGrammar(path.join(temp.dir, '.git', 'config')).name).toBe "Git Config" # based on end of the path (.git/config)
|
||||
expect(syntax.selectGrammar("Rakefile").name).toBe "Ruby" # based on the file's basename (Rakefile)
|
||||
expect(syntax.selectGrammar("curb").name).toBe "Null Grammar"
|
||||
expect(syntax.selectGrammar("/hu.git/config").name).toBe "Null Grammar"
|
||||
expect(atom.syntax.selectGrammar("file.js").name).toBe "JavaScript" # based on extension (.js)
|
||||
expect(atom.syntax.selectGrammar(path.join(temp.dir, '.git', 'config')).name).toBe "Git Config" # based on end of the path (.git/config)
|
||||
expect(atom.syntax.selectGrammar("Rakefile").name).toBe "Ruby" # based on the file's basename (Rakefile)
|
||||
expect(atom.syntax.selectGrammar("curb").name).toBe "Null Grammar"
|
||||
expect(atom.syntax.selectGrammar("/hu.git/config").name).toBe "Null Grammar"
|
||||
|
||||
it "uses the filePath's shebang line if the grammar cannot be determined by the extension or basename", ->
|
||||
filePath = require.resolve("./fixtures/shebang")
|
||||
expect(syntax.selectGrammar(filePath).name).toBe "Ruby"
|
||||
expect(atom.syntax.selectGrammar(filePath).name).toBe "Ruby"
|
||||
|
||||
it "uses the number of newlines in the first line regex to determine the number of lines to test against", ->
|
||||
atom.activatePackage('language-property-list', sync: true)
|
||||
atom.packages.activatePackage('language-property-list', sync: true)
|
||||
|
||||
fileContent = "first-line\n<html>"
|
||||
expect(syntax.selectGrammar("dummy.coffee", fileContent).name).toBe "CoffeeScript"
|
||||
expect(atom.syntax.selectGrammar("dummy.coffee", fileContent).name).toBe "CoffeeScript"
|
||||
|
||||
fileContent = '<?xml version="1.0" encoding="UTF-8"?>'
|
||||
expect(syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Null Grammar"
|
||||
expect(atom.syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Null Grammar"
|
||||
|
||||
fileContent += '\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
|
||||
expect(syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Property List (XML)"
|
||||
expect(atom.syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Property List (XML)"
|
||||
|
||||
it "doesn't read the file when the file contents are specified", ->
|
||||
filePath = require.resolve("./fixtures/shebang")
|
||||
filePathContents = fs.readFileSync(filePath, 'utf8')
|
||||
spyOn(fs, 'read').andCallThrough()
|
||||
expect(syntax.selectGrammar(filePath, filePathContents).name).toBe "Ruby"
|
||||
expect(atom.syntax.selectGrammar(filePath, filePathContents).name).toBe "Ruby"
|
||||
expect(fs.read).not.toHaveBeenCalled()
|
||||
|
||||
it "allows the default grammar to be overridden for a path", ->
|
||||
filePath = '/foo/bar/file.js'
|
||||
expect(syntax.selectGrammar(filePath).name).not.toBe 'Ruby'
|
||||
syntax.setGrammarOverrideForPath(filePath, 'source.ruby')
|
||||
expect(syntax.selectGrammar(filePath).name).toBe 'Ruby'
|
||||
syntax.clearGrammarOverrideForPath(filePath)
|
||||
expect(syntax.selectGrammar(filePath).name).not.toBe 'Ruby'
|
||||
expect(atom.syntax.selectGrammar(filePath).name).not.toBe 'Ruby'
|
||||
atom.syntax.setGrammarOverrideForPath(filePath, 'source.ruby')
|
||||
expect(atom.syntax.selectGrammar(filePath).name).toBe 'Ruby'
|
||||
atom.syntax.clearGrammarOverrideForPath(filePath)
|
||||
expect(atom.syntax.selectGrammar(filePath).name).not.toBe 'Ruby'
|
||||
|
||||
describe "when multiple grammars have matching fileTypes", ->
|
||||
it "selects the grammar with the longest fileType match", ->
|
||||
@@ -72,46 +72,46 @@ describe "the `syntax` global", ->
|
||||
scopeName: 'source2'
|
||||
fileTypes: ['test']
|
||||
|
||||
syntax.addGrammar(grammar1)
|
||||
syntax.addGrammar(grammar2)
|
||||
atom.syntax.addGrammar(grammar1)
|
||||
atom.syntax.addGrammar(grammar2)
|
||||
|
||||
expect(syntax.selectGrammar('more.test', '')).toBe grammar1
|
||||
expect(atom.syntax.selectGrammar('more.test', '')).toBe grammar1
|
||||
|
||||
describe "when there is no file path", ->
|
||||
it "does not throw an exception (regression)", ->
|
||||
expect(-> syntax.selectGrammar(null, '#!/usr/bin/ruby')).not.toThrow()
|
||||
expect(-> syntax.selectGrammar(null, '')).not.toThrow()
|
||||
expect(-> syntax.selectGrammar(null, null)).not.toThrow()
|
||||
expect(-> atom.syntax.selectGrammar(null, '#!/usr/bin/ruby')).not.toThrow()
|
||||
expect(-> atom.syntax.selectGrammar(null, '')).not.toThrow()
|
||||
expect(-> atom.syntax.selectGrammar(null, null)).not.toThrow()
|
||||
|
||||
describe ".removeGrammar(grammar)", ->
|
||||
it "removes the grammar, so it won't be returned by selectGrammar", ->
|
||||
grammar = syntax.selectGrammar('foo.js')
|
||||
syntax.removeGrammar(grammar)
|
||||
expect(syntax.selectGrammar('foo.js').name).not.toBe grammar.name
|
||||
grammar = atom.syntax.selectGrammar('foo.js')
|
||||
atom.syntax.removeGrammar(grammar)
|
||||
expect(atom.syntax.selectGrammar('foo.js').name).not.toBe grammar.name
|
||||
|
||||
describe ".getProperty(scopeDescriptor)", ->
|
||||
it "returns the property with the most specific scope selector", ->
|
||||
syntax.addProperties(".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42)
|
||||
syntax.addProperties(".source .string.quoted.double", foo: bar: baz: 22)
|
||||
syntax.addProperties(".source", foo: bar: baz: 11)
|
||||
atom.syntax.addProperties(".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42)
|
||||
atom.syntax.addProperties(".source .string.quoted.double", foo: bar: baz: 22)
|
||||
atom.syntax.addProperties(".source", foo: bar: baz: 11)
|
||||
|
||||
expect(syntax.getProperty([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42
|
||||
expect(syntax.getProperty([".source.js", ".string.quoted.double.js"], "foo.bar.baz")).toBe 22
|
||||
expect(syntax.getProperty([".source.js", ".variable.assignment.js"], "foo.bar.baz")).toBe 11
|
||||
expect(syntax.getProperty([".text"], "foo.bar.baz")).toBeUndefined()
|
||||
expect(atom.syntax.getProperty([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42
|
||||
expect(atom.syntax.getProperty([".source.js", ".string.quoted.double.js"], "foo.bar.baz")).toBe 22
|
||||
expect(atom.syntax.getProperty([".source.js", ".variable.assignment.js"], "foo.bar.baz")).toBe 11
|
||||
expect(atom.syntax.getProperty([".text"], "foo.bar.baz")).toBeUndefined()
|
||||
|
||||
it "favors the most recently added properties in the event of a specificity tie", ->
|
||||
syntax.addProperties(".source.coffee .string.quoted.single", foo: bar: baz: 42)
|
||||
syntax.addProperties(".source.coffee .string.quoted.double", foo: bar: baz: 22)
|
||||
atom.syntax.addProperties(".source.coffee .string.quoted.single", foo: bar: baz: 42)
|
||||
atom.syntax.addProperties(".source.coffee .string.quoted.double", foo: bar: baz: 22)
|
||||
|
||||
expect(syntax.getProperty([".source.coffee", ".string.quoted.single"], "foo.bar.baz")).toBe 42
|
||||
expect(syntax.getProperty([".source.coffee", ".string.quoted.single.double"], "foo.bar.baz")).toBe 22
|
||||
expect(atom.syntax.getProperty([".source.coffee", ".string.quoted.single"], "foo.bar.baz")).toBe 42
|
||||
expect(atom.syntax.getProperty([".source.coffee", ".string.quoted.single.double"], "foo.bar.baz")).toBe 22
|
||||
|
||||
describe ".removeProperties(name)", ->
|
||||
it "allows properties to be removed by name", ->
|
||||
syntax.addProperties("a", ".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42)
|
||||
syntax.addProperties("b", ".source .string.quoted.double", foo: bar: baz: 22)
|
||||
atom.syntax.addProperties("a", ".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42)
|
||||
atom.syntax.addProperties("b", ".source .string.quoted.double", foo: bar: baz: 22)
|
||||
|
||||
syntax.removeProperties("b")
|
||||
expect(syntax.getProperty([".source.js", ".string.quoted.double.js"], "foo.bar.baz")).toBeUndefined()
|
||||
expect(syntax.getProperty([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42
|
||||
atom.syntax.removeProperties("b")
|
||||
expect(atom.syntax.getProperty([".source.js", ".string.quoted.double.js"], "foo.bar.baz")).toBeUndefined()
|
||||
expect(atom.syntax.getProperty([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42
|
||||
|
||||
@@ -10,26 +10,26 @@ describe 'TextBuffer', ->
|
||||
beforeEach ->
|
||||
filePath = require.resolve('./fixtures/sample.js')
|
||||
fileContents = fs.readFileSync(filePath, 'utf8')
|
||||
buffer = project.bufferForPathSync(filePath)
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
|
||||
afterEach ->
|
||||
buffer?.release()
|
||||
buffer?.destroy()
|
||||
|
||||
describe 'constructor', ->
|
||||
beforeEach ->
|
||||
buffer.release()
|
||||
buffer.destroy()
|
||||
buffer = null
|
||||
|
||||
describe "when given a path", ->
|
||||
describe "when a file exists for the path", ->
|
||||
it "loads the contents of that file", ->
|
||||
filePath = require.resolve './fixtures/sample.txt'
|
||||
buffer = project.bufferForPathSync(filePath)
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
expect(buffer.getText()).toBe fs.readFileSync(filePath, 'utf8')
|
||||
|
||||
it "does not allow the initial state of the buffer to be undone", ->
|
||||
filePath = require.resolve './fixtures/sample.txt'
|
||||
buffer = project.bufferForPathSync(filePath)
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
buffer.undo()
|
||||
expect(buffer.getText()).toBe fs.readFileSync(filePath, 'utf8')
|
||||
|
||||
@@ -37,13 +37,13 @@ describe 'TextBuffer', ->
|
||||
it "is modified and is initially empty", ->
|
||||
filePath = "does-not-exist.txt"
|
||||
expect(fs.existsSync(filePath)).toBeFalsy()
|
||||
buffer = project.bufferForPathSync(filePath)
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
expect(buffer.isModified()).toBeTruthy()
|
||||
expect(buffer.getText()).toBe ''
|
||||
|
||||
describe "when no path is given", ->
|
||||
it "creates an empty buffer", ->
|
||||
buffer = project.bufferForPathSync(null)
|
||||
buffer = atom.project.bufferForPathSync(null)
|
||||
expect(buffer .getText()).toBe ""
|
||||
|
||||
describe "path-changed event", ->
|
||||
@@ -53,7 +53,7 @@ describe 'TextBuffer', ->
|
||||
filePath = path.join(__dirname, "fixtures", "atom-manipulate-me")
|
||||
newPath = "#{filePath}-i-moved"
|
||||
fs.writeFileSync(filePath, "")
|
||||
bufferToChange = project.bufferForPathSync(filePath)
|
||||
bufferToChange = atom.project.bufferForPathSync(filePath)
|
||||
eventHandler = jasmine.createSpy('eventHandler')
|
||||
bufferToChange.on 'path-changed', eventHandler
|
||||
|
||||
@@ -85,7 +85,7 @@ describe 'TextBuffer', ->
|
||||
buffer.release()
|
||||
filePath = temp.openSync('atom').path
|
||||
fs.writeFileSync(filePath, "first")
|
||||
buffer = project.bufferForPathSync(filePath).retain()
|
||||
buffer = atom.project.bufferForPathSync(filePath).retain()
|
||||
|
||||
afterEach ->
|
||||
buffer.release()
|
||||
@@ -156,7 +156,7 @@ describe 'TextBuffer', ->
|
||||
beforeEach ->
|
||||
filePath = path.join(temp.dir, 'atom-file-to-delete.txt')
|
||||
fs.writeFileSync(filePath, 'delete me')
|
||||
bufferToDelete = project.bufferForPathSync(filePath)
|
||||
bufferToDelete = atom.project.bufferForPathSync(filePath)
|
||||
filePath = bufferToDelete.getPath() # symlinks may have been converted
|
||||
|
||||
expect(bufferToDelete.getPath()).toBe filePath
|
||||
@@ -214,7 +214,7 @@ describe 'TextBuffer', ->
|
||||
buffer.release()
|
||||
filePath = path.join(temp.dir, 'atom-tmp-file')
|
||||
fs.writeFileSync(filePath, 'delete me')
|
||||
buffer = project.bufferForPathSync(filePath)
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
modifiedHandler = jasmine.createSpy("modifiedHandler")
|
||||
buffer.on 'modified-status-changed', modifiedHandler
|
||||
|
||||
@@ -227,7 +227,7 @@ describe 'TextBuffer', ->
|
||||
filePath = path.join(temp.dir, 'atom-tmp-file')
|
||||
fs.writeFileSync(filePath, '')
|
||||
buffer.release()
|
||||
buffer = project.bufferForPathSync(filePath)
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
modifiedHandler = jasmine.createSpy("modifiedHandler")
|
||||
buffer.on 'modified-status-changed', modifiedHandler
|
||||
|
||||
@@ -251,7 +251,7 @@ describe 'TextBuffer', ->
|
||||
filePath = path.join(temp.dir, 'atom-tmp-file')
|
||||
fs.writeFileSync(filePath, '')
|
||||
buffer.release()
|
||||
buffer = project.bufferForPathSync(filePath)
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
modifiedHandler = jasmine.createSpy("modifiedHandler")
|
||||
buffer.on 'modified-status-changed', modifiedHandler
|
||||
|
||||
@@ -275,7 +275,7 @@ describe 'TextBuffer', ->
|
||||
fs.removeSync(filePath) if fs.existsSync(filePath)
|
||||
expect(fs.existsSync(filePath)).toBeFalsy()
|
||||
buffer.release()
|
||||
buffer = project.bufferForPathSync(filePath)
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
modifiedHandler = jasmine.createSpy("modifiedHandler")
|
||||
buffer.on 'modified-status-changed', modifiedHandler
|
||||
|
||||
@@ -298,12 +298,12 @@ describe 'TextBuffer', ->
|
||||
|
||||
it "returns false for an empty buffer with no path", ->
|
||||
buffer.release()
|
||||
buffer = project.bufferForPathSync(null)
|
||||
buffer = atom.project.bufferForPathSync(null)
|
||||
expect(buffer.isModified()).toBeFalsy()
|
||||
|
||||
it "returns true for a non-empty buffer with no path", ->
|
||||
buffer.release()
|
||||
buffer = project.bufferForPathSync(null)
|
||||
buffer = atom.project.bufferForPathSync(null)
|
||||
buffer.setText('a')
|
||||
expect(buffer.isModified()).toBeTruthy()
|
||||
buffer.setText('\n')
|
||||
@@ -311,8 +311,8 @@ describe 'TextBuffer', ->
|
||||
|
||||
it "returns false until the buffer is fully loaded", ->
|
||||
buffer.release()
|
||||
filePath = temp.openSync('atom').path
|
||||
buffer = new TextBuffer({project, filePath})
|
||||
buffer = new TextBuffer({filePath: temp.openSync('atom').path})
|
||||
atom.project.addBuffer(buffer)
|
||||
|
||||
expect(buffer.isModified()).toBeFalsy()
|
||||
|
||||
@@ -432,7 +432,7 @@ describe 'TextBuffer', ->
|
||||
expect(event.newText).toBe "foo\nbar"
|
||||
|
||||
it "allows a 'changed' event handler to safely undo the change", ->
|
||||
buffer.one 'changed', -> buffer.undo()
|
||||
buffer.once 'changed', -> buffer.undo()
|
||||
buffer.change([0, 0], "hello")
|
||||
expect(buffer.lineForRow(0)).toBe "var quicksort = function () {"
|
||||
|
||||
@@ -466,7 +466,7 @@ describe 'TextBuffer', ->
|
||||
beforeEach ->
|
||||
filePath = path.join(temp.dir, 'temp.txt')
|
||||
fs.writeFileSync(filePath, "")
|
||||
saveBuffer = project.bufferForPathSync(filePath)
|
||||
saveBuffer = atom.project.bufferForPathSync(filePath)
|
||||
saveBuffer.setText("blah")
|
||||
|
||||
it "saves the contents of the buffer to the path", ->
|
||||
@@ -500,7 +500,7 @@ describe 'TextBuffer', ->
|
||||
|
||||
describe "when the buffer has no path", ->
|
||||
it "throws an exception", ->
|
||||
saveBuffer = project.bufferForPathSync(null)
|
||||
saveBuffer = atom.project.bufferForPathSync(null)
|
||||
saveBuffer.setText "hi"
|
||||
expect(-> saveBuffer.save()).toThrow()
|
||||
|
||||
@@ -524,7 +524,7 @@ describe 'TextBuffer', ->
|
||||
filePath = path.join(temp.dir, 'temp.txt')
|
||||
fs.removeSync filePath if fs.existsSync(filePath)
|
||||
|
||||
saveAsBuffer = project.bufferForPathSync(null).retain()
|
||||
saveAsBuffer = atom.project.bufferForPathSync(null).retain()
|
||||
eventHandler = jasmine.createSpy('eventHandler')
|
||||
saveAsBuffer.on 'path-changed', eventHandler
|
||||
|
||||
@@ -539,7 +539,7 @@ describe 'TextBuffer', ->
|
||||
newPath = path.join(temp.dir, 'new.txt')
|
||||
fs.writeFileSync(originalPath, "")
|
||||
|
||||
saveAsBuffer = project.bufferForPathSync(originalPath).retain()
|
||||
saveAsBuffer = atom.project.bufferForPathSync(originalPath).retain()
|
||||
changeHandler = jasmine.createSpy('changeHandler')
|
||||
saveAsBuffer.on 'changed', changeHandler
|
||||
saveAsBuffer.saveAs(newPath)
|
||||
@@ -554,35 +554,6 @@ describe 'TextBuffer', ->
|
||||
waitsFor ->
|
||||
changeHandler.callCount > 0
|
||||
|
||||
describe ".getRelativePath()", ->
|
||||
[filePath, newPath, bufferToChange, eventHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
filePath = path.join(__dirname, "fixtures", "atom-manipulate-me")
|
||||
newPath = "#{filePath}-i-moved"
|
||||
fs.writeFileSync(filePath, "")
|
||||
bufferToChange = project.bufferForPathSync(filePath)
|
||||
eventHandler = jasmine.createSpy('eventHandler')
|
||||
bufferToChange.on 'path-changed', eventHandler
|
||||
|
||||
afterEach ->
|
||||
bufferToChange.destroy()
|
||||
fs.removeSync(filePath) if fs.existsSync(filePath)
|
||||
fs.removeSync(newPath) if fs.existsSync(newPath)
|
||||
|
||||
it "updates when the text buffer's file is moved", ->
|
||||
expect(bufferToChange.getRelativePath()).toBe('atom-manipulate-me')
|
||||
|
||||
jasmine.unspy(window, "setTimeout")
|
||||
eventHandler.reset()
|
||||
fs.moveSync(filePath, newPath)
|
||||
|
||||
waitsFor "buffer path change", ->
|
||||
eventHandler.callCount > 0
|
||||
|
||||
runs ->
|
||||
expect(bufferToChange.getRelativePath()).toBe('atom-manipulate-me-i-moved')
|
||||
|
||||
describe ".getTextInRange(range)", ->
|
||||
describe "when range is empty", ->
|
||||
it "returns an empty string", ->
|
||||
@@ -926,22 +897,28 @@ describe 'TextBuffer', ->
|
||||
expect(buffer.getText()).toBe "\ninitialtexthello\n1\n2\n"
|
||||
|
||||
describe "serialization", ->
|
||||
buffer2 = null
|
||||
[buffer2, project2] = []
|
||||
|
||||
beforeEach ->
|
||||
buffer.destroy()
|
||||
|
||||
filePath = temp.openSync('atom').path
|
||||
fs.writeFileSync(filePath, "words")
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
|
||||
afterEach ->
|
||||
buffer2?.release()
|
||||
project2?.destroy()
|
||||
|
||||
describe "when the serialized buffer had no unsaved changes", ->
|
||||
it "loads the current contents of the file at the serialized path", ->
|
||||
expect(buffer.isModified()).toBeFalsy()
|
||||
|
||||
state = buffer.serialize()
|
||||
state.get('text').insertTextAtPoint([0, 0], 'simulate divergence of on-disk contents from serialized contents')
|
||||
project2 = atom.replicate().get('project')
|
||||
buffer2 = project2.getBuffers()[0]
|
||||
|
||||
buffer2 = deserialize(state, {project})
|
||||
|
||||
waitsFor ->
|
||||
buffer2.cachedDiskContents
|
||||
waitsForPromise ->
|
||||
buffer2.load()
|
||||
|
||||
runs ->
|
||||
expect(buffer2.isModified()).toBeFalsy()
|
||||
@@ -951,18 +928,11 @@ describe 'TextBuffer', ->
|
||||
describe "when the serialized buffer had unsaved changes", ->
|
||||
describe "when the disk contents were changed since serialization", ->
|
||||
it "loads the disk contents instead of the previous unsaved state", ->
|
||||
buffer.release()
|
||||
|
||||
filePath = temp.openSync('atom').path
|
||||
fs.writeFileSync(filePath, "words")
|
||||
{buffer} = project.openSync(filePath)
|
||||
buffer.setText("BUFFER CHANGE")
|
||||
|
||||
state = buffer.serialize()
|
||||
expect(state.getObject('text')).toBe 'BUFFER CHANGE'
|
||||
fs.writeFileSync(filePath, "DISK CHANGE")
|
||||
|
||||
buffer2 = deserialize(state, {project})
|
||||
project2 = atom.replicate().get('project')
|
||||
buffer2 = project2.getBuffers()[0]
|
||||
|
||||
waitsFor ->
|
||||
buffer2.cachedDiskContents
|
||||
@@ -976,14 +946,14 @@ describe 'TextBuffer', ->
|
||||
it "restores the previous unsaved state of the buffer", ->
|
||||
previousText = buffer.getText()
|
||||
buffer.setText("abc")
|
||||
buffer.retain()
|
||||
|
||||
state = buffer.serialize()
|
||||
expect(state.getObject('text')).toBe 'abc'
|
||||
buffer.getState().serializeForPersistence()
|
||||
project2 = atom.replicate().get('project')
|
||||
buffer2 = project2.getBuffers()[0]
|
||||
|
||||
buffer2 = deserialize(state, {project})
|
||||
|
||||
waitsFor ->
|
||||
buffer2.cachedDiskContents
|
||||
waitsForPromise ->
|
||||
buffer2.load()
|
||||
|
||||
runs ->
|
||||
expect(buffer2.getPath()).toBe(buffer.getPath())
|
||||
@@ -996,13 +966,13 @@ describe 'TextBuffer', ->
|
||||
it "restores the previous unsaved state of the buffer", ->
|
||||
buffer.release()
|
||||
|
||||
buffer = project.bufferForPathSync()
|
||||
buffer = atom.project.bufferForPathSync()
|
||||
buffer.setText("abc")
|
||||
|
||||
state = buffer.serialize()
|
||||
state = buffer.getState().clone()
|
||||
expect(state.get('path')).toBeUndefined()
|
||||
expect(state.getObject('text')).toBe 'abc'
|
||||
|
||||
buffer2 = deserialize(state)
|
||||
buffer2 = atom.project.addBuffer(new TextBuffer(state))
|
||||
expect(buffer2.getPath()).toBeUndefined()
|
||||
expect(buffer2.getText()).toBe("abc")
|
||||
|
||||
@@ -6,14 +6,14 @@ describe "TextMateGrammar", ->
|
||||
grammar = null
|
||||
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-text', sync: true)
|
||||
atom.activatePackage('language-javascript', sync: true)
|
||||
atom.activatePackage('language-coffee-script', sync: true)
|
||||
atom.activatePackage('language-ruby', sync: true)
|
||||
atom.activatePackage('language-html', sync: true)
|
||||
atom.activatePackage('language-php', sync: true)
|
||||
atom.activatePackage('language-python', sync: true)
|
||||
grammar = syntax.selectGrammar("hello.coffee")
|
||||
atom.packages.activatePackage('language-text', sync: true)
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
atom.packages.activatePackage('language-coffee-script', sync: true)
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
atom.packages.activatePackage('language-html', sync: true)
|
||||
atom.packages.activatePackage('language-php', sync: true)
|
||||
atom.packages.activatePackage('language-python', sync: true)
|
||||
grammar = atom.syntax.selectGrammar("hello.coffee")
|
||||
|
||||
describe "@loadSync(path)", ->
|
||||
it "loads grammars from plists", ->
|
||||
@@ -49,7 +49,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the line doesn't match any patterns", ->
|
||||
it "returns the entire line as a single simple token with the grammar's scope", ->
|
||||
textGrammar = syntax.selectGrammar('foo.txt')
|
||||
textGrammar = atom.syntax.selectGrammar('foo.txt')
|
||||
{tokens} = textGrammar.tokenizeLine("abc def")
|
||||
expect(tokens.length).toBe 1
|
||||
|
||||
@@ -126,20 +126,20 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the line matches no patterns", ->
|
||||
it "does not infinitely loop", ->
|
||||
grammar = syntax.selectGrammar("sample.txt")
|
||||
grammar = atom.syntax.selectGrammar("sample.txt")
|
||||
{tokens} = grammar.tokenizeLine('hoo')
|
||||
expect(tokens.length).toBe 1
|
||||
expect(tokens[0]).toEqual value: 'hoo', scopes: ["text.plain", "meta.paragraph.text"]
|
||||
|
||||
describe "when the line matches a pattern with a 'contentName'", ->
|
||||
it "creates tokens using the content of contentName as the token name", ->
|
||||
grammar = syntax.selectGrammar("sample.txt")
|
||||
grammar = atom.syntax.selectGrammar("sample.txt")
|
||||
{tokens} = grammar.tokenizeLine('ok, cool')
|
||||
expect(tokens[0]).toEqual value: 'ok, cool', scopes: ["text.plain", "meta.paragraph.text"]
|
||||
|
||||
describe "when the line matches a pattern with no `name` or `contentName`", ->
|
||||
it "creates tokens without adding a new scope", ->
|
||||
grammar = syntax.selectGrammar('foo.rb')
|
||||
grammar = atom.syntax.selectGrammar('foo.rb')
|
||||
{tokens} = grammar.tokenizeLine('%w|oh \\look|')
|
||||
expect(tokens.length).toBe 5
|
||||
expect(tokens[0]).toEqual value: '%w|', scopes: ["source.ruby", "string.quoted.other.literal.lower.ruby", "punctuation.definition.string.begin.ruby"]
|
||||
@@ -184,7 +184,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the end pattern contains a back reference", ->
|
||||
it "constructs the end rule based on its back-references to captures in the begin rule", ->
|
||||
grammar = syntax.selectGrammar('foo.rb')
|
||||
grammar = atom.syntax.selectGrammar('foo.rb')
|
||||
{tokens} = grammar.tokenizeLine('%w|oh|,')
|
||||
expect(tokens.length).toBe 4
|
||||
expect(tokens[0]).toEqual value: '%w|', scopes: ["source.ruby", "string.quoted.other.literal.lower.ruby", "punctuation.definition.string.begin.ruby"]
|
||||
@@ -193,7 +193,7 @@ describe "TextMateGrammar", ->
|
||||
expect(tokens[3]).toEqual value: ',', scopes: ["source.ruby", "punctuation.separator.object.ruby"]
|
||||
|
||||
it "allows the rule containing that end pattern to be pushed to the stack multiple times", ->
|
||||
grammar = syntax.selectGrammar('foo.rb')
|
||||
grammar = atom.syntax.selectGrammar('foo.rb')
|
||||
{tokens} = grammar.tokenizeLine('%Q+matz had some #{%Q-crazy ideas-} for ruby syntax+ # damn.')
|
||||
expect(tokens[0]).toEqual value: '%Q+', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","punctuation.definition.string.begin.ruby"]
|
||||
expect(tokens[1]).toEqual value: 'matz had some ', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby"]
|
||||
@@ -211,10 +211,10 @@ describe "TextMateGrammar", ->
|
||||
describe "when the pattern includes rules from another grammar", ->
|
||||
describe "when a grammar matching the desired scope is available", ->
|
||||
it "parses tokens inside the begin/end patterns based on the included grammar's rules", ->
|
||||
atom.activatePackage('language-html', sync: true)
|
||||
atom.activatePackage('language-ruby-on-rails', sync: true)
|
||||
atom.packages.activatePackage('language-html', sync: true)
|
||||
atom.packages.activatePackage('language-ruby-on-rails', sync: true)
|
||||
|
||||
grammar = syntax.grammarForScopeName('text.html.ruby')
|
||||
grammar = atom.syntax.grammarForScopeName('text.html.ruby')
|
||||
{tokens} = grammar.tokenizeLine("<div class='name'><%= User.find(2).full_name %></div>")
|
||||
|
||||
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"]
|
||||
@@ -242,17 +242,17 @@ describe "TextMateGrammar", ->
|
||||
expect(tokens[22]).toEqual value: '>', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.end.html"]
|
||||
|
||||
it "updates the grammar if the included grammar is updated later", ->
|
||||
atom.activatePackage('language-html', sync: true)
|
||||
atom.activatePackage('language-ruby-on-rails', sync: true)
|
||||
atom.packages.activatePackage('language-html', sync: true)
|
||||
atom.packages.activatePackage('language-ruby-on-rails', sync: true)
|
||||
|
||||
grammar = syntax.selectGrammar('foo.html.erb')
|
||||
grammar = atom.syntax.selectGrammar('foo.html.erb')
|
||||
grammarUpdatedHandler = jasmine.createSpy("grammarUpdatedHandler")
|
||||
grammar.on 'grammar-updated', grammarUpdatedHandler
|
||||
|
||||
{tokens} = grammar.tokenizeLine("<div class='name'><% <<-SQL select * from users;")
|
||||
expect(tokens[12].value).toBe " select * from users;"
|
||||
|
||||
atom.activatePackage('language-sql', sync: true)
|
||||
atom.packages.activatePackage('language-sql', sync: true)
|
||||
expect(grammarUpdatedHandler).toHaveBeenCalled()
|
||||
{tokens} = grammar.tokenizeLine("<div class='name'><% <<-SQL select * from users;")
|
||||
expect(tokens[12].value).toBe " "
|
||||
@@ -260,17 +260,17 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when a grammar matching the desired scope is unavailable", ->
|
||||
it "updates the grammar if a matching grammar is added later", ->
|
||||
atom.deactivatePackage('language-html')
|
||||
atom.activatePackage('language-ruby-on-rails', sync: true)
|
||||
atom.packages.deactivatePackage('language-html')
|
||||
atom.packages.activatePackage('language-ruby-on-rails', sync: true)
|
||||
|
||||
grammar = syntax.grammarForScopeName('text.html.ruby')
|
||||
grammar = atom.syntax.grammarForScopeName('text.html.ruby')
|
||||
{tokens} = grammar.tokenizeLine("<div class='name'><%= User.find(2).full_name %></div>")
|
||||
expect(tokens[0]).toEqual value: "<div class='name'>", scopes: ["text.html.ruby"]
|
||||
expect(tokens[1]).toEqual value: '<%=', scopes: ["text.html.ruby","source.ruby.rails.embedded.html","punctuation.section.embedded.ruby"]
|
||||
expect(tokens[2]).toEqual value: ' ', scopes: ["text.html.ruby","source.ruby.rails.embedded.html"]
|
||||
expect(tokens[3]).toEqual value: 'User', scopes: ["text.html.ruby","source.ruby.rails.embedded.html","support.class.ruby"]
|
||||
|
||||
atom.activatePackage('language-html', sync: true)
|
||||
atom.packages.activatePackage('language-html', sync: true)
|
||||
{tokens} = grammar.tokenizeLine("<div class='name'><%= User.find(2).full_name %></div>")
|
||||
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"]
|
||||
expect(tokens[1]).toEqual value: 'div', scopes: ["text.html.ruby","meta.tag.block.any.html","entity.name.tag.block.any.html"]
|
||||
@@ -308,21 +308,21 @@ describe "TextMateGrammar", ->
|
||||
expect(tokens[1].value).toBe " a singleLineComment"
|
||||
|
||||
it "does not loop infinitely (regression)", ->
|
||||
grammar = syntax.selectGrammar("hello.js")
|
||||
grammar = atom.syntax.selectGrammar("hello.js")
|
||||
{tokens, ruleStack} = grammar.tokenizeLine("// line comment")
|
||||
{tokens, ruleStack} = grammar.tokenizeLine(" // second line comment with a single leading space", ruleStack)
|
||||
|
||||
describe "when inside a C block", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-c', sync: true)
|
||||
atom.packages.activatePackage('language-c', sync: true)
|
||||
|
||||
it "correctly parses a method. (regression)", ->
|
||||
grammar = syntax.selectGrammar("hello.c")
|
||||
grammar = atom.syntax.selectGrammar("hello.c")
|
||||
{tokens, ruleStack} = grammar.tokenizeLine("if(1){m()}")
|
||||
expect(tokens[5]).toEqual value: "m", scopes: ["source.c", "meta.block.c", "meta.function-call.c", "support.function.any-method.c"]
|
||||
|
||||
it "correctly parses nested blocks. (regression)", ->
|
||||
grammar = syntax.selectGrammar("hello.c")
|
||||
grammar = atom.syntax.selectGrammar("hello.c")
|
||||
{tokens, ruleStack} = grammar.tokenizeLine("if(1){if(1){m()}}")
|
||||
expect(tokens[5]).toEqual value: "if", scopes: ["source.c", "meta.block.c", "keyword.control.c"]
|
||||
expect(tokens[10]).toEqual value: "m", scopes: ["source.c", "meta.block.c", "meta.block.c", "meta.function-call.c", "support.function.any-method.c"]
|
||||
@@ -330,8 +330,8 @@ describe "TextMateGrammar", ->
|
||||
describe "when the grammar can infinitely loop over a line", ->
|
||||
it "aborts tokenization", ->
|
||||
spyOn(console, 'error')
|
||||
atom.activatePackage("package-with-infinite-loop-grammar")
|
||||
grammar = syntax.selectGrammar("something.package-with-infinite-loop-grammar")
|
||||
atom.packages.activatePackage("package-with-infinite-loop-grammar")
|
||||
grammar = atom.syntax.selectGrammar("something.package-with-infinite-loop-grammar")
|
||||
{tokens} = grammar.tokenizeLine("abc")
|
||||
expect(tokens[0].value).toBe "a"
|
||||
expect(tokens[1].value).toBe "bc"
|
||||
@@ -339,14 +339,14 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when a grammar has a pattern that has back references in the match value", ->
|
||||
it "does not special handle the back references and instead allows oniguruma to resolve them", ->
|
||||
atom.activatePackage('language-sass', sync: true)
|
||||
grammar = syntax.selectGrammar("style.scss")
|
||||
atom.packages.activatePackage('language-sass', sync: true)
|
||||
grammar = atom.syntax.selectGrammar("style.scss")
|
||||
{tokens} = grammar.tokenizeLine("@mixin x() { -moz-selector: whatever; }")
|
||||
expect(tokens[9]).toEqual value: "-moz-selector", scopes: ["source.css.scss", "meta.property-list.scss", "meta.property-name.scss"]
|
||||
|
||||
describe "when a line has more tokens than `maxTokensPerLine`", ->
|
||||
it "creates a final token with the remaining text and resets the ruleStack to match the begining of the line", ->
|
||||
grammar = syntax.selectGrammar("hello.js")
|
||||
grammar = atom.syntax.selectGrammar("hello.js")
|
||||
spyOn(grammar, 'getMaxTokensPerLine').andCallFake -> 5
|
||||
originalRuleStack = [grammar.initialRule, grammar.initialRule, grammar.initialRule]
|
||||
{tokens, ruleStack} = grammar.tokenizeLine("one(two(three(four(five(_param_)))))", originalRuleStack)
|
||||
@@ -356,7 +356,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when a grammar has a capture with patterns", ->
|
||||
it "matches the patterns and includes the scope specified as the pattern's match name", ->
|
||||
grammar = syntax.selectGrammar("hello.php")
|
||||
grammar = atom.syntax.selectGrammar("hello.php")
|
||||
{tokens} = grammar.tokenizeLine("<?php public final function meth() {} ?>")
|
||||
|
||||
expect(tokens[2].value).toBe "public"
|
||||
@@ -402,7 +402,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the grammar has injections", ->
|
||||
it "correctly includes the injected patterns when tokenizing", ->
|
||||
grammar = syntax.selectGrammar("hello.php")
|
||||
grammar = atom.syntax.selectGrammar("hello.php")
|
||||
{tokens} = grammar.tokenizeLine("<div><?php function hello() {} ?></div>")
|
||||
|
||||
expect(tokens[3].value).toBe "<?php"
|
||||
@@ -428,15 +428,15 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the grammar's pattern name has a group number in it", ->
|
||||
it "replaces the group number with the matched captured text", ->
|
||||
atom.activatePackage('language-hyperlink', sync: true)
|
||||
grammar = syntax.grammarForScopeName("text.hyperlink")
|
||||
atom.packages.activatePackage('language-hyperlink', sync: true)
|
||||
grammar = atom.syntax.grammarForScopeName("text.hyperlink")
|
||||
{tokens} = grammar.tokenizeLine("https://github.com")
|
||||
expect(tokens[0].scopes).toEqual ["text.hyperlink", "markup.underline.link.https.hyperlink"]
|
||||
|
||||
describe "when the grammar has an injection selector", ->
|
||||
it "includes the grammar's patterns when the selector matches the current scope in other grammars", ->
|
||||
atom.activatePackage('language-hyperlink', sync: true)
|
||||
grammar = syntax.selectGrammar("text.js")
|
||||
atom.packages.activatePackage('language-hyperlink', sync: true)
|
||||
grammar = atom.syntax.selectGrammar("text.js")
|
||||
{tokens} = grammar.tokenizeLine("var i; // http://github.com")
|
||||
|
||||
expect(tokens[0].value).toBe "var"
|
||||
@@ -447,29 +447,29 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the grammar is added", ->
|
||||
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
|
||||
editSession = project.openSync('sample.js')
|
||||
editSession.setText("// http://github.com")
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor.setText("// http://github.com")
|
||||
|
||||
{tokens} = editSession.lineForScreenRow(0)
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " http://github.com"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
atom.activatePackage('language-hyperlink', sync: true)
|
||||
atom.packages.activatePackage('language-hyperlink', sync: true)
|
||||
|
||||
{tokens} = editSession.lineForScreenRow(0)
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "http://github.com"
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
|
||||
describe "when the grammar is updated", ->
|
||||
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
|
||||
editSession = project.openSync('sample.js')
|
||||
editSession.setText("// SELECT * FROM OCTOCATS")
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor.setText("// SELECT * FROM OCTOCATS")
|
||||
|
||||
{tokens} = editSession.lineForScreenRow(0)
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
syntax.addGrammar(new TextMateGrammar(
|
||||
atom.syntax.addGrammar(new TextMateGrammar(
|
||||
name: "test"
|
||||
scopeName: "source.test"
|
||||
repository: {}
|
||||
@@ -477,13 +477,13 @@ describe "TextMateGrammar", ->
|
||||
patterns: [ { include: "source.sql" } ]
|
||||
))
|
||||
|
||||
{tokens} = editSession.lineForScreenRow(0)
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
atom.activatePackage('language-sql', sync: true)
|
||||
atom.packages.activatePackage('language-sql', sync: true)
|
||||
|
||||
{tokens} = editSession.lineForScreenRow(0)
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "SELECT"
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "keyword.other.DML.sql"]
|
||||
|
||||
@@ -514,8 +514,8 @@ describe "TextMateGrammar", ->
|
||||
lines = null
|
||||
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-todo', sync: true)
|
||||
grammar = syntax.selectGrammar('main.rb')
|
||||
atom.packages.activatePackage('language-todo', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('main.rb')
|
||||
lines = grammar.tokenizeLines "# TODO be nicer"
|
||||
|
||||
it "replaces the number with the capture group and translates the text", ->
|
||||
@@ -528,8 +528,8 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "Git commit messages", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-git', sync: true)
|
||||
grammar = syntax.selectGrammar('COMMIT_EDITMSG')
|
||||
atom.packages.activatePackage('language-git', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('COMMIT_EDITMSG')
|
||||
lines = grammar.tokenizeLines """
|
||||
longggggggggggggggggggggggggggggggggggggggggggggggg
|
||||
# Please enter the commit message for your changes. Lines starting
|
||||
@@ -547,8 +547,8 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "C++", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-c', sync: true)
|
||||
grammar = syntax.selectGrammar('includes.cc')
|
||||
atom.packages.activatePackage('language-c', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('includes.cc')
|
||||
lines = grammar.tokenizeLines """
|
||||
#include "a.h"
|
||||
#include "b.h"
|
||||
@@ -570,7 +570,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "Ruby", ->
|
||||
beforeEach ->
|
||||
grammar = syntax.selectGrammar('hello.rb')
|
||||
grammar = atom.syntax.selectGrammar('hello.rb')
|
||||
lines = grammar.tokenizeLines """
|
||||
a = {
|
||||
"b" => "c",
|
||||
@@ -585,9 +585,9 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "Objective-C", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-c', sync: true)
|
||||
atom.activatePackage('language-objective-c', sync: true)
|
||||
grammar = syntax.selectGrammar('function.mm')
|
||||
atom.packages.activatePackage('language-c', sync: true)
|
||||
atom.packages.activatePackage('language-objective-c', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('function.mm')
|
||||
lines = grammar.tokenizeLines """
|
||||
void test() {
|
||||
NSString *a = @"a\\nb";
|
||||
@@ -612,8 +612,8 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "Java", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-java', sync: true)
|
||||
grammar = syntax.selectGrammar('Function.java')
|
||||
atom.packages.activatePackage('language-java', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('Function.java')
|
||||
|
||||
it "correctly parses single line comments", ->
|
||||
lines = grammar.tokenizeLines """
|
||||
@@ -635,11 +635,9 @@ describe "TextMateGrammar", ->
|
||||
expect(lastToken.value).toEqual ';'
|
||||
|
||||
describe "HTML (Ruby - ERB)", ->
|
||||
beforeEach ->
|
||||
grammar = syntax.selectGrammar('page.erb')
|
||||
lines = grammar.tokenizeLines '<% page_title "My Page" %>'
|
||||
|
||||
it "correctly parses strings inside tags", ->
|
||||
grammar = atom.syntax.selectGrammar('page.erb')
|
||||
lines = grammar.tokenizeLines '<% page_title "My Page" %>'
|
||||
tokens = lines[0]
|
||||
|
||||
expect(tokens[2].value).toEqual '"'
|
||||
@@ -649,10 +647,20 @@ describe "TextMateGrammar", ->
|
||||
expect(tokens[4].value).toEqual '"'
|
||||
expect(tokens[4].scopes).toEqual ["text.html.erb", "meta.embedded.line.erb", "string.quoted.double.ruby", "punctuation.definition.string.end.ruby"]
|
||||
|
||||
it "does not loop infinitely on <%>", ->
|
||||
atom.packages.activatePackage('language-html', sync: true)
|
||||
atom.packages.activatePackage('language-ruby-on-rails', sync: true)
|
||||
|
||||
grammar = atom.syntax.selectGrammar('foo.html.erb')
|
||||
[tokens] = grammar.tokenizeLines '<%>'
|
||||
expect(tokens.length).toBe 1
|
||||
expect(tokens[0].value).toEqual '<%>'
|
||||
expect(tokens[0].scopes).toEqual ["text.html.erb"]
|
||||
|
||||
describe "Unicode support", ->
|
||||
describe "Surrogate pair characters", ->
|
||||
beforeEach ->
|
||||
grammar = syntax.selectGrammar('main.js')
|
||||
grammar = atom.syntax.selectGrammar('main.js')
|
||||
lines = grammar.tokenizeLines "'\uD835\uDF97'"
|
||||
|
||||
it "correctly parses JavaScript strings containing surrogate pair characters", ->
|
||||
@@ -664,8 +672,8 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the line contains unicode characters", ->
|
||||
it "correctly parses tokens starting after them", ->
|
||||
atom.activatePackage('language-json', sync: true)
|
||||
grammar = syntax.selectGrammar('package.json')
|
||||
atom.packages.activatePackage('language-json', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('package.json')
|
||||
{tokens} = grammar.tokenizeLine '{"\u2026": 1}'
|
||||
|
||||
expect(tokens.length).toBe 8
|
||||
@@ -674,7 +682,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "python", ->
|
||||
it "parses import blocks correctly", ->
|
||||
grammar = syntax.selectGrammar("file.py")
|
||||
grammar = atom.syntax.selectGrammar("file.py")
|
||||
lines = grammar.tokenizeLines "import a\nimport b"
|
||||
|
||||
line1 = lines[0]
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
path = require 'path'
|
||||
{$, $$, fs, RootView} = require 'atom'
|
||||
{$, $$, fs, WorkspaceView} = require 'atom'
|
||||
|
||||
ThemeManager = require '../src/theme-manager'
|
||||
AtomPackage = require '../src/atom-package'
|
||||
|
||||
describe "ThemeManager", ->
|
||||
themeManager = null
|
||||
resourcePath = atom.getLoadSettings().resourcePath
|
||||
configDirPath = atom.getConfigDirPath()
|
||||
|
||||
beforeEach ->
|
||||
themeManager = new ThemeManager(atom.packages)
|
||||
themeManager = new ThemeManager({packageManager: atom.packages, resourcePath, configDirPath})
|
||||
|
||||
afterEach ->
|
||||
themeManager.deactivateThemes()
|
||||
@@ -30,7 +32,7 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "getImportPaths()", ->
|
||||
it "returns the theme directories before the themes are loaded", ->
|
||||
config.set('core.themes', ['atom-dark-syntax', 'atom-dark-ui', 'atom-light-ui'])
|
||||
atom.config.set('core.themes', ['atom-dark-syntax', 'atom-dark-ui', 'atom-light-ui'])
|
||||
|
||||
paths = themeManager.getImportPaths()
|
||||
|
||||
@@ -40,7 +42,7 @@ describe "ThemeManager", ->
|
||||
expect(paths[1]).toContain 'atom-light-ui'
|
||||
|
||||
it "ignores themes that cannot be resolved to a directory", ->
|
||||
config.set('core.themes', ['definitely-not-a-theme'])
|
||||
atom.config.set('core.themes', ['definitely-not-a-theme'])
|
||||
expect(-> themeManager.getImportPaths()).not.toThrow()
|
||||
|
||||
describe "when the core.themes config value changes", ->
|
||||
@@ -49,24 +51,24 @@ describe "ThemeManager", ->
|
||||
spyOn(themeManager, 'getUserStylesheetPath').andCallFake -> null
|
||||
themeManager.activateThemes()
|
||||
|
||||
config.set('core.themes', [])
|
||||
atom.config.set('core.themes', [])
|
||||
expect($('style.theme').length).toBe 0
|
||||
expect(reloadHandler).toHaveBeenCalled()
|
||||
|
||||
config.set('core.themes', ['atom-dark-syntax'])
|
||||
atom.config.set('core.themes', ['atom-dark-syntax'])
|
||||
expect($('style.theme').length).toBe 1
|
||||
expect($('style.theme:eq(0)').attr('id')).toMatch /atom-dark-syntax/
|
||||
|
||||
config.set('core.themes', ['atom-light-syntax', 'atom-dark-syntax'])
|
||||
atom.config.set('core.themes', ['atom-light-syntax', 'atom-dark-syntax'])
|
||||
expect($('style.theme').length).toBe 2
|
||||
expect($('style.theme:eq(0)').attr('id')).toMatch /atom-dark-syntax/
|
||||
expect($('style.theme:eq(1)').attr('id')).toMatch /atom-light-syntax/
|
||||
|
||||
config.set('core.themes', [])
|
||||
atom.config.set('core.themes', [])
|
||||
expect($('style.theme').length).toBe 0
|
||||
|
||||
# atom-dark-ui has an directory path, the syntax ones dont.
|
||||
config.set('core.themes', ['atom-light-syntax', 'atom-dark-ui', 'atom-dark-syntax'])
|
||||
atom.config.set('core.themes', ['atom-light-syntax', 'atom-dark-ui', 'atom-dark-syntax'])
|
||||
importPaths = themeManager.getImportPaths()
|
||||
expect(importPaths.length).toBe 1
|
||||
expect(importPaths[0]).toContain 'atom-dark-ui'
|
||||
@@ -78,7 +80,7 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "requireStylesheet(path)", ->
|
||||
it "synchronously loads css at the given path and installs a style tag for it in the head", ->
|
||||
cssPath = project.resolve('css.css')
|
||||
cssPath = atom.project.resolve('css.css')
|
||||
lengthBefore = $('head style').length
|
||||
|
||||
themeManager.requireStylesheet(cssPath)
|
||||
@@ -95,7 +97,7 @@ describe "ThemeManager", ->
|
||||
$('head style[id*="css.css"]').remove()
|
||||
|
||||
it "synchronously loads and parses less files at the given path and installs a style tag for it in the head", ->
|
||||
lessPath = project.resolve('sample.less')
|
||||
lessPath = atom.project.resolve('sample.less')
|
||||
lengthBefore = $('head style').length
|
||||
themeManager.requireStylesheet(lessPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
@@ -119,9 +121,9 @@ describe "ThemeManager", ->
|
||||
|
||||
it "supports requiring css and less stylesheets without an explicit extension", ->
|
||||
themeManager.requireStylesheet path.join(__dirname, 'fixtures', 'css')
|
||||
expect($('head style[id*="css.css"]').attr('id')).toBe themeManager.stringToId(project.resolve('css.css'))
|
||||
expect($('head style[id*="css.css"]').attr('id')).toBe themeManager.stringToId(atom.project.resolve('css.css'))
|
||||
themeManager.requireStylesheet path.join(__dirname, 'fixtures', 'sample')
|
||||
expect($('head style[id*="sample.less"]').attr('id')).toBe themeManager.stringToId(project.resolve('sample.less'))
|
||||
expect($('head style[id*="sample.less"]').attr('id')).toBe themeManager.stringToId(atom.project.resolve('sample.less'))
|
||||
|
||||
$('head style[id*="css.css"]').remove()
|
||||
$('head style[id*="sample.less"]').remove()
|
||||
@@ -138,16 +140,16 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "base stylesheet loading", ->
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
rootView.append $$ -> @div class: 'editor'
|
||||
rootView.attachToDom()
|
||||
atom.workspaceView = new WorkspaceView
|
||||
atom.workspaceView.append $$ -> @div class: 'editor'
|
||||
atom.workspaceView.attachToDom()
|
||||
themeManager.activateThemes()
|
||||
|
||||
it "loads the correct values from the theme's ui-variables file", ->
|
||||
config.set('core.themes', ['theme-with-ui-variables'])
|
||||
atom.config.set('core.themes', ['theme-with-ui-variables'])
|
||||
|
||||
# an override loaded in the base css
|
||||
expect(rootView.css("background-color")).toBe "rgb(0, 0, 255)"
|
||||
expect(atom.workspaceView.css("background-color")).toBe "rgb(0, 0, 255)"
|
||||
|
||||
# from within the theme itself
|
||||
expect($(".editor").css("padding-top")).toBe "150px"
|
||||
|
||||
@@ -5,7 +5,7 @@ describe "TokenizedBuffer", ->
|
||||
[tokenizedBuffer, buffer, changeHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-javascript', sync: true)
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
# enable async tokenization
|
||||
TokenizedBuffer.prototype.chunkSize = 5
|
||||
jasmine.unspy(TokenizedBuffer.prototype, 'tokenizeInBackground')
|
||||
@@ -20,15 +20,15 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "@deserialize(state)", ->
|
||||
it "constructs a tokenized buffer with the same buffer and tabLength setting", ->
|
||||
buffer = project.bufferForPathSync('sample.js')
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer1 = new TokenizedBuffer(buffer: buffer, tabLength: 4)
|
||||
tokenizedBuffer2 = deserialize(tokenizedBuffer1.serialize())
|
||||
tokenizedBuffer2 = atom.deserializers.deserialize(tokenizedBuffer1.serialize())
|
||||
expect(tokenizedBuffer2.buffer).toBe tokenizedBuffer1.buffer
|
||||
expect(tokenizedBuffer2.getTabLength()).toBe tokenizedBuffer1.getTabLength()
|
||||
|
||||
describe "when the buffer is destroyed", ->
|
||||
beforeEach ->
|
||||
buffer = project.bufferForPathSync('sample.js')
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
|
||||
@@ -40,7 +40,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "when the buffer contains soft-tabs", ->
|
||||
beforeEach ->
|
||||
buffer = project.bufferForPathSync('sample.js')
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
|
||||
@@ -319,8 +319,8 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "when the buffer contains hard-tabs", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-coffee-script', sync: true)
|
||||
buffer = project.bufferForPathSync('sample-with-tabs.coffee')
|
||||
atom.packages.activatePackage('language-coffee-script', sync: true)
|
||||
buffer = atom.project.bufferForPathSync('sample-with-tabs.coffee')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
|
||||
@@ -349,8 +349,8 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "when the buffer contains surrogate pairs", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-javascript', sync: true)
|
||||
buffer = project.buildBufferSync 'sample-with-pairs.js'
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
buffer = atom.project.bufferForPathSync 'sample-with-pairs.js'
|
||||
buffer.setText """
|
||||
'abc\uD835\uDF97def'
|
||||
//\uD835\uDF97xyz
|
||||
@@ -387,19 +387,19 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "when the grammar is updated because a grammar it includes is activated", ->
|
||||
it "retokenizes the buffer", ->
|
||||
atom.activatePackage('language-ruby-on-rails', sync: true)
|
||||
atom.activatePackage('language-ruby', sync: true)
|
||||
atom.packages.activatePackage('language-ruby-on-rails', sync: true)
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
|
||||
buffer = project.bufferForPathSync()
|
||||
buffer = atom.project.bufferForPathSync()
|
||||
buffer.setText "<div class='name'><%= User.find(2).full_name %></div>"
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer.setGrammar(syntax.selectGrammar('test.erb'))
|
||||
tokenizedBuffer.setGrammar(atom.syntax.selectGrammar('test.erb'))
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
{tokens} = tokenizedBuffer.lineForScreenRow(0)
|
||||
expect(tokens[0]).toEqual value: "<div class='name'>", scopes: ["text.html.ruby"]
|
||||
|
||||
atom.activatePackage('language-html', sync: true)
|
||||
atom.packages.activatePackage('language-html', sync: true)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
{tokens} = tokenizedBuffer.lineForScreenRow(0)
|
||||
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"]
|
||||
@@ -410,7 +410,7 @@ describe "TokenizedBuffer", ->
|
||||
buffer.release()
|
||||
|
||||
it "returns the correct token (regression)", ->
|
||||
buffer = project.bufferForPathSync('sample.js')
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenForPosition([1,0]).scopes).toEqual ["source.js"]
|
||||
@@ -419,7 +419,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe ".bufferRangeForScopeAtPosition(selector, position)", ->
|
||||
beforeEach ->
|
||||
buffer = project.bufferForPathSync('sample.js')
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
|
||||
+29
-29
@@ -8,11 +8,11 @@ describe "Window", ->
|
||||
beforeEach ->
|
||||
spyOn(atom, 'hide')
|
||||
atom.getLoadSettings() # Causes atom.loadSettings to be initialized
|
||||
atom.loadSettings.initialPath = project.getPath()
|
||||
project.destroy()
|
||||
atom.loadSettings.initialPath = atom.project.getPath()
|
||||
atom.project.destroy()
|
||||
windowEventHandler = new WindowEventHandler()
|
||||
window.deserializeEditorWindow()
|
||||
projectPath = project.getPath()
|
||||
atom.deserializeEditorWindow()
|
||||
projectPath = atom.project.getPath()
|
||||
|
||||
afterEach ->
|
||||
windowEventHandler.unsubscribe()
|
||||
@@ -58,50 +58,50 @@ describe "Window", ->
|
||||
beforeUnloadEvent = $.Event(new Event('beforeunload'))
|
||||
|
||||
describe "when pane items are are modified", ->
|
||||
it "prompts user to save and and calls rootView.confirmClose", ->
|
||||
spyOn(rootView, 'confirmClose').andCallThrough()
|
||||
spyOn(atom, "confirmSync").andReturn(2)
|
||||
editSession = rootView.openSync("sample.js")
|
||||
editSession.insertText("I look different, I feel different.")
|
||||
it "prompts user to save and and calls workspaceView.confirmClose", ->
|
||||
spyOn(atom.workspaceView, 'confirmClose').andCallThrough()
|
||||
spyOn(atom, "confirm").andReturn(2)
|
||||
editor = atom.workspaceView.openSync("sample.js")
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(rootView.confirmClose).toHaveBeenCalled()
|
||||
expect(atom.confirmSync).toHaveBeenCalled()
|
||||
expect(atom.workspaceView.confirmClose).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "prompts user to save and handler returns true if don't save", ->
|
||||
spyOn(atom, "confirmSync").andReturn(2)
|
||||
editSession = rootView.openSync("sample.js")
|
||||
editSession.insertText("I look different, I feel different.")
|
||||
spyOn(atom, "confirm").andReturn(2)
|
||||
editor = atom.workspaceView.openSync("sample.js")
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.confirmSync).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "prompts user to save and handler returns false if dialog is canceled", ->
|
||||
spyOn(atom, "confirmSync").andReturn(1)
|
||||
editSession = rootView.openSync("sample.js")
|
||||
editSession.insertText("I look different, I feel different.")
|
||||
spyOn(atom, "confirm").andReturn(1)
|
||||
editor = atom.workspaceView.openSync("sample.js")
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.confirmSync).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
describe ".unloadEditorWindow()", ->
|
||||
it "saves the serialized state of the window so it can be deserialized after reload", ->
|
||||
rootViewState = rootView.serialize()
|
||||
syntaxState = syntax.serialize()
|
||||
workspaceViewState = atom.workspaceView.serialize()
|
||||
syntaxState = atom.syntax.serialize()
|
||||
|
||||
window.unloadEditorWindow()
|
||||
atom.unloadEditorWindow()
|
||||
|
||||
expect(atom.getWindowState().getObject('rootView')).toEqual rootViewState.toObject()
|
||||
expect(atom.getWindowState().getObject('workspaceView')).toEqual workspaceViewState.toObject()
|
||||
expect(atom.getWindowState().getObject('syntax')).toEqual syntaxState
|
||||
expect(atom.saveWindowState).toHaveBeenCalled()
|
||||
|
||||
it "unsubscribes from all buffers", ->
|
||||
rootView.openSync('sample.js')
|
||||
buffer = rootView.getActivePaneItem().buffer
|
||||
pane = rootView.getActivePane()
|
||||
atom.workspaceView.openSync('sample.js')
|
||||
buffer = atom.workspaceView.getActivePaneItem().buffer
|
||||
pane = atom.workspaceView.getActivePane()
|
||||
pane.splitRight(pane.copyActiveItem())
|
||||
expect(window.rootView.find('.editor').length).toBe 2
|
||||
expect(atom.workspaceView.find('.editor').length).toBe 2
|
||||
|
||||
window.unloadEditorWindow()
|
||||
atom.unloadEditorWindow()
|
||||
|
||||
expect(buffer.subscriptionCount()).toBe 0
|
||||
expect(buffer.getSubscriptionCount()).toBe 0
|
||||
|
||||
describe "drag and drop", ->
|
||||
buildDragEvent = (type, files) ->
|
||||
|
||||
@@ -0,0 +1,528 @@
|
||||
{$, $$, fs, WorkspaceView, View} = require 'atom'
|
||||
Q = require 'q'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
Pane = require '../src/pane'
|
||||
|
||||
describe "WorkspaceView", ->
|
||||
pathToOpen = null
|
||||
|
||||
beforeEach ->
|
||||
atom.project.setPath(atom.project.resolve('dir'))
|
||||
pathToOpen = atom.project.resolve('a')
|
||||
atom.workspaceView = new WorkspaceView
|
||||
atom.workspaceView.enableKeymap()
|
||||
atom.workspaceView.openSync(pathToOpen)
|
||||
atom.workspaceView.focus()
|
||||
|
||||
describe "@deserialize()", ->
|
||||
viewState = null
|
||||
|
||||
refreshWorkspaceViewAndProject = ->
|
||||
workspaceViewState = atom.workspaceView.serialize()
|
||||
atom.project.getState().serializeForPersistence()
|
||||
project2 = atom.replicate().get('project')
|
||||
atom.workspaceView.remove()
|
||||
atom.project.destroy()
|
||||
atom.project = project2
|
||||
atom.workspaceView = atom.deserializers.deserialize(workspaceViewState)
|
||||
atom.workspaceView.attachToDom()
|
||||
|
||||
describe "when the serialized WorkspaceView has an unsaved buffer", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
atom.workspaceView.attachToDom()
|
||||
atom.workspaceView.openSync()
|
||||
editor1 = atom.workspaceView.getActiveView()
|
||||
buffer = editor1.getBuffer()
|
||||
editor1.splitRight()
|
||||
expect(atom.workspaceView.getActiveView()).toBe atom.workspaceView.getEditorViews()[2]
|
||||
|
||||
refreshWorkspaceViewAndProject()
|
||||
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 2
|
||||
expect(atom.workspaceView.getActiveView()).toBe atom.workspaceView.getEditorViews()[1]
|
||||
expect(atom.workspaceView.title).toBe "untitled - #{atom.project.getPath()}"
|
||||
|
||||
describe "when there are open editors", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
atom.workspaceView.attachToDom()
|
||||
pane1 = atom.workspaceView.getActivePane()
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitRight()
|
||||
pane4 = pane2.splitDown()
|
||||
pane2.showItem(atom.project.openSync('b'))
|
||||
pane3.showItem(atom.project.openSync('../sample.js'))
|
||||
pane3.activeItem.setCursorScreenPosition([2, 4])
|
||||
pane4.showItem(atom.project.openSync('../sample.txt'))
|
||||
pane4.activeItem.setCursorScreenPosition([0, 2])
|
||||
pane2.focus()
|
||||
|
||||
refreshWorkspaceViewAndProject()
|
||||
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 4
|
||||
editor1 = atom.workspaceView.panes.find('.row > .pane .editor:eq(0)').view()
|
||||
editor3 = atom.workspaceView.panes.find('.row > .pane .editor:eq(1)').view()
|
||||
editor2 = atom.workspaceView.panes.find('.row > .column > .pane .editor:eq(0)').view()
|
||||
editor4 = atom.workspaceView.panes.find('.row > .column > .pane .editor:eq(1)').view()
|
||||
|
||||
expect(editor1.getPath()).toBe atom.project.resolve('a')
|
||||
expect(editor2.getPath()).toBe atom.project.resolve('b')
|
||||
expect(editor3.getPath()).toBe atom.project.resolve('../sample.js')
|
||||
expect(editor3.getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(editor4.getPath()).toBe atom.project.resolve('../sample.txt')
|
||||
expect(editor4.getCursorScreenPosition()).toEqual [0, 2]
|
||||
|
||||
# ensure adjust pane dimensions is called
|
||||
expect(editor1.width()).toBeGreaterThan 0
|
||||
expect(editor2.width()).toBeGreaterThan 0
|
||||
expect(editor3.width()).toBeGreaterThan 0
|
||||
expect(editor4.width()).toBeGreaterThan 0
|
||||
|
||||
# ensure correct editor is focused again
|
||||
expect(editor2.isFocused).toBeTruthy()
|
||||
expect(editor1.isFocused).toBeFalsy()
|
||||
expect(editor3.isFocused).toBeFalsy()
|
||||
expect(editor4.isFocused).toBeFalsy()
|
||||
|
||||
expect(atom.workspaceView.title).toBe "#{path.basename(editor2.getPath())} - #{atom.project.getPath()}"
|
||||
|
||||
describe "where there are no open editors", ->
|
||||
it "constructs the view with no open editors", ->
|
||||
atom.workspaceView.getActivePane().remove()
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 0
|
||||
refreshWorkspaceViewAndProject()
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 0
|
||||
|
||||
describe "focus", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView.attachToDom()
|
||||
|
||||
describe "when there is an active view", ->
|
||||
it "hands off focus to the active view", ->
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
editorView.isFocused = false
|
||||
atom.workspaceView.focus()
|
||||
expect(editorView.isFocused).toBeTruthy()
|
||||
|
||||
describe "when there is no active view", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView.getActivePane().remove()
|
||||
expect(atom.workspaceView.getActiveView()).toBeUndefined()
|
||||
atom.workspaceView.attachToDom()
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
describe "when are visible focusable elements (with a -1 tabindex)", ->
|
||||
it "passes focus to the first focusable element", ->
|
||||
focusable1 = $$ -> @div "One", id: 'one', tabindex: -1
|
||||
focusable2 = $$ -> @div "Two", id: 'two', tabindex: -1
|
||||
atom.workspaceView.horizontal.append(focusable1, focusable2)
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
atom.workspaceView.focus()
|
||||
expect(document.activeElement).toBe focusable1[0]
|
||||
|
||||
describe "when there are no visible focusable elements", ->
|
||||
it "surrenders focus to the body", ->
|
||||
focusable = $$ -> @div "One", id: 'one', tabindex: -1
|
||||
atom.workspaceView.horizontal.append(focusable)
|
||||
focusable.hide()
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
atom.workspaceView.focus()
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
describe "keymap wiring", ->
|
||||
commandHandler = null
|
||||
beforeEach ->
|
||||
commandHandler = jasmine.createSpy('commandHandler')
|
||||
atom.workspaceView.on('foo-command', commandHandler)
|
||||
|
||||
atom.keymap.bindKeys('name', '*', 'x': 'foo-command')
|
||||
|
||||
describe "when a keydown event is triggered in the WorkspaceView", ->
|
||||
it "triggers matching keybindings for that event", ->
|
||||
event = keydownEvent 'x', target: atom.workspaceView[0]
|
||||
|
||||
atom.workspaceView.trigger(event)
|
||||
expect(commandHandler).toHaveBeenCalled()
|
||||
|
||||
describe "window title", ->
|
||||
describe "when the project has no path", ->
|
||||
it "sets the title to 'untitled'", ->
|
||||
atom.project.setPath(undefined)
|
||||
expect(atom.workspaceView.title).toBe 'untitled'
|
||||
|
||||
describe "when the project has a path", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView.openSync('b')
|
||||
|
||||
describe "when there is an active pane item", ->
|
||||
it "sets the title to the pane item's title plus the project path", ->
|
||||
item = atom.workspaceView.getActivePaneItem()
|
||||
expect(atom.workspaceView.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
|
||||
describe "when the title of the active pane item changes", ->
|
||||
it "updates the window title based on the item's new title", ->
|
||||
editor = atom.workspaceView.getActivePaneItem()
|
||||
editor.buffer.setPath(path.join(temp.dir, 'hi'))
|
||||
expect(atom.workspaceView.title).toBe "#{editor.getTitle()} - #{atom.project.getPath()}"
|
||||
|
||||
describe "when the active pane's item changes", ->
|
||||
it "updates the title to the new item's title plus the project path", ->
|
||||
atom.workspaceView.getActivePane().showNextItem()
|
||||
item = atom.workspaceView.getActivePaneItem()
|
||||
expect(atom.workspaceView.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
|
||||
describe "when the last pane item is removed", ->
|
||||
it "updates the title to contain the project's path", ->
|
||||
atom.workspaceView.getActivePane().remove()
|
||||
expect(atom.workspaceView.getActivePaneItem()).toBeUndefined()
|
||||
expect(atom.workspaceView.title).toBe atom.project.getPath()
|
||||
|
||||
describe "when an inactive pane's item changes", ->
|
||||
it "does not update the title", ->
|
||||
pane = atom.workspaceView.getActivePane()
|
||||
pane.splitRight()
|
||||
initialTitle = atom.workspaceView.title
|
||||
pane.showNextItem()
|
||||
expect(atom.workspaceView.title).toBe initialTitle
|
||||
|
||||
describe "when the root view is deserialized", ->
|
||||
it "updates the title to contain the project's path", ->
|
||||
workspaceView2 = atom.deserializers.deserialize(atom.workspaceView.serialize())
|
||||
item = atom.workspaceView.getActivePaneItem()
|
||||
expect(workspaceView2.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
workspaceView2.remove()
|
||||
|
||||
describe "font size adjustment", ->
|
||||
it "increases/decreases font size when increase/decrease-font-size events are triggered", ->
|
||||
fontSizeBefore = atom.config.get('editor.fontSize')
|
||||
atom.workspaceView.trigger 'window:increase-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 1
|
||||
atom.workspaceView.trigger 'window:increase-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 2
|
||||
atom.workspaceView.trigger 'window:decrease-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 1
|
||||
atom.workspaceView.trigger 'window:decrease-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore
|
||||
|
||||
it "does not allow the font size to be less than 1", ->
|
||||
atom.config.set("editor.fontSize", 1)
|
||||
atom.workspaceView.trigger 'window:decrease-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe 1
|
||||
|
||||
describe ".openSync(filePath, options)", ->
|
||||
describe "when there is no active pane", ->
|
||||
beforeEach ->
|
||||
spyOn(Pane.prototype, 'focus')
|
||||
atom.workspaceView.getActivePane().remove()
|
||||
expect(atom.workspaceView.getActivePane()).toBeUndefined()
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "creates a empty edit session as an item on a new pane, and focuses the pane", ->
|
||||
editor = atom.workspaceView.openSync()
|
||||
expect(atom.workspaceView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(atom.workspaceView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
it "can create multiple empty edit sessions as an item on a new pane", ->
|
||||
editor = atom.workspaceView.openSync()
|
||||
editor2 = atom.workspaceView.openSync()
|
||||
expect(atom.workspaceView.getActivePane().getItems().length).toBe 2
|
||||
expect(editor).not.toBe editor2
|
||||
|
||||
describe "when called with a path", ->
|
||||
it "creates an edit session for the given path as an item on a new pane, and focuses the pane", ->
|
||||
editor = atom.workspaceView.openSync('b')
|
||||
expect(atom.workspaceView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
expect(atom.workspaceView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the changeFocus option is false", ->
|
||||
it "does not focus the new pane", ->
|
||||
editor = atom.workspaceView.openSync('b', changeFocus: false)
|
||||
expect(atom.workspaceView.getActivePane().focus).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the split option is 'right'", ->
|
||||
it "creates a new pane and opens the file in said pane", ->
|
||||
editor = atom.workspaceView.openSync('b', split: 'right')
|
||||
expect(atom.workspaceView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
|
||||
describe "when there is an active pane", ->
|
||||
[activePane, initialItemCount] = []
|
||||
beforeEach ->
|
||||
activePane = atom.workspaceView.getActivePane()
|
||||
spyOn(activePane, 'focus')
|
||||
initialItemCount = activePane.getItems().length
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "opens an edit session with an empty buffer as an item in the active pane and focuses it", ->
|
||||
editor = atom.workspaceView.openSync()
|
||||
expect(activePane.getItems().length).toBe initialItemCount + 1
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a path", ->
|
||||
describe "when the active pane already has an edit session item for the path being opened", ->
|
||||
it "shows the existing edit session in the pane", ->
|
||||
previousEditor = activePane.activeItem
|
||||
|
||||
editor = atom.workspaceView.openSync('b')
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor).not.toBe previousEditor
|
||||
|
||||
editor = atom.workspaceView.openSync(previousEditor.getPath())
|
||||
expect(editor).toBe previousEditor
|
||||
expect(activePane.activeItem).toBe editor
|
||||
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the active pane does not have an edit session item for the path being opened", ->
|
||||
it "creates a new edit session for the given path in the active editor", ->
|
||||
editor = atom.workspaceView.openSync('b')
|
||||
expect(activePane.items.length).toBe 2
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the changeFocus option is false", ->
|
||||
it "does not focus the active pane", ->
|
||||
editor = atom.workspaceView.openSync('b', changeFocus: false)
|
||||
expect(activePane.focus).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the split option is 'right'", ->
|
||||
it "creates a new pane and opens the file in said pane", ->
|
||||
pane1 = atom.workspaceView.getActivePane()
|
||||
|
||||
editor = atom.workspaceView.openSync('b', split: 'right')
|
||||
pane2 = atom.workspaceView.getActivePane()
|
||||
expect(pane2[0]).not.toBe pane1[0]
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
|
||||
expect(atom.workspaceView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
editor = atom.workspaceView.openSync('file1', split: 'right')
|
||||
pane3 = atom.workspaceView.getActivePane()
|
||||
expect(pane3[0]).toBe pane2[0]
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/file1')
|
||||
|
||||
expect(atom.workspaceView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
describe ".openSingletonSync(filePath, options)", ->
|
||||
describe "when there is an active pane", ->
|
||||
[pane1] = []
|
||||
beforeEach ->
|
||||
spyOn(Pane.prototype, 'focus').andCallFake -> @makeActive()
|
||||
pane1 = atom.workspaceView.getActivePane()
|
||||
|
||||
it "creates a new pane and reuses the file when already open", ->
|
||||
atom.workspaceView.openSingletonSync('b', split: 'right')
|
||||
pane2 = atom.workspaceView.getActivePane()
|
||||
expect(pane2[0]).not.toBe pane1[0]
|
||||
expect(pane1.itemForUri('b')).toBeFalsy()
|
||||
expect(pane2.itemForUri('b')).not.toBeFalsy()
|
||||
expect(atom.workspaceView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
pane1.focus()
|
||||
expect(atom.workspaceView.getActivePane()[0]).toBe pane1[0]
|
||||
|
||||
atom.workspaceView.openSingletonSync('b', split: 'right')
|
||||
pane3 = atom.workspaceView.getActivePane()
|
||||
expect(pane3[0]).toBe pane2[0]
|
||||
expect(pane1.itemForUri('b')).toBeFalsy()
|
||||
expect(pane2.itemForUri('b')).not.toBeFalsy()
|
||||
expect(atom.workspaceView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
it "handles split: left by opening to the left pane when necessary", ->
|
||||
atom.workspaceView.openSingletonSync('b', split: 'right')
|
||||
pane2 = atom.workspaceView.getActivePane()
|
||||
expect(pane2[0]).not.toBe pane1[0]
|
||||
|
||||
atom.workspaceView.openSingletonSync('file1', split: 'left')
|
||||
|
||||
activePane = atom.workspaceView.getActivePane()
|
||||
expect(activePane[0]).toBe pane1[0]
|
||||
|
||||
expect(pane1.itemForUri('file1')).toBeTruthy()
|
||||
expect(pane2.itemForUri('file1')).toBeFalsy()
|
||||
expect(atom.workspaceView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
pane2.focus()
|
||||
expect(atom.workspaceView.getActivePane()[0]).toBe pane2[0]
|
||||
|
||||
atom.workspaceView.openSingletonSync('file1', split: 'left')
|
||||
activePane = atom.workspaceView.getActivePane()
|
||||
expect(activePane[0]).toBe pane1[0]
|
||||
expect(atom.workspaceView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
it "reuses the file when already open", ->
|
||||
atom.workspaceView.openSync('b')
|
||||
atom.workspaceView.openSingletonSync('b', split: 'right')
|
||||
expect(atom.workspaceView.panes.find('.pane').toArray()).toEqual [pane1[0]]
|
||||
|
||||
describe ".open(filePath)", ->
|
||||
beforeEach ->
|
||||
spyOn(Pane.prototype, 'focus')
|
||||
|
||||
describe "when there is no active pane", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView.getActivePane().remove()
|
||||
expect(atom.workspaceView.getActivePane()).toBeUndefined()
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "creates a empty edit session as an item on a new pane, and focuses the pane", ->
|
||||
editor = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open().then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(atom.workspaceView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(atom.workspaceView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
it "can create multiple empty edit sessions as items on a pane", ->
|
||||
editor1 = null
|
||||
editor2 = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open()
|
||||
.then (o) ->
|
||||
editor1 = o
|
||||
atom.workspaceView.open()
|
||||
.then (o) ->
|
||||
editor2 = o
|
||||
|
||||
runs ->
|
||||
expect(atom.workspaceView.getActivePane().getItems().length).toBe 2
|
||||
expect(editor1).not.toBe editor2
|
||||
|
||||
describe "when called with a path", ->
|
||||
it "creates an edit session for the given path as an item on a new pane, and focuses the pane", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open('b').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(atom.workspaceView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
expect(atom.workspaceView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
describe "when there is an active pane", ->
|
||||
[activePane] = []
|
||||
|
||||
beforeEach ->
|
||||
activePane = atom.workspaceView.getActivePane()
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "opens an edit session with an empty buffer as an item in the active pane and focuses it", ->
|
||||
editor = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open().then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(activePane.getItems().length).toBe 2
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a path", ->
|
||||
describe "when the active pane already has an item for the given path", ->
|
||||
it "shows the existing edit session in the pane", ->
|
||||
previousEditor = activePane.activeItem
|
||||
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open('b').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor).not.toBe previousEditor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open(previousEditor.getPath()).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor).toBe previousEditor
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the active pane does not have an existing item for the given path", ->
|
||||
it "creates a new edit session for the given path in the active pane", ->
|
||||
editor = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open('b').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(activePane.getItems().length).toBe 2
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "window:toggle-invisibles event", ->
|
||||
it "shows/hides invisibles in all open and future editors", ->
|
||||
atom.workspaceView.height(200)
|
||||
atom.workspaceView.attachToDom()
|
||||
rightEditor = atom.workspaceView.getActiveView()
|
||||
rightEditor.setText(" \t ")
|
||||
leftEditor = rightEditor.splitLeft()
|
||||
expect(rightEditor.find(".line:first").text()).toBe " "
|
||||
expect(leftEditor.find(".line:first").text()).toBe " "
|
||||
|
||||
withInvisiblesShowing = "#{rightEditor.invisibles.space}#{rightEditor.invisibles.tab} #{rightEditor.invisibles.space}#{rightEditor.invisibles.eol}"
|
||||
|
||||
atom.workspaceView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
expect(leftEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
lowerLeftEditor = leftEditor.splitDown()
|
||||
expect(lowerLeftEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
atom.workspaceView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditor.find(".line:first").text()).toBe " "
|
||||
expect(leftEditor.find(".line:first").text()).toBe " "
|
||||
|
||||
lowerRightEditor = rightEditor.splitDown()
|
||||
expect(lowerRightEditor.find(".line:first").text()).toBe " "
|
||||
|
||||
describe ".eachEditorView(callback)", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView.attachToDom()
|
||||
|
||||
it "invokes the callback for existing editor", ->
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
callback = (editor) ->
|
||||
callbackEditor = editor
|
||||
count++
|
||||
atom.workspaceView.eachEditorView(callback)
|
||||
expect(count).toBe 1
|
||||
expect(callbackEditor).toBe atom.workspaceView.getActiveView()
|
||||
|
||||
it "invokes the callback for new editor", ->
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
callback = (editor) ->
|
||||
callbackEditor = editor
|
||||
count++
|
||||
|
||||
atom.workspaceView.eachEditorView(callback)
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
atom.workspaceView.getActiveView().splitRight()
|
||||
expect(count).toBe 1
|
||||
expect(callbackEditor).toBe atom.workspaceView.getActiveView()
|
||||
|
||||
it "returns a subscription that can be disabled", ->
|
||||
count = 0
|
||||
callback = (editor) -> count++
|
||||
|
||||
subscription = atom.workspaceView.eachEditorView(callback)
|
||||
expect(count).toBe 1
|
||||
atom.workspaceView.getActiveView().splitRight()
|
||||
expect(count).toBe 2
|
||||
subscription.off()
|
||||
atom.workspaceView.getActiveView().splitRight()
|
||||
expect(count).toBe 2
|
||||
+12
-12
@@ -89,7 +89,7 @@ class AtomPackage extends Package
|
||||
|
||||
@requireMainModule()
|
||||
if @mainModule?
|
||||
config.setDefaults(@name, @mainModule.configDefaults)
|
||||
atom.config.setDefaults(@name, @mainModule.configDefaults)
|
||||
@mainModule.activateConfig?()
|
||||
@configActivated = true
|
||||
|
||||
@@ -105,9 +105,9 @@ class AtomPackage extends Package
|
||||
atom.keymap.add(keymapPath, map) for [keymapPath, map] in @keymaps
|
||||
atom.contextMenu.add(menuPath, map['context-menu']) for [menuPath, map] in @menus
|
||||
atom.menu.add(map.menu) for [menuPath, map] in @menus when map.menu
|
||||
syntax.addGrammar(grammar) for grammar in @grammars
|
||||
atom.syntax.addGrammar(grammar) for grammar in @grammars
|
||||
for [scopedPropertiesPath, selector, properties] in @scopedProperties
|
||||
syntax.addProperties(scopedPropertiesPath, selector, properties)
|
||||
atom.syntax.addProperties(scopedPropertiesPath, selector, properties)
|
||||
|
||||
loadKeymaps: ->
|
||||
@keymaps = @getKeymapPaths().map (keymapPath) -> [keymapPath, CSON.readFileSync(keymapPath)]
|
||||
@@ -180,8 +180,8 @@ class AtomPackage extends Package
|
||||
@configActivated = false
|
||||
|
||||
deactivateResources: ->
|
||||
syntax.removeGrammar(grammar) for grammar in @grammars
|
||||
syntax.removeProperties(scopedPropertiesPath) for [scopedPropertiesPath] in @scopedProperties
|
||||
atom.syntax.removeGrammar(grammar) for grammar in @grammars
|
||||
atom.syntax.removeProperties(scopedPropertiesPath) for [scopedPropertiesPath] in @scopedProperties
|
||||
atom.keymap.remove(keymapPath) for [keymapPath] in @keymaps
|
||||
atom.themes.removeStylesheet(stylesheetPath) for [stylesheetPath] in @stylesheets
|
||||
@stylesheetsActivated = false
|
||||
@@ -212,18 +212,18 @@ class AtomPackage extends Package
|
||||
|
||||
registerDeferredDeserializers: ->
|
||||
for deserializerName in @metadata.deferredDeserializers ? []
|
||||
registerDeferredDeserializer deserializerName, =>
|
||||
atom.deserializers.addDeferred deserializerName, =>
|
||||
@activateStylesheets()
|
||||
@requireMainModule()
|
||||
|
||||
subscribeToActivationEvents: ->
|
||||
return unless @metadata.activationEvents?
|
||||
if _.isArray(@metadata.activationEvents)
|
||||
rootView.command(event, @handleActivationEvent) for event in @metadata.activationEvents
|
||||
atom.workspaceView.command(event, @handleActivationEvent) for event in @metadata.activationEvents
|
||||
else if _.isString(@metadata.activationEvents)
|
||||
rootView.command(@metadata.activationEvents, @handleActivationEvent)
|
||||
atom.workspaceView.command(@metadata.activationEvents, @handleActivationEvent)
|
||||
else
|
||||
rootView.command(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
|
||||
atom.workspaceView.command(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
|
||||
|
||||
handleActivationEvent: (event) =>
|
||||
bubblePathEventHandlers = @disableEventHandlersOnBubblePath(event)
|
||||
@@ -234,11 +234,11 @@ class AtomPackage extends Package
|
||||
|
||||
unsubscribeFromActivationEvents: ->
|
||||
if _.isArray(@metadata.activationEvents)
|
||||
rootView.off(event, @handleActivationEvent) for event in @metadata.activationEvents
|
||||
atom.workspaceView.off(event, @handleActivationEvent) for event in @metadata.activationEvents
|
||||
else if _.isString(@metadata.activationEvents)
|
||||
rootView.off(@metadata.activationEvents, @handleActivationEvent)
|
||||
atom.workspaceView.off(@metadata.activationEvents, @handleActivationEvent)
|
||||
else
|
||||
rootView.off(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
|
||||
atom.workspaceView.off(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
|
||||
|
||||
disableEventHandlersOnBubblePath: (event) ->
|
||||
bubblePathEventHandlers = []
|
||||
|
||||
+224
-93
@@ -1,49 +1,52 @@
|
||||
#TODO remove once all packages have been updated
|
||||
{Emitter} = require 'emissary'
|
||||
Emitter::one = (args...) -> @once(args...)
|
||||
Emitter::trigger = (args...) -> @emit(args...)
|
||||
Emitter::subscriptionCount = (args...) -> @getSubscriptionCount(args...)
|
||||
|
||||
#TODO remove once all packages have been updated
|
||||
fs = require 'fs-plus'
|
||||
fs.exists = fs.existsSync
|
||||
fs.makeTree = fs.makeTreeSync
|
||||
fs.move = fs.moveSync
|
||||
fs.read = (filePath) -> fs.readFileSync(filePath, 'utf8')
|
||||
fs.remove = fs.removeSync
|
||||
fs.write = fs.writeFile
|
||||
fs.writeSync = fs.writeFileSync
|
||||
|
||||
fs = require 'fs-plus'
|
||||
{$} = require './space-pen-extensions'
|
||||
_ = require 'underscore-plus'
|
||||
Package = require './package'
|
||||
crypto = require 'crypto'
|
||||
ipc = require 'ipc'
|
||||
os = require 'os'
|
||||
path = require 'path'
|
||||
remote = require 'remote'
|
||||
shell = require 'shell'
|
||||
crypto = require 'crypto'
|
||||
path = require 'path'
|
||||
os = require 'os'
|
||||
dialog = remote.require 'dialog'
|
||||
app = remote.require 'app'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
{Document} = require 'telepath'
|
||||
DeserializerManager = require './deserializer-manager'
|
||||
fs = require 'fs-plus'
|
||||
{Subscriber} = require 'emissary'
|
||||
|
||||
{$} = require './space-pen-extensions'
|
||||
DeserializerManager = require './deserializer-manager'
|
||||
Package = require './package'
|
||||
SiteShim = require './site-shim'
|
||||
WindowEventHandler = require './window-event-handler'
|
||||
|
||||
# Public: Atom global for dealing with packages, themes, menus, and the window.
|
||||
#
|
||||
# An instance of this class is always available as the `atom` global.
|
||||
#
|
||||
# ## Useful properties available:
|
||||
#
|
||||
# * `atom.config` - A {Config} instance
|
||||
# * `atom.contextMenu` - A {ContextMenuManager} instance
|
||||
# * `atom.keymap` - A {Keymap} instance
|
||||
# * `atom.menu` - A {MenuManager} instance
|
||||
# * `atom.workspaceView` - A {WorkspaceView} instance
|
||||
# * `atom.packages` - A {PackageManager} instance
|
||||
# * `atom.pasteboard` - A {Pasteboard} instance
|
||||
# * `atom.project` - A {Project} instance
|
||||
# * `atom.syntax` - A {Syntax} instance
|
||||
# * `atom.themes` - A {ThemeManager} instance
|
||||
module.exports =
|
||||
class Atom
|
||||
Subscriber.includeInto(this)
|
||||
|
||||
# Private:
|
||||
constructor: ->
|
||||
@rootViewParentSelector = 'body'
|
||||
@workspaceViewParentSelector = 'body'
|
||||
@deserializers = new DeserializerManager()
|
||||
|
||||
# Private: Initialize all the properties in this object.
|
||||
initialize: ->
|
||||
@unsubscribe()
|
||||
@setBodyPlatformClass()
|
||||
|
||||
{devMode, resourcePath} = atom.getLoadSettings()
|
||||
configDirPath = @getConfigDirPath()
|
||||
@@ -58,20 +61,25 @@ class Atom
|
||||
MenuManager = require './menu-manager'
|
||||
|
||||
@config = new Config({configDirPath, resourcePath})
|
||||
@keymap = new Keymap()
|
||||
@keymap = new Keymap({configDirPath, resourcePath})
|
||||
@packages = new PackageManager({devMode, configDirPath, resourcePath})
|
||||
|
||||
#TODO Remove once packages have been updated to not touch atom.packageStates directly
|
||||
@__defineGetter__ 'packageStates', => @packages.packageStates
|
||||
@__defineSetter__ 'packageStates', (packageStates) => @packages.packageStates = packageStates
|
||||
|
||||
@subscribe @packages, 'activated', => @watchThemes()
|
||||
@themes = new ThemeManager(@packages)
|
||||
@themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath})
|
||||
@contextMenu = new ContextMenuManager(devMode)
|
||||
@menu = new MenuManager()
|
||||
@menu = new MenuManager({resourcePath})
|
||||
@pasteboard = new Pasteboard()
|
||||
@syntax = deserialize(@getWindowState('syntax')) ? new Syntax()
|
||||
@syntax = @deserializers.deserialize(@getWindowState('syntax')) ? new Syntax()
|
||||
|
||||
# Private: This method is called in any window needing a general environment, including specs
|
||||
setUpEnvironment: (@windowMode) ->
|
||||
@initialize()
|
||||
|
||||
# Private:
|
||||
setBodyPlatformClass: ->
|
||||
document.body.classList.add("platform-#{process.platform}")
|
||||
|
||||
# Public: Get the current window
|
||||
getCurrentWindow: ->
|
||||
remote.getCurrentWindow()
|
||||
|
||||
@@ -106,6 +114,7 @@ class Atom
|
||||
else
|
||||
browserWindow.center()
|
||||
|
||||
# Private:
|
||||
restoreDimensions: ->
|
||||
dimensions = @getWindowState().getObject('dimensions')
|
||||
unless dimensions?.width and dimensions?.height
|
||||
@@ -122,54 +131,95 @@ class Atom
|
||||
@loadSettings ?= _.deepClone(@getCurrentWindow().loadSettings)
|
||||
_.deepClone(@loadSettings)
|
||||
|
||||
# Private:
|
||||
deserializeProject: ->
|
||||
Project = require './project'
|
||||
state = @getWindowState()
|
||||
@project = deserialize(state.get('project'))
|
||||
unless @project?
|
||||
@project = new Project(@getLoadSettings().initialPath)
|
||||
state.set('project', @project.getState())
|
||||
@project = @getWindowState('project')
|
||||
unless @project instanceof Project
|
||||
@project = new Project(path: @getLoadSettings().initialPath)
|
||||
@setWindowState('project', @project)
|
||||
|
||||
deserializeRootView: ->
|
||||
RootView = require './root-view'
|
||||
# Private:
|
||||
deserializeWorkspaceView: ->
|
||||
WorkspaceView = require './workspace-view'
|
||||
state = @getWindowState()
|
||||
@rootView = deserialize(state.get('rootView'))
|
||||
unless @rootView?
|
||||
@rootView = new RootView()
|
||||
state.set('rootView', @rootView.getState())
|
||||
$(@rootViewParentSelector).append(@rootView)
|
||||
@workspaceView = @deserializers.deserialize(state.get('workspaceView'))
|
||||
unless @workspaceView?
|
||||
@workspaceView = new WorkspaceView()
|
||||
state.set('workspaceView', @workspaceView.getState())
|
||||
$(@workspaceViewParentSelector).append(@workspaceView)
|
||||
|
||||
# Private:
|
||||
deserializePackageStates: ->
|
||||
state = @getWindowState()
|
||||
@packages.packageStates = state.getObject('packageStates') ? {}
|
||||
state.remove('packageStates')
|
||||
|
||||
#TODO Remove theses once packages have been migrated
|
||||
getPackageState: (args...) -> @packages.getPackageState(args...)
|
||||
setPackageState: (args...) -> @packages.setPackageState(args...)
|
||||
activatePackages: (args...) -> @packages.activatePackages(args...)
|
||||
activatePackage: (args...) -> @packages.activatePackage(args...)
|
||||
deactivatePackages: (args...) -> @packages.deactivatePackages(args...)
|
||||
deactivatePackage: (args...) -> @packages.deactivatePackage(args...)
|
||||
getActivePackage: (args...) -> @packages.getActivePackage(args...)
|
||||
isPackageActive: (args...) -> @packages.isPackageActive(args...)
|
||||
getActivePackages: (args...) -> @packages.getActivePackages(args...)
|
||||
loadPackages: (args...) -> @packages.loadPackages(args...)
|
||||
loadPackage: (args...) -> @packages.loadPackage(args...)
|
||||
unloadPackage: (args...) -> @packages.unloadPackage(args...)
|
||||
resolvePackagePath: (args...) -> @packages.resolvePackagePath(args...)
|
||||
isInternalPackage: (args...) -> @packages.isInternalPackage(args...)
|
||||
getLoadedPackage: (args...) -> @packages.getLoadedPackage(args...)
|
||||
isPackageLoaded: (args...) -> @packages.isPackageLoaded(args...)
|
||||
getLoadedPackages: (args...) -> @packages.getLoadedPackages(args...)
|
||||
isPackageDisabled: (args...) -> @packages.isPackageDisabled(args...)
|
||||
getAvailablePackagePaths: (args...) -> @packages.getAvailablePackagePaths(args...)
|
||||
getAvailablePackageNames: (args...) -> @packages.getAvailablePackageNames(args...)
|
||||
getAvailablePackageMetadata: (args...)-> @packages.getAvailablePackageMetadata(args...)
|
||||
# Private:
|
||||
deserializeEditorWindow: ->
|
||||
@deserializePackageStates()
|
||||
@deserializeProject()
|
||||
@deserializeWorkspaceView()
|
||||
|
||||
# Private: This method is only called when opening a real application window
|
||||
startEditorWindow: ->
|
||||
if process.platform is 'darwin'
|
||||
CommandInstaller = require './command-installer'
|
||||
CommandInstaller.installAtomCommand()
|
||||
CommandInstaller.installApmCommand()
|
||||
|
||||
@windowEventHandler = new WindowEventHandler
|
||||
@restoreDimensions()
|
||||
@config.load()
|
||||
@config.setDefaults('core', require('./workspace-view').configDefaults)
|
||||
@config.setDefaults('editor', require('./editor-view').configDefaults)
|
||||
@keymap.loadBundledKeymaps()
|
||||
@themes.loadBaseStylesheets()
|
||||
@packages.loadPackages()
|
||||
@deserializeEditorWindow()
|
||||
@packages.activate()
|
||||
@keymap.loadUserKeymap()
|
||||
@requireUserInitScript()
|
||||
@menu.update()
|
||||
|
||||
$(window).on 'unload', =>
|
||||
$(document.body).hide()
|
||||
@unloadEditorWindow()
|
||||
false
|
||||
|
||||
@displayWindow()
|
||||
|
||||
# Private:
|
||||
unloadEditorWindow: ->
|
||||
return if not @project and not @workspaceView
|
||||
|
||||
windowState = @getWindowState()
|
||||
windowState.set('project', @project)
|
||||
windowState.set('syntax', @syntax.serialize())
|
||||
windowState.set('workspaceView', @workspaceView.serialize())
|
||||
@packages.deactivatePackages()
|
||||
windowState.set('packageStates', @packages.packageStates)
|
||||
@saveWindowState()
|
||||
@workspaceView.remove()
|
||||
@project.destroy()
|
||||
@windowEventHandler?.unsubscribe()
|
||||
|
||||
# Set up the default event handlers and menus for a non-editor window.
|
||||
#
|
||||
# This can be used by packages to have a minimum level of keybindings and
|
||||
# menus available when not using the standard editor window.
|
||||
#
|
||||
# This should only be called after setUpEnvironment() has been called.
|
||||
setUpDefaultEvents: ->
|
||||
@windowEventHandler = new WindowEventHandler
|
||||
@keymap.loadBundledKeymaps()
|
||||
@menu.update()
|
||||
|
||||
# Private:
|
||||
loadThemes: ->
|
||||
@themes.load()
|
||||
|
||||
# Private:
|
||||
watchThemes: ->
|
||||
@themes.on 'reloaded', =>
|
||||
# Only reload stylesheets from non-theme packages
|
||||
@@ -177,84 +227,148 @@ class Atom
|
||||
pack.reloadStylesheets?()
|
||||
null
|
||||
|
||||
# Public: Open a new Atom window using the given options.
|
||||
#
|
||||
# Calling this method without an options parameter will open a prompt to pick
|
||||
# a file/folder to open in the new window.
|
||||
#
|
||||
# * options
|
||||
# * pathsToOpen: A string array of paths to open
|
||||
open: (options) ->
|
||||
ipc.sendChannel('open', options)
|
||||
|
||||
confirm: (message, detailedMessage, buttonLabelsAndCallbacks...) ->
|
||||
buttons = []
|
||||
callbacks = []
|
||||
while buttonLabelsAndCallbacks.length
|
||||
do =>
|
||||
buttons.push buttonLabelsAndCallbacks.shift()
|
||||
callbacks.push buttonLabelsAndCallbacks.shift()
|
||||
# Public: Open a confirm dialog.
|
||||
#
|
||||
# ## Example:
|
||||
# ```coffeescript
|
||||
# atom.confirm
|
||||
# message: 'How you feeling?'
|
||||
# detailedMessage: 'Be honest.'
|
||||
# buttons:
|
||||
# Good: -> window.alert('good to hear')
|
||||
# Bad: -> window.alert('bummer')
|
||||
# ```
|
||||
#
|
||||
# * options:
|
||||
# + message: The string message to display.
|
||||
# + detailedMessage: The string detailed message to display.
|
||||
# + buttons: Either an array of strings or an object where the values
|
||||
# are callbacks to invoke when clicked.
|
||||
#
|
||||
# Returns the chosen index if buttons was an array or the return of the
|
||||
# callback if buttons was an object.
|
||||
confirm: ({message, detailedMessage, buttons}={}) ->
|
||||
buttons ?= {}
|
||||
if _.isArray(buttons)
|
||||
buttonLabels = buttons
|
||||
else
|
||||
buttonLabels = Object.keys(buttons)
|
||||
|
||||
chosen = @confirmSync(message, detailedMessage, buttons)
|
||||
callbacks[chosen]?()
|
||||
|
||||
confirmSync: (message, detailedMessage, buttons, browserWindow=@getCurrentWindow()) ->
|
||||
dialog.showMessageBox browserWindow,
|
||||
chosen = dialog.showMessageBox @getCurrentWindow(),
|
||||
type: 'info'
|
||||
message: message
|
||||
detail: detailedMessage
|
||||
buttons: buttons
|
||||
buttons: buttonLabels
|
||||
|
||||
if _.isArray(buttons)
|
||||
chosen
|
||||
else
|
||||
callback = buttons[buttonLabels[chosen]]
|
||||
callback?()
|
||||
|
||||
# Private:
|
||||
showSaveDialog: (callback) ->
|
||||
callback(showSaveDialogSync())
|
||||
|
||||
# Private:
|
||||
showSaveDialogSync: (defaultPath) ->
|
||||
defaultPath ?= project?.getPath()
|
||||
defaultPath ?= @project?.getPath()
|
||||
currentWindow = @getCurrentWindow()
|
||||
dialog.showSaveDialog currentWindow, {title: 'Save File', defaultPath}
|
||||
|
||||
# Public: Open the dev tools for the current window.
|
||||
openDevTools: ->
|
||||
@getCurrentWindow().openDevTools()
|
||||
|
||||
# Public: Toggle the visibility of the dev tools for the current window.
|
||||
toggleDevTools: ->
|
||||
@getCurrentWindow().toggleDevTools()
|
||||
|
||||
# Public: Reload the current window.
|
||||
reload: ->
|
||||
@getCurrentWindow().restart()
|
||||
|
||||
# Public: Focus the current window.
|
||||
focus: ->
|
||||
@getCurrentWindow().focus()
|
||||
$(window).focus()
|
||||
|
||||
# Public: Show the current window.
|
||||
show: ->
|
||||
@getCurrentWindow().show()
|
||||
|
||||
# Public: Hide the current window.
|
||||
hide: ->
|
||||
@getCurrentWindow().hide()
|
||||
|
||||
# Private: Schedule the window to be shown and focused on the next tick.
|
||||
#
|
||||
# This is done in a next tick to prevent a white flicker from occurring
|
||||
# if called synchronously.
|
||||
displayWindow: ->
|
||||
setImmediate =>
|
||||
@show()
|
||||
@focus()
|
||||
@setFullScreen(true) if @workspaceView.getState().get('fullScreen')
|
||||
|
||||
# Public: Close the current window.
|
||||
close: ->
|
||||
@getCurrentWindow().close()
|
||||
|
||||
# Private:
|
||||
exit: (status) -> app.exit(status)
|
||||
|
||||
# Public: Is the current window in development mode?
|
||||
inDevMode: ->
|
||||
@getLoadSettings().devMode
|
||||
|
||||
# Public: Is the current window running specs?
|
||||
inSpecMode: ->
|
||||
@getLoadSettings().isSpec
|
||||
|
||||
# Public: Toggle the full screen state of the current window.
|
||||
toggleFullScreen: ->
|
||||
@setFullScreen(!@isFullScreen())
|
||||
|
||||
# Public: Set the full screen state of the current window.
|
||||
setFullScreen: (fullScreen=false) ->
|
||||
@getCurrentWindow().setFullScreen(fullScreen)
|
||||
|
||||
# Public: Is the current window in full screen mode?
|
||||
isFullScreen: ->
|
||||
@getCurrentWindow().isFullScreen()
|
||||
|
||||
getHomeDirPath: ->
|
||||
process.env[if process.platform is 'win32' then 'USERPROFILE' else 'HOME']
|
||||
# Public: Get the version of the Atom application.
|
||||
getVersion: ->
|
||||
app.getVersion()
|
||||
|
||||
getTempDirPath: ->
|
||||
if process.platform is 'win32' then os.tmpdir() else '/tmp'
|
||||
# Public: Gets the user agent of the atom instance.
|
||||
getUserAgent: ->
|
||||
"GitHubAtom/#{app.getVersion()}"
|
||||
|
||||
# Public: Get the directory path to Atom's configuration area.
|
||||
#
|
||||
# Returns the absolute path to ~/.atom
|
||||
getConfigDirPath: ->
|
||||
@configDirPath ?= fs.absolute('~/.atom')
|
||||
|
||||
# Public: Get the directory path to Atom's storage area.
|
||||
#
|
||||
# Returns the absoluste path to ~/.atom/storage
|
||||
getStorageDirPath: ->
|
||||
@storageDirPath ?= path.join(@getConfigDirPath(), 'storage')
|
||||
|
||||
# Private:
|
||||
getWindowStatePath: ->
|
||||
switch @windowMode
|
||||
when 'spec'
|
||||
@@ -266,15 +380,17 @@ class Atom
|
||||
filename = "editor-#{sha1}"
|
||||
|
||||
if filename
|
||||
path.join(@config.userStoragePath, filename)
|
||||
path.join(@getStorageDirPath(), filename)
|
||||
else
|
||||
null
|
||||
|
||||
# Public: Set the window state of the given keypath to the value.
|
||||
setWindowState: (keyPath, value) ->
|
||||
windowState = @getWindowState()
|
||||
windowState.set(keyPath, value)
|
||||
windowState
|
||||
|
||||
# Private:
|
||||
loadWindowState: ->
|
||||
if windowStatePath = @getWindowStatePath()
|
||||
if fs.existsSync(windowStatePath)
|
||||
@@ -292,6 +408,7 @@ class Atom
|
||||
|
||||
doc = Document.deserialize(documentState) if documentState?
|
||||
doc ?= Document.create()
|
||||
doc.registerModelClasses(require('./text-buffer'), require('./project'))
|
||||
# TODO: Remove this when everything is using telepath models
|
||||
if @site?
|
||||
@site.setRootDocument(doc)
|
||||
@@ -299,13 +416,15 @@ class Atom
|
||||
@site = new SiteShim(doc)
|
||||
doc
|
||||
|
||||
# Private:
|
||||
saveWindowState: ->
|
||||
windowState = @getWindowState()
|
||||
if windowStatePath = @getWindowStatePath()
|
||||
windowState.saveSync(windowStatePath)
|
||||
else
|
||||
@getCurrentWindow().loadSettings.windowState = JSON.stringify(windowState.serialize())
|
||||
@getCurrentWindow().loadSettings.windowState = JSON.stringify(windowState.serializeForPersistence())
|
||||
|
||||
# Public: Get the window state for the key path.
|
||||
getWindowState: (keyPath) ->
|
||||
@windowState ?= @loadWindowState()
|
||||
if keyPath
|
||||
@@ -313,23 +432,35 @@ class Atom
|
||||
else
|
||||
@windowState
|
||||
|
||||
# Private: Returns a replicated copy of the current state.
|
||||
replicate: ->
|
||||
@getWindowState().replicate()
|
||||
|
||||
# Private:
|
||||
crashMainProcess: ->
|
||||
remote.process.crash()
|
||||
|
||||
# Private:
|
||||
crashRenderProcess: ->
|
||||
process.crash()
|
||||
|
||||
# Public: Visually and audibly trigger a beep.
|
||||
beep: ->
|
||||
shell.beep() if @config.get('core.audioBeep')
|
||||
@rootView.trigger 'beep'
|
||||
@workspaceView.trigger 'beep'
|
||||
|
||||
# Private:
|
||||
requireUserInitScript: ->
|
||||
userInitScriptPath = path.join(@config.configDirPath, "user.coffee")
|
||||
try
|
||||
require userInitScriptPath if fs.isFileSync(userInitScriptPath)
|
||||
catch error
|
||||
console.error "Failed to load `#{userInitScriptPath}`", error.stack, error
|
||||
if userInitScriptPath = fs.resolve(@getConfigDirPath(), 'user', ['js', 'coffee'])
|
||||
try
|
||||
require userInitScriptPath
|
||||
catch error
|
||||
console.error "Failed to load `#{userInitScriptPath}`", error.stack, error
|
||||
|
||||
# Public: Require the module with the given globals.
|
||||
#
|
||||
# The globals will be set on the `window` object and removed after the
|
||||
# require completes.
|
||||
requireWithGlobals: (id, globals={}) ->
|
||||
existingGlobals = {}
|
||||
for key, value of globals
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
{$} = require './space-pen-extensions'
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
{specificity} = require 'clear-cut'
|
||||
PEG = require 'pegjs'
|
||||
|
||||
### Internal ###
|
||||
|
||||
module.exports =
|
||||
class BindingSet
|
||||
|
||||
@parser: null
|
||||
|
||||
selector: null
|
||||
commandsByKeystrokes: null
|
||||
parser: null
|
||||
name: null
|
||||
|
||||
constructor: (selector, commandsByKeystrokes, @index, @name) ->
|
||||
keystrokePattern = fs.readFileSync(require.resolve('./keystroke-pattern.pegjs'), 'utf8')
|
||||
BindingSet.parser ?= PEG.buildParser(keystrokePattern)
|
||||
@specificity = specificity(selector)
|
||||
@selector = selector.replace(/!important/g, '')
|
||||
@commandsByKeystrokes = @normalizeCommandsByKeystrokes(commandsByKeystrokes)
|
||||
|
||||
# Private:
|
||||
getName: ->
|
||||
@name
|
||||
|
||||
# Private:
|
||||
getSelector: ->
|
||||
@selector
|
||||
|
||||
# Private:
|
||||
getCommandsByKeystrokes: ->
|
||||
@commandsByKeystrokes
|
||||
|
||||
commandForEvent: (event) ->
|
||||
for keystrokes, command of @commandsByKeystrokes
|
||||
return command if event.keystrokes == keystrokes
|
||||
null
|
||||
|
||||
matchesKeystrokePrefix: (event) ->
|
||||
eventKeystrokes = event.keystrokes.split(' ')
|
||||
for keystrokes, command of @commandsByKeystrokes
|
||||
bindingKeystrokes = keystrokes.split(' ')
|
||||
continue unless eventKeystrokes.length < bindingKeystrokes.length
|
||||
return true if _.isEqual(eventKeystrokes, bindingKeystrokes[0...eventKeystrokes.length])
|
||||
false
|
||||
|
||||
normalizeCommandsByKeystrokes: (commandsByKeystrokes) ->
|
||||
normalizedCommandsByKeystrokes = {}
|
||||
for keystrokes, command of commandsByKeystrokes
|
||||
normalizedCommandsByKeystrokes[@normalizeKeystrokes(keystrokes)] = command
|
||||
normalizedCommandsByKeystrokes
|
||||
|
||||
normalizeKeystrokes: (keystrokes) ->
|
||||
normalizedKeystrokes = keystrokes.split(/\s+/).map (keystroke) =>
|
||||
@normalizeKeystroke(keystroke)
|
||||
normalizedKeystrokes.join(' ')
|
||||
|
||||
normalizeKeystroke: (keystroke) ->
|
||||
keys = BindingSet.parser.parse(keystroke)
|
||||
modifiers = keys[0...-1]
|
||||
modifiers.sort()
|
||||
[modifiers..., _.last(keys)].join('-')
|
||||
@@ -22,7 +22,7 @@ class ApplicationMenu
|
||||
# The Object which describes the menu to display.
|
||||
# * keystrokesByCommand:
|
||||
# An Object where the keys are commands and the values are Arrays containing
|
||||
# the keystrokes.
|
||||
# the keystroke.
|
||||
update: (template, keystrokesByCommand) ->
|
||||
@translateTemplate(template, keystrokesByCommand)
|
||||
@substituteVersion(template)
|
||||
@@ -97,14 +97,14 @@ class ApplicationMenu
|
||||
]
|
||||
]
|
||||
|
||||
# Private: Combines a menu template with the appropriate keystrokes.
|
||||
# Private: Combines a menu template with the appropriate keystroke.
|
||||
#
|
||||
# * template:
|
||||
# An Object conforming to atom-shell's menu api but lacking accelerator and
|
||||
# click properties.
|
||||
# * keystrokesByCommand:
|
||||
# An Object where the keys are commands and the values are Arrays containing
|
||||
# the keystrokes.
|
||||
# the keystroke.
|
||||
#
|
||||
# Returns a complete menu configuration object for atom-shell's menu API.
|
||||
translateTemplate: (template, keystrokesByCommand) ->
|
||||
@@ -123,21 +123,21 @@ class ApplicationMenu
|
||||
# The name of the command.
|
||||
# * keystrokesByCommand:
|
||||
# An Object where the keys are commands and the values are Arrays containing
|
||||
# the keystrokes.
|
||||
# the keystroke.
|
||||
#
|
||||
# Returns a String containing the keystroke in a format that can be interpreted
|
||||
# by atom shell to provide nice icons where available.
|
||||
acceleratorForCommand: (command, keystrokesByCommand) ->
|
||||
keystroke = keystrokesByCommand[command]?[0]
|
||||
return null unless keystroke
|
||||
firstKeystroke = keystrokesByCommand[command]?[0]
|
||||
return null unless firstKeystroke
|
||||
|
||||
modifiers = keystroke.split('-')
|
||||
modifiers = firstKeystroke.split('-')
|
||||
key = modifiers.pop()
|
||||
|
||||
modifiers.push("Shift") if key != key.toLowerCase()
|
||||
modifiers = modifiers.map (modifier) ->
|
||||
modifier.replace(/shift/ig, "Shift")
|
||||
.replace(/meta/ig, "Command")
|
||||
.replace(/cmd/ig, "Command")
|
||||
.replace(/ctrl/ig, "Ctrl")
|
||||
.replace(/alt/ig, "Alt")
|
||||
|
||||
|
||||
@@ -349,8 +349,8 @@ class AtomApplication
|
||||
# A Boolean which controls whether any newly opened windows should be in
|
||||
# dev mode or not.
|
||||
promptForPath: ({devMode}={}) ->
|
||||
pathsToOpen = dialog.showOpenDialog title: 'Open', properties: ['openFile', 'openDirectory', 'multiSelections', 'createDirectory']
|
||||
@openPaths({pathsToOpen, devMode})
|
||||
dialog.showOpenDialog title: 'Open', properties: ['openFile', 'openDirectory', 'multiSelections', 'createDirectory'], (pathsToOpen) =>
|
||||
@openPaths({pathsToOpen, devMode})
|
||||
|
||||
# Public: If an update is available, it returns the new version string
|
||||
# otherwise it returns null.
|
||||
|
||||
@@ -10,6 +10,8 @@ _ = require 'underscore-plus'
|
||||
# Private:
|
||||
module.exports =
|
||||
class AtomWindow
|
||||
@iconPath: path.resolve(__dirname, '..', '..', 'resources', 'atom.png')
|
||||
|
||||
browserWindow: null
|
||||
loaded: null
|
||||
isSpec: null
|
||||
@@ -19,7 +21,7 @@ class AtomWindow
|
||||
global.atomApplication.addWindow(this)
|
||||
|
||||
@setupNodePath(@resourcePath)
|
||||
@browserWindow = new BrowserWindow show: false, title: 'Atom'
|
||||
@browserWindow = new BrowserWindow show: false, title: 'Atom', icon: @constructor.iconPath
|
||||
@browserWindow.restart = _.wrap _.bind(@browserWindow.restart, @browserWindow), (restart) =>
|
||||
@setupNodePath(@resourcePath)
|
||||
restart()
|
||||
@@ -68,7 +70,7 @@ class AtomWindow
|
||||
chosen = dialog.showMessageBox @browserWindow,
|
||||
type: 'warning'
|
||||
buttons: ['Close', 'Keep Waiting']
|
||||
message: 'Editor is not responsing'
|
||||
message: 'Editor is not responding'
|
||||
detail: 'The editor is not responding. Would you like to force close it or just keep waiting?'
|
||||
@browserWindow.destroy() if chosen is 0
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
Menu = require 'menu'
|
||||
|
||||
# Private:
|
||||
module.exports =
|
||||
class ContextMenu
|
||||
constructor: (template, browserWindow) ->
|
||||
|
||||
@@ -62,14 +62,12 @@ delegate.browserMainParts.preMainMessageLoopRun = ->
|
||||
AtomApplication = require './atom-application'
|
||||
|
||||
AtomApplication.open(args)
|
||||
console.log("App load time: #{Date.now() - startTime}ms")
|
||||
console.log("App load time: #{Date.now() - startTime}ms") unless args.test
|
||||
|
||||
global.devResourcePath = path.join(app.getHomeDir(), 'github', 'atom')
|
||||
|
||||
setupCrashReporter = ->
|
||||
crashReporter.setCompanyName 'GitHub'
|
||||
crashReporter.setSubmissionUrl 'https://speakeasy.githubapp.com/submit_crash_log'
|
||||
crashReporter.setAutoSubmit true
|
||||
crashReporter.start(productName: 'Atom', companyName: 'GitHub')
|
||||
|
||||
setupAutoUpdater = ->
|
||||
autoUpdater.setFeedUrl 'https://speakeasy.githubapp.com/apps/27/appcast.xml'
|
||||
@@ -92,11 +90,11 @@ parseCommandLine = ->
|
||||
options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.')
|
||||
args = options.argv
|
||||
|
||||
if args.h
|
||||
if args.help
|
||||
process.stdout.write(options.help())
|
||||
process.exit(0)
|
||||
|
||||
if args.v
|
||||
if args.version
|
||||
process.stdout.write("#{version}\n")
|
||||
process.exit(0)
|
||||
|
||||
|
||||
@@ -47,3 +47,13 @@ module.exports =
|
||||
symlinkCommand(commandPath, destinationPath, installCallback)
|
||||
else
|
||||
installCallback(new Error("No destination directory exists to install"))
|
||||
|
||||
installAtomCommand: (callback) ->
|
||||
{resourcePath} = atom.getLoadSettings()
|
||||
commandPath = path.join(resourcePath, 'atom.sh')
|
||||
@install(commandPath, callback)
|
||||
|
||||
installApmCommand: (callback) ->
|
||||
{resourcePath} = atom.getLoadSettings()
|
||||
commandPath = path.join(resourcePath, 'node_modules', '.bin', 'apm')
|
||||
@install(commandPath, callback)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
module.exports =
|
||||
observeConfig: (keyPath, args...) ->
|
||||
@configSubscriptions ?= {}
|
||||
@configSubscriptions[keyPath] = config.observe(keyPath, args...)
|
||||
@configSubscriptions[keyPath] = atom.config.observe(keyPath, args...)
|
||||
|
||||
unobserveConfig: ->
|
||||
if @configSubscriptions?
|
||||
subscription.cancel() for keyPath, subscription of @configSubscriptions
|
||||
subscription.off() for keyPath, subscription of @configSubscriptions
|
||||
@configSubscriptions = null
|
||||
|
||||
+11
-19
@@ -33,23 +33,7 @@ class Config
|
||||
|
||||
# Private: Created during initialization, available as `global.config`
|
||||
constructor: ({@configDirPath, @resourcePath}={}) ->
|
||||
@bundledKeymapsDirPath = path.join(@resourcePath, "keymaps")
|
||||
@bundledMenusDirPath = path.join(resourcePath, "menus")
|
||||
@nodeModulesDirPath = path.join(@resourcePath, "node_modules")
|
||||
@bundledPackageDirPaths = [@nodeModulesDirPath]
|
||||
@lessSearchPaths = [
|
||||
path.join(@resourcePath, 'static', 'variables')
|
||||
path.join(@resourcePath, 'static')
|
||||
]
|
||||
@packageDirPaths = [path.join(@configDirPath, "packages")]
|
||||
if atom.getLoadSettings().devMode
|
||||
@packageDirPaths.unshift(path.join(@configDirPath, "dev", "packages"))
|
||||
@userPackageDirPaths = _.clone(@packageDirPaths)
|
||||
@userStoragePath = path.join(@configDirPath, "storage")
|
||||
|
||||
@defaultSettings =
|
||||
core: _.clone(require('./root-view').configDefaults)
|
||||
editor: _.clone(require('./editor').configDefaults)
|
||||
@defaultSettings = {}
|
||||
@settings = {}
|
||||
@configFilePath = fs.resolve(@configDirPath, 'config', ['json', 'cson'])
|
||||
@configFilePath ?= path.join(@configDirPath, 'config.cson')
|
||||
@@ -163,6 +147,15 @@ class Config
|
||||
@update()
|
||||
value
|
||||
|
||||
# Public: Toggle the value at the key path.
|
||||
#
|
||||
# The new value will be `true` if the value is currently falsy and will be
|
||||
# `false` if the value is currently truthy.
|
||||
#
|
||||
# Returns the new value.
|
||||
toggle: (keyPath) ->
|
||||
@set(keyPath, !@get(keyPath))
|
||||
|
||||
# Public: Push the value to the array at the key path.
|
||||
#
|
||||
# keyPath - The {String} key path.
|
||||
@@ -223,8 +216,7 @@ class Config
|
||||
callback(value, {previous})
|
||||
|
||||
eventName = "updated.#{keyPath.replace(/\./, '-')}"
|
||||
subscription = { cancel: => @off eventName, updateCallback }
|
||||
@on eventName, updateCallback
|
||||
subscription = @on eventName, updateCallback
|
||||
callback(value) if options.callNow ? true
|
||||
subscription
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ class ContextMenuManager
|
||||
@devModeDefinitions = {}
|
||||
@activeElement = null
|
||||
|
||||
@devModeDefinitions['#root-view'] = [
|
||||
@devModeDefinitions['.workspace'] = [
|
||||
label: 'Inspect Element'
|
||||
command: 'application:inspect'
|
||||
executeAtBuild: (e) ->
|
||||
|
||||
@@ -9,14 +9,14 @@ class CursorView extends View
|
||||
@div class: 'cursor idle', => @raw ' '
|
||||
|
||||
blinkPeriod: 800
|
||||
editor: null
|
||||
editorView: null
|
||||
visible: true
|
||||
|
||||
needsUpdate: true
|
||||
needsRemoval: false
|
||||
shouldPauseBlinking: false
|
||||
|
||||
initialize: (@cursor, @editor) ->
|
||||
initialize: (@cursor, @editorView) ->
|
||||
@cursor.on 'moved.cursor-view', =>
|
||||
@needsUpdate = true
|
||||
@shouldPauseBlinking = true
|
||||
@@ -25,7 +25,7 @@ class CursorView extends View
|
||||
@needsUpdate = true
|
||||
|
||||
@cursor.on 'autoscrolled.cursor-view', =>
|
||||
@editor.requestDisplayUpdate()
|
||||
@editorView.requestDisplayUpdate()
|
||||
|
||||
@cursor.on 'destroyed.cursor-view', =>
|
||||
@needsRemoval = true
|
||||
@@ -34,7 +34,7 @@ class CursorView extends View
|
||||
@addClass("site-#{@cursor.marker.getOriginSiteId()}")
|
||||
|
||||
beforeRemove: ->
|
||||
@editor.removeCursorView(this)
|
||||
@editorView.removeCursorView(this)
|
||||
@cursor.off('.cursor-view')
|
||||
@stopBlinking()
|
||||
|
||||
@@ -52,7 +52,7 @@ class CursorView extends View
|
||||
else if !@startBlinkingTimeout
|
||||
@startBlinking()
|
||||
|
||||
@setVisible(@cursor.isVisible() and not @editor.isFoldedAtScreenRow(screenPosition.row))
|
||||
@setVisible(@cursor.isVisible() and not @editorView.isFoldedAtScreenRow(screenPosition.row))
|
||||
|
||||
# Override for speed. The base function checks the computedStyle
|
||||
isHidden: ->
|
||||
@@ -69,7 +69,7 @@ class CursorView extends View
|
||||
@cursor.clearAutoscroll()
|
||||
|
||||
getPixelPosition: ->
|
||||
@editor.pixelPositionForScreenPosition(@getScreenPosition())
|
||||
@editorView.pixelPositionForScreenPosition(@getScreenPosition())
|
||||
|
||||
setVisible: (visible) ->
|
||||
unless @visible == visible
|
||||
|
||||
+46
-34
@@ -5,7 +5,7 @@ _ = require 'underscore-plus'
|
||||
# Public: The `Cursor` class represents the little blinking line identifying
|
||||
# where text can be inserted.
|
||||
#
|
||||
# Cursors belong to {EditSession}s and have some metadata attached in the form
|
||||
# Cursors belong to {Editor}s and have some metadata attached in the form
|
||||
# of a {StringMarker}.
|
||||
module.exports =
|
||||
class Cursor
|
||||
@@ -17,8 +17,8 @@ class Cursor
|
||||
visible: true
|
||||
needsAutoscroll: null
|
||||
|
||||
# Private: Instantiated by an {EditSession}
|
||||
constructor: ({@editSession, @marker}) ->
|
||||
# Private: Instantiated by an {Editor}
|
||||
constructor: ({@editor, @marker}) ->
|
||||
@updateVisibility()
|
||||
@marker.on 'changed', (e) =>
|
||||
@updateVisibility()
|
||||
@@ -37,10 +37,10 @@ class Cursor
|
||||
textChanged: textChanged
|
||||
|
||||
@emit 'moved', movedEvent
|
||||
@editSession.emit 'cursor-moved', movedEvent
|
||||
@editor.emit 'cursor-moved', movedEvent
|
||||
@marker.on 'destroyed', =>
|
||||
@destroyed = true
|
||||
@editSession.removeCursor(this)
|
||||
@editor.removeCursor(this)
|
||||
@emit 'destroyed'
|
||||
@needsAutoscroll = true
|
||||
|
||||
@@ -62,7 +62,7 @@ class Cursor
|
||||
# An {Array} of two numbers: the screen row, and the screen column.
|
||||
# * options:
|
||||
# + autoscroll:
|
||||
# A Boolean which, if `true`, scrolls the {EditSession} to wherever the
|
||||
# A Boolean which, if `true`, scrolls the {Editor} to wherever the
|
||||
# cursor moves to.
|
||||
setScreenPosition: (screenPosition, options={}) ->
|
||||
@changePosition options, =>
|
||||
@@ -78,7 +78,7 @@ class Cursor
|
||||
# An {Array} of two numbers: the buffer row, and the buffer column.
|
||||
# * options:
|
||||
# + autoscroll:
|
||||
# A Boolean which, if `true`, scrolls the {EditSession} to wherever the
|
||||
# A Boolean which, if `true`, scrolls the {Editor} to wherever the
|
||||
# cursor moves to.
|
||||
setBufferPosition: (bufferPosition, options={}) ->
|
||||
@changePosition options, =>
|
||||
@@ -111,20 +111,20 @@ class Cursor
|
||||
# Returns a RegExp.
|
||||
wordRegExp: ({includeNonWordCharacters}={})->
|
||||
includeNonWordCharacters ?= true
|
||||
nonWordCharacters = config.get('editor.nonWordCharacters')
|
||||
nonWordCharacters = atom.config.get('editor.nonWordCharacters')
|
||||
segments = ["^[\t ]*$"]
|
||||
segments.push("[^\\s#{_.escapeRegExp(nonWordCharacters)}]+")
|
||||
if includeNonWordCharacters
|
||||
segments.push("[#{_.escapeRegExp(nonWordCharacters)}]+")
|
||||
new RegExp(segments.join("|"), "g")
|
||||
|
||||
# Public: Identifies if this cursor is the last in the {EditSession}.
|
||||
# Public: Identifies if this cursor is the last in the {Editor}.
|
||||
#
|
||||
# "Last" is defined as the most recently added cursor.
|
||||
#
|
||||
# Returns a Boolean.
|
||||
isLastCursor: ->
|
||||
this == @editSession.getCursor()
|
||||
this == @editor.getCursor()
|
||||
|
||||
# Public: Identifies if the cursor is surrounded by whitespace.
|
||||
#
|
||||
@@ -135,7 +135,7 @@ class Cursor
|
||||
isSurroundedByWhitespace: ->
|
||||
{row, column} = @getBufferPosition()
|
||||
range = [[row, Math.min(0, column - 1)], [row, Math.max(0, column + 1)]]
|
||||
/^\s+$/.test @editSession.getTextInBufferRange(range)
|
||||
/^\s+$/.test @editor.getTextInBufferRange(range)
|
||||
|
||||
# Public: Returns whether the cursor is currently between a word and non-word
|
||||
# character. The non-word characters are defined by the
|
||||
@@ -150,17 +150,17 @@ class Cursor
|
||||
|
||||
{row, column} = @getBufferPosition()
|
||||
range = [[row, column - 1], [row, column + 1]]
|
||||
[before, after] = @editSession.getTextInBufferRange(range)
|
||||
[before, after] = @editor.getTextInBufferRange(range)
|
||||
return false if /\s/.test(before) or /\s/.test(after)
|
||||
|
||||
nonWordCharacters = config.get('editor.nonWordCharacters').split('')
|
||||
nonWordCharacters = atom.config.get('editor.nonWordCharacters').split('')
|
||||
_.contains(nonWordCharacters, before) isnt _.contains(nonWordCharacters, after)
|
||||
|
||||
# Public: Returns whether this cursor is between a word's start and end.
|
||||
isInsideWord: ->
|
||||
{row, column} = @getBufferPosition()
|
||||
range = [[row, column], [row, Infinity]]
|
||||
@editSession.getTextInBufferRange(range).search(@wordRegExp()) == 0
|
||||
@editor.getTextInBufferRange(range).search(@wordRegExp()) == 0
|
||||
|
||||
# Public: Prevents this cursor from causing scrolling.
|
||||
clearAutoscroll: ->
|
||||
@@ -189,7 +189,7 @@ class Cursor
|
||||
# Public: Returns the cursor's current buffer row of text excluding its line
|
||||
# ending.
|
||||
getCurrentBufferLine: ->
|
||||
@editSession.lineForBufferRow(@getBufferRow())
|
||||
@editor.lineForBufferRow(@getBufferRow())
|
||||
|
||||
# Public: Moves the cursor up one screen row.
|
||||
moveUp: (rowCount = 1, {moveToEndOfSelection}={}) ->
|
||||
@@ -248,7 +248,7 @@ class Cursor
|
||||
|
||||
# Public: Moves the cursor to the bottom of the buffer.
|
||||
moveToBottom: ->
|
||||
@setBufferPosition(@editSession.getEofBufferPosition())
|
||||
@setBufferPosition(@editor.getEofBufferPosition())
|
||||
|
||||
# Public: Moves the cursor to the beginning of the screen line.
|
||||
moveToBeginningOfLine: ->
|
||||
@@ -258,7 +258,7 @@ class Cursor
|
||||
# line.
|
||||
moveToFirstCharacterOfLine: ->
|
||||
{row, column} = @getScreenPosition()
|
||||
screenline = @editSession.lineForScreenRow(row)
|
||||
screenline = @editor.lineForScreenRow(row)
|
||||
|
||||
goalColumn = screenline.text.search(/\S/)
|
||||
return if goalColumn == -1
|
||||
@@ -272,7 +272,7 @@ class Cursor
|
||||
position = @getBufferPosition()
|
||||
scanRange = @getCurrentLineBufferRange()
|
||||
endOfLeadingWhitespace = null
|
||||
@editSession.scanInBufferRange /^[ \t]*/, scanRange, ({range}) =>
|
||||
@editor.scanInBufferRange /^[ \t]*/, scanRange, ({range}) =>
|
||||
endOfLeadingWhitespace = range.end
|
||||
|
||||
@setBufferPosition(endOfLeadingWhitespace) if endOfLeadingWhitespace.isGreaterThan(position)
|
||||
@@ -318,11 +318,11 @@ class Cursor
|
||||
getBeginningOfCurrentWordBufferPosition: (options = {}) ->
|
||||
allowPrevious = options.allowPrevious ? true
|
||||
currentBufferPosition = @getBufferPosition()
|
||||
previousNonBlankRow = @editSession.buffer.previousNonBlankRow(currentBufferPosition.row)
|
||||
previousNonBlankRow = @editor.buffer.previousNonBlankRow(currentBufferPosition.row)
|
||||
scanRange = [[previousNonBlankRow, 0], currentBufferPosition]
|
||||
|
||||
beginningOfWordPosition = null
|
||||
@editSession.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) =>
|
||||
@editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) =>
|
||||
if range.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious
|
||||
beginningOfWordPosition = range.start
|
||||
if not beginningOfWordPosition?.isEqual(currentBufferPosition)
|
||||
@@ -334,11 +334,11 @@ class Cursor
|
||||
# the current word, or the previous word.
|
||||
getPreviousWordBoundaryBufferPosition: (options = {}) ->
|
||||
currentBufferPosition = @getBufferPosition()
|
||||
previousNonBlankRow = @editSession.buffer.previousNonBlankRow(currentBufferPosition.row)
|
||||
previousNonBlankRow = @editor.buffer.previousNonBlankRow(currentBufferPosition.row)
|
||||
scanRange = [[previousNonBlankRow, 0], currentBufferPosition]
|
||||
|
||||
beginningOfWordPosition = null
|
||||
@editSession.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
|
||||
@editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
|
||||
if range.start.row < currentBufferPosition.row and currentBufferPosition.column > 0
|
||||
# force it to stop at the beginning of each line
|
||||
beginningOfWordPosition = new Point(currentBufferPosition.row, 0)
|
||||
@@ -356,10 +356,10 @@ class Cursor
|
||||
# the current word, or the previous word.
|
||||
getMoveNextWordBoundaryBufferPosition: (options = {}) ->
|
||||
currentBufferPosition = @getBufferPosition()
|
||||
scanRange = [currentBufferPosition, @editSession.getEofBufferPosition()]
|
||||
scanRange = [currentBufferPosition, @editor.getEofBufferPosition()]
|
||||
|
||||
endOfWordPosition = null
|
||||
@editSession.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
|
||||
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
|
||||
if range.start.row > currentBufferPosition.row
|
||||
# force it to stop at the beginning of each line
|
||||
endOfWordPosition = new Point(range.start.row, 0)
|
||||
@@ -386,10 +386,10 @@ class Cursor
|
||||
getEndOfCurrentWordBufferPosition: (options = {}) ->
|
||||
allowNext = options.allowNext ? true
|
||||
currentBufferPosition = @getBufferPosition()
|
||||
scanRange = [currentBufferPosition, @editSession.getEofBufferPosition()]
|
||||
scanRange = [currentBufferPosition, @editor.getEofBufferPosition()]
|
||||
|
||||
endOfWordPosition = null
|
||||
@editSession.scanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) =>
|
||||
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) =>
|
||||
if range.start.isLessThanOrEqual(currentBufferPosition) or allowNext
|
||||
endOfWordPosition = range.end
|
||||
if not endOfWordPosition?.isEqual(currentBufferPosition)
|
||||
@@ -407,10 +407,10 @@ class Cursor
|
||||
getBeginningOfNextWordBufferPosition: (options = {}) ->
|
||||
currentBufferPosition = @getBufferPosition()
|
||||
start = if @isInsideWord() then @getEndOfCurrentWordBufferPosition() else currentBufferPosition
|
||||
scanRange = [start, @editSession.getEofBufferPosition()]
|
||||
scanRange = [start, @editor.getEofBufferPosition()]
|
||||
|
||||
beginningOfNextWordPosition = null
|
||||
@editSession.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
|
||||
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
|
||||
beginningOfNextWordPosition = range.start
|
||||
stop()
|
||||
|
||||
@@ -432,7 +432,7 @@ class Cursor
|
||||
# + includeNewline:
|
||||
# A boolean which controls whether the Range should include the newline.
|
||||
getCurrentLineBufferRange: (options) ->
|
||||
@editSession.bufferRangeForBufferRow(@getBufferRow(), options)
|
||||
@editor.bufferRangeForBufferRow(@getBufferRow(), options)
|
||||
|
||||
# Public: Retrieves the range for the current paragraph.
|
||||
#
|
||||
@@ -440,11 +440,11 @@ class Cursor
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getCurrentParagraphBufferRange: ->
|
||||
@editSession.languageMode.rowRangeForParagraphAtBufferRow(@getBufferRow())
|
||||
@editor.languageMode.rowRangeForParagraphAtBufferRow(@getBufferRow())
|
||||
|
||||
# Public: Returns the characters preceeding the cursor in the current word.
|
||||
getCurrentWordPrefix: ->
|
||||
@editSession.getTextInBufferRange([@getBeginningOfCurrentWordBufferPosition(), @getBufferPosition()])
|
||||
@editor.getTextInBufferRange([@getBeginningOfCurrentWordBufferPosition(), @getBufferPosition()])
|
||||
|
||||
# Public: Returns whether the cursor is at the start of a line.
|
||||
isAtBeginningOfLine: ->
|
||||
@@ -452,8 +452,8 @@ class Cursor
|
||||
|
||||
# Public: Returns the indentation level of the current line.
|
||||
getIndentLevel: ->
|
||||
if @editSession.getSoftTabs()
|
||||
@getBufferColumn() / @editSession.getTabLength()
|
||||
if @editor.getSoftTabs()
|
||||
@getBufferColumn() / @editor.getTabLength()
|
||||
else
|
||||
@getBufferColumn()
|
||||
|
||||
@@ -465,4 +465,16 @@ class Cursor
|
||||
#
|
||||
# Returns an {Array} of {String}s.
|
||||
getScopes: ->
|
||||
@editSession.scopesForBufferPosition(@getBufferPosition())
|
||||
@editor.scopesForBufferPosition(@getBufferPosition())
|
||||
|
||||
# Public: Returns true if this cursor has no non-whitespace characters before
|
||||
# its current position.
|
||||
hasPrecedingCharactersOnLine: ->
|
||||
bufferPosition = @getBufferPosition()
|
||||
line = @editor.lineForBufferRow(bufferPosition.row)
|
||||
firstCharacterColumn = line.search(/\S/)
|
||||
|
||||
if firstCharacterColumn is -1
|
||||
false
|
||||
else
|
||||
bufferPosition.column > firstCharacterColumn
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
{Document} = require 'telepath'
|
||||
|
||||
# Public: Manages the deserializers used for serialized state
|
||||
#
|
||||
# Should be accessed via `atom.deserializers`
|
||||
module.exports =
|
||||
class DeserializerManager
|
||||
constructor: ->
|
||||
@deserializers = {}
|
||||
@deferredDeserializers = {}
|
||||
|
||||
# Public: Add a deserializer.
|
||||
# Public: Register the given class(es) as deserializers.
|
||||
add: (klasses...) ->
|
||||
@deserializers[klass.name] = klass for klass in klasses
|
||||
|
||||
# Public: Add a deferred deserializer.
|
||||
# Public: Add a deferred deserializer for the given class name.
|
||||
addDeferred: (name, fn) ->
|
||||
@deferredDeserializers[name] = fn
|
||||
|
||||
# Public: Remove a deserializer.
|
||||
# Public: Remove the given class(es) as deserializers.
|
||||
remove: (klasses...) ->
|
||||
delete @deserializers[klass.name] for klass in klasses
|
||||
|
||||
@@ -32,7 +34,7 @@ class DeserializerManager
|
||||
else
|
||||
console.warn "No deserializer found for", state
|
||||
|
||||
# Public: Get the deserializer for the state.
|
||||
# Private: Get the deserializer for the state.
|
||||
get: (state) ->
|
||||
return unless state?
|
||||
|
||||
|
||||
+10
-14
@@ -18,7 +18,7 @@ class DisplayBuffer
|
||||
_.extend @prototype, ConfigObserver
|
||||
|
||||
@acceptsDocuments: true
|
||||
registerDeserializer(this)
|
||||
atom.deserializers.add(this)
|
||||
@version: 2
|
||||
|
||||
@deserialize: (state) -> new this(state)
|
||||
@@ -27,18 +27,18 @@ class DisplayBuffer
|
||||
if optionsOrState instanceof telepath.Document
|
||||
@state = optionsOrState
|
||||
@id = @state.get('id')
|
||||
@tokenizedBuffer = deserialize(@state.get('tokenizedBuffer'))
|
||||
@tokenizedBuffer = atom.deserializers.deserialize(@state.get('tokenizedBuffer'))
|
||||
@buffer = @tokenizedBuffer.buffer
|
||||
else
|
||||
{@buffer, softWrap, editorWidthInChars} = optionsOrState
|
||||
@id = guid.create().toString()
|
||||
@tokenizedBuffer = new TokenizedBuffer(optionsOrState)
|
||||
@state = site.createDocument
|
||||
@state = atom.site.createDocument
|
||||
deserializer: @constructor.name
|
||||
version: @constructor.version
|
||||
id: @id
|
||||
tokenizedBuffer: @tokenizedBuffer.getState()
|
||||
softWrap: softWrap ? config.get('editor.softWrap') ? false
|
||||
softWrap: softWrap ? atom.config.get('editor.softWrap') ? false
|
||||
editorWidthInChars: editorWidthInChars
|
||||
|
||||
@markers = {}
|
||||
@@ -56,7 +56,7 @@ class DisplayBuffer
|
||||
@updateWrappedScreenLines()
|
||||
|
||||
@observeConfig 'editor.preferredLineLength', callNow: false, =>
|
||||
@updateWrappedScreenLines() if @getSoftWrap() and config.get('editor.softWrapAtPreferredLineLength')
|
||||
@updateWrappedScreenLines() if @getSoftWrap() and atom.config.get('editor.softWrapAtPreferredLineLength')
|
||||
|
||||
@observeConfig 'editor.softWrapAtPreferredLineLength', callNow: false, =>
|
||||
@updateWrappedScreenLines() if @getSoftWrap()
|
||||
@@ -113,8 +113,8 @@ class DisplayBuffer
|
||||
|
||||
getSoftWrapColumn: ->
|
||||
editorWidthInChars = @state.get('editorWidthInChars')
|
||||
if config.get('editor.softWrapAtPreferredLineLength')
|
||||
Math.min(editorWidthInChars, config.getPositiveInt('editor.preferredLineLength', editorWidthInChars))
|
||||
if atom.config.get('editor.softWrapAtPreferredLineLength')
|
||||
Math.min(editorWidthInChars, atom.config.getPositiveInt('editor.preferredLineLength', editorWidthInChars))
|
||||
else
|
||||
editorWidthInChars
|
||||
|
||||
@@ -365,7 +365,9 @@ class DisplayBuffer
|
||||
setTabLength: (tabLength) ->
|
||||
@tokenizedBuffer.setTabLength(tabLength)
|
||||
|
||||
# Retrieves the grammar for the buffer.
|
||||
# Get the grammar for this buffer.
|
||||
#
|
||||
# Returns the current {TextMateGrammar} or the {NullGrammar}.
|
||||
getGrammar: ->
|
||||
@tokenizedBuffer.grammar
|
||||
|
||||
@@ -582,12 +584,6 @@ class DisplayBuffer
|
||||
line = @lineForRow(row).text
|
||||
console.log row, line, line.length
|
||||
|
||||
getDebugSnapshot: ->
|
||||
lines = ["Display Buffer:"]
|
||||
for screenLine, row in @linesForRows(0, @getLastRow())
|
||||
lines.push "#{row}: #{screenLine.text}"
|
||||
lines.join('\n')
|
||||
|
||||
### Internal ###
|
||||
|
||||
handleTokenizedBufferChange: (tokenizedBufferChange) =>
|
||||
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+1311
-1734
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+3
-1
@@ -58,7 +58,7 @@ class File
|
||||
previouslyExisted = @exists()
|
||||
@cachedContents = text
|
||||
fs.writeFileSync(@getPath(), text)
|
||||
@subscribeToNativeChangeEvents() if not previouslyExisted and @subscriptionCount() > 0
|
||||
@subscribeToNativeChangeEvents() if not previouslyExisted and @hasSubscriptions()
|
||||
|
||||
# Private: Deprecated
|
||||
readSync: (flushCache) ->
|
||||
@@ -112,9 +112,11 @@ class File
|
||||
exists: ->
|
||||
fs.existsSync(@getPath())
|
||||
|
||||
# Private:
|
||||
setDigest: (contents) ->
|
||||
@digest = crypto.createHash('sha1').update(contents ? '').digest('hex')
|
||||
|
||||
# Public: Get the SHA-1 digest of this file
|
||||
getDigest: ->
|
||||
@digest ? @setDigest(@readSync())
|
||||
|
||||
|
||||
+7
-7
@@ -12,7 +12,7 @@ GitUtils = require 'git-utils'
|
||||
# ## Example
|
||||
#
|
||||
# ```coffeescript
|
||||
# git = global.project.getRepo()
|
||||
# git = atom.project.getRepo()
|
||||
# console.log git.getOriginUrl()
|
||||
# ```
|
||||
module.exports =
|
||||
@@ -61,7 +61,7 @@ class Git
|
||||
|
||||
@statuses = {}
|
||||
@upstream = {ahead: 0, behind: 0}
|
||||
{project, refreshOnWindowFocus} = options
|
||||
{@project, refreshOnWindowFocus} = options
|
||||
|
||||
refreshOnWindowFocus ?= true
|
||||
if refreshOnWindowFocus
|
||||
@@ -70,9 +70,8 @@ class Git
|
||||
@refreshIndex()
|
||||
@refreshStatus()
|
||||
|
||||
if project?
|
||||
@subscribeToBuffer(buffer) for buffer in project.getBuffers()
|
||||
@subscribe project, 'buffer-created', (buffer) => @subscribeToBuffer(buffer)
|
||||
if @project?
|
||||
@subscribe @project.buffers.onEach (buffer) => @subscribeToBuffer(buffer)
|
||||
|
||||
# Private: Subscribes to buffer events.
|
||||
subscribeToBuffer: (buffer) ->
|
||||
@@ -146,7 +145,7 @@ class Git
|
||||
# Public: Returns true if at the root, false if in a subfolder of the
|
||||
# repository.
|
||||
isProjectAtRoot: ->
|
||||
@projectAtRoot ?= project.relativize(@getWorkingDirectory()) is ''
|
||||
@projectAtRoot ?= @project?.relativize(@getWorkingDirectory()) is ''
|
||||
|
||||
# Public: Makes a path relative to the repository's working directory.
|
||||
relativize: (path) -> @getRepo().relativize(path)
|
||||
@@ -216,7 +215,8 @@ class Git
|
||||
#
|
||||
# Returns a Number representing the status.
|
||||
getDirectoryStatus: (directoryPath) ->
|
||||
directoryPath = "#{directoryPath}/"
|
||||
{sep} = require 'path'
|
||||
directoryPath = "#{directoryPath}#{sep}"
|
||||
directoryStatus = 0
|
||||
for path, status of @statuses
|
||||
directoryStatus |= status if path.indexOf(directoryPath) is 0
|
||||
|
||||
+22
-22
@@ -2,7 +2,7 @@
|
||||
{Range} = require 'telepath'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
# Private: Represents the portion of the {Editor} containing row numbers.
|
||||
# Private: Represents the portion of the {EditorView} containing row numbers.
|
||||
#
|
||||
# The gutter also indicates if rows are folded.
|
||||
module.exports =
|
||||
@@ -25,37 +25,37 @@ class Gutter extends View
|
||||
@attached = true
|
||||
|
||||
highlightLines = => @highlightLines()
|
||||
@getEditor().on 'cursor:moved', highlightLines
|
||||
@getEditor().on 'selection:changed', highlightLines
|
||||
@getEditorView().on 'cursor:moved', highlightLines
|
||||
@getEditorView().on 'selection:changed', highlightLines
|
||||
@on 'mousedown', (e) => @handleMouseEvents(e)
|
||||
|
||||
beforeRemove: ->
|
||||
$(document).off(".gutter-#{@getEditor().id}")
|
||||
$(document).off(".gutter-#{@getEditorView().id}")
|
||||
|
||||
handleMouseEvents: (e) ->
|
||||
editor = @getEditor()
|
||||
startRow = editor.screenPositionFromMouseEvent(e).row
|
||||
editorView = @getEditorView()
|
||||
startRow = editorView.screenPositionFromMouseEvent(e).row
|
||||
if e.shiftKey
|
||||
editor.selectToScreenPosition([startRow + 1, 0])
|
||||
editorView.selectToScreenPosition([startRow + 1, 0])
|
||||
return
|
||||
else
|
||||
editor.getSelection().setScreenRange([[startRow, 0], [startRow, 0]])
|
||||
editorView.getSelection().setScreenRange([[startRow, 0], [startRow, 0]])
|
||||
|
||||
moveHandler = (e) =>
|
||||
start = startRow
|
||||
end = editor.screenPositionFromMouseEvent(e).row
|
||||
end = editorView.screenPositionFromMouseEvent(e).row
|
||||
if end > start then end++ else start++
|
||||
editor.getSelection().setScreenRange([[start, 0], [end, 0]])
|
||||
editorView.getSelection().setScreenRange([[start, 0], [end, 0]])
|
||||
|
||||
$(document).on "mousemove.gutter-#{@getEditor().id}", moveHandler
|
||||
$(document).one "mouseup.gutter-#{@getEditor().id}", => $(document).off 'mousemove', moveHandler
|
||||
$(document).on "mousemove.gutter-#{@getEditorView().id}", moveHandler
|
||||
$(document).one "mouseup.gutter-#{@getEditorView().id}", => $(document).off 'mousemove', moveHandler
|
||||
|
||||
### Public ###
|
||||
|
||||
# Retrieves the containing {Editor}.
|
||||
# Retrieves the containing {EditorView}.
|
||||
#
|
||||
# Returns an {Editor}.
|
||||
getEditor: ->
|
||||
# Returns an {EditorView}.
|
||||
getEditorView: ->
|
||||
@parentView
|
||||
|
||||
# Defines whether to show the gutter or not.
|
||||
@@ -192,9 +192,9 @@ class Gutter extends View
|
||||
@elementBuilder.children
|
||||
|
||||
buildLineElementsHtml: (startScreenRow, endScreenRow) =>
|
||||
editor = @getEditor()
|
||||
maxDigits = editor.getLineCount().toString().length
|
||||
rows = editor.bufferRowsForScreenRows(startScreenRow, endScreenRow)
|
||||
editorView = @getEditorView()
|
||||
maxDigits = editorView.getLineCount().toString().length
|
||||
rows = editorView.bufferRowsForScreenRows(startScreenRow, endScreenRow)
|
||||
|
||||
html = ''
|
||||
for row in rows
|
||||
@@ -204,7 +204,7 @@ class Gutter extends View
|
||||
rowValue = (row + 1).toString()
|
||||
|
||||
classes = "line-number line-number-#{row}"
|
||||
classes += ' fold' if editor.isFoldedAtBufferRow(row)
|
||||
classes += ' fold' if editorView.isFoldedAtBufferRow(row)
|
||||
|
||||
rowValuePadding = _.multiplyString(' ', maxDigits - rowValue.length)
|
||||
|
||||
@@ -230,8 +230,8 @@ class Gutter extends View
|
||||
@highlightedLineNumbers.push(highlightedLineNumber)
|
||||
|
||||
highlightLines: ->
|
||||
if @getEditor().getSelection().isEmpty()
|
||||
row = @getEditor().getCursorScreenPosition().row
|
||||
if @getEditorView().getSelection().isEmpty()
|
||||
row = @getEditorView().getCursorScreenPosition().row
|
||||
rowRange = new Range([row, 0], [row, 0])
|
||||
return if @selectionEmpty and @highlightedRows?.isEqual(rowRange)
|
||||
|
||||
@@ -240,7 +240,7 @@ class Gutter extends View
|
||||
@highlightedRows = rowRange
|
||||
@selectionEmpty = true
|
||||
else
|
||||
selectedRows = @getEditor().getSelection().getScreenRange()
|
||||
selectedRows = @getEditorView().getSelection().getScreenRange()
|
||||
endRow = selectedRows.end.row
|
||||
endRow-- if selectedRows.end.column is 0
|
||||
selectedRows = new Range([selectedRows.start.row, 0], [endRow, 0])
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
{$} = require './space-pen-extensions'
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
{specificity} = require 'clear-cut'
|
||||
PEG = require 'pegjs'
|
||||
|
||||
### Internal ###
|
||||
|
||||
module.exports =
|
||||
class KeyBinding
|
||||
@parser: null
|
||||
@currentIndex: 1
|
||||
|
||||
@normalizeKeystroke: (keystroke) ->
|
||||
normalizedKeystroke = keystroke.split(/\s+/).map (keystroke) =>
|
||||
keys = @getParser().parse(keystroke)
|
||||
modifiers = keys[0...-1]
|
||||
modifiers.sort()
|
||||
[modifiers..., _.last(keys)].join('-')
|
||||
normalizedKeystroke.join(' ')
|
||||
|
||||
@getParser: ->
|
||||
if not KeyBinding.parser
|
||||
keystrokePattern = fs.readFileSync(require.resolve('./keystroke-pattern.pegjs'), 'utf8')
|
||||
KeyBinding.parser = PEG.buildParser(keystrokePattern)
|
||||
|
||||
KeyBinding.parser
|
||||
|
||||
constructor: (source, command, keystroke, selector) ->
|
||||
@source = source
|
||||
@command = command
|
||||
@keystroke = KeyBinding.normalizeKeystroke(keystroke)
|
||||
@selector = selector.replace(/!important/g, '')
|
||||
@specificity = specificity(selector)
|
||||
@index = KeyBinding.currentIndex++
|
||||
|
||||
matches: (keystroke) ->
|
||||
multiKeystroke = /\s/.test keystroke
|
||||
if multiKeystroke
|
||||
keystroke == @keystroke
|
||||
else
|
||||
keystroke.split(' ')[0] == @keystroke.split(' ')[0]
|
||||
|
||||
compare: (keyBinding) ->
|
||||
if keyBinding.specificity == @specificity
|
||||
keyBinding.index - @index
|
||||
else
|
||||
keyBinding.specificity - @specificity
|
||||
+120
-165
@@ -3,10 +3,10 @@ _ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
CSON = require 'season'
|
||||
BindingSet = require './binding-set'
|
||||
KeyBinding = require './key-binding'
|
||||
{Emitter} = require 'emissary'
|
||||
|
||||
Modifiers = ['alt', 'control', 'ctrl', 'shift', 'meta']
|
||||
Modifiers = ['alt', 'control', 'ctrl', 'shift', 'cmd']
|
||||
|
||||
# Internal: Associates keymaps with actions.
|
||||
#
|
||||
@@ -25,166 +25,63 @@ module.exports =
|
||||
class Keymap
|
||||
Emitter.includeInto(this)
|
||||
|
||||
bindingSets: null
|
||||
nextBindingSetIndex: 0
|
||||
bindingSetsByFirstKeystroke: null
|
||||
queuedKeystrokes: null
|
||||
constructor: ({@resourcePath, @configDirPath})->
|
||||
@keyBindings = []
|
||||
|
||||
constructor: ->
|
||||
@bindingSets = []
|
||||
@bindingSetsByFirstKeystroke = {}
|
||||
# Public: Returns an array of all {KeyBinding}s.
|
||||
getKeyBindings: ->
|
||||
_.clone(@keyBindings)
|
||||
|
||||
loadBundledKeymaps: ->
|
||||
@loadDirectory(config.bundledKeymapsDirPath)
|
||||
@emit('bundled-keymaps-loaded')
|
||||
|
||||
loadUserKeymap: ->
|
||||
userKeymapPath = CSON.resolve(path.join(config.configDirPath, 'keymap'))
|
||||
@load(userKeymapPath) if userKeymapPath
|
||||
|
||||
loadDirectory: (directoryPath) ->
|
||||
@load(filePath) for filePath in fs.listSync(directoryPath, ['.cson', '.json'])
|
||||
|
||||
load: (path) ->
|
||||
@add(path, CSON.readFileSync(path))
|
||||
|
||||
add: (args...) ->
|
||||
name = args.shift() if args.length > 1
|
||||
keymap = args.shift()
|
||||
for selector, bindings of keymap
|
||||
@bindKeys(name, selector, bindings)
|
||||
|
||||
remove: (name) ->
|
||||
for bindingSet in @bindingSets.filter((bindingSet) -> bindingSet.name is name)
|
||||
_.remove(@bindingSets, bindingSet)
|
||||
for keystrokes of bindingSet.commandsByKeystrokes
|
||||
keystroke = keystrokes.split(' ')[0]
|
||||
_.remove(@bindingSetsByFirstKeystroke[keystroke], bindingSet)
|
||||
|
||||
# Public: Returns an array of objects that represent every keystroke to
|
||||
# command mapping. Each object contains the following keys `source`,
|
||||
# `selector`, `command`, `keystrokes`.
|
||||
getAllKeyMappings: ->
|
||||
mappings = []
|
||||
for bindingSet in @bindingSets
|
||||
selector = bindingSet.getSelector()
|
||||
source = @determineSource(bindingSet.getName())
|
||||
for keystrokes, command of bindingSet.getCommandsByKeystrokes()
|
||||
mappings.push {keystrokes, command, selector, source}
|
||||
|
||||
mappings
|
||||
|
||||
# Private: Returns a user friendly description of where a keybinding was
|
||||
# loaded from.
|
||||
# Public: Returns a array of {KeyBinding}s (sorted by selector specificity)
|
||||
# that match a keystroke and element.
|
||||
#
|
||||
# * filePath:
|
||||
# The absolute path from which the keymap was loaded
|
||||
# * keystroke:
|
||||
# The string representing the keys pressed (e.g. ctrl-P).
|
||||
# * element:
|
||||
# The DOM node that will match a {KeyBinding}'s selector.
|
||||
keyBindingsForKeystrokeMatchingElement: (keystroke, element) ->
|
||||
keyBindings = @keyBindingsForKeystroke(keystroke)
|
||||
@keyBindingsMatchingElement(element, keyBindings)
|
||||
|
||||
# Public: Returns a array of {KeyBinding}s (sorted by selector specificity)
|
||||
# that match a command.
|
||||
#
|
||||
# Returns one of:
|
||||
# * `Core` indicates it comes from a bundled package.
|
||||
# * `User` indicates that it was defined by a user.
|
||||
# * `<package-name>` the package which defined it.
|
||||
# * `Unknown` if an invalid path was passed in.
|
||||
determineSource: (filePath) ->
|
||||
return 'Unknown' unless filePath
|
||||
# * command:
|
||||
# The string representing the command (tree-view:toggle)
|
||||
# * element:
|
||||
# The DOM node that will match a {KeyBinding}'s selector.
|
||||
keyBindingsForCommandMatchingElement: (command, element) ->
|
||||
keyBindings = @keyBindingsForCommand(command)
|
||||
@keyBindingsMatchingElement(element, keyBindings)
|
||||
|
||||
pathParts = filePath.split(path.sep)
|
||||
if _.contains(pathParts, 'node_modules') or _.contains(pathParts, 'atom') or _.contains(pathParts, 'src')
|
||||
'Core'
|
||||
else if _.contains(pathParts, '.atom') and _.contains(pathParts, 'keymaps') and !_.contains(pathParts, 'packages')
|
||||
'User'
|
||||
else
|
||||
packageNameIndex = pathParts.length - 3
|
||||
pathParts[packageNameIndex]
|
||||
# Public: Returns an array of {KeyBinding}s that match a keystroke
|
||||
# * keystroke:
|
||||
# The string representing the keys pressed (e.g. ctrl-P)
|
||||
keyBindingsForKeystroke: (keystroke) ->
|
||||
keystroke = KeyBinding.normalizeKeystroke(keystroke)
|
||||
@keyBindings.filter (keyBinding) -> keyBinding.matches(keystroke)
|
||||
|
||||
bindKeys: (args...) ->
|
||||
name = args.shift() if args.length > 2
|
||||
[selector, bindings] = args
|
||||
bindingSet = new BindingSet(selector, bindings, @nextBindingSetIndex++, name)
|
||||
@bindingSets.unshift(bindingSet)
|
||||
for keystrokes of bindingSet.commandsByKeystrokes
|
||||
keystroke = keystrokes.split(' ')[0] # only index by first keystroke
|
||||
@bindingSetsByFirstKeystroke[keystroke] ?= []
|
||||
@bindingSetsByFirstKeystroke[keystroke].push(bindingSet)
|
||||
# Public: Returns an array of {KeyBinding}s that match a command
|
||||
# * keystroke:
|
||||
# The string representing the keys pressed (e.g. ctrl-P)
|
||||
keyBindingsForCommand: (command) ->
|
||||
@keyBindings.filter (keyBinding) -> keyBinding.command == command
|
||||
|
||||
unbindKeys: (selector, bindings) ->
|
||||
bindingSet = _.detect @bindingSets, (bindingSet) ->
|
||||
bindingSet.selector is selector and bindingSet.bindings is bindings
|
||||
# Public: Returns a array of {KeyBinding}s (sorted by selector specificity)
|
||||
# whos selector matches the element.
|
||||
#
|
||||
# * element:
|
||||
# The DOM node that will match a {KeyBinding}'s selector.
|
||||
keyBindingsMatchingElement: (element, keyBindings=@keyBindings) ->
|
||||
keyBindings = keyBindings.filter ({selector}) -> $(element).closest(selector).length > 0
|
||||
keyBindings.sort (a, b) -> a.compare(b)
|
||||
|
||||
if bindingSet
|
||||
_.remove(@bindingSets, bindingSet)
|
||||
|
||||
bindingsForElement: (element) ->
|
||||
keystrokeMap = {}
|
||||
currentNode = $(element)
|
||||
|
||||
while currentNode.length
|
||||
bindingSets = @bindingSetsForNode(currentNode)
|
||||
_.defaults(keystrokeMap, set.commandsByKeystrokes) for set in bindingSets
|
||||
currentNode = currentNode.parent()
|
||||
|
||||
keystrokeMap
|
||||
|
||||
handleKeyEvent: (event) =>
|
||||
event.keystrokes = @multiKeystrokeStringForEvent(event)
|
||||
isMultiKeystroke = @queuedKeystrokes?
|
||||
@queuedKeystrokes = null
|
||||
|
||||
firstKeystroke = event.keystrokes.split(' ')[0]
|
||||
bindingSetsForFirstKeystroke = @bindingSetsByFirstKeystroke[firstKeystroke]
|
||||
if bindingSetsForFirstKeystroke?
|
||||
currentNode = $(event.target)
|
||||
currentNode = rootView if currentNode is $('body')[0]
|
||||
while currentNode.length
|
||||
candidateBindingSets = @bindingSetsForNode(currentNode, bindingSetsForFirstKeystroke)
|
||||
for bindingSet in candidateBindingSets
|
||||
command = bindingSet.commandForEvent(event)
|
||||
if command is 'native!'
|
||||
return true
|
||||
else if command
|
||||
continue if @triggerCommandEvent(event, command)
|
||||
return false
|
||||
else if command == false
|
||||
return false
|
||||
|
||||
if bindingSet.matchesKeystrokePrefix(event)
|
||||
@queuedKeystrokes = event.keystrokes
|
||||
return false
|
||||
currentNode = currentNode.parent()
|
||||
|
||||
return false if isMultiKeystroke
|
||||
return false if firstKeystroke is 'tab'
|
||||
|
||||
bindingSetsForNode: (node, candidateBindingSets = @bindingSets) ->
|
||||
bindingSets = candidateBindingSets.filter (set) -> node.is(set.selector)
|
||||
bindingSets.sort (a, b) ->
|
||||
if b.specificity == a.specificity
|
||||
b.index - a.index
|
||||
else
|
||||
b.specificity - a.specificity
|
||||
|
||||
triggerCommandEvent: (keyEvent, commandName) ->
|
||||
keyEvent.target = rootView[0] if keyEvent.target == document.body and window.rootView
|
||||
commandEvent = $.Event(commandName)
|
||||
commandEvent.keyEvent = keyEvent
|
||||
aborted = false
|
||||
commandEvent.abortKeyBinding = ->
|
||||
@stopImmediatePropagation()
|
||||
aborted = true
|
||||
$(keyEvent.target).trigger(commandEvent)
|
||||
aborted
|
||||
|
||||
multiKeystrokeStringForEvent: (event) ->
|
||||
currentKeystroke = @keystrokeStringForEvent(event)
|
||||
if @queuedKeystrokes
|
||||
if currentKeystroke in Modifiers
|
||||
@queuedKeystrokes
|
||||
else
|
||||
@queuedKeystrokes + ' ' + currentKeystroke
|
||||
else
|
||||
currentKeystroke
|
||||
|
||||
keystrokeStringForEvent: (event) ->
|
||||
# Public: Returns a keystroke string derived from an event.
|
||||
# * event:
|
||||
# A DOM or jQuery event
|
||||
# * previousKeystroke:
|
||||
# An optional string used for multiKeystrokes
|
||||
keystrokeStringForEvent: (event, previousKeystroke) ->
|
||||
if event.originalEvent.keyIdentifier.indexOf('U+') == 0
|
||||
hexCharCode = event.originalEvent.keyIdentifier[2..]
|
||||
charCode = parseInt(hexCharCode, 16)
|
||||
@@ -196,10 +93,10 @@ class Keymap
|
||||
modifiers = []
|
||||
if event.altKey and key not in Modifiers
|
||||
modifiers.push 'alt'
|
||||
if event.metaKey and key not in Modifiers
|
||||
modifiers.push 'cmd'
|
||||
if event.ctrlKey and key not in Modifiers
|
||||
modifiers.push 'ctrl'
|
||||
if event.metaKey and key not in Modifiers
|
||||
modifiers.push 'meta'
|
||||
|
||||
if event.shiftKey and key not in Modifiers
|
||||
isNamedKey = key.length > 1
|
||||
@@ -207,16 +104,74 @@ class Keymap
|
||||
else
|
||||
key = key.toLowerCase()
|
||||
|
||||
[modifiers..., key].join('-')
|
||||
keystroke = [modifiers..., key].join('-')
|
||||
|
||||
keystrokesByCommandForSelector: (selector)->
|
||||
keystrokesByCommand = {}
|
||||
for bindingSet in @bindingSets
|
||||
for keystroke, command of bindingSet.commandsByKeystrokes
|
||||
continue if selector? and selector != bindingSet.selector
|
||||
keystrokesByCommand[command] ?= []
|
||||
keystrokesByCommand[command].push keystroke
|
||||
keystrokesByCommand
|
||||
if previousKeystroke
|
||||
if keystroke in Modifiers
|
||||
previousKeystroke
|
||||
else
|
||||
"#{previousKeystroke} #{keystroke}"
|
||||
else
|
||||
keystroke
|
||||
|
||||
loadBundledKeymaps: ->
|
||||
@loadDirectory(path.join(@resourcePath, 'keymaps'))
|
||||
@emit('bundled-keymaps-loaded')
|
||||
|
||||
loadUserKeymap: ->
|
||||
userKeymapPath = CSON.resolve(path.join(@configDirPath, 'keymap'))
|
||||
@load(userKeymapPath) if userKeymapPath
|
||||
|
||||
loadDirectory: (directoryPath) ->
|
||||
@load(filePath) for filePath in fs.listSync(directoryPath, ['.cson', '.json'])
|
||||
|
||||
load: (path) ->
|
||||
@add(path, CSON.readFileSync(path))
|
||||
|
||||
add: (source, keyMappingsBySelector) ->
|
||||
for selector, keyMappings of keyMappingsBySelector
|
||||
@bindKeys(source, selector, keyMappings)
|
||||
|
||||
remove: (source) ->
|
||||
@keyBindings = @keyBindings.filter (keyBinding) -> keyBinding.source isnt source
|
||||
|
||||
bindKeys: (source, selector, keyMappings) ->
|
||||
for keystroke, command of keyMappings
|
||||
@keyBindings.push new KeyBinding(source, command, keystroke, selector)
|
||||
|
||||
handleKeyEvent: (event) ->
|
||||
element = event.target
|
||||
element = atom.workspaceView if element == document.body
|
||||
keystroke = @keystrokeStringForEvent(event, @queuedKeystroke)
|
||||
keyBindings = @keyBindingsForKeystrokeMatchingElement(keystroke, element)
|
||||
|
||||
if keyBindings.length == 0 and @queuedKeystroke
|
||||
@queuedKeystroke = null
|
||||
return false
|
||||
else
|
||||
@queuedKeystroke = null
|
||||
|
||||
for keyBinding in keyBindings
|
||||
partialMatch = keyBinding.keystroke isnt keystroke
|
||||
if partialMatch
|
||||
@queuedKeystroke = keystroke
|
||||
shouldBubble = false
|
||||
else
|
||||
if keyBinding.command is 'native!'
|
||||
shouldBubble = true
|
||||
else if @triggerCommandEvent(element, keyBinding.command, event)
|
||||
shouldBubble = false
|
||||
|
||||
break if shouldBubble?
|
||||
|
||||
shouldBubble ? true
|
||||
|
||||
triggerCommandEvent: (element, commandName, event) ->
|
||||
commandEvent = $.Event(commandName)
|
||||
commandEvent.originalEvent = event
|
||||
commandEvent.abortKeyBinding = -> commandEvent.stopImmediatePropagation()
|
||||
$(element).trigger(commandEvent)
|
||||
not commandEvent.isImmediatePropagationStopped()
|
||||
|
||||
isAscii: (charCode) ->
|
||||
0 <= charCode <= 127
|
||||
|
||||
+53
-52
@@ -12,7 +12,7 @@ class LanguageMode
|
||||
|
||||
buffer: null
|
||||
grammar: null
|
||||
editSession: null
|
||||
editor: null
|
||||
currentGrammarScore: null
|
||||
|
||||
### Internal ###
|
||||
@@ -22,11 +22,11 @@ class LanguageMode
|
||||
|
||||
### Public ###
|
||||
|
||||
# Sets up a `LanguageMode` for the given {EditSession}.
|
||||
# Sets up a `LanguageMode` for the given {Editor}.
|
||||
#
|
||||
# editSession - The {EditSession} to associate with
|
||||
constructor: (@editSession) ->
|
||||
@buffer = @editSession.buffer
|
||||
# editor - The {Editor} to associate with
|
||||
constructor: (@editor) ->
|
||||
@buffer = @editor.buffer
|
||||
|
||||
# Wraps the lines between two rows in comments.
|
||||
#
|
||||
@@ -37,8 +37,8 @@ class LanguageMode
|
||||
#
|
||||
# Returns an {Array} of the commented {Ranges}.
|
||||
toggleLineCommentsForBufferRows: (start, end) ->
|
||||
scopes = @editSession.scopesForBufferPosition([start, 0])
|
||||
properties = syntax.propertiesForScope(scopes, "editor.commentStart")[0]
|
||||
scopes = @editor.scopesForBufferPosition([start, 0])
|
||||
properties = atom.syntax.propertiesForScope(scopes, "editor.commentStart")[0]
|
||||
return unless properties
|
||||
|
||||
commentStartString = _.valueForKeyPath(properties, "editor.commentStart")
|
||||
@@ -46,7 +46,7 @@ class LanguageMode
|
||||
|
||||
return unless commentStartString
|
||||
|
||||
buffer = @editSession.buffer
|
||||
buffer = @editor.buffer
|
||||
commentStartRegexString = _.escapeRegExp(commentStartString).replace(/(\s+)$/, '($1)?')
|
||||
commentStartRegex = new OnigRegExp("^(\\s*)(#{commentStartRegexString})")
|
||||
shouldUncomment = commentStartRegex.test(buffer.lineForRow(start))
|
||||
@@ -83,8 +83,8 @@ class LanguageMode
|
||||
buffer.change([[row, columnStart], [row, columnEnd]], "")
|
||||
else
|
||||
indent = @minIndentLevelForRowRange(start, end)
|
||||
indentString = @editSession.buildIndentString(indent)
|
||||
tabLength = @editSession.getTabLength()
|
||||
indentString = @editor.buildIndentString(indent)
|
||||
tabLength = @editor.getTabLength()
|
||||
indentRegex = new RegExp("(\t|[ ]{#{tabLength}}){#{Math.floor(indent)}}")
|
||||
for row in [start..end]
|
||||
line = buffer.lineForRow(row)
|
||||
@@ -98,12 +98,12 @@ class LanguageMode
|
||||
for currentRow in [0..@buffer.getLastRow()]
|
||||
[startRow, endRow] = @rowRangeForFoldAtBufferRow(currentRow) ? []
|
||||
continue unless startRow?
|
||||
@editSession.createFold(startRow, endRow)
|
||||
@editor.createFold(startRow, endRow)
|
||||
|
||||
# Unfolds all the foldable lines in the buffer.
|
||||
unfoldAll: ->
|
||||
for row in [@buffer.getLastRow()..0]
|
||||
fold.destroy() for fold in @editSession.displayBuffer.foldsStartingAtBufferRow(row)
|
||||
fold.destroy() for fold in @editor.displayBuffer.foldsStartingAtBufferRow(row)
|
||||
|
||||
# Fold all comment and code blocks at a given indentLevel
|
||||
#
|
||||
@@ -114,8 +114,8 @@ class LanguageMode
|
||||
continue unless startRow?
|
||||
|
||||
# assumption: startRow will always be the min indent level for the entire range
|
||||
if @editSession.indentationForBufferRow(startRow) == indentLevel
|
||||
@editSession.createFold(startRow, endRow)
|
||||
if @editor.indentationForBufferRow(startRow) == indentLevel
|
||||
@editor.createFold(startRow, endRow)
|
||||
|
||||
# Given a buffer row, creates a fold at it.
|
||||
#
|
||||
@@ -126,14 +126,14 @@ class LanguageMode
|
||||
for currentRow in [bufferRow..0]
|
||||
[startRow, endRow] = @rowRangeForFoldAtBufferRow(currentRow) ? []
|
||||
continue unless startRow? and startRow <= bufferRow <= endRow
|
||||
fold = @editSession.displayBuffer.largestFoldStartingAtBufferRow(startRow)
|
||||
return @editSession.createFold(startRow, endRow) unless fold
|
||||
fold = @editor.displayBuffer.largestFoldStartingAtBufferRow(startRow)
|
||||
return @editor.createFold(startRow, endRow) unless fold
|
||||
|
||||
# Given a buffer row, this unfolds it.
|
||||
#
|
||||
# bufferRow - A {Number} indicating the buffer row
|
||||
unfoldBufferRow: (bufferRow) ->
|
||||
@editSession.displayBuffer.largestFoldContainingBufferRow(bufferRow)?.destroy()
|
||||
@editor.displayBuffer.largestFoldContainingBufferRow(bufferRow)?.destroy()
|
||||
|
||||
# Find the row range for a fold at a given bufferRow. Will handle comments
|
||||
# and code.
|
||||
@@ -147,30 +147,30 @@ class LanguageMode
|
||||
rowRange
|
||||
|
||||
rowRangeForCommentAtBufferRow: (bufferRow) ->
|
||||
return unless @editSession.displayBuffer.tokenizedBuffer.lineForScreenRow(bufferRow).isComment()
|
||||
return unless @editor.displayBuffer.tokenizedBuffer.lineForScreenRow(bufferRow).isComment()
|
||||
|
||||
startRow = bufferRow
|
||||
for currentRow in [bufferRow-1..0]
|
||||
break if @buffer.isRowBlank(currentRow)
|
||||
break unless @editSession.displayBuffer.tokenizedBuffer.lineForScreenRow(currentRow).isComment()
|
||||
break unless @editor.displayBuffer.tokenizedBuffer.lineForScreenRow(currentRow).isComment()
|
||||
startRow = currentRow
|
||||
endRow = bufferRow
|
||||
for currentRow in [bufferRow+1..@buffer.getLastRow()]
|
||||
break if @buffer.isRowBlank(currentRow)
|
||||
break unless @editSession.displayBuffer.tokenizedBuffer.lineForScreenRow(currentRow).isComment()
|
||||
break unless @editor.displayBuffer.tokenizedBuffer.lineForScreenRow(currentRow).isComment()
|
||||
endRow = currentRow
|
||||
return [startRow, endRow] if startRow isnt endRow
|
||||
|
||||
rowRangeForCodeFoldAtBufferRow: (bufferRow) ->
|
||||
return null unless @doesBufferRowStartFold(bufferRow)
|
||||
|
||||
startIndentLevel = @editSession.indentationForBufferRow(bufferRow)
|
||||
scopes = @editSession.scopesForBufferPosition([bufferRow, 0])
|
||||
for row in [(bufferRow + 1)..@editSession.getLastBufferRow()]
|
||||
continue if @editSession.isBufferRowBlank(row)
|
||||
indentation = @editSession.indentationForBufferRow(row)
|
||||
startIndentLevel = @editor.indentationForBufferRow(bufferRow)
|
||||
scopes = @editor.scopesForBufferPosition([bufferRow, 0])
|
||||
for row in [(bufferRow + 1)..@editor.getLastBufferRow()]
|
||||
continue if @editor.isBufferRowBlank(row)
|
||||
indentation = @editor.indentationForBufferRow(row)
|
||||
if indentation <= startIndentLevel
|
||||
includeRowInFold = indentation == startIndentLevel and @foldEndRegexForScopes(scopes)?.search(@editSession.lineForBufferRow(row))
|
||||
includeRowInFold = indentation == startIndentLevel and @foldEndRegexForScopes(scopes)?.search(@editor.lineForBufferRow(row))
|
||||
foldEndRow = row if includeRowInFold
|
||||
break
|
||||
|
||||
@@ -179,19 +179,19 @@ class LanguageMode
|
||||
[bufferRow, foldEndRow]
|
||||
|
||||
doesBufferRowStartFold: (bufferRow) ->
|
||||
return false if @editSession.isBufferRowBlank(bufferRow)
|
||||
nextNonEmptyRow = @editSession.nextNonBlankBufferRow(bufferRow)
|
||||
return false if @editor.isBufferRowBlank(bufferRow)
|
||||
nextNonEmptyRow = @editor.nextNonBlankBufferRow(bufferRow)
|
||||
return false unless nextNonEmptyRow?
|
||||
@editSession.indentationForBufferRow(nextNonEmptyRow) > @editSession.indentationForBufferRow(bufferRow)
|
||||
@editor.indentationForBufferRow(nextNonEmptyRow) > @editor.indentationForBufferRow(bufferRow)
|
||||
|
||||
# Find a row range for a 'paragraph' around specified bufferRow.
|
||||
# Right now, a paragraph is a block of text bounded by and empty line or a
|
||||
# block of text that is not the same type (comments next to source code).
|
||||
rowRangeForParagraphAtBufferRow: (bufferRow) ->
|
||||
return unless /\w/.test(@editSession.lineForBufferRow(bufferRow))
|
||||
return unless /\w/.test(@editor.lineForBufferRow(bufferRow))
|
||||
|
||||
isRowComment = (row) =>
|
||||
@editSession.displayBuffer.tokenizedBuffer.lineForScreenRow(row).isComment()
|
||||
@editor.displayBuffer.tokenizedBuffer.lineForScreenRow(row).isComment()
|
||||
|
||||
if isRowComment(bufferRow)
|
||||
isOriginalRowComment = true
|
||||
@@ -199,22 +199,22 @@ class LanguageMode
|
||||
[firstRow, lastRow] = range or [bufferRow, bufferRow]
|
||||
else
|
||||
isOriginalRowComment = false
|
||||
[firstRow, lastRow] = [0, @editSession.getLastBufferRow()-1]
|
||||
[firstRow, lastRow] = [0, @editor.getLastBufferRow()-1]
|
||||
|
||||
startRow = bufferRow
|
||||
while startRow > firstRow
|
||||
break if isRowComment(startRow - 1) != isOriginalRowComment
|
||||
break unless /\w/.test(@editSession.lineForBufferRow(startRow - 1))
|
||||
break unless /\w/.test(@editor.lineForBufferRow(startRow - 1))
|
||||
startRow--
|
||||
|
||||
endRow = bufferRow
|
||||
lastRow = @editSession.getLastBufferRow()
|
||||
lastRow = @editor.getLastBufferRow()
|
||||
while endRow < lastRow
|
||||
break if isRowComment(endRow + 1) != isOriginalRowComment
|
||||
break unless /\w/.test(@editSession.lineForBufferRow(endRow + 1))
|
||||
break unless /\w/.test(@editor.lineForBufferRow(endRow + 1))
|
||||
endRow++
|
||||
|
||||
new Range([startRow, 0], [endRow, @editSession.lineLengthForBufferRow(endRow)])
|
||||
new Range([startRow, 0], [endRow, @editor.lineLengthForBufferRow(endRow)])
|
||||
|
||||
# Given a buffer row, this returns a suggested indentation level.
|
||||
#
|
||||
@@ -224,8 +224,8 @@ class LanguageMode
|
||||
#
|
||||
# Returns a {Number}.
|
||||
suggestedIndentForBufferRow: (bufferRow) ->
|
||||
currentIndentLevel = @editSession.indentationForBufferRow(bufferRow)
|
||||
scopes = @editSession.scopesForBufferPosition([bufferRow, 0])
|
||||
currentIndentLevel = @editor.indentationForBufferRow(bufferRow)
|
||||
scopes = @editor.scopesForBufferPosition([bufferRow, 0])
|
||||
return currentIndentLevel unless increaseIndentRegex = @increaseIndentRegexForScopes(scopes)
|
||||
|
||||
currentLine = @buffer.lineForRow(bufferRow)
|
||||
@@ -233,8 +233,8 @@ class LanguageMode
|
||||
return currentIndentLevel unless precedingRow?
|
||||
|
||||
precedingLine = @buffer.lineForRow(precedingRow)
|
||||
desiredIndentLevel = @editSession.indentationForBufferRow(precedingRow)
|
||||
desiredIndentLevel += 1 if increaseIndentRegex.test(precedingLine) and not @editSession.isBufferRowCommented(precedingRow)
|
||||
desiredIndentLevel = @editor.indentationForBufferRow(precedingRow)
|
||||
desiredIndentLevel += 1 if increaseIndentRegex.test(precedingLine) and not @editor.isBufferRowCommented(precedingRow)
|
||||
|
||||
return desiredIndentLevel unless decreaseIndentRegex = @decreaseIndentRegexForScopes(scopes)
|
||||
desiredIndentLevel -= 1 if decreaseIndentRegex.test(currentLine)
|
||||
@@ -248,7 +248,7 @@ class LanguageMode
|
||||
#
|
||||
# Returns a {Number} of the indent level of the block of lines.
|
||||
minIndentLevelForRowRange: (startRow, endRow) ->
|
||||
indents = (@editSession.indentationForBufferRow(row) for row in [startRow..endRow] when not @editSession.isBufferRowBlank(row))
|
||||
indents = (@editor.indentationForBufferRow(row) for row in [startRow..endRow] when not @editor.isBufferRowBlank(row))
|
||||
indents = [0] unless indents.length
|
||||
Math.min(indents...)
|
||||
|
||||
@@ -264,13 +264,13 @@ class LanguageMode
|
||||
# bufferRow - The row {Number}
|
||||
autoIndentBufferRow: (bufferRow) ->
|
||||
indentLevel = @suggestedIndentForBufferRow(bufferRow)
|
||||
@editSession.setIndentationForBufferRow(bufferRow, indentLevel)
|
||||
@editor.setIndentationForBufferRow(bufferRow, indentLevel)
|
||||
|
||||
# Given a buffer row, this decreases the indentation.
|
||||
#
|
||||
# bufferRow - The row {Number}
|
||||
autoDecreaseIndentForBufferRow: (bufferRow) ->
|
||||
scopes = @editSession.scopesForBufferPosition([bufferRow, 0])
|
||||
scopes = @editor.scopesForBufferPosition([bufferRow, 0])
|
||||
increaseIndentRegex = @increaseIndentRegexForScopes(scopes)
|
||||
decreaseIndentRegex = @decreaseIndentRegexForScopes(scopes)
|
||||
return unless increaseIndentRegex and decreaseIndentRegex
|
||||
@@ -278,28 +278,29 @@ class LanguageMode
|
||||
line = @buffer.lineForRow(bufferRow)
|
||||
return unless decreaseIndentRegex.test(line)
|
||||
|
||||
currentIndentLevel = @editSession.indentationForBufferRow(bufferRow)
|
||||
currentIndentLevel = @editor.indentationForBufferRow(bufferRow)
|
||||
return if currentIndentLevel is 0
|
||||
precedingRow = @buffer.previousNonBlankRow(bufferRow)
|
||||
return unless precedingRow?
|
||||
precedingLine = @buffer.lineForRow(precedingRow)
|
||||
|
||||
desiredIndentLevel = @editSession.indentationForBufferRow(precedingRow)
|
||||
desiredIndentLevel = @editor.indentationForBufferRow(precedingRow)
|
||||
desiredIndentLevel -= 1 unless increaseIndentRegex.test(precedingLine)
|
||||
if desiredIndentLevel >= 0 and desiredIndentLevel < currentIndentLevel
|
||||
@editSession.setIndentationForBufferRow(bufferRow, desiredIndentLevel)
|
||||
@editor.setIndentationForBufferRow(bufferRow, desiredIndentLevel)
|
||||
|
||||
tokenizeLine: (line, stack, firstLine) ->
|
||||
{tokens, stack} = @grammar.tokenizeLine(line, stack, firstLine)
|
||||
|
||||
getRegexForProperty: (scopes, property) ->
|
||||
if pattern = atom.syntax.getProperty(scopes, property)
|
||||
new OnigRegExp(pattern)
|
||||
|
||||
increaseIndentRegexForScopes: (scopes) ->
|
||||
if increaseIndentPattern = syntax.getProperty(scopes, 'editor.increaseIndentPattern')
|
||||
new OnigRegExp(increaseIndentPattern)
|
||||
@getRegexForProperty(scopes, 'editor.increaseIndentPattern')
|
||||
|
||||
decreaseIndentRegexForScopes: (scopes) ->
|
||||
if decreaseIndentPattern = syntax.getProperty(scopes, 'editor.decreaseIndentPattern')
|
||||
new OnigRegExp(decreaseIndentPattern)
|
||||
@getRegexForProperty(scopes, 'editor.decreaseIndentPattern')
|
||||
|
||||
foldEndRegexForScopes: (scopes) ->
|
||||
if foldEndPattern = syntax.getProperty(scopes, 'editor.foldEndPattern')
|
||||
new OnigRegExp(foldEndPattern)
|
||||
@getRegexForProperty(scopes, 'editor.foldEndPattern')
|
||||
|
||||
@@ -5,22 +5,32 @@ LessCache = require 'less-cache'
|
||||
|
||||
tmpDir = if process.platform is 'win32' then os.tmpdir() else '/tmp'
|
||||
|
||||
# Private: {LessCache} wrapper used by {ThemeManager} to read stylesheets.
|
||||
module.exports =
|
||||
class LessCompileCache
|
||||
Subscriber.includeInto(this)
|
||||
|
||||
@cacheDir: path.join(tmpDir, 'atom-compile-cache', 'less')
|
||||
|
||||
constructor: ->
|
||||
constructor: ({resourcePath, importPaths}) ->
|
||||
@lessSearchPaths = [
|
||||
path.join(resourcePath, 'static', 'variables')
|
||||
path.join(resourcePath, 'static')
|
||||
]
|
||||
|
||||
if importPaths?
|
||||
importPaths = importPaths.concat(@lessSearchPaths)
|
||||
else
|
||||
importPaths = @lessSearchPaths
|
||||
|
||||
@cache = new LessCache
|
||||
cacheDir: @constructor.cacheDir
|
||||
importPaths: @getImportPaths()
|
||||
resourcePath: window.resourcePath
|
||||
fallbackDir: path.join(window.resourcePath, 'less-compile-cache')
|
||||
importPaths: importPaths
|
||||
resourcePath: resourcePath
|
||||
fallbackDir: path.join(resourcePath, 'less-compile-cache')
|
||||
|
||||
@subscribe atom.themes, 'reloaded', => @cache.setImportPaths(@getImportPaths())
|
||||
|
||||
getImportPaths: -> atom.themes.getImportPaths().concat(config.lessSearchPaths)
|
||||
setImportPaths: (importPaths=[]) ->
|
||||
@cache.setImportPaths(importPaths.concat(@lessSearchPaths))
|
||||
|
||||
read: (stylesheetPath) -> @cache.readFileSync(stylesheetPath)
|
||||
|
||||
|
||||
+46
-16
@@ -12,9 +12,9 @@ fs = require 'fs-plus'
|
||||
module.exports =
|
||||
class MenuManager
|
||||
# Private:
|
||||
constructor: ->
|
||||
constructor: ({@resourcePath}) ->
|
||||
@template = []
|
||||
atom.keymap.on 'bundled-keymaps-loaded', => @loadCoreItems()
|
||||
atom.keymap.on 'bundled-keymaps-loaded', => @loadPlatformItems()
|
||||
|
||||
# Public: Adds the given item definition to the existing template.
|
||||
#
|
||||
@@ -27,34 +27,55 @@ class MenuManager
|
||||
@merge(@template, item) for item in items
|
||||
@update()
|
||||
|
||||
# Private: Should the binding for the given selector be included in the menu
|
||||
# commands.
|
||||
#
|
||||
# * selector: A String selector to check.
|
||||
#
|
||||
# Returns true to include the selector, false otherwise.
|
||||
includeSelector: (selector) ->
|
||||
return true if document.body.webkitMatchesSelector(selector)
|
||||
|
||||
# Simulate an .editor element attached to a body element that has the same
|
||||
# classes as the current body element.
|
||||
unless @testEditor?
|
||||
@testEditor = document.createElement('div')
|
||||
@testEditor.classList.add('editor')
|
||||
testBody = document.createElement('body')
|
||||
testBody.classList.add(document.body.classList.toString().split(' ')...)
|
||||
testBody.appendChild(@testEditor)
|
||||
|
||||
@testEditor.webkitMatchesSelector(selector)
|
||||
|
||||
# Public: Refreshes the currently visible menu.
|
||||
update: ->
|
||||
keystrokesByCommand = atom.keymap.keystrokesByCommandForSelector('body')
|
||||
_.extend(keystrokesByCommand, atom.keymap.keystrokesByCommandForSelector('.editor'))
|
||||
_.extend(keystrokesByCommand, atom.keymap.keystrokesByCommandForSelector('.editor:not(.mini)'))
|
||||
keystrokesByCommand = {}
|
||||
for binding in atom.keymap.getKeyBindings() when @includeSelector(binding.selector)
|
||||
keystrokesByCommand[binding.command] ?= []
|
||||
keystrokesByCommand[binding.command].push binding.keystroke
|
||||
@sendToBrowserProcess(@template, keystrokesByCommand)
|
||||
|
||||
# Private
|
||||
loadCoreItems: ->
|
||||
menuPaths = fs.listSync(atom.config.bundledMenusDirPath, ['cson', 'json'])
|
||||
for menuPath in menuPaths
|
||||
data = CSON.readFileSync(menuPath)
|
||||
@add(data.menu)
|
||||
# Private:
|
||||
loadPlatformItems: ->
|
||||
menusDirPath = path.join(@resourcePath, 'menus')
|
||||
platformMenuPath = fs.resolve(menusDirPath, process.platform, ['cson', 'json'])
|
||||
{menu} = CSON.readFileSync(platformMenuPath)
|
||||
@add(menu)
|
||||
|
||||
# Private: Merges an item in a submenu aware way such that new items are always
|
||||
# appended to the bottom of existing menus where possible.
|
||||
merge: (menu, item) ->
|
||||
item = _.deepClone(item)
|
||||
|
||||
if item.submenu? and match = _.find(menu, (i) -> i.submenu? and i.label == item.label)
|
||||
if item.submenu? and match = _.find(menu, (i) => i.submenu? and @normalizeLabel(i.label) == @normalizeLabel(item.label))
|
||||
@merge(match.submenu, i) for i in item.submenu
|
||||
else
|
||||
menu.push(item) unless _.find(menu, (i) -> i.label == item.label)
|
||||
menu.push(item) unless _.find(menu, (i) => @normalizeLabel(i.label) == @normalizeLabel(item.label))
|
||||
|
||||
# Private: OSX can't handle displaying accelerators for multiple keystrokes.
|
||||
# If they are sent across, it will stop processing accelerators for the rest
|
||||
# of the menu items.
|
||||
filterMultipleKeystrokes: (keystrokesByCommand) ->
|
||||
filterMultipleKeystroke: (keystrokesByCommand) ->
|
||||
filtered = {}
|
||||
for key, bindings of keystrokesByCommand
|
||||
for binding in bindings
|
||||
@@ -64,7 +85,16 @@ class MenuManager
|
||||
filtered[key].push(binding)
|
||||
filtered
|
||||
|
||||
# Private
|
||||
# Private:
|
||||
sendToBrowserProcess: (template, keystrokesByCommand) ->
|
||||
keystrokesByCommand = @filterMultipleKeystrokes(keystrokesByCommand)
|
||||
keystrokesByCommand = @filterMultipleKeystroke(keystrokesByCommand)
|
||||
ipc.sendChannel 'update-application-menu', template, keystrokesByCommand
|
||||
|
||||
# Private:
|
||||
normalizeLabel: (label) ->
|
||||
return undefined unless label?
|
||||
|
||||
if process.platform is 'win32'
|
||||
label.replace(/\&/g, '')
|
||||
else
|
||||
label
|
||||
|
||||
@@ -4,25 +4,26 @@ _ = require 'underscore-plus'
|
||||
Package = require './package'
|
||||
path = require 'path'
|
||||
|
||||
###
|
||||
Packages have a lifecycle
|
||||
|
||||
* The paths to all non-disabled packages and themes are found on disk (these are available packages)
|
||||
* Every package (except those in core.disabledPackages) is 'loaded', meaning
|
||||
`Package` objects are created, and their metadata loaded. This includes themes,
|
||||
as themes are packages
|
||||
* The ThemeManager.activateThemes() is called 'activating' all the themes, meaning
|
||||
their stylesheets are loaded into the window.
|
||||
* The PackageManager.activatePackages() function is called 'activating' non-theme
|
||||
package, meaning its resources -- keymaps, classes, etc. -- are loaded, and
|
||||
the package's activate() method is called.
|
||||
* Packages and themes can then be enabled and disabled via the public
|
||||
.enablePackage(name) and .disablePackage(name) functions.
|
||||
###
|
||||
# Public: Package manager for coordinating the lifecycle of Atom packages.
|
||||
#
|
||||
# Packages can be loaded, activated, and deactivated, and unloaded:
|
||||
# * Loading a package reads and parses the package's metadata and resources
|
||||
# such as keymaps, menus, stylesheets, etc.
|
||||
# * Activating a package registers the loaded resources and calls `activate()`
|
||||
# on the package's main module.
|
||||
# * Deactivating a package unregisters the package's resources and calls
|
||||
# `deactivate()` on the package's main module.
|
||||
# * Unloading a package removes it completely from the package manager.
|
||||
#
|
||||
# Packages can also be enabled/disabled via the `core.disabledPackages` config
|
||||
# settings and also by calling `enablePackage()/disablePackage()`.
|
||||
#
|
||||
# An instance of this class is globally available via `atom.packages`.
|
||||
module.exports =
|
||||
class PackageManager
|
||||
Emitter.includeInto(this)
|
||||
|
||||
# Private:
|
||||
constructor: ({configDirPath, devMode, @resourcePath}) ->
|
||||
@packageDirPaths = [path.join(configDirPath, "packages")]
|
||||
if devMode
|
||||
@@ -40,42 +41,50 @@ class PackageManager
|
||||
getApmPath: ->
|
||||
@apmPath ?= require.resolve('atom-package-manager/bin/apm')
|
||||
|
||||
# Public: Get the paths being used to look for packages.
|
||||
#
|
||||
# Returns an Array of String directory paths.
|
||||
getPackageDirPaths: ->
|
||||
_.clone(@packageDirPaths)
|
||||
|
||||
# Private:
|
||||
getPackageState: (name) ->
|
||||
@packageStates[name]
|
||||
|
||||
# Private:
|
||||
setPackageState: (name, state) ->
|
||||
@packageStates[name] = state
|
||||
|
||||
# Public:
|
||||
# Public: Enable the package with the given name
|
||||
enablePackage: (name) ->
|
||||
pack = @loadPackage(name)
|
||||
pack?.enable()
|
||||
pack
|
||||
|
||||
# Public:
|
||||
# Public: Disable the package with the given name
|
||||
disablePackage: (name) ->
|
||||
pack = @loadPackage(name)
|
||||
pack?.disable()
|
||||
pack
|
||||
|
||||
# Internal-only: Activate all the packages that should be activated.
|
||||
# Private: Activate all the packages that should be activated.
|
||||
activate: ->
|
||||
for [activator, types] in @packageActivators
|
||||
packages = @getLoadedPackagesForTypes(types)
|
||||
activator.activatePackages(packages)
|
||||
@emit 'activated'
|
||||
|
||||
# Public: another type of package manager can handle other package types.
|
||||
# Private: another type of package manager can handle other package types.
|
||||
# See ThemeManager
|
||||
registerPackageActivator: (activator, types) ->
|
||||
@packageActivators.push([activator, types])
|
||||
|
||||
# Internal-only:
|
||||
# Private:
|
||||
activatePackages: (packages) ->
|
||||
@activatePackage(pack.name) for pack in packages
|
||||
@observeDisabledPackages()
|
||||
|
||||
# Internal-only: Activate a single package by name
|
||||
# Private: Activate a single package by name
|
||||
activatePackage: (name, options) ->
|
||||
return pack if pack = @getActivePackage(name)
|
||||
if pack = @loadPackage(name, options)
|
||||
@@ -83,10 +92,12 @@ class PackageManager
|
||||
pack.activate(options)
|
||||
pack
|
||||
|
||||
# Private: Deactivate all packages
|
||||
deactivatePackages: ->
|
||||
@deactivatePackage(pack.name) for pack in @getActivePackages()
|
||||
@unobserveDisabledPackages()
|
||||
|
||||
# Private: Deactivate the package with the given name
|
||||
deactivatePackage: (name) ->
|
||||
if pack = @getActivePackage(name)
|
||||
@setPackageState(pack.name, state) if state = pack.serialize?()
|
||||
@@ -95,24 +106,29 @@ class PackageManager
|
||||
else
|
||||
throw new Error("No active package for name '#{name}'")
|
||||
|
||||
# Public: Get an array of all the active packages
|
||||
getActivePackages: ->
|
||||
_.values(@activePackages)
|
||||
|
||||
# Public: Get the active package with the given name
|
||||
getActivePackage: (name) ->
|
||||
@activePackages[name]
|
||||
|
||||
# Public: Is the package with the given name active?
|
||||
isPackageActive: (name) ->
|
||||
@getActivePackage(name)?
|
||||
|
||||
# Private:
|
||||
unobserveDisabledPackages: ->
|
||||
return unless @observingDisabledPackages
|
||||
config.unobserve('core.disabledPackages')
|
||||
atom.config.unobserve('core.disabledPackages')
|
||||
@observingDisabledPackages = false
|
||||
|
||||
# Private:
|
||||
observeDisabledPackages: ->
|
||||
return if @observingDisabledPackages
|
||||
|
||||
config.observe 'core.disabledPackages', callNow: false, (disabledPackages, {previous}) =>
|
||||
atom.config.observe 'core.disabledPackages', callNow: false, (disabledPackages, {previous}) =>
|
||||
packagesToEnable = _.difference(previous, disabledPackages)
|
||||
packagesToDisable = _.difference(disabledPackages, previous)
|
||||
|
||||
@@ -122,6 +138,7 @@ class PackageManager
|
||||
|
||||
@observingDisabledPackages = true
|
||||
|
||||
# Private:
|
||||
loadPackages: (options) ->
|
||||
# Ensure atom exports is already in the require cache so the load time
|
||||
# of the first package isn't skewed by being the first to require atom
|
||||
@@ -133,6 +150,7 @@ class PackageManager
|
||||
@loadPackage(packagePath, options) for packagePath in packagePaths
|
||||
@emit 'loaded'
|
||||
|
||||
# Private:
|
||||
loadPackage: (nameOrPath, options) ->
|
||||
if packagePath = @resolvePackagePath(nameOrPath)
|
||||
name = path.basename(nameOrPath)
|
||||
@@ -144,10 +162,12 @@ class PackageManager
|
||||
else
|
||||
throw new Error("Could not resolve '#{nameOrPath}' to a package path")
|
||||
|
||||
# Private:
|
||||
unloadPackages: ->
|
||||
@unloadPackage(name) for name in _.keys(@loadedPackages)
|
||||
null
|
||||
|
||||
# Private:
|
||||
unloadPackage: (name) ->
|
||||
if @isPackageActive(name)
|
||||
throw new Error("Tried to unload active package '#{name}'")
|
||||
@@ -157,12 +177,15 @@ class PackageManager
|
||||
else
|
||||
throw new Error("No loaded package for name '#{name}'")
|
||||
|
||||
# Public: Get the loaded package with the given name
|
||||
getLoadedPackage: (name) ->
|
||||
@loadedPackages[name]
|
||||
|
||||
# Public: Is the package with the given name loaded?
|
||||
isPackageLoaded: (name) ->
|
||||
@getLoadedPackage(name)?
|
||||
|
||||
# Public: Get an array of all the loaded packages
|
||||
getLoadedPackages: ->
|
||||
_.values(@loadedPackages)
|
||||
|
||||
@@ -172,6 +195,7 @@ class PackageManager
|
||||
getLoadedPackagesForTypes: (types) ->
|
||||
pack for pack in @getLoadedPackages() when pack.getType() in types
|
||||
|
||||
# Public: Resolve the given package name to a path on disk.
|
||||
resolvePackagePath: (name) ->
|
||||
return name if fs.isDirectorySync(name)
|
||||
|
||||
@@ -179,15 +203,36 @@ class PackageManager
|
||||
return packagePath if fs.isDirectorySync(packagePath)
|
||||
|
||||
packagePath = path.join(@resourcePath, 'node_modules', name)
|
||||
return packagePath if @isInternalPackage(packagePath)
|
||||
return packagePath if @hasAtomEngine(packagePath)
|
||||
|
||||
# Public: Is the package with the given name disabled?
|
||||
isPackageDisabled: (name) ->
|
||||
_.include(config.get('core.disabledPackages') ? [], name)
|
||||
_.include(atom.config.get('core.disabledPackages') ? [], name)
|
||||
|
||||
isInternalPackage: (packagePath) ->
|
||||
{engines} = Package.loadMetadata(packagePath, true)
|
||||
engines?.atom?
|
||||
# Private:
|
||||
hasAtomEngine: (packagePath) ->
|
||||
metadata = Package.loadMetadata(packagePath, true)
|
||||
metadata?.engines?.atom?
|
||||
|
||||
# Public: Is the package with the given name bundled with Atom?
|
||||
isBundledPackage: (name) ->
|
||||
@getPackageDependencies().hasOwnProperty(name)
|
||||
|
||||
# Private:
|
||||
getPackageDependencies: ->
|
||||
unless @packageDependencies?
|
||||
try
|
||||
metadataPath = path.join(@resourcePath, 'package.json')
|
||||
{@packageDependencies} = JSON.parse(fs.readFileSync(metadataPath)) ? {}
|
||||
@packageDependencies ?= {}
|
||||
|
||||
# Temporarily ignore 'grunt-download-atom-shell' here, should remove this
|
||||
# when it became a public npm module.
|
||||
delete @packageDependencies['grunt-download-atom-shell']
|
||||
|
||||
@packageDependencies
|
||||
|
||||
# Public: Get an array of all the available package paths.
|
||||
getAvailablePackagePaths: ->
|
||||
packagePaths = []
|
||||
|
||||
@@ -195,19 +240,18 @@ class PackageManager
|
||||
for packagePath in fs.listSync(packageDirPath)
|
||||
packagePaths.push(packagePath) if fs.isDirectorySync(packagePath)
|
||||
|
||||
try
|
||||
metadataPath = path.join(@resourcePath, 'package.json')
|
||||
{packageDependencies} = JSON.parse(fs.readFileSync(metadataPath)) ? {}
|
||||
packagesPath = path.join(@resourcePath, 'node_modules')
|
||||
for packageName, packageVersion of packageDependencies ? {}
|
||||
for packageName, packageVersion of @getPackageDependencies()
|
||||
packagePath = path.join(packagesPath, packageName)
|
||||
packagePaths.push(packagePath) if fs.isDirectorySync(packagePath)
|
||||
|
||||
_.uniq(packagePaths)
|
||||
|
||||
# Public: Get an array of all the available package names.
|
||||
getAvailablePackageNames: ->
|
||||
_.uniq _.map @getAvailablePackagePaths(), (packagePath) -> path.basename(packagePath)
|
||||
|
||||
# Public: Get an array of all the available package metadata.
|
||||
getAvailablePackageMetadata: ->
|
||||
packages = []
|
||||
for packagePath in @getAvailablePackagePaths()
|
||||
|
||||
@@ -45,7 +45,7 @@ class Package
|
||||
@name = basename(@path)
|
||||
|
||||
isActive: ->
|
||||
atom.isPackageActive(@name)
|
||||
atom.packages.isPackageActive(@name)
|
||||
|
||||
isTheme: ->
|
||||
!!@metadata?.theme
|
||||
|
||||
@@ -12,9 +12,10 @@ class PaneAxis extends View
|
||||
initialize: (args...) ->
|
||||
if args[0] instanceof telepath.Document
|
||||
@state = args[0]
|
||||
@state.get('children').each (child, index) => @addChild(deserialize(child), index, updateState: false)
|
||||
@state.get('children').each (child, index) =>
|
||||
@addChild(atom.deserializers.deserialize(child), index, updateState: false)
|
||||
else
|
||||
@state = site.createDocument(deserializer: @className(), children: [])
|
||||
@state = atom.site.createDocument(deserializer: @className(), children: [])
|
||||
@addChild(child) for child in args
|
||||
|
||||
@state.get('children').on 'changed', ({index, insertedValues, removedValues, siteId}) =>
|
||||
@@ -22,7 +23,7 @@ class PaneAxis extends View
|
||||
for childState in removedValues
|
||||
@removeChild(@children(":eq(#{index})").view(), updateState: false)
|
||||
for childState, i in insertedValues
|
||||
@addChild(deserialize(childState), index + i, updateState: false)
|
||||
@addChild(atom.deserializers.deserialize(childState), index + i, updateState: false)
|
||||
|
||||
addChild: (child, index=@children().length, options={}) ->
|
||||
@insertAt(index, child)
|
||||
@@ -69,7 +70,7 @@ class PaneAxis extends View
|
||||
child.detach()
|
||||
|
||||
getContainer: ->
|
||||
@closest('#panes').view()
|
||||
@closest('.panes').view()
|
||||
|
||||
getActivePaneItem: ->
|
||||
@getActivePane()?.activeItem
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
Pane = require './pane'
|
||||
telepath = require 'telepath'
|
||||
|
||||
# Private: Manages the list of panes within a {RootView}
|
||||
# Private: Manages the list of panes within a {WorkspaceView}
|
||||
module.exports =
|
||||
class PaneContainer extends View
|
||||
registerDeserializer(this)
|
||||
atom.deserializers.add(this)
|
||||
|
||||
### Internal ###
|
||||
@acceptsDocuments: true
|
||||
@@ -16,16 +16,16 @@ class PaneContainer extends View
|
||||
container
|
||||
|
||||
@content: ->
|
||||
@div id: 'panes'
|
||||
@div class: 'panes'
|
||||
|
||||
initialize: (state) ->
|
||||
@destroyedItemStates = []
|
||||
|
||||
if state instanceof telepath.Document
|
||||
@state = state
|
||||
@setRoot(deserialize(@state.get('root')))
|
||||
@setRoot(atom.deserializers.deserialize(@state.get('root')))
|
||||
else
|
||||
@state = site.createDocument(deserializer: 'PaneContainer')
|
||||
@state = atom.site.createDocument(deserializer: 'PaneContainer')
|
||||
|
||||
@subscribe @state, 'changed', ({newValues, siteId}) =>
|
||||
return if siteId is @state.siteId
|
||||
@@ -89,10 +89,10 @@ class PaneContainer extends View
|
||||
reopenItem: ->
|
||||
if lastItemState = @destroyedItemStates.pop()
|
||||
if activePane = @getActivePane()
|
||||
activePane.showItem(deserialize(lastItemState))
|
||||
activePane.showItem(atom.deserializers.deserialize(lastItemState))
|
||||
true
|
||||
else
|
||||
newPane = new Pane(deserialize(lastItemState))
|
||||
newPane = new Pane(atom.deserializers.deserialize(lastItemState))
|
||||
@setRoot(newPane)
|
||||
newPane.focus()
|
||||
|
||||
@@ -147,7 +147,7 @@ class PaneContainer extends View
|
||||
callback(pane) for pane in @getPanes()
|
||||
paneAttached = (e) -> callback($(e.target).view())
|
||||
@on 'pane:attached', paneAttached
|
||||
cancel: => @off 'pane:attached', paneAttached
|
||||
off: => @off 'pane:attached', paneAttached
|
||||
|
||||
getFocusedPane: ->
|
||||
@find('.pane:has(:focus)').view()
|
||||
|
||||
+14
-14
@@ -7,7 +7,7 @@ PaneColumn = require './pane-column'
|
||||
|
||||
# Public: A container which can contains multiple items to be switched between.
|
||||
#
|
||||
# Items can be almost anything however most commonly they're {Editor}s.
|
||||
# Items can be almost anything however most commonly they're {EditorView}s.
|
||||
#
|
||||
# Most packages won't need to use this class, unless you're interested in
|
||||
# building a package that deals with switching between panes or tiems.
|
||||
@@ -33,10 +33,11 @@ class Pane extends View
|
||||
initialize: (args...) ->
|
||||
if args[0] instanceof telepath.Document
|
||||
@state = args[0]
|
||||
@items = _.compact(@state.get('items').map (item) -> deserialize(item))
|
||||
@items = _.compact @state.get('items').map (item) ->
|
||||
atom.deserializers.deserialize(item)
|
||||
else
|
||||
@items = args
|
||||
@state = site.createDocument
|
||||
@state = atom.site.createDocument
|
||||
deserializer: 'Pane'
|
||||
items: @items.map (item) -> item.getState?() ? item.serialize()
|
||||
|
||||
@@ -45,10 +46,10 @@ class Pane extends View
|
||||
for itemState in removedValues
|
||||
@removeItemAtIndex(index, updateState: false)
|
||||
for itemState, i in insertedValues
|
||||
@addItem(deserialize(itemState), index + i, updateState: false)
|
||||
@addItem(atom.deserializers.deserialize(itemState), index + i, updateState: false)
|
||||
|
||||
@subscribe @state, 'changed', ({newValues, siteId}) =>
|
||||
return if site is @state.siteId
|
||||
return if siteId is @state.siteId
|
||||
if newValues.activeItemUri
|
||||
@showItemForUri(newValues.activeItemUri)
|
||||
|
||||
@@ -219,12 +220,11 @@ class Pane extends View
|
||||
return true unless item.shouldPromptToSave?()
|
||||
|
||||
uri = item.getUri()
|
||||
chosen = atom.confirmSync(
|
||||
"'#{item.getTitle?() ? item.getUri()}' has changes, do you want to save them?"
|
||||
"Your changes will be lost if you close this item without saving."
|
||||
["Save", "Cancel", "Don't Save"]
|
||||
atom.getCurrentWindow()
|
||||
)
|
||||
chosen = atom.confirm
|
||||
message: "'#{item.getTitle?() ? item.getUri()}' has changes, do you want to save them?"
|
||||
detailedMessage: "Your changes will be lost if you close this item without saving."
|
||||
buttons: ["Save", "Cancel", "Don't Save"]
|
||||
|
||||
switch chosen
|
||||
when 0 then @saveItem(item, -> true)
|
||||
when 1 then false
|
||||
@@ -412,11 +412,11 @@ class Pane extends View
|
||||
|
||||
# Private:
|
||||
getContainer: ->
|
||||
@closest('#panes').view()
|
||||
@closest('.panes').view()
|
||||
|
||||
# Private:
|
||||
copyActiveItem: ->
|
||||
@activeItem.copy?() ? deserialize(@activeItem.serialize())
|
||||
@activeItem.copy?() ? atom.deserializers.deserialize(@activeItem.serialize())
|
||||
|
||||
# Private:
|
||||
remove: (selector, keepData) ->
|
||||
@@ -426,7 +426,7 @@ class Pane extends View
|
||||
# Private:
|
||||
beforeRemove: ->
|
||||
if @is(':has(:focus)')
|
||||
@getContainer().focusNextPane() or rootView?.focus()
|
||||
@getContainer().focusNextPane() or atom.workspaceView?.focus()
|
||||
else if @isActive()
|
||||
@getContainer().makeNextPaneActive()
|
||||
|
||||
|
||||
+119
-123
@@ -5,10 +5,9 @@ _ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
Q = require 'q'
|
||||
telepath = require 'telepath'
|
||||
{Range} = telepath
|
||||
|
||||
TextBuffer = require './text-buffer'
|
||||
EditSession = require './edit-session'
|
||||
Editor = require './editor'
|
||||
{Emitter} = require 'emissary'
|
||||
Directory = require './directory'
|
||||
Task = require './task'
|
||||
@@ -19,37 +18,52 @@ Git = require './git'
|
||||
# Ultimately, a project is a git directory that's been opened. It's a collection
|
||||
# of directories and files that you can operate on.
|
||||
module.exports =
|
||||
class Project
|
||||
class Project extends telepath.Model
|
||||
Emitter.includeInto(this)
|
||||
|
||||
@acceptsDocuments: true
|
||||
@version: 1
|
||||
|
||||
registerDeserializer(this)
|
||||
|
||||
# Private:
|
||||
@deserialize: (state) -> new Project(state)
|
||||
@properties
|
||||
buffers: []
|
||||
path: null
|
||||
|
||||
# Public: Find the local path for the given repository URL.
|
||||
@pathForRepositoryUrl: (repoUrl) ->
|
||||
[repoName] = url.parse(repoUrl).path.split('/')[-1..]
|
||||
repoName = repoName.replace(/\.git$/, '')
|
||||
path.join(config.get('core.projectHome'), repoName)
|
||||
path.join(atom.config.get('core.projectHome'), repoName)
|
||||
|
||||
rootDirectory: null
|
||||
editSessions: null
|
||||
ignoredPathRegexes: null
|
||||
openers: null
|
||||
# Private: Called by telepath.
|
||||
attached: ->
|
||||
for buffer in @buffers.getValues()
|
||||
buffer.once 'destroyed', (buffer) => @removeBuffer(buffer)
|
||||
|
||||
# Public:
|
||||
@openers = []
|
||||
@editors = []
|
||||
@setPath(@path)
|
||||
|
||||
# Private: Called by telepath.
|
||||
beforePersistence: ->
|
||||
@destroyUnretainedBuffers()
|
||||
|
||||
# Public: Register an opener for project files.
|
||||
#
|
||||
# An {Editor} will be used if no openers return a value.
|
||||
#
|
||||
# ## Example:
|
||||
# ```coffeescript
|
||||
# atom.project.registerOpener (filePath) ->
|
||||
# if path.extname(filePath) is '.toml'
|
||||
# return new TomlEditor(filePath)
|
||||
# ```
|
||||
#
|
||||
# * opener: A function to be called when a path is being opened.
|
||||
registerOpener: (opener) -> @openers.push(opener)
|
||||
|
||||
# Public:
|
||||
# Public: Remove a previously registered opener.
|
||||
unregisterOpener: (opener) -> _.remove(@openers, opener)
|
||||
|
||||
# Private:
|
||||
destroy: ->
|
||||
editSession.destroy() for editSession in @getEditSessions()
|
||||
editor.destroy() for editor in @getEditors()
|
||||
buffer.release() for buffer in @getBuffers()
|
||||
@destroyRepo()
|
||||
|
||||
@@ -59,51 +73,10 @@ class Project
|
||||
@repo.destroy()
|
||||
@repo = null
|
||||
|
||||
# Public: Establishes a new project at a given path.
|
||||
#
|
||||
# path - The {String} name of the path
|
||||
constructor: (pathOrState) ->
|
||||
@openers = []
|
||||
@editSessions = []
|
||||
@buffers = []
|
||||
|
||||
if pathOrState instanceof telepath.Document
|
||||
@state = pathOrState
|
||||
if projectPath = @state.remove('path')
|
||||
@setPath(projectPath)
|
||||
else
|
||||
@setPath(@constructor.pathForRepositoryUrl(@state.get('repoUrl')))
|
||||
|
||||
@state.get('buffers').each (bufferState) =>
|
||||
if buffer = deserialize(bufferState, project: this)
|
||||
@addBuffer(buffer, updateState: false)
|
||||
else
|
||||
@state = site.createDocument(deserializer: @constructor.name, version: @constructor.version, buffers: [])
|
||||
@setPath(pathOrState)
|
||||
|
||||
@state.get('buffers').on 'changed', ({index, insertedValues, removedValues, siteId}) =>
|
||||
return if siteId is @state.siteId
|
||||
|
||||
for removedBuffer in removedValues
|
||||
@removeBufferAtIndex(index, updateState: false)
|
||||
for insertedBuffer, i in insertedValues
|
||||
@addBufferAtIndex(deserialize(insertedBuffer, project: this), index + i, updateState: false)
|
||||
|
||||
# Private:
|
||||
serialize: ->
|
||||
state = @state.clone()
|
||||
state.set('path', @getPath())
|
||||
@destroyUnretainedBuffers()
|
||||
state.set('buffers', buffer.serialize() for buffer in @getBuffers())
|
||||
state
|
||||
|
||||
# Private:
|
||||
destroyUnretainedBuffers: ->
|
||||
buffer.destroy() for buffer in @getBuffers() when not buffer.isRetained()
|
||||
|
||||
# Public: ?
|
||||
getState: -> @state
|
||||
|
||||
# Public: Returns the {Git} repository if available.
|
||||
getRepo: -> @repo
|
||||
|
||||
@@ -113,6 +86,7 @@ class Project
|
||||
|
||||
# Public: Sets the project's fullpath.
|
||||
setPath: (projectPath) ->
|
||||
@path = projectPath
|
||||
@rootDirectory?.off()
|
||||
|
||||
@destroyRepo()
|
||||
@@ -125,27 +99,12 @@ class Project
|
||||
else
|
||||
@rootDirectory = null
|
||||
|
||||
if originUrl = @repo?.getOriginUrl()
|
||||
@state.set('repoUrl', originUrl)
|
||||
|
||||
@emit "path-changed"
|
||||
|
||||
# Public: Returns the name of the root directory.
|
||||
# Public: Returns the root {Directory} object for this project.
|
||||
getRootDirectory: ->
|
||||
@rootDirectory
|
||||
|
||||
# Public: Determines if a path is ignored via Atom configuration.
|
||||
isPathIgnored: (path) ->
|
||||
for segment in path.split("/")
|
||||
ignoredNames = config.get("core.ignoredNames") or []
|
||||
return true if _.contains(ignoredNames, segment)
|
||||
|
||||
@ignoreRepositoryPath(path)
|
||||
|
||||
# Public: Determines if a given path is ignored via repository configuration.
|
||||
ignoreRepositoryPath: (repositoryPath) ->
|
||||
config.get("core.hideGitIgnoredFiles") and @repo?.isPathIgnored(path.join(@getPath(), repositoryPath))
|
||||
|
||||
# Public: Given a uri, this resolves it relative to the project directory. If
|
||||
# the path is already absolute or if it is prefixed with a scheme, it is
|
||||
# returned unchanged.
|
||||
@@ -165,6 +124,7 @@ class Project
|
||||
|
||||
# Public: Make the given path relative to the project directory.
|
||||
relativize: (fullPath) ->
|
||||
return fullPath if fullPath?.match(/[A-Za-z0-9+-.]+:\/\//) # leave path alone if it has a scheme
|
||||
@rootDirectory?.relativize(fullPath) ? fullPath
|
||||
|
||||
# Public: Returns whether the given path is inside this project.
|
||||
@@ -172,14 +132,14 @@ class Project
|
||||
@rootDirectory?.contains(pathToCheck) ? false
|
||||
|
||||
# Public: Given a path to a file, this constructs and associates a new
|
||||
# {EditSession}, showing the file.
|
||||
# {Editor}, showing the file.
|
||||
#
|
||||
# * filePath:
|
||||
# The {String} path of the file to associate with
|
||||
# * editSessionOptions:
|
||||
# Options that you can pass to the {EditSession} constructor
|
||||
# * options:
|
||||
# Options that you can pass to the {Editor} constructor
|
||||
#
|
||||
# Returns a promise that resolves to an {EditSession}.
|
||||
# Returns a promise that resolves to an {Editor}.
|
||||
open: (filePath, options={}) ->
|
||||
filePath = @resolve(filePath)
|
||||
resource = null
|
||||
@@ -189,7 +149,7 @@ class Project
|
||||
Q(resource)
|
||||
else
|
||||
@bufferForPath(filePath).then (buffer) =>
|
||||
@buildEditSessionForBuffer(buffer, options)
|
||||
@buildEditorForBuffer(buffer, options)
|
||||
|
||||
# Private: Only be used in specs
|
||||
openSync: (filePath, options={}) ->
|
||||
@@ -197,42 +157,42 @@ class Project
|
||||
for opener in @openers
|
||||
return resource if resource = opener(filePath, options)
|
||||
|
||||
@buildEditSessionForBuffer(@bufferForPathSync(filePath), options)
|
||||
@buildEditorForBuffer(@bufferForPathSync(filePath), options)
|
||||
|
||||
# Public: Retrieves all {EditSession}s for all open files.
|
||||
# Public: Retrieves all {Editor}s for all open files.
|
||||
#
|
||||
# Returns an {Array} of {EditSession}s.
|
||||
getEditSessions: ->
|
||||
new Array(@editSessions...)
|
||||
# Returns an {Array} of {Editor}s.
|
||||
getEditors: ->
|
||||
new Array(@editors...)
|
||||
|
||||
# Public: Add the given {EditSession}.
|
||||
addEditSession: (editSession) ->
|
||||
@editSessions.push editSession
|
||||
@emit 'edit-session-created', editSession
|
||||
# Public: Add the given {Editor}.
|
||||
addEditor: (editor) ->
|
||||
@editors.push editor
|
||||
@emit 'editor-created', editor
|
||||
|
||||
# Public: Return and removes the given {EditSession}.
|
||||
removeEditSession: (editSession) ->
|
||||
_.remove(@editSessions, editSession)
|
||||
# Public: Return and removes the given {Editor}.
|
||||
removeEditor: (editor) ->
|
||||
_.remove(@editors, editor)
|
||||
|
||||
# Private: Retrieves all the {TextBuffer}s in the project; that is, the
|
||||
# buffers for all open files.
|
||||
#
|
||||
# Returns an {Array} of {TextBuffer}s.
|
||||
getBuffers: ->
|
||||
new Array(@buffers...)
|
||||
new Array(@buffers.getValues()...)
|
||||
|
||||
# Private: Is the buffer for the given path modified?
|
||||
isPathModified: (filePath) ->
|
||||
absoluteFilePath = @resolve(filePath)
|
||||
existingBuffer = _.find @buffers, (buffer) -> buffer.getPath() == absoluteFilePath
|
||||
existingBuffer?.isModified()
|
||||
@findBufferForPath(@resolve(filePath))?.isModified()
|
||||
|
||||
# Private:
|
||||
findBufferForPath: (filePath) ->
|
||||
_.find @buffers.getValues(), (buffer) -> buffer.getPath() == filePath
|
||||
|
||||
# Private: Only to be used in specs
|
||||
bufferForPathSync: (filePath) ->
|
||||
absoluteFilePath = @resolve(filePath)
|
||||
|
||||
if filePath
|
||||
existingBuffer = _.find @buffers, (buffer) -> buffer.getPath() == absoluteFilePath
|
||||
|
||||
existingBuffer = @findBufferForPath(absoluteFilePath) if filePath
|
||||
existingBuffer ? @buildBufferSync(absoluteFilePath)
|
||||
|
||||
# Private: Given a file path, this retrieves or creates a new {TextBuffer}.
|
||||
@@ -245,9 +205,7 @@ class Project
|
||||
# Returns a promise that resolves to the {TextBuffer}.
|
||||
bufferForPath: (filePath) ->
|
||||
absoluteFilePath = @resolve(filePath)
|
||||
if absoluteFilePath
|
||||
existingBuffer = _.find @buffers, (buffer) -> buffer.getPath() == absoluteFilePath
|
||||
|
||||
existingBuffer = @findBufferForPath(absoluteFilePath) if absoluteFilePath
|
||||
Q(existingBuffer ? @buildBuffer(absoluteFilePath))
|
||||
|
||||
# Private:
|
||||
@@ -256,9 +214,9 @@ class Project
|
||||
|
||||
# Private: DEPRECATED
|
||||
buildBufferSync: (absoluteFilePath) ->
|
||||
buffer = new TextBuffer({project: this, filePath: absoluteFilePath})
|
||||
buffer.loadSync()
|
||||
buffer = new TextBuffer({filePath: absoluteFilePath})
|
||||
@addBuffer(buffer)
|
||||
buffer.loadSync()
|
||||
buffer
|
||||
|
||||
# Private: Given a file path, this sets its {TextBuffer}.
|
||||
@@ -268,10 +226,11 @@ class Project
|
||||
#
|
||||
# Returns a promise that resolves to the {TextBuffer}.
|
||||
buildBuffer: (absoluteFilePath) ->
|
||||
buffer = new TextBuffer({project: this, filePath: absoluteFilePath})
|
||||
buffer.load().then (buffer) =>
|
||||
@addBuffer(buffer)
|
||||
buffer
|
||||
buffer = new TextBuffer({filePath: absoluteFilePath})
|
||||
@addBuffer(buffer)
|
||||
buffer.load()
|
||||
.then((buffer) -> buffer)
|
||||
.catch(=> @removeBuffer(buffer))
|
||||
|
||||
# Private:
|
||||
addBuffer: (buffer, options={}) ->
|
||||
@@ -279,9 +238,10 @@ class Project
|
||||
|
||||
# Private:
|
||||
addBufferAtIndex: (buffer, index, options={}) ->
|
||||
@buffers[index] = buffer
|
||||
@state.get('buffers').insert(index, buffer.getState()) if options.updateState ? true
|
||||
buffer = @buffers.insert(index, buffer)
|
||||
buffer.once 'destroyed', => @removeBuffer(buffer)
|
||||
@emit 'buffer-created', buffer
|
||||
buffer
|
||||
|
||||
# Private: Removes a {TextBuffer} association from the project.
|
||||
#
|
||||
@@ -293,7 +253,6 @@ class Project
|
||||
# Private:
|
||||
removeBufferAtIndex: (index, options={}) ->
|
||||
[buffer] = @buffers.splice(index, 1)
|
||||
@state.get('buffers')?.remove(index) if options.updateState ? true
|
||||
buffer?.destroy()
|
||||
|
||||
# Public: Performs a search across all the files in the project.
|
||||
@@ -315,8 +274,8 @@ class Project
|
||||
ignoreCase: regex.ignoreCase
|
||||
inclusions: options.paths
|
||||
includeHidden: true
|
||||
excludeVcsIgnores: config.get('core.excludeVcsIgnoredPaths')
|
||||
exclusions: config.get('core.ignoredNames')
|
||||
excludeVcsIgnores: atom.config.get('core.excludeVcsIgnoredPaths')
|
||||
exclusions: atom.config.get('core.ignoredNames')
|
||||
|
||||
task = Task.once require.resolve('./scan-handler'), @getPath(), regex.source, searchOptions, ->
|
||||
deferred.resolve()
|
||||
@@ -328,7 +287,7 @@ class Project
|
||||
task.on 'scan:paths-searched', (numberOfPathsSearched) ->
|
||||
options.onPathsSearched(numberOfPathsSearched)
|
||||
|
||||
for buffer in @buffers when buffer.isModified()
|
||||
for buffer in @buffers.getValues() when buffer.isModified()
|
||||
filePath = buffer.getPath()
|
||||
matches = []
|
||||
buffer.scan regex, (match) -> matches.push match
|
||||
@@ -336,16 +295,53 @@ class Project
|
||||
|
||||
deferred.promise
|
||||
|
||||
# Private:
|
||||
buildEditSessionForBuffer: (buffer, editSessionOptions) ->
|
||||
editSession = new EditSession(_.extend({buffer}, editSessionOptions))
|
||||
@addEditSession(editSession)
|
||||
editSession
|
||||
# Public: Performs a replace across all the specified files in the project.
|
||||
#
|
||||
# * regex: A RegExp to search with
|
||||
# * replacementText: Text to replace all matches of regex with
|
||||
# * filePaths: List of file path strings to run the replace on.
|
||||
# * iterator: A Function callback on each file with replacements. ({filePath, replacements}) ->
|
||||
replace: (regex, replacementText, filePaths, iterator) ->
|
||||
deferred = Q.defer()
|
||||
|
||||
openPaths = (buffer.getPath() for buffer in @buffers.getValues())
|
||||
outOfProcessPaths = _.difference(filePaths, openPaths)
|
||||
|
||||
inProcessFinished = !openPaths.length
|
||||
outOfProcessFinished = !outOfProcessPaths.length
|
||||
checkFinished = ->
|
||||
deferred.resolve() if outOfProcessFinished and inProcessFinished
|
||||
|
||||
unless outOfProcessFinished.length
|
||||
flags = 'g'
|
||||
flags += 'i' if regex.ignoreCase
|
||||
|
||||
task = Task.once require.resolve('./replace-handler'), outOfProcessPaths, regex.source, flags, replacementText, ->
|
||||
outOfProcessFinished = true
|
||||
checkFinished()
|
||||
|
||||
task.on 'replace:path-replaced', iterator
|
||||
|
||||
for buffer in @buffers.getValues()
|
||||
continue unless buffer.getPath() in filePaths
|
||||
replacements = buffer.replace(regex, replacementText, iterator)
|
||||
iterator({filePath: buffer.getPath(), replacements}) if replacements
|
||||
|
||||
inProcessFinished = true
|
||||
checkFinished()
|
||||
|
||||
deferred.promise
|
||||
|
||||
# Private:
|
||||
eachEditSession: (callback) ->
|
||||
callback(editSession) for editSession in @getEditSessions()
|
||||
@on 'edit-session-created', (editSession) -> callback(editSession)
|
||||
buildEditorForBuffer: (buffer, editorOptions) ->
|
||||
editor = new Editor(_.extend({buffer}, editorOptions))
|
||||
@addEditor(editor)
|
||||
editor
|
||||
|
||||
# Private:
|
||||
eachEditor: (callback) ->
|
||||
callback(editor) for editor in @getEditors()
|
||||
@on 'editor-created', (editor) -> callback(editor)
|
||||
|
||||
# Private:
|
||||
eachBuffer: (args...) ->
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{PathReplacer} = require 'scandal'
|
||||
|
||||
module.exports = (filePaths, regexSource, regexFlags, replacementText) ->
|
||||
callback = @async()
|
||||
|
||||
replacer = new PathReplacer()
|
||||
regex = new RegExp(regexSource, regexFlags)
|
||||
|
||||
replacer.on 'path-replaced', (result) ->
|
||||
emit('replace:path-replaced', result)
|
||||
|
||||
replacer.replacePaths regex, replacementText, filePaths, ->
|
||||
callback()
|
||||
+50
-18
@@ -1,5 +1,5 @@
|
||||
{$, View} = require './space-pen-extensions'
|
||||
Editor = require './editor'
|
||||
EditorView = require './editor-view'
|
||||
fuzzyFilter = require('fuzzaldrin').filter
|
||||
|
||||
# Public: Provides a widget for users to make a selection from a list of
|
||||
@@ -10,7 +10,7 @@ class SelectList extends View
|
||||
# Private:
|
||||
@content: ->
|
||||
@div class: @viewClass(), =>
|
||||
@subview 'miniEditor', new Editor(mini: true)
|
||||
@subview 'miniEditor', new EditorView(mini: true)
|
||||
@div class: 'error-message', outlet: 'error'
|
||||
@div class: 'loading', outlet: 'loadingArea', =>
|
||||
@span class: 'loading-message', outlet: 'loading'
|
||||
@@ -25,7 +25,10 @@ class SelectList extends View
|
||||
inputThrottle: 50
|
||||
cancelling: false
|
||||
|
||||
# Public:
|
||||
# Public: Initialize the select list view.
|
||||
#
|
||||
# This method can be overridden by subclasses but `super` should always
|
||||
# be called.
|
||||
initialize: ->
|
||||
@miniEditor.getBuffer().on 'changed', => @schedulePopulateList()
|
||||
@miniEditor.hiddenInput.on 'focusout', => @cancel() unless @cancelling
|
||||
@@ -57,12 +60,16 @@ class SelectList extends View
|
||||
@populateList() if @isOnDom()
|
||||
@scheduleTimeout = setTimeout(populateCallback, @inputThrottle)
|
||||
|
||||
# Public:
|
||||
setArray: (@array) ->
|
||||
# Public: Set the array of items to display in the list.
|
||||
#
|
||||
# * array: The array of model elements to display in the list.
|
||||
setArray: (@array=[]) ->
|
||||
@populateList()
|
||||
@setLoading()
|
||||
|
||||
# Public:
|
||||
# Public: Set the error message to display.
|
||||
#
|
||||
# * message: The error message.
|
||||
setError: (message='') ->
|
||||
if message.length is 0
|
||||
@error.text('').hide()
|
||||
@@ -70,7 +77,9 @@ class SelectList extends View
|
||||
@setLoading()
|
||||
@error.text(message).show()
|
||||
|
||||
# Public:
|
||||
# Public: Set the loading message to display.
|
||||
#
|
||||
# * message: The loading message.
|
||||
setLoading: (message='') ->
|
||||
if message.length is 0
|
||||
@loading.text("")
|
||||
@@ -81,11 +90,18 @@ class SelectList extends View
|
||||
@loading.text(message)
|
||||
@loadingArea.show()
|
||||
|
||||
# Public:
|
||||
# Public: Get the filter query to use when fuzzy filtering the visible
|
||||
# elements.
|
||||
#
|
||||
# By default this method returns the text in the mini editor but it can be
|
||||
# overridden by subclasses if needed.
|
||||
#
|
||||
# Returns a {String} to use when fuzzy filtering the elements to display.
|
||||
getFilterQuery: ->
|
||||
@miniEditor.getText()
|
||||
|
||||
# Public:
|
||||
# Public: Build the DOM elements using the array from the last call to
|
||||
# {.setArray}.
|
||||
populateList: ->
|
||||
return unless @array?
|
||||
|
||||
@@ -109,7 +125,12 @@ class SelectList extends View
|
||||
else
|
||||
@setError(@getEmptyMessage(@array.length, filteredArray.length))
|
||||
|
||||
# Public:
|
||||
# Public: Get the message to display when there are no items.
|
||||
#
|
||||
# Subclasses may override this method to customize the message.
|
||||
#
|
||||
# * itemCount: The number of items in the array specified to {.setArray}
|
||||
# * filteredItemCount: The number of items that pass the fuzzy filter test.
|
||||
getEmptyMessage: (itemCount, filteredItemCount) -> 'No matches found'
|
||||
|
||||
# Private:
|
||||
@@ -124,14 +145,14 @@ class SelectList extends View
|
||||
item = @list.find('li:first') unless item.length
|
||||
@selectItem(item)
|
||||
|
||||
# Public:
|
||||
# Private:
|
||||
selectItem: (item) ->
|
||||
return unless item.length
|
||||
@list.find('.selected').removeClass('selected')
|
||||
item.addClass 'selected'
|
||||
@scrollToItem(item)
|
||||
|
||||
# Public:
|
||||
# Private:
|
||||
scrollToItem: (item) ->
|
||||
scrollTop = @list.scrollTop()
|
||||
desiredTop = item.position().top + scrollTop
|
||||
@@ -142,15 +163,19 @@ class SelectList extends View
|
||||
else if desiredBottom > @list.scrollBottom()
|
||||
@list.scrollBottom(desiredBottom)
|
||||
|
||||
# Public:
|
||||
# Public: Get the selected DOM element.
|
||||
#
|
||||
# Call {.getSelectedElement} to get the selected model element.
|
||||
getSelectedItem: ->
|
||||
@list.find('li.selected')
|
||||
|
||||
# Public:
|
||||
# Public: Get the selected model element.
|
||||
#
|
||||
# Call {.getSelectedItem} to get the selected DOM element.
|
||||
getSelectedElement: ->
|
||||
@getSelectedItem().data('select-list-element')
|
||||
|
||||
# Public:
|
||||
# Private:
|
||||
confirmSelection: ->
|
||||
element = @getSelectedElement()
|
||||
if element?
|
||||
@@ -158,6 +183,13 @@ class SelectList extends View
|
||||
else
|
||||
@cancel()
|
||||
|
||||
# Public: Callback function for when a selection is made.
|
||||
#
|
||||
# This method should be overridden by subclasses.
|
||||
#
|
||||
# * element: The selected model element.
|
||||
confirmed: (element) ->
|
||||
|
||||
# Private:
|
||||
attach: ->
|
||||
@storeFocusedElement()
|
||||
@@ -171,14 +203,14 @@ class SelectList extends View
|
||||
if @previouslyFocusedElement?.isOnDom()
|
||||
@previouslyFocusedElement.focus()
|
||||
else
|
||||
rootView.focus()
|
||||
atom.workspaceView.focus()
|
||||
|
||||
# Public:
|
||||
# Private:
|
||||
cancelled: ->
|
||||
@miniEditor.setText('')
|
||||
@miniEditor.updateDisplay()
|
||||
|
||||
# Public:
|
||||
# Public: Cancel and close the select list dialog.
|
||||
cancel: ->
|
||||
@list.empty()
|
||||
@cancelling = true
|
||||
|
||||
@@ -11,12 +11,12 @@ class SelectionView extends View
|
||||
regions: null
|
||||
needsRemoval: false
|
||||
|
||||
initialize: ({@editor, @selection} = {}) ->
|
||||
initialize: ({@editorView, @selection} = {}) ->
|
||||
@regions = []
|
||||
@selection.on 'screen-range-changed', => @editor.requestDisplayUpdate()
|
||||
@selection.on 'screen-range-changed', => @editorView.requestDisplayUpdate()
|
||||
@selection.on 'destroyed', =>
|
||||
@needsRemoval = true
|
||||
@editor.requestDisplayUpdate()
|
||||
@editorView.requestDisplayUpdate()
|
||||
|
||||
if @selection.marker.isRemote()
|
||||
@addClass("site-#{@selection.marker.getOriginSiteId()}")
|
||||
@@ -26,7 +26,7 @@ class SelectionView extends View
|
||||
range = @getScreenRange()
|
||||
|
||||
@trigger 'selection:changed'
|
||||
@editor.highlightFoldsContainingBufferRange(@getBufferRange())
|
||||
@editorView.highlightFoldsContainingBufferRange(@getBufferRange())
|
||||
return if range.isEmpty()
|
||||
|
||||
rowSpan = range.end.row - range.start.row
|
||||
@@ -40,11 +40,11 @@ class SelectionView extends View
|
||||
@appendRegion(1, { row: range.end.row, column: 0 }, range.end)
|
||||
|
||||
appendRegion: (rows, start, end) ->
|
||||
{ lineHeight, charWidth } = @editor
|
||||
css = @editor.pixelPositionForScreenPosition(start)
|
||||
{ lineHeight, charWidth } = @editorView
|
||||
css = @editorView.pixelPositionForScreenPosition(start)
|
||||
css.height = lineHeight * rows
|
||||
if end
|
||||
css.width = @editor.pixelPositionForScreenPosition(end).left - css.left
|
||||
css.width = @editorView.pixelPositionForScreenPosition(end).left - css.left
|
||||
else
|
||||
css.right = 0
|
||||
|
||||
@@ -57,7 +57,7 @@ class SelectionView extends View
|
||||
startRow = start.row
|
||||
endRow = end.row
|
||||
endRow-- if end.column == 0
|
||||
@editor.pixelPositionForScreenPosition([((startRow + endRow + 1) / 2), start.column])
|
||||
@editorView.pixelPositionForScreenPosition([((startRow + endRow + 1) / 2), start.column])
|
||||
|
||||
clearRegions: ->
|
||||
region.remove() for region in @regions
|
||||
@@ -85,5 +85,5 @@ class SelectionView extends View
|
||||
@removeClass('highlighted')
|
||||
|
||||
remove: ->
|
||||
@editor.removeSelectionView(this)
|
||||
@editorView.removeSelectionView(this)
|
||||
super
|
||||
|
||||
+60
-55
@@ -2,27 +2,26 @@
|
||||
{Emitter} = require 'emissary'
|
||||
{pick} = require 'underscore-plus'
|
||||
|
||||
# Public: Represents a selection in the {EditSession}.
|
||||
# Public: Represents a selection in the {Editor}.
|
||||
module.exports =
|
||||
class Selection
|
||||
Emitter.includeInto(this)
|
||||
|
||||
cursor: null
|
||||
marker: null
|
||||
editSession: null
|
||||
editor: null
|
||||
initialScreenRange: null
|
||||
wordwise: false
|
||||
needsAutoscroll: null
|
||||
|
||||
|
||||
# Private:
|
||||
constructor: ({@cursor, @marker, @editSession}) ->
|
||||
constructor: ({@cursor, @marker, @editor}) ->
|
||||
@cursor.selection = this
|
||||
@marker.on 'changed', => @screenRangeChanged()
|
||||
@marker.on 'destroyed', =>
|
||||
@destroyed = true
|
||||
@editSession.removeSelection(this)
|
||||
@emit 'destroyed' unless @editSession.destroyed
|
||||
@editor.removeSelection(this)
|
||||
@emit 'destroyed' unless @editor.destroyed
|
||||
|
||||
# Private:
|
||||
destroy: ->
|
||||
@@ -65,7 +64,7 @@ class Selection
|
||||
# * options:
|
||||
# + A hash of options matching those found in {.setBufferRange}
|
||||
setScreenRange: (screenRange, options) ->
|
||||
@setBufferRange(@editSession.bufferRangeForScreenRange(screenRange), options)
|
||||
@setBufferRange(@editor.bufferRangeForScreenRange(screenRange), options)
|
||||
|
||||
# Public: Returns the buffer {Range} for the selection.
|
||||
getBufferRange: ->
|
||||
@@ -79,12 +78,12 @@ class Selection
|
||||
# + preserveFolds:
|
||||
# if `true`, the fold settings are preserved after the selection moves
|
||||
# + autoscroll:
|
||||
# if `true`, the {EditSession} scrolls to the new selection
|
||||
# if `true`, the {Editor} scrolls to the new selection
|
||||
setBufferRange: (bufferRange, options={}) ->
|
||||
bufferRange = Range.fromObject(bufferRange)
|
||||
@needsAutoscroll = options.autoscroll
|
||||
options.isReversed ?= @isReversed()
|
||||
@editSession.destroyFoldsIntersectingBufferRange(bufferRange) unless options.preserveFolds
|
||||
@editor.destroyFoldsIntersectingBufferRange(bufferRange) unless options.preserveFolds
|
||||
@modifySelection =>
|
||||
@cursor.needsAutoscroll = false if options.autoscroll?
|
||||
@marker.setBufferRange(bufferRange, options)
|
||||
@@ -102,7 +101,7 @@ class Selection
|
||||
|
||||
# Public: Returns the text in the selection.
|
||||
getText: ->
|
||||
@editSession.buffer.getTextInRange(@getBufferRange())
|
||||
@editor.buffer.getTextInRange(@getBufferRange())
|
||||
|
||||
# Public: Clears the selection, moving the marker to the head.
|
||||
clear: ->
|
||||
@@ -132,7 +131,7 @@ class Selection
|
||||
# * row:
|
||||
# The line Number to select (default: the row of the cursor)
|
||||
selectLine: (row=@cursor.getBufferPosition().row) ->
|
||||
range = @editSession.bufferRangeForBufferRow(row, includeNewline: true)
|
||||
range = @editor.bufferRangeForBufferRow(row, includeNewline: true)
|
||||
@setBufferRange(range)
|
||||
@linewise = true
|
||||
@wordwise = false
|
||||
@@ -202,7 +201,7 @@ class Selection
|
||||
|
||||
# Public: Selects all the text in the buffer.
|
||||
selectAll: ->
|
||||
@setBufferRange(@editSession.buffer.getRange(), autoscroll: false)
|
||||
@setBufferRange(@editor.buffer.getRange(), autoscroll: false)
|
||||
|
||||
# Public: Selects all the text from the current cursor position to the
|
||||
# beginning of the line.
|
||||
@@ -247,17 +246,17 @@ class Selection
|
||||
range = (@getGoalBufferRange() ? @getBufferRange()).copy()
|
||||
nextRow = range.end.row + 1
|
||||
|
||||
for row in [nextRow..@editSession.getLastBufferRow()]
|
||||
for row in [nextRow..@editor.getLastBufferRow()]
|
||||
range.start.row = row
|
||||
range.end.row = row
|
||||
clippedRange = @editSession.clipBufferRange(range)
|
||||
clippedRange = @editor.clipBufferRange(range)
|
||||
|
||||
if range.isEmpty()
|
||||
continue if range.end.column > 0 and clippedRange.end.column is 0
|
||||
else
|
||||
continue if clippedRange.isEmpty()
|
||||
|
||||
@editSession.addSelectionForBufferRange(range, goalBufferRange: range)
|
||||
@editor.addSelectionForBufferRange(range, goalBufferRange: range)
|
||||
break
|
||||
|
||||
# Public:
|
||||
@@ -274,14 +273,14 @@ class Selection
|
||||
for row in [previousRow..0]
|
||||
range.start.row = row
|
||||
range.end.row = row
|
||||
clippedRange = @editSession.clipBufferRange(range)
|
||||
clippedRange = @editor.clipBufferRange(range)
|
||||
|
||||
if range.isEmpty()
|
||||
continue if range.end.column > 0 and clippedRange.end.column is 0
|
||||
else
|
||||
continue if clippedRange.isEmpty()
|
||||
|
||||
@editSession.addSelectionForBufferRange(range, goalBufferRange: range)
|
||||
@editor.addSelectionForBufferRange(range, goalBufferRange: range)
|
||||
break
|
||||
|
||||
# Public: Replaces text at the current selection.
|
||||
@@ -302,7 +301,7 @@ class Selection
|
||||
# if `skip`, skips the undo stack for this operation.
|
||||
insertText: (text, options={}) ->
|
||||
oldBufferRange = @getBufferRange()
|
||||
@editSession.destroyFoldsContainingBufferRow(oldBufferRange.end.row)
|
||||
@editor.destroyFoldsContainingBufferRow(oldBufferRange.end.row)
|
||||
wasReversed = @isReversed()
|
||||
@clear()
|
||||
@cursor.needsAutoscroll = @cursor.isLastCursor()
|
||||
@@ -310,18 +309,18 @@ class Selection
|
||||
if options.indentBasis? and not options.autoIndent
|
||||
text = @normalizeIndents(text, options.indentBasis)
|
||||
|
||||
newBufferRange = @editSession.buffer.change(oldBufferRange, text, pick(options, 'undo'))
|
||||
newBufferRange = @editor.buffer.change(oldBufferRange, text, pick(options, 'undo'))
|
||||
if options.select
|
||||
@setBufferRange(newBufferRange, isReversed: wasReversed)
|
||||
else
|
||||
@cursor.setBufferPosition(newBufferRange.end, skipAtomicTokens: true) if wasReversed
|
||||
|
||||
if options.autoIndent
|
||||
@editSession.autoIndentBufferRow(row) for row in newBufferRange.getRows()
|
||||
@editor.autoIndentBufferRow(row) for row in newBufferRange.getRows()
|
||||
else if options.autoIndentNewline and text == '\n'
|
||||
@editSession.autoIndentBufferRow(newBufferRange.end.row)
|
||||
@editor.autoIndentBufferRow(newBufferRange.end.row)
|
||||
else if options.autoDecreaseIndent and /\S/.test text
|
||||
@editSession.autoDecreaseIndentForBufferRow(newBufferRange.start.row)
|
||||
@editor.autoDecreaseIndentForBufferRow(newBufferRange.start.row)
|
||||
|
||||
newBufferRange
|
||||
|
||||
@@ -336,9 +335,9 @@ class Selection
|
||||
isCursorInsideExistingLine = /\S/.test(textPrecedingCursor)
|
||||
|
||||
lines = text.split('\n')
|
||||
firstLineIndentLevel = @editSession.indentLevelForLine(lines[0])
|
||||
firstLineIndentLevel = @editor.indentLevelForLine(lines[0])
|
||||
if isCursorInsideExistingLine
|
||||
minimumIndentLevel = @editSession.indentationForBufferRow(@cursor.getBufferRow())
|
||||
minimumIndentLevel = @editor.indentationForBufferRow(@cursor.getBufferRow())
|
||||
else
|
||||
minimumIndentLevel = @cursor.getIndentLevel()
|
||||
|
||||
@@ -349,7 +348,7 @@ class Selection
|
||||
else if line == '' # remove all indentation from empty lines
|
||||
indentLevel = 0
|
||||
else
|
||||
lineIndentLevel = @editSession.indentLevelForLine(lines[i])
|
||||
lineIndentLevel = @editor.indentLevelForLine(lines[i])
|
||||
indentLevel = minimumIndentLevel + (lineIndentLevel - indentBasis)
|
||||
|
||||
normalizedLines.push(@setIndentationForLine(line, indentLevel))
|
||||
@@ -361,19 +360,19 @@ class Selection
|
||||
# * options - A hash with one key,
|
||||
# + autoIndent:
|
||||
# If `true`, the indentation is performed appropriately. Otherwise,
|
||||
# {EditSession.getTabText} is used
|
||||
# {Editor.getTabText} is used
|
||||
indent: ({ autoIndent }={})->
|
||||
{ row, column } = @cursor.getBufferPosition()
|
||||
|
||||
if @isEmpty()
|
||||
@cursor.skipLeadingWhitespace()
|
||||
desiredIndent = @editSession.suggestedIndentForBufferRow(row)
|
||||
desiredIndent = @editor.suggestedIndentForBufferRow(row)
|
||||
delta = desiredIndent - @cursor.getIndentLevel()
|
||||
|
||||
if autoIndent and delta > 0
|
||||
@insertText(@editSession.buildIndentString(delta))
|
||||
@insertText(@editor.buildIndentString(delta))
|
||||
else
|
||||
@insertText(@editSession.getTabText())
|
||||
@insertText(@editor.getTabText())
|
||||
else
|
||||
@indentSelectedRows()
|
||||
|
||||
@@ -381,18 +380,18 @@ class Selection
|
||||
indentSelectedRows: ->
|
||||
[start, end] = @getBufferRowRange()
|
||||
for row in [start..end]
|
||||
@editSession.buffer.insert([row, 0], @editSession.getTabText()) unless @editSession.buffer.lineLengthForRow(row) == 0
|
||||
@editor.buffer.insert([row, 0], @editor.getTabText()) unless @editor.buffer.lineLengthForRow(row) == 0
|
||||
|
||||
# Public: ?
|
||||
setIndentationForLine: (line, indentLevel) ->
|
||||
desiredIndentLevel = Math.max(0, indentLevel)
|
||||
desiredIndentString = @editSession.buildIndentString(desiredIndentLevel)
|
||||
desiredIndentString = @editor.buildIndentString(desiredIndentLevel)
|
||||
line.replace(/^[\t ]*/, desiredIndentString)
|
||||
|
||||
# Public: Removes the first character before the selection if the selection
|
||||
# is empty otherwise it deletes the selection.
|
||||
backspace: ->
|
||||
@selectLeft() if @isEmpty() and not @editSession.isFoldedAtScreenRow(@cursor.getScreenRow())
|
||||
@selectLeft() if @isEmpty() and not @editor.isFoldedAtScreenRow(@cursor.getScreenRow())
|
||||
@deleteSelectedText()
|
||||
|
||||
# Public: Removes from the start of the selection to the beginning of the
|
||||
@@ -414,7 +413,7 @@ class Selection
|
||||
# selection if the selection is empty.
|
||||
delete: ->
|
||||
if @isEmpty()
|
||||
if @cursor.isAtEndOfLine() and fold = @editSession.largestFoldStartingAtScreenRow(@cursor.getScreenRow() + 1)
|
||||
if @cursor.isAtEndOfLine() and fold = @editor.largestFoldStartingAtScreenRow(@cursor.getScreenRow() + 1)
|
||||
@selectToBufferPosition(fold.getBufferRange().end)
|
||||
else
|
||||
@selectRight()
|
||||
@@ -429,9 +428,9 @@ class Selection
|
||||
# Public: Removes only the selected text.
|
||||
deleteSelectedText: ->
|
||||
bufferRange = @getBufferRange()
|
||||
if bufferRange.isEmpty() and fold = @editSession.largestFoldContainingBufferRow(bufferRange.start.row)
|
||||
if bufferRange.isEmpty() and fold = @editor.largestFoldContainingBufferRow(bufferRange.start.row)
|
||||
bufferRange = bufferRange.union(fold.getBufferRange(includeNewline: true))
|
||||
@editSession.buffer.delete(bufferRange) unless bufferRange.isEmpty()
|
||||
@editor.buffer.delete(bufferRange) unless bufferRange.isEmpty()
|
||||
@cursor?.setBufferPosition(bufferRange.start)
|
||||
|
||||
# Public: Removes the line at the beginning of the selection if the selection
|
||||
@@ -440,18 +439,18 @@ class Selection
|
||||
deleteLine: ->
|
||||
if @isEmpty()
|
||||
start = @cursor.getScreenRow()
|
||||
range = @editSession.bufferRowsForScreenRows(start, start + 1)
|
||||
range = @editor.bufferRowsForScreenRows(start, start + 1)
|
||||
if range[1] > range[0]
|
||||
@editSession.buffer.deleteRows(range[0], range[1] - 1)
|
||||
@editor.buffer.deleteRows(range[0], range[1] - 1)
|
||||
else
|
||||
@editSession.buffer.deleteRow(range[0])
|
||||
@editor.buffer.deleteRow(range[0])
|
||||
else
|
||||
range = @getBufferRange()
|
||||
start = range.start.row
|
||||
end = range.end.row
|
||||
if end isnt @editSession.buffer.getLastRow() and range.end.column is 0
|
||||
if end isnt @editor.buffer.getLastRow() and range.end.column is 0
|
||||
end--
|
||||
@editSession.buffer.deleteRows(start, end)
|
||||
@editor.buffer.deleteRows(start, end)
|
||||
|
||||
# Public: Joins the current line with the one below it.
|
||||
#
|
||||
@@ -459,16 +458,16 @@ class Selection
|
||||
joinLine: ->
|
||||
selectedRange = @getBufferRange()
|
||||
if selectedRange.isEmpty()
|
||||
return if selectedRange.start.row is @editSession.buffer.getLastRow()
|
||||
return if selectedRange.start.row is @editor.buffer.getLastRow()
|
||||
else
|
||||
joinMarker = @editSession.markBufferRange(selectedRange, invalidationStrategy: 'never')
|
||||
joinMarker = @editor.markBufferRange(selectedRange, invalidationStrategy: 'never')
|
||||
|
||||
rowCount = Math.max(1, selectedRange.getRowCount() - 1)
|
||||
for row in [0...rowCount]
|
||||
@cursor.setBufferPosition([selectedRange.start.row])
|
||||
@cursor.moveToEndOfLine()
|
||||
nextRow = selectedRange.start.row + 1
|
||||
if nextRow <= @editSession.buffer.getLastRow() and @editSession.buffer.lineLengthForRow(nextRow) > 0
|
||||
if nextRow <= @editor.buffer.getLastRow() and @editor.buffer.lineLengthForRow(nextRow) > 0
|
||||
@insertText(' ')
|
||||
@cursor.moveToEndOfLine()
|
||||
@modifySelection =>
|
||||
@@ -484,8 +483,8 @@ class Selection
|
||||
# Public: Removes one level of indent from the currently selected rows.
|
||||
outdentSelectedRows: ->
|
||||
[start, end] = @getBufferRowRange()
|
||||
buffer = @editSession.buffer
|
||||
leadingTabRegex = new RegExp("^ {1,#{@editSession.getTabLength()}}|\t")
|
||||
buffer = @editor.buffer
|
||||
leadingTabRegex = new RegExp("^ {1,#{@editor.getTabLength()}}|\t")
|
||||
for row in [start..end]
|
||||
if matchLength = buffer.lineForRow(row).match(leadingTabRegex)?[0].length
|
||||
buffer.delete [[row, 0], [row, matchLength]]
|
||||
@@ -494,7 +493,7 @@ class Selection
|
||||
# by the relevant grammars.
|
||||
autoIndentSelectedRows: ->
|
||||
[start, end] = @getBufferRowRange()
|
||||
@editSession.autoIndentBufferRows(start, end)
|
||||
@editor.autoIndentBufferRows(start, end)
|
||||
|
||||
# Public: Wraps the selected lines in comments if they aren't currently part
|
||||
# of a comment.
|
||||
@@ -503,7 +502,7 @@ class Selection
|
||||
#
|
||||
# Returns an Array of the commented {Range}s.
|
||||
toggleLineComments: ->
|
||||
@editSession.toggleLineCommentsForBufferRows(@getBufferRowRange()...)
|
||||
@editor.toggleLineCommentsForBufferRows(@getBufferRowRange()...)
|
||||
|
||||
# Public: Cuts the selection until the end of the line.
|
||||
#
|
||||
@@ -527,19 +526,19 @@ class Selection
|
||||
# ?
|
||||
copy: (maintainPasteboard=false) ->
|
||||
return if @isEmpty()
|
||||
text = @editSession.buffer.getTextInRange(@getBufferRange())
|
||||
text = @editor.buffer.getTextInRange(@getBufferRange())
|
||||
if maintainPasteboard
|
||||
[currentText, metadata] = pasteboard.read()
|
||||
[currentText, metadata] = atom.pasteboard.read()
|
||||
text = currentText + '\n' + text
|
||||
else
|
||||
metadata = { indentBasis: @editSession.indentationForBufferRow(@getBufferRange().start.row) }
|
||||
metadata = { indentBasis: @editor.indentationForBufferRow(@getBufferRange().start.row) }
|
||||
|
||||
pasteboard.write(text, metadata)
|
||||
atom.pasteboard.write(text, metadata)
|
||||
|
||||
# Public: Creates a fold containing the current selection.
|
||||
fold: ->
|
||||
range = @getBufferRange()
|
||||
@editSession.createFold(range.start.row, range.end.row)
|
||||
@editor.createFold(range.start.row, range.end.row)
|
||||
@cursor.setBufferPosition([range.end.row + 1, 0])
|
||||
|
||||
# Public: ?
|
||||
@@ -592,9 +591,15 @@ class Selection
|
||||
@setBufferRange(@getBufferRange().union(otherSelection.getBufferRange()), options)
|
||||
otherSelection.destroy()
|
||||
|
||||
# Public: ?
|
||||
compare: (other) ->
|
||||
@getBufferRange().compare(other.getBufferRange())
|
||||
# Public: Compare this selection's buffer range to another selection's buffer
|
||||
# range.
|
||||
#
|
||||
# See {Range.compare} for more details.
|
||||
#
|
||||
# * otherSelection:
|
||||
# A {Selection} to compare with.
|
||||
compare: (otherSelection) ->
|
||||
@getBufferRange().compare(otherSelection.getBufferRange())
|
||||
|
||||
# Public: Returns true if it was locally created.
|
||||
isLocal: ->
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Private: TODO remove once telepath upgrades are complete.
|
||||
module.exports =
|
||||
class SiteShim
|
||||
constructor: (document) ->
|
||||
|
||||
@@ -15,4 +15,78 @@ jQuery.cleanData = (elements) ->
|
||||
view.unsubscribe()
|
||||
originalCleanData(elements)
|
||||
|
||||
tooltipDefaults =
|
||||
delay:
|
||||
show: 1000
|
||||
hide: 100
|
||||
container: 'body'
|
||||
html: true
|
||||
placement: 'auto top'
|
||||
viewportPadding: 2
|
||||
|
||||
modifiers =
|
||||
cmd: '⌘'
|
||||
ctrl: '⌃'
|
||||
alt: '⌥'
|
||||
option: '⌥'
|
||||
shift: '⇧'
|
||||
enter: '⏎'
|
||||
left: '←'
|
||||
right: '→'
|
||||
up: '↑'
|
||||
down: '↓'
|
||||
|
||||
replaceKey = (key) ->
|
||||
if modifiers[key]
|
||||
modifiers[key]
|
||||
else if key.length == 1 and key == key.toUpperCase() and key.toUpperCase() != key.toLowerCase()
|
||||
[modifiers.shift, key.toUpperCase()]
|
||||
else if key.length == 1
|
||||
key.toUpperCase()
|
||||
else
|
||||
key
|
||||
|
||||
replaceModifiersInSingleKeystroke = (keystroke) ->
|
||||
keys = keystroke.split('-')
|
||||
keys = _.flatten(replaceKey(key) for key in keys)
|
||||
keys.join('')
|
||||
|
||||
replaceModifiers = (keystroke) ->
|
||||
keystrokes = keystroke.split(' ')
|
||||
keystrokes = (replaceModifiersInSingleKeystroke(stroke) for stroke in keystrokes)
|
||||
keystrokes.join(' ')
|
||||
|
||||
getKeystroke = (bindings) ->
|
||||
if bindings?.length
|
||||
"<span class=\"keystroke\">#{replaceModifiers(bindings[0].keystroke)}</span>"
|
||||
else
|
||||
''
|
||||
# options from http://getbootstrap.com/javascript/#tooltips
|
||||
jQuery.fn.setTooltip = (tooltipOptions, {command, commandElement}={}) ->
|
||||
atom.requireWithGlobals('bootstrap/js/tooltip', {jQuery})
|
||||
|
||||
tooltipOptions = {title: tooltipOptions} if _.isString(tooltipOptions)
|
||||
|
||||
bindings = if commandElement
|
||||
atom.keymap.keyBindingsForCommandMatchingElement(command, commandElement)
|
||||
else
|
||||
atom.keymap.keyBindingsForCommand(command)
|
||||
|
||||
tooltipOptions.title = "#{tooltipOptions.title} #{getKeystroke(bindings)}"
|
||||
|
||||
this.tooltip(jQuery.extend({}, tooltipDefaults, tooltipOptions))
|
||||
|
||||
jQuery.fn.hideTooltip = ->
|
||||
tip = @data('bs.tooltip')
|
||||
if tip
|
||||
tip.leave(currentTarget: this)
|
||||
tip.hide()
|
||||
|
||||
jQuery.fn.destroyTooltip = ->
|
||||
@hideTooltip()
|
||||
@tooltip('destroy')
|
||||
|
||||
jQuery.fn.setTooltip.getKeystroke = getKeystroke
|
||||
jQuery.fn.setTooltip.replaceModifiers = replaceModifiers
|
||||
|
||||
module.exports = spacePen
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ module.exports =
|
||||
class Syntax
|
||||
Emitter.includeInto(this)
|
||||
|
||||
registerDeserializer(this)
|
||||
atom.deserializers.add(this)
|
||||
|
||||
@deserialize: ({grammarOverridesByPath}) ->
|
||||
syntax = new Syntax()
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ class Task
|
||||
@callback = args.pop() if _.isFunction(args[args.length - 1])
|
||||
@send({event: 'start', args})
|
||||
|
||||
# Public: Send message to the task
|
||||
# Public: Send message to the task.
|
||||
#
|
||||
# * message:
|
||||
# The message to send
|
||||
|
||||
+57
-72
@@ -1,32 +1,28 @@
|
||||
crypto = require 'crypto'
|
||||
_ = require 'underscore-plus'
|
||||
{Emitter, Subscriber} = require 'emissary'
|
||||
guid = require 'guid'
|
||||
Q = require 'q'
|
||||
{P} = require 'scandal'
|
||||
telepath = require 'telepath'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
File = require './file'
|
||||
|
||||
{Point, Range} = telepath
|
||||
|
||||
# Private: Represents the contents of a file.
|
||||
#
|
||||
# The `Buffer` is often associated with a {File}. However, this is not always
|
||||
# the case, as a `Buffer` could be an unsaved chunk of text.
|
||||
# The `TextBuffer` is often associated with a {File}. However, this is not always
|
||||
# the case, as a `TextBuffer` could be an unsaved chunk of text.
|
||||
module.exports =
|
||||
class TextBuffer
|
||||
class TextBuffer extends telepath.Model
|
||||
Emitter.includeInto(this)
|
||||
Subscriber.includeInto(this)
|
||||
|
||||
@acceptsDocuments: true
|
||||
@version: 2
|
||||
registerDeserializer(this)
|
||||
|
||||
@deserialize: (state, params) ->
|
||||
buffer = new this(state, params)
|
||||
buffer.load()
|
||||
buffer
|
||||
@properties
|
||||
text: -> new telepath.String('', replicated: false)
|
||||
filePath: null
|
||||
relativePath: null
|
||||
modifiedWhenLastPersisted: false
|
||||
digestWhenLastPersisted: null
|
||||
|
||||
stoppedChangingDelay: 300
|
||||
stoppedChangingTimeout: null
|
||||
@@ -36,34 +32,28 @@ class TextBuffer
|
||||
file: null
|
||||
refcount: 0
|
||||
|
||||
# Creates a new buffer.
|
||||
#
|
||||
# * optionsOrState - An {Object} or a telepath.Document
|
||||
# + filePath - A {String} representing the file path
|
||||
constructor: (optionsOrState={}, params={}) ->
|
||||
if optionsOrState instanceof telepath.Document
|
||||
{@project} = params
|
||||
@state = optionsOrState
|
||||
@id = @state.get('id')
|
||||
filePath = @state.get('relativePath')
|
||||
@text = @state.get('text')
|
||||
@useSerializedText = @state.get('isModified') != false
|
||||
else
|
||||
{@project, filePath} = optionsOrState
|
||||
@text = new telepath.String(initialText ? '', replicated: false)
|
||||
@id = guid.create().toString()
|
||||
@state = site.createDocument
|
||||
id: @id
|
||||
deserializer: @constructor.name
|
||||
version: @constructor.version
|
||||
text: @text
|
||||
constructor: ->
|
||||
super
|
||||
|
||||
@loadWhenAttached = @getState()?
|
||||
|
||||
# Private: Called by telepath.
|
||||
attached: ->
|
||||
@loaded = false
|
||||
@useSerializedText = @modifiedWhenLastPersisted != false
|
||||
|
||||
@subscribe @text, 'changed', @handleTextChange
|
||||
@subscribe @text, 'marker-created', (marker) => @emit 'marker-created', marker
|
||||
@subscribe @text, 'markers-updated', => @emit 'markers-updated'
|
||||
|
||||
@setPath(@project.resolve(filePath)) if @project
|
||||
@setPath(@filePath)
|
||||
|
||||
@load() if @loadWhenAttached
|
||||
|
||||
# Private: Called by telepath.
|
||||
beforePersistence: ->
|
||||
@modifiedWhenLastPersisted = @isModified()
|
||||
@digestWhenLastPersisted = @file?.getDigest()
|
||||
|
||||
loadSync: ->
|
||||
@updateCachedDiskContentsSync()
|
||||
@@ -74,7 +64,7 @@ class TextBuffer
|
||||
|
||||
finishLoading: ->
|
||||
@loaded = true
|
||||
if @useSerializedText and @state.get('diskContentsDigest') == @file?.getDigest()
|
||||
if @useSerializedText and @digestWhenLastPersisted is @file?.getDigest()
|
||||
@emitModifiedStatusChanged(true)
|
||||
else
|
||||
@reload()
|
||||
@@ -92,11 +82,11 @@ class TextBuffer
|
||||
|
||||
destroy: ->
|
||||
unless @destroyed
|
||||
@cancelStoppedChangingTimeout()
|
||||
@file?.off()
|
||||
@unsubscribe()
|
||||
@destroyed = true
|
||||
@project?.removeBuffer(this)
|
||||
@emit 'destroyed'
|
||||
@emit 'destroyed', this
|
||||
|
||||
isRetained: -> @refcount > 0
|
||||
|
||||
@@ -109,16 +99,6 @@ class TextBuffer
|
||||
@destroy() unless @isRetained()
|
||||
this
|
||||
|
||||
serialize: ->
|
||||
state = @state.clone()
|
||||
state.set('isModified', @isModified())
|
||||
state.set('diskContentsDigest', @file.getDigest()) if @file
|
||||
for marker in state.get('text').getMarkers() when marker.isRemote()
|
||||
marker.destroy()
|
||||
state
|
||||
|
||||
getState: -> @state
|
||||
|
||||
subscribeToFile: ->
|
||||
@file.on "contents-changed", =>
|
||||
@conflict = true if @isModified()
|
||||
@@ -140,19 +120,18 @@ class TextBuffer
|
||||
@emitModifiedStatusChanged(@isModified())
|
||||
|
||||
@file.on "moved", =>
|
||||
@state.set('relativePath', @project.relativize(@getPath()))
|
||||
@emit "path-changed", this
|
||||
|
||||
### Public ###
|
||||
|
||||
# Identifies if the buffer belongs to multiple editors.
|
||||
#
|
||||
# For example, if the {Editor} was split.
|
||||
# For example, if the {EditorView} was split.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
hasMultipleEditors: -> @refcount > 1
|
||||
|
||||
# Reloads a file in the {EditSession}.
|
||||
# Reloads a file in the {Editor}.
|
||||
#
|
||||
# Sets the buffer's content to the cached disk contents
|
||||
reload: ->
|
||||
@@ -183,10 +162,7 @@ class TextBuffer
|
||||
@file?.getPath()
|
||||
|
||||
getUri: ->
|
||||
@getRelativePath()
|
||||
|
||||
getRelativePath: ->
|
||||
@state.get('relativePath')
|
||||
atom.project.relativize(@getPath())
|
||||
|
||||
# Sets the path for the file.
|
||||
#
|
||||
@@ -202,7 +178,6 @@ class TextBuffer
|
||||
else
|
||||
@file = null
|
||||
|
||||
@state.set('relativePath', @project.relativize(path))
|
||||
@emit "path-changed", this
|
||||
|
||||
# Retrieves the current buffer's file extension.
|
||||
@@ -411,12 +386,12 @@ class TextBuffer
|
||||
saveAs: (path) ->
|
||||
unless path then throw new Error("Can't save buffer with no file path")
|
||||
|
||||
@emit 'will-be-saved'
|
||||
@emit 'will-be-saved', this
|
||||
@setPath(path)
|
||||
@cachedDiskContents = @getText()
|
||||
@file.write(@getText())
|
||||
@emitModifiedStatusChanged(false)
|
||||
@emit 'saved'
|
||||
@emit 'saved', this
|
||||
|
||||
# Identifies if the buffer was modified.
|
||||
#
|
||||
@@ -538,6 +513,25 @@ class TextBuffer
|
||||
result.lineTextOffset = 0
|
||||
iterator(result)
|
||||
|
||||
# Replace all matches of regex with replacementText
|
||||
#
|
||||
# regex: A {RegExp} representing the text to find
|
||||
# replacementText: A {String} representing the text to replace
|
||||
#
|
||||
# Returns the number of replacements made
|
||||
replace: (regex, replacementText) ->
|
||||
doSave = !@isModified()
|
||||
replacements = 0
|
||||
|
||||
@transact =>
|
||||
@scan regex, ({matchText, replace}) ->
|
||||
replace(matchText.replace(regex, replacementText))
|
||||
replacements++
|
||||
|
||||
@save() if doSave
|
||||
|
||||
replacements
|
||||
|
||||
# Scans for text in a given range, calling a function on each match.
|
||||
#
|
||||
# regex - A {RegExp} representing the text to find
|
||||
@@ -634,12 +628,6 @@ class TextBuffer
|
||||
return match[0][0] != '\t'
|
||||
undefined
|
||||
|
||||
# Checks out the current `HEAD` revision of the file.
|
||||
checkoutHead: ->
|
||||
path = @getPath()
|
||||
return unless path
|
||||
@project.getRepo()?.checkoutHead(path)
|
||||
|
||||
### Internal ###
|
||||
|
||||
transact: (fn) -> @text.transact fn
|
||||
@@ -661,8 +649,11 @@ class TextBuffer
|
||||
else
|
||||
text
|
||||
|
||||
scheduleModifiedEvents: ->
|
||||
cancelStoppedChangingTimeout: ->
|
||||
clearTimeout(@stoppedChangingTimeout) if @stoppedChangingTimeout
|
||||
|
||||
scheduleModifiedEvents: ->
|
||||
@cancelStoppedChangingTimeout()
|
||||
stoppedChangingCallback = =>
|
||||
@stoppedChangingTimeout = null
|
||||
modifiedStatus = @isModified()
|
||||
@@ -679,9 +670,3 @@ class TextBuffer
|
||||
for row in [start..end]
|
||||
line = @lineForRow(row)
|
||||
console.log row, line, line.length
|
||||
|
||||
getDebugSnapshot: ->
|
||||
lines = ['Buffer:']
|
||||
for row in [0..@getLastRow()]
|
||||
lines.push "#{row}: #{@lineForRow(row)}"
|
||||
lines.join('\n')
|
||||
|
||||
@@ -68,14 +68,14 @@ class TextMateGrammar
|
||||
grammarUpdated: (scopeName) ->
|
||||
return false unless _.include(@includedGrammarScopes, scopeName)
|
||||
@clearRules()
|
||||
syntax.grammarUpdated(@scopeName)
|
||||
atom.syntax.grammarUpdated(@scopeName)
|
||||
@emit 'grammar-updated'
|
||||
true
|
||||
|
||||
getScore: (filePath, contents) ->
|
||||
contents = fs.readFileSync(filePath, 'utf8') if not contents? and fs.isFileSync(filePath)
|
||||
|
||||
if syntax.grammarOverrideForPath(filePath) is @scopeName
|
||||
if atom.syntax.grammarOverrideForPath(filePath) is @scopeName
|
||||
2 + (filePath?.length ? 0)
|
||||
else if @matchesContents(contents)
|
||||
1 + (filePath?.length ? 0)
|
||||
@@ -157,7 +157,16 @@ class TextMateGrammar
|
||||
ruleStack.pop()
|
||||
else if ruleStack.length > previousRuleStackLength # Stack size increased with zero length match
|
||||
[penultimateRule, lastRule] = ruleStack[-2..]
|
||||
|
||||
# Same exact rule was pushed but position wasn't advanced
|
||||
if lastRule? and lastRule == penultimateRule
|
||||
popStack = true
|
||||
|
||||
# Rule with same scope name as previous rule was pushed but position wasn't advanced
|
||||
if lastRule?.scopeName? and penultimateRule.scopeName == lastRule.scopeName
|
||||
popStack = true
|
||||
|
||||
if popStack
|
||||
ruleStack.pop()
|
||||
tokens.push(new Token(
|
||||
value: line[position...line.length]
|
||||
@@ -288,7 +297,7 @@ class Rule
|
||||
results.push(result)
|
||||
|
||||
scopes = scopesFromStack(ruleStack)
|
||||
for injectionGrammar in _.without(syntax.injectionGrammars, @grammar, baseGrammar)
|
||||
for injectionGrammar in _.without(atom.syntax.injectionGrammars, @grammar, baseGrammar)
|
||||
if injectionGrammar.injectionSelector.matches(scopes)
|
||||
scanner = injectionGrammar.getInitialRule().getScanner(injectionGrammar, position, firstLine)
|
||||
if result = scanner.findNextMatch(lineWithNewline, position)
|
||||
@@ -409,7 +418,7 @@ class Pattern
|
||||
baseGrammar.getInitialRule()
|
||||
else
|
||||
@grammar.addIncludedGrammarScope(name)
|
||||
syntax.grammarForScopeName(name)?.getInitialRule()
|
||||
atom.syntax.grammarForScopeName(name)?.getInitialRule()
|
||||
|
||||
getIncludedPatterns: (baseGrammar, included) ->
|
||||
if @include
|
||||
|
||||
@@ -41,15 +41,15 @@ class TextMatePackage extends Package
|
||||
|
||||
activate: ->
|
||||
@measure 'activateTime', =>
|
||||
syntax.addGrammar(grammar) for grammar in @grammars
|
||||
atom.syntax.addGrammar(grammar) for grammar in @grammars
|
||||
for { selector, properties } in @scopedProperties
|
||||
syntax.addProperties(@path, selector, properties)
|
||||
atom.syntax.addProperties(@path, selector, properties)
|
||||
|
||||
activateConfig: -> # noop
|
||||
|
||||
deactivate: ->
|
||||
syntax.removeGrammar(grammar) for grammar in @grammars
|
||||
syntax.removeProperties(@path)
|
||||
atom.syntax.removeGrammar(grammar) for grammar in @grammars
|
||||
atom.syntax.removeProperties(@path)
|
||||
|
||||
legalGrammarExtensions: ['plist', 'tmLanguage', 'tmlanguage', 'json', 'cson']
|
||||
|
||||
@@ -77,7 +77,7 @@ class TextMatePackage extends Package
|
||||
|
||||
addGrammar: (grammar) ->
|
||||
@grammars.push(grammar)
|
||||
syntax.addGrammar(grammar) if @isActive()
|
||||
atom.syntax.addGrammar(grammar) if @isActive()
|
||||
|
||||
getGrammars: -> @grammars
|
||||
|
||||
@@ -98,38 +98,38 @@ class TextMatePackage extends Package
|
||||
loadScopedPropertiesSync: ->
|
||||
for grammar in @getGrammars()
|
||||
if properties = @propertiesFromTextMateSettings(grammar)
|
||||
selector = syntax.cssSelectorFromScopeSelector(grammar.scopeName)
|
||||
selector = atom.syntax.cssSelectorFromScopeSelector(grammar.scopeName)
|
||||
@scopedProperties.push({selector, properties})
|
||||
|
||||
for preferencePath in fs.listSync(@getPreferencesPath())
|
||||
{scope, settings} = fs.readObjectSync(preferencePath)
|
||||
if properties = @propertiesFromTextMateSettings(settings)
|
||||
selector = syntax.cssSelectorFromScopeSelector(scope) if scope?
|
||||
selector = atom.syntax.cssSelectorFromScopeSelector(scope) if scope?
|
||||
@scopedProperties.push({selector, properties})
|
||||
|
||||
if @isActive()
|
||||
for {selector, properties} in @scopedProperties
|
||||
syntax.addProperties(@path, selector, properties)
|
||||
atom.syntax.addProperties(@path, selector, properties)
|
||||
|
||||
loadScopedProperties: (callback) ->
|
||||
scopedProperties = []
|
||||
|
||||
for grammar in @getGrammars()
|
||||
if properties = @propertiesFromTextMateSettings(grammar)
|
||||
selector = syntax.cssSelectorFromScopeSelector(grammar.scopeName)
|
||||
selector = atom.syntax.cssSelectorFromScopeSelector(grammar.scopeName)
|
||||
scopedProperties.push({selector, properties})
|
||||
|
||||
preferenceObjects = []
|
||||
done = =>
|
||||
for {scope, settings} in preferenceObjects
|
||||
if properties = @propertiesFromTextMateSettings(settings)
|
||||
selector = syntax.cssSelectorFromScopeSelector(scope) if scope?
|
||||
selector = atom.syntax.cssSelectorFromScopeSelector(scope) if scope?
|
||||
scopedProperties.push({selector, properties})
|
||||
|
||||
@scopedProperties = scopedProperties
|
||||
if @isActive()
|
||||
for {selector, properties} in @scopedProperties
|
||||
syntax.addProperties(@path, selector, properties)
|
||||
atom.syntax.addProperties(@path, selector, properties)
|
||||
callback?()
|
||||
@loadTextMatePreferenceObjects(preferenceObjects, done)
|
||||
|
||||
|
||||
+20
-17
@@ -8,16 +8,14 @@ Package = require './package'
|
||||
AtomPackage = require './atom-package'
|
||||
{$} = require './space-pen-extensions'
|
||||
|
||||
|
||||
# Private: Handles discovering and loading available themes.
|
||||
###
|
||||
Themes are a subset of packages
|
||||
###
|
||||
#
|
||||
# Themes are a subset of packages
|
||||
module.exports =
|
||||
class ThemeManager
|
||||
Emitter.includeInto(this)
|
||||
|
||||
constructor: (@packageManager) ->
|
||||
constructor: ({@packageManager, @resourcePath, @configDirPath}) ->
|
||||
@lessCache = null
|
||||
@packageManager.registerPackageActivator(this, ['theme'])
|
||||
|
||||
@@ -56,6 +54,7 @@ class ThemeManager
|
||||
themeNames = _.clone(themeNames).reverse()
|
||||
|
||||
@packageManager.activatePackage(themeName) for themeName in themeNames
|
||||
@refreshLessCache()
|
||||
@loadUserStylesheet()
|
||||
@reloadBaseStylesheets()
|
||||
@emit('reloaded')
|
||||
@@ -66,6 +65,10 @@ class ThemeManager
|
||||
@packageManager.deactivatePackage(pack.name) for pack in @getActiveThemes()
|
||||
null
|
||||
|
||||
# Internal-only:
|
||||
refreshLessCache: ->
|
||||
@lessCache?.setImportPaths(@getImportPaths())
|
||||
|
||||
# Public: Set the list of enabled themes.
|
||||
#
|
||||
# * enabledThemeNames: An {Array} of {String} theme names.
|
||||
@@ -87,7 +90,7 @@ class ThemeManager
|
||||
|
||||
# Public:
|
||||
getUserStylesheetPath: ->
|
||||
stylesheetPath = fs.resolve(path.join(atom.config.configDirPath, 'user'), ['css', 'less'])
|
||||
stylesheetPath = fs.resolve(path.join(@configDirPath, 'user'), ['css', 'less'])
|
||||
if fs.isFileSync(stylesheetPath)
|
||||
stylesheetPath
|
||||
else
|
||||
@@ -112,8 +115,8 @@ class ThemeManager
|
||||
@requireStylesheet(nativeStylesheetPath)
|
||||
|
||||
# Internal-only:
|
||||
stylesheetElementForId: (id) ->
|
||||
$("""head style[id="#{id}"]""")
|
||||
stylesheetElementForId: (id, htmlElement=$('html')) ->
|
||||
htmlElement.find("""head style[id="#{id}"]""")
|
||||
|
||||
# Internal-only:
|
||||
resolveStylesheet: (stylesheetPath) ->
|
||||
@@ -128,10 +131,10 @@ class ThemeManager
|
||||
# LESS file in the stylesheets path.
|
||||
#
|
||||
# Returns the absolute path to the stylesheet
|
||||
requireStylesheet: (stylesheetPath) ->
|
||||
requireStylesheet: (stylesheetPath, ttype = 'bundled', htmlElement) ->
|
||||
if fullPath = @resolveStylesheet(stylesheetPath)
|
||||
content = @loadStylesheet(fullPath)
|
||||
@applyStylesheet(fullPath, content)
|
||||
@applyStylesheet(fullPath, content, ttype = 'bundled', htmlElement)
|
||||
else
|
||||
throw new Error("Could not find a file at path '#{stylesheetPath}'")
|
||||
|
||||
@@ -146,9 +149,9 @@ class ThemeManager
|
||||
|
||||
# Internal-only:
|
||||
loadLessStylesheet: (lessStylesheetPath) ->
|
||||
unless lessCache?
|
||||
unless @lessCache?
|
||||
LessCompileCache = require './less-compile-cache'
|
||||
@lessCache = new LessCompileCache()
|
||||
@lessCache = new LessCompileCache({@resourcePath, importPaths: @getImportPaths()})
|
||||
|
||||
try
|
||||
@lessCache.read(lessStylesheetPath)
|
||||
@@ -170,12 +173,12 @@ class ThemeManager
|
||||
@stylesheetElementForId(@stringToId(fullPath)).remove()
|
||||
|
||||
# Internal-only:
|
||||
applyStylesheet: (path, text, ttype = 'bundled') ->
|
||||
styleElement = @stylesheetElementForId(@stringToId(path))
|
||||
applyStylesheet: (path, text, ttype = 'bundled', htmlElement=$('html')) ->
|
||||
styleElement = @stylesheetElementForId(@stringToId(path), htmlElement)
|
||||
if styleElement.length
|
||||
styleElement.text(text)
|
||||
else
|
||||
if $("head style.#{ttype}").length
|
||||
$("head style.#{ttype}:last").after "<style class='#{ttype}' id='#{@stringToId(path)}'>#{text}</style>"
|
||||
if htmlElement.find("head style.#{ttype}").length
|
||||
htmlElement.find("head style.#{ttype}:last").after "<style class='#{ttype}' id='#{@stringToId(path)}'>#{text}</style>"
|
||||
else
|
||||
$("head").append "<style class='#{ttype}' id='#{@stringToId(path)}'>#{text}</style>"
|
||||
htmlElement.find("head").append "<style class='#{ttype}' id='#{@stringToId(path)}'>#{text}</style>"
|
||||
|
||||
@@ -21,7 +21,7 @@ class TokenizedBuffer
|
||||
visible: false
|
||||
|
||||
@acceptsDocuments: true
|
||||
registerDeserializer(this)
|
||||
atom.deserializers.add(this)
|
||||
|
||||
@deserialize: (state) ->
|
||||
new this(state)
|
||||
@@ -31,15 +31,15 @@ class TokenizedBuffer
|
||||
@state = optionsOrState
|
||||
|
||||
# TODO: This needs to be made async, but should wait until the new Telepath changes land
|
||||
@buffer = project.bufferForPathSync(optionsOrState.get('bufferPath'))
|
||||
@buffer = atom.project.bufferForPathSync(optionsOrState.get('bufferPath'))
|
||||
else
|
||||
{ @buffer, tabLength } = optionsOrState
|
||||
@state = site.createDocument
|
||||
@state = atom.site.createDocument
|
||||
deserializer: @constructor.name
|
||||
bufferPath: @buffer.getRelativePath()
|
||||
tabLength: tabLength ? config.get('editor.tabLength') ? 2
|
||||
bufferPath: @buffer.getPath()
|
||||
tabLength: tabLength ? atom.config.get('editor.tabLength') ? 2
|
||||
|
||||
@subscribe syntax, 'grammar-added grammar-updated', (grammar) =>
|
||||
@subscribe atom.syntax, 'grammar-added grammar-updated', (grammar) =>
|
||||
if grammar.injectionSelector?
|
||||
@resetTokenizedLines() if @hasTokenForSelector(grammar.injectionSelector)
|
||||
else
|
||||
@@ -48,7 +48,7 @@ class TokenizedBuffer
|
||||
|
||||
@on 'grammar-changed grammar-updated', => @resetTokenizedLines()
|
||||
@subscribe @buffer, "changed", (e) => @handleBufferChange(e)
|
||||
@subscribe @buffer, "path-changed", => @state.set('bufferPath', @buffer.getRelativePath())
|
||||
@subscribe @buffer, "path-changed", => @state.set('bufferPath', @buffer.getPath())
|
||||
|
||||
@reloadGrammar()
|
||||
|
||||
@@ -64,7 +64,7 @@ class TokenizedBuffer
|
||||
@emit 'grammar-changed', grammar
|
||||
|
||||
reloadGrammar: ->
|
||||
if grammar = syntax.selectGrammar(@buffer.getPath(), @buffer.getText())
|
||||
if grammar = atom.syntax.selectGrammar(@buffer.getPath(), @buffer.getText())
|
||||
@setGrammar(grammar)
|
||||
else
|
||||
throw new Error("No grammar found for path: #{path}")
|
||||
@@ -322,9 +322,3 @@ class TokenizedBuffer
|
||||
for row in [start..end]
|
||||
line = @lineForScreenRow(row).text
|
||||
console.log row, line, line.length
|
||||
|
||||
getDebugSnapshot: ->
|
||||
lines = ["Tokenized Buffer:"]
|
||||
for screenLine, row in @linesForScreenRows(0, @getLastRow())
|
||||
lines.push "#{row}: #{screenLine.text}"
|
||||
lines.join('\n')
|
||||
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário