Comparar commits
603 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 0eb44e06d4 | |||
| 94b7fc8a75 | |||
| 0fde4382d2 | |||
| 4e1434eafa | |||
| 7cffe77efa | |||
| f9e94e9779 | |||
| 59a4cbafcf | |||
| 9c066d93fa | |||
| e754689014 | |||
| 6d89c893b7 | |||
| 16df6a32d6 | |||
| cdb6de05b3 | |||
| 1b262eadaa | |||
| fe31d2d28a | |||
| 71e83962ac | |||
| f79039e4f7 | |||
| 08e6dc20e1 | |||
| 4f14d8a64f | |||
| da53dfc903 | |||
| 875cfefd36 | |||
| b6fb996ceb | |||
| 316559ae09 | |||
| 08d28d7ed4 | |||
| 3a3adf43a4 | |||
| 0951305962 | |||
| 481935c880 | |||
| 8ffcdad89a | |||
| 9e3ce09658 | |||
| 8c5d0775bc | |||
| 36e8b10917 | |||
| b2b048994c | |||
| 6db23e67e8 | |||
| 7872e95561 | |||
| 45eeee9aea | |||
| 9cbc693c77 | |||
| ee9d4ab70e | |||
| 68c3113b75 | |||
| 3a71b0470b | |||
| 421cdb41b9 | |||
| a8538a1361 | |||
| c3cedd2dc9 | |||
| da38218941 | |||
| c700ca0430 | |||
| 0fe3f2d03c | |||
| 65b1e13b83 | |||
| 2acb387446 | |||
| 746273a2ef | |||
| 778beba901 | |||
| 9278920093 | |||
| ece868c9de | |||
| 651ed9b91e | |||
| ac993680db | |||
| 48b33ed07d | |||
| 724953206f | |||
| c4e3b90201 | |||
| a722d1aa36 | |||
| 25e3ae0325 | |||
| e26da5887d | |||
| 31037797e4 | |||
| 2ae7cba452 | |||
| fa3ebc8c0f | |||
| ff36781c98 | |||
| 2f038cbe66 | |||
| f962888b35 | |||
| 6880368a79 | |||
| 13ba6883b4 | |||
| 1e6e804ebb | |||
| 5690d9fe81 | |||
| 6fef8de63e | |||
| e29185ef11 | |||
| c999a6e0e4 | |||
| 2bbf5c7800 | |||
| 273203e4c9 | |||
| 1211680998 | |||
| 84e76556da | |||
| 55881d594b | |||
| 6f0882a098 | |||
| dda465d08a | |||
| 124c517056 | |||
| 6fa6863244 | |||
| 0ad26c337a | |||
| 353eb27d2e | |||
| 6edb0b7a3d | |||
| cd5f0c0047 | |||
| 5716c7c574 | |||
| ce30299122 | |||
| 9b5593d020 | |||
| 4d642b91ef | |||
| 8b4cff474f | |||
| 73ce81d597 | |||
| 09e4c585aa | |||
| 2c5af98cca | |||
| cbe07b49aa | |||
| aa557a7bdf | |||
| 65cafdda03 | |||
| 9a18ff5954 | |||
| d2bc7ab192 | |||
| bf7d2defd6 | |||
| c4e4429744 | |||
| bfc382c398 | |||
| 795399e184 | |||
| 37bdfb716b | |||
| 405afca1c6 | |||
| 7627e0b0f0 | |||
| 25d2206471 | |||
| 2d96444e21 | |||
| f5c3bdb845 | |||
| 22b932ebee | |||
| ad157fe423 | |||
| 50a6d251d6 | |||
| f3d36a9726 | |||
| c2b8cedef1 | |||
| 314833bbac | |||
| e643fe7e7d | |||
| 0d8a05bdb1 | |||
| 21fd2b8f1d | |||
| c7461f476e | |||
| 7410f9a90d | |||
| 3d98f66330 | |||
| 8e65d30a84 | |||
| b4977ff617 | |||
| d397001c33 | |||
| bd7de18c7a | |||
| 9ca506de4b | |||
| 626964f15b | |||
| 532f119b9d | |||
| 187cf2a710 | |||
| c058483422 | |||
| a83a6e5127 | |||
| 57e6419d1d | |||
| 9b7547cbe0 | |||
| e74dfe3438 | |||
| fe82e3e30f | |||
| b000e8e4a2 | |||
| 64c82f1c87 | |||
| 0ad2730353 | |||
| 6017b73acf | |||
| 0334177696 | |||
| 54cec0a5ff | |||
| c5fa2bf12d | |||
| bc8a1756f3 | |||
| 3f01e2f748 | |||
| 7dfe829fc8 | |||
| c87bc57f9e | |||
| 89bd241a78 | |||
| d15fd34f7a | |||
| a118cdd32b | |||
| 3a2de9c698 | |||
| c60e5d90fd | |||
| e9bff37e06 | |||
| 695f8da3c3 | |||
| ea5c5c9e84 | |||
| 4f9108980f | |||
| 8148e4e50d | |||
| f07a832c83 | |||
| 070d239f41 | |||
| 9b02055db9 | |||
| 0162247bd7 | |||
| 9001d34ddf | |||
| cbcc30b384 | |||
| ce9fe90217 | |||
| 7a9278e6a7 | |||
| 01622140e3 | |||
| e44027b186 | |||
| d53f97ecfe | |||
| f3efd7d60b | |||
| 8d25da9474 | |||
| 1aee276b45 | |||
| 191bc115cf | |||
| 63488997ee | |||
| a22480d857 | |||
| 757ae6de39 | |||
| cfc08e8b98 | |||
| c8e9282557 | |||
| a36163ce86 | |||
| e3d1a6aef8 | |||
| 7d8256d343 | |||
| bf9f8597a7 | |||
| 9f2c8c1756 | |||
| 0ae8765a8a | |||
| 308960309d | |||
| b8ac8516fe | |||
| 3f1ce617a7 | |||
| 10e609ba27 | |||
| 6cae6981d8 | |||
| bff396ab1a | |||
| 7f442d045b | |||
| 8918eb4758 | |||
| 628ea72943 | |||
| fc2830bacb | |||
| b5bff9f8b8 | |||
| 5e2181e665 | |||
| b0e91f8b33 | |||
| 635af7f838 | |||
| e537080b64 | |||
| f2c7d171bf | |||
| 7a4a85cb20 | |||
| 2952f4c2ad | |||
| ce668e7139 | |||
| 917b223c6c | |||
| e3302b3f73 | |||
| 38a347ddab | |||
| e3dbd412e1 | |||
| fc9a11959c | |||
| 71155abf57 | |||
| d5458c1865 | |||
| 56af2ca4d7 | |||
| f30c56c237 | |||
| cc1e6e2a1f | |||
| d7c98cb394 | |||
| 4ff5f96fd4 | |||
| 1c1c3617e9 | |||
| bb9d67a1b1 | |||
| 187e264445 | |||
| 493cd24059 | |||
| 3c766d87c2 | |||
| f360ca2cb7 | |||
| ba8bd80173 | |||
| 6102143faf | |||
| 3eadc61a3b | |||
| 2bef7e26d7 | |||
| 7d655bf840 | |||
| 42c4dc6937 | |||
| a4059110f9 | |||
| 952c96d03b | |||
| 4c73f4e968 | |||
| 5f36406b52 | |||
| 385cf0f3df | |||
| e70ad97273 | |||
| 0b7bf34a0f | |||
| 713bce7f0b | |||
| 534704a32c | |||
| 37241a7919 | |||
| 333c5b66d1 | |||
| a47d55f016 | |||
| 58c55cdf81 | |||
| 50a616d91c | |||
| a2be86d15a | |||
| 73f8c28add | |||
| d015793851 | |||
| 0b5b741db4 | |||
| aeac32ae47 | |||
| fab60e7faa | |||
| 6296665f46 | |||
| 7882ac60d8 | |||
| 4fa0b6e783 | |||
| 3a8ddc0cd6 | |||
| 3803b473c5 | |||
| 93d993d876 | |||
| 2b3a3ac3c0 | |||
| c8b2e6ed2a | |||
| 58fa414c21 | |||
| 8872b0bc9b | |||
| c77a6b10de | |||
| f86280a77b | |||
| 65ae582d96 | |||
| 5694f9c703 | |||
| 6c4d1be004 | |||
| 9cfa46ea37 | |||
| 11d5dfee3a | |||
| 864f61c430 | |||
| ca164a0b0b | |||
| e011becc1a | |||
| 25f5717ccf | |||
| 3f0640f4c3 | |||
| 65e8de9db8 | |||
| c1ed25d5dc | |||
| f731769afd | |||
| c466cef7d1 | |||
| b4cb92af99 | |||
| 5e7cbbf506 | |||
| 397e0a8ac2 | |||
| 111b5d1fbe | |||
| b2e86c80c4 | |||
| b77ea04056 | |||
| ba94f38166 | |||
| 7cdaaf2f78 | |||
| 6caf60bd6b | |||
| 19c7086200 | |||
| 1519dda294 | |||
| a7efca8bb4 | |||
| dbe15f7dda | |||
| c81a6737ad | |||
| 5b0d974b43 | |||
| 2b73dff0f4 | |||
| adf0ff0a67 | |||
| c79ef0473d | |||
| d51b955e09 | |||
| 774d7ec0af | |||
| d6f4b00e16 | |||
| 9d81df8670 | |||
| 14a430b939 | |||
| 07de4a70a1 | |||
| eb84ac829b | |||
| b8db56a77b | |||
| 1036f16d1e | |||
| 5ed1cfc259 | |||
| ce5c29fb47 | |||
| f0fd48202c | |||
| d5eb8c21b2 | |||
| 76b9982e04 | |||
| ca7f11f7d0 | |||
| 1c1ace90db | |||
| bdd605e85b | |||
| e1b4b921ba | |||
| ab1ede5fe6 | |||
| 5e6d91d66c | |||
| dbd271f70a | |||
| 527ada47f9 | |||
| afb70d0a95 | |||
| b5f910ad06 | |||
| e412371b88 | |||
| 7b4bc16531 | |||
| e6df30e94c | |||
| d9ba9262bf | |||
| c4be32a5dd | |||
| 0a32f6b5f0 | |||
| f9fe5efbb0 | |||
| 87e723e33b | |||
| 3f0302b256 | |||
| 88d024e73b | |||
| 47a2e57633 | |||
| e7acbb314e | |||
| d6a4c70929 | |||
| 839cad0c2a | |||
| d3845db403 | |||
| 3d68bdf126 | |||
| 0d190d2cd4 | |||
| 4afae028ec | |||
| 5165f0df88 | |||
| 5b7b3501a6 | |||
| c3937d0c4c | |||
| 6e27208c5c | |||
| 91342db0ba | |||
| 8f7123ae12 | |||
| 3a22b3d4b8 | |||
| 919ca82ccd | |||
| d35baac054 | |||
| 9302242299 | |||
| ed90a78ea5 | |||
| 9aa2df7cee | |||
| 41ea18a8f1 | |||
| 5cf37fd13e | |||
| 3e12695914 | |||
| ed1c8897ec | |||
| 75eb0182e9 | |||
| 554165ca48 | |||
| 5a5cb869e2 | |||
| a649d75ab8 | |||
| 255cdbb60a | |||
| bd4e56fd08 | |||
| 5246d784db | |||
| e945b83318 | |||
| 38bd996996 | |||
| 8f3a72e11b | |||
| ddc62efb44 | |||
| 8f08e497a0 | |||
| 8048f8af5f | |||
| 4fc4e36902 | |||
| c66d3fdba0 | |||
| 9b49c2f987 | |||
| bceed13606 | |||
| d99c9edc43 | |||
| 31306a3243 | |||
| 67de17e0c3 | |||
| 3371252656 | |||
| a7e9037e5b | |||
| b3e376ce7c | |||
| d24b664873 | |||
| b8394830a0 | |||
| 7d76105530 | |||
| bbe399196f | |||
| 8c6cdf7358 | |||
| f71bb9349b | |||
| 6bb260140b | |||
| 88aec85f92 | |||
| 61cdee9743 | |||
| 15c3efa6e4 | |||
| a1835efb4e | |||
| 5e0f132d33 | |||
| 2c7c4c95f2 | |||
| 81f115d3db | |||
| c4f872acff | |||
| b3dbb18889 | |||
| 02a278d80c | |||
| c7954a4c5e | |||
| 61fff23be2 | |||
| 7070ed8ae4 | |||
| 101b18e408 | |||
| f0bd3b1c20 | |||
| 921bf8501f | |||
| 594a2d201b | |||
| cb0ba55871 | |||
| 27bcf046b1 | |||
| 44f19610b6 | |||
| a1d2e253ea | |||
| a961a8f644 | |||
| 86b7fec0bb | |||
| 6060e0d8a9 | |||
| c160601a9d | |||
| 56a1ecf6c1 | |||
| 648441ee5c | |||
| 03f0e084e1 | |||
| ab57dc840f | |||
| 2ff5309d54 | |||
| a308903735 | |||
| b6cb604330 | |||
| fb557e9b90 | |||
| 3180ab067c | |||
| 4b7d982eb4 | |||
| fbabc6f455 | |||
| fa759d8128 | |||
| e9ed45671f | |||
| 8cc871f326 | |||
| 3651adbefd | |||
| ff70ded25b | |||
| 244c06b524 | |||
| e7309b254c | |||
| 0cdce9c665 | |||
| 7a10bf1c33 | |||
| a6d5a4ab5d | |||
| 68f3c98872 | |||
| 0b8c0cc431 | |||
| d9eaf8d334 | |||
| 8d29ec4116 | |||
| 806ff4e141 | |||
| 4b0ddbf2ab | |||
| 1ea54f8c92 | |||
| 31cb4c58c2 | |||
| b9bffc32b0 | |||
| b267a781da | |||
| 6f7209c68b | |||
| fb6782ef31 | |||
| fae8aafc17 | |||
| ba38dddf4d | |||
| 0a4600409a | |||
| 706e4476d5 | |||
| c113a0a217 | |||
| ef05278537 | |||
| 1254425ba8 | |||
| e3641286e7 | |||
| 3a03f470c7 | |||
| 4ea44d9612 | |||
| 15c2540c29 | |||
| 112f90979c | |||
| cbe73fd916 | |||
| 3ea28c8ed6 | |||
| 5a219a9087 | |||
| 11eae4d5db | |||
| fe4016a49d | |||
| 0b786427f4 | |||
| 4900dc6bcd | |||
| 31b06b2e47 | |||
| cf6d83a896 | |||
| 637410a8c5 | |||
| 084e4ab830 | |||
| 5a53b34697 | |||
| 8f3b76d3c9 | |||
| f5df111b5c | |||
| 1d31a10dc4 | |||
| db61de6cf3 | |||
| c75d36ef93 | |||
| bb2527bbb8 | |||
| eb31c6c85f | |||
| 546f81f2d0 | |||
| 7bb6cd99eb | |||
| 3dbdaee25c | |||
| b458de0f2d | |||
| 240747ed76 | |||
| 8b5fd64dd4 | |||
| 1c514bdc6b | |||
| f4d9abc789 | |||
| e5197f6f0f | |||
| 8f57246571 | |||
| 7658107e3b | |||
| c33227830e | |||
| 43f2153c86 | |||
| 084d84660a | |||
| 4120b34d22 | |||
| c4a0bce92f | |||
| 858c18114b | |||
| 91f9302fc4 | |||
| 4f3bf04668 | |||
| 11c9417e8d | |||
| db141d9d7f | |||
| 5624596c88 | |||
| cda7ca047c | |||
| ac25ad3985 | |||
| 1bf4e7ab96 | |||
| 49587ace05 | |||
| 54fb06d4b0 | |||
| 8f81b5dc63 | |||
| d5e1649607 | |||
| e2c1f6c0e2 | |||
| 8ec7d1273c | |||
| 7ffc5cc02b | |||
| aac27db552 | |||
| 03acb1c40a | |||
| 5eb332042e | |||
| 66943c4820 | |||
| 4ed9e34893 | |||
| 4f4da78e54 | |||
| 832dfd057e | |||
| dd74280146 | |||
| a7d1a68fb8 | |||
| 6b8b454625 | |||
| 5c5b7df70d | |||
| 7d3abda86f | |||
| 7f824ffbb3 | |||
| 0b7475a4c1 | |||
| cf62d3212c | |||
| b260cbb43a | |||
| 1367f08de8 | |||
| 27b02a7981 | |||
| e62ae02c00 | |||
| 233402dd33 | |||
| 69821b7a6b | |||
| efcffe5205 | |||
| 802e651b6e | |||
| 0e8b951050 | |||
| 8ef97bd78e | |||
| 6749988de1 | |||
| 4a26f8f28b | |||
| a4ee412e96 | |||
| 4f655d7de5 | |||
| 33c02ef3e1 | |||
| aa4749f175 | |||
| 6ace49b0e8 | |||
| 9cf642e910 | |||
| 2990f36595 | |||
| 032fc86a4d | |||
| 868cbb680c | |||
| 98d2aaf10e | |||
| f7dead17c0 | |||
| f3168b2672 | |||
| e8c62b501f | |||
| 6ff2b2a01e | |||
| 960285a270 | |||
| 17cd979299 | |||
| b5846f9755 | |||
| dd39ffdf3b | |||
| 368b871270 | |||
| 9c06b4d344 | |||
| f48241bc5e | |||
| a7b94e1709 | |||
| 3d2dedb49f | |||
| dd5ab8098e | |||
| f5b833c49c | |||
| ba032be402 | |||
| a96a448063 | |||
| faeed9f604 | |||
| 0a8f2422d3 | |||
| 24dbe19a82 | |||
| 7d8901072c | |||
| 848a4e2052 | |||
| e8090b16f3 | |||
| 950964eee3 | |||
| b403f76d81 | |||
| 8f2cdae0eb | |||
| fa4b3c8dea | |||
| e76bbc0f13 | |||
| c9a377d4ac | |||
| c7e308bca0 | |||
| 5193e6e46b | |||
| 66628e8c52 | |||
| bec65cf2ac | |||
| 606b249430 | |||
| 8bc1b7e955 | |||
| c03dec2783 | |||
| 9d92a27ccf | |||
| b652dc8fd8 | |||
| c1929540a3 | |||
| 1601423f30 | |||
| 1de2c14e50 | |||
| c1907054b7 | |||
| 5f524c1181 | |||
| b897603b66 | |||
| 0e5c530006 | |||
| 4a60b5787a | |||
| 88cc69f87a | |||
| 42e91defb9 | |||
| 832cc40a7f | |||
| d89b4534f1 | |||
| 725f6f7cf2 | |||
| 38c25feee0 | |||
| a15bb260c0 | |||
| 6b833043db | |||
| b30a2c9442 | |||
| 3eb99c9391 | |||
| 10636b96bc | |||
| b5ac7c89a3 | |||
| 01298bb470 | |||
| 3bd7bfcbf1 | |||
| 3e94c0be22 | |||
| db0cce6234 | |||
| f3d4cbbd8f | |||
| 81df4ed238 | |||
| 7b756ec3f3 | |||
| 0d95718d1f | |||
| 1da23e8d04 | |||
| 43792d019a | |||
| ec0b4a2dc6 | |||
| 1083136dec |
+1
-1
@@ -1,6 +1,7 @@
|
||||
*.swp
|
||||
*~
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
.project
|
||||
.svn
|
||||
.nvm-version
|
||||
@@ -12,4 +13,3 @@ debug.log
|
||||
docs/output
|
||||
docs/includes
|
||||
spec/fixtures/evil-files/
|
||||
/apm
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[submodule "vendor/apm"]
|
||||
path = vendor/apm
|
||||
url = https://github.com/atom/apm.git
|
||||
+69
-46
@@ -1,77 +1,100 @@
|
||||
# :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.
|
||||
The following is a set of guidelines for contributing to Atom and its packages,
|
||||
which are hosted in the [Atom Organization](https://github.com/atom) on GitHub.
|
||||
If you're unsure which package is causing your problem or if you're having an
|
||||
issue with Atom core, please use the feedback form in the application or email
|
||||
[atom@github.com](mailto:atom@github.com). 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.
|
||||
* 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.
|
||||
## Submitting Issues
|
||||
|
||||
* Include screenshots and animated GIFs whenever possible; they are immensely
|
||||
helpful.
|
||||
* Include the behavior you expected and other places you've seen that behavior
|
||||
such as Emacs, vi, Xcode, etc.
|
||||
* Check the dev tools (`alt-cmd-i`) for errors and stack traces to include.
|
||||
* On Mac, check Console.app for stack traces to include if reporting a crash.
|
||||
* Perform a cursory search to see if a similar issue has already been submitted.
|
||||
|
||||
### Package Repositories
|
||||
|
||||
This is the repository for the core Atom editor only. Atom comes bundled with
|
||||
many packages and themes that are stored in other repos under the
|
||||
[atom organization](https://github.com/atom) such as [tabs](https://github.com/atom/tabs),
|
||||
[Atom organization](https://github.com/atom) such as
|
||||
[tabs](https://github.com/atom/tabs),
|
||||
[find-and-replace](https://github.com/atom/find-and-replace),
|
||||
[language-javascript](https://github.com/atom/language-javascript),
|
||||
and [atom-light-ui](http://github.com/atom/atom-light-ui).
|
||||
[language-javascript](https://github.com/atom/language-javascript), and
|
||||
[atom-light-ui](http://github.com/atom/atom-light-ui).
|
||||
|
||||
If you think you know which package is causing the issue you are reporting, feel
|
||||
free to open up the issue in that specific repository instead. When in doubt
|
||||
just open the issue here but be aware that it may get closed here and reopened
|
||||
in the proper package's repository.
|
||||
For more information on how to work with Atom's official packages, see
|
||||
[Contributing to Atom Packages](https://atom.io/docs/latest/contributing-to-packages.html)
|
||||
|
||||
## Pull Requests
|
||||
* Include screenshots and animated GIFs whenever possible.
|
||||
* Follow the [CoffeeScript](#coffeescript-styleguide),
|
||||
[JavaScript](https://github.com/styleguide/javascript),
|
||||
and [CSS](https://github.com/styleguide/css) styleguides
|
||||
* Include thoughtfully worded [Jasmine](http://pivotal.github.com/jasmine)
|
||||
specs
|
||||
* 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:
|
||||
|
||||
* Include screenshots and animated GIFs in your pull request whenever possible.
|
||||
* Follow the [CoffeeScript](#coffeescript-styleguide),
|
||||
[JavaScript](https://github.com/styleguide/javascript),
|
||||
and [CSS](https://github.com/styleguide/css) styleguides.
|
||||
* Include thoughtfully-worded, well-structured
|
||||
[Jasmine](http://jasmine.github.io/) specs.
|
||||
* Document new code based on the
|
||||
[Documentation Styleguide](#documentation-styleguide)
|
||||
* End files with a newline.
|
||||
* Place requires 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 methods (methods starting with a `@`)
|
||||
* Instance methods
|
||||
* Beware of platform differences
|
||||
* Place class properties in the following order:
|
||||
* Class methods and properties (methods starting with a `@`)
|
||||
* Instance methods and properties
|
||||
* Avoid platform-dependent code:
|
||||
* 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
|
||||
* Use `os.tmpdir()` rather than `/tmp` when you need to reference the
|
||||
temporary directory.
|
||||
|
||||
## 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
|
||||
* :penguin: when fixing something on Linux
|
||||
|
||||
* Use the present tense ("Add feature" not "Added feature")
|
||||
* Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
|
||||
* Limit the first line to 72 characters or less
|
||||
* Reference issues and pull requests liberally
|
||||
* Consider starting the commit message with an applicable emoji:
|
||||
* :lipstick: `:lipstick:` when improving the format/structure of the code
|
||||
* :racehorse: `:racehorse:` when improving performance
|
||||
* :non-potable_water: `:non-potable_water:` when plugging memory leaks
|
||||
* :memo: `:memo:` when writing docs
|
||||
* :penguin: `:penguin:` when fixing something on Linux
|
||||
* :apple: `:apple:` when fixing something on Mac OS
|
||||
* :bug: `:bug:` when fixing a bug
|
||||
* :fire: `:fire:` when removing code or files
|
||||
* :green_heart: `:green_heart:` when fixing the CI build
|
||||
* :white_check_mark: `:white_check_mark:` when adding tests
|
||||
* :lock: `:lock:` when dealing with security
|
||||
|
||||
## CoffeeScript Styleguide
|
||||
|
||||
* Set parameter defaults without spaces around the equal sign
|
||||
* `clear = (count=1) ->` instead of `clear = (count = 1) ->`
|
||||
* `clear = (count=1) ->` instead of `clear = (count = 1) ->`
|
||||
* Use parentheses if it improves code clarity.
|
||||
* Prefer alphabetic keywords to symbolic keywords:
|
||||
* `a is b` instead of `a == b`
|
||||
* Avoid spaces inside the curly-braces of hash literals:
|
||||
* `{a: 1, b: 2}` instead of `{ a: 1, b: 2 }`
|
||||
* Include a single line of whitespace between methods.
|
||||
|
||||
## Documentation Styleguide
|
||||
|
||||
* Use [TomDoc](http://tomdoc.org).
|
||||
* Use [Markdown](https://daringfireball.net/projects/markdown).
|
||||
* Reference classes with `{ClassName}`.
|
||||
* Reference instance methods with `{ClassName::methodName}`.
|
||||
* Reference class methods with `{ClassName.methodName}`.
|
||||
* Delegate to comments elsewhere with `{Delegates to: ClassName.methodName}`
|
||||
style notation.
|
||||
* Reference methods and classes in markdown with the custom `{}` notation:
|
||||
* Reference classes with `{ClassName}`
|
||||
* Reference instance methods with `{ClassName::methodName}`
|
||||
* Reference class methods with `{ClassName.methodName}`
|
||||
* Delegate to comments elsewhere with `{Delegates to: ClassName.methodName}`
|
||||
style notation.
|
||||
|
||||
### Example
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Copyright 2014 GitHub, Inc.
|
||||
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2014 GitHub Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+9
-5
@@ -1,8 +1,8 @@
|
||||
# Atom — The hackable editor
|
||||

|
||||
|
||||

|
||||
Atom is a hackable text editor for the 21st century, built on [atom-shell](http://github.com/atom/atom-shell), and based on everything we love about our favorite editors. We designed it to be deeply customizable, but still approachable using the default configuration.
|
||||
|
||||
Check out our [guides and API documentation](https://atom.io/docs/latest).
|
||||
Visit [atom.io](https://atom.io) to learn more.
|
||||
|
||||
## Installing
|
||||
|
||||
@@ -12,6 +12,10 @@ Atom will automatically update when a new release is available.
|
||||
|
||||
## Building
|
||||
|
||||
Follow the instructions in the [build docs][building].
|
||||
* [Linux](docs/build-instructions/linux.md)
|
||||
* [OS X](docs/build-instructions/os-x.md)
|
||||
* [FreeBSD](docs/build-instructions/freebsd.md)
|
||||
* [Windows](docs/build-instructions/windows.md)
|
||||
|
||||
[building]: https://github.com/atom/atom/blob/master/docs/building-atom.md
|
||||
## Developing
|
||||
Check out the [guides](https://atom.io/docs/latest) and the [API reference](https://atom.io/docs/api).
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
This folder is where [apm](https://github.com/atom/apm) is installed to so that
|
||||
it is bundled with Atom.
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "atom-bundled-apm",
|
||||
"description": "Atom's bundled APM",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "0.57.0"
|
||||
}
|
||||
}
|
||||
+17
-9
@@ -1,17 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ "`uname`" == 'Darwin' ]; then
|
||||
if [ "$(uname)" == 'Darwin' ]; then
|
||||
OS='Mac'
|
||||
elif [ "`expr substr $(uname -s) 1 5`" == 'Linux' ]; then
|
||||
elif [ "$(expr substr $(uname -s) 1 5)" == 'Linux' ]; then
|
||||
OS='Linux'
|
||||
elif [ "`expr substr $(uname -s) 1 10`" == 'MINGW32_NT' ]; then
|
||||
elif [ "$(expr substr $(uname -s) 1 10)" == 'MINGW32_NT' ]; then
|
||||
OS='Cygwin'
|
||||
else
|
||||
echo "Your platform (`uname -a`) is not supported."
|
||||
echo "Your platform ($(uname -a)) is not supported."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
while getopts ":wtfvhs-:" opt; do
|
||||
while getopts ":wtfvh-:" opt; do
|
||||
case "$opt" in
|
||||
-)
|
||||
case "${OPTARG}" in
|
||||
@@ -66,17 +66,25 @@ if [ $OS == 'Mac' ]; then
|
||||
open -a "$ATOM_PATH/$ATOM_APP_NAME" -n --args --executed-from="$(pwd)" --pid=$$ "$@"
|
||||
fi
|
||||
elif [ $OS == 'Linux' ]; then
|
||||
SCRIPT=`readlink -f "$0"`
|
||||
USR_DIRECTORY=`readlink -f $(dirname $SCRIPT)/..`
|
||||
SCRIPT=$(readlink -f "$0")
|
||||
USR_DIRECTORY=$(readlink -f $(dirname $SCRIPT)/..)
|
||||
ATOM_PATH="$USR_DIRECTORY/share/atom/atom"
|
||||
|
||||
[ -x "$ATOM_PATH" ] || ATOM_PATH='/tmp/atom-build/Atom/atom'
|
||||
: ${TMPDIR:=/tmp}
|
||||
|
||||
[ -x "$ATOM_PATH" ] || ATOM_PATH="$TMPDIR/atom-build/Atom/atom"
|
||||
|
||||
if [ $EXPECT_OUTPUT ]; then
|
||||
"$ATOM_PATH" --executed-from="$(pwd)" --pid=$$ "$@"
|
||||
exit $?
|
||||
else
|
||||
nohup "$ATOM_PATH" --executed-from="$(pwd)" --pid=$$ "$@" > /dev/null 2>&1 &
|
||||
(
|
||||
nohup "$ATOM_PATH" --executed-from="$(pwd)" --pid=$$ "$@" > "$TMPDIR/atom-nohup.out" 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
cat "$TMPDIR/atom-nohup.out"
|
||||
exit $?
|
||||
fi
|
||||
) &
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
@@ -36,35 +36,23 @@ module.exports = (grunt) ->
|
||||
grunt.log.write = (args...) -> grunt.log
|
||||
|
||||
[major, minor, patch] = packageJson.version.split('.')
|
||||
tmpDir = os.tmpdir()
|
||||
appName = if process.platform is 'darwin' then 'Atom.app' else 'Atom'
|
||||
buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'atom-build')
|
||||
atomShellDownloadDir = path.join(os.tmpdir(), 'atom-cached-atom-shells')
|
||||
symbolsDir = path.join(buildDir, 'Atom.breakpad.syms')
|
||||
shellAppDir = path.join(buildDir, appName)
|
||||
if process.platform is 'win32'
|
||||
appName = 'Atom'
|
||||
tmpDir = os.tmpdir()
|
||||
buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'atom-build')
|
||||
symbolsDir = path.join(buildDir, 'Atom.breakpad.syms')
|
||||
shellAppDir = path.join(buildDir, appName)
|
||||
contentsDir = shellAppDir
|
||||
appDir = path.join(shellAppDir, 'resources', 'app')
|
||||
atomShellDownloadDir = path.join(os.tmpdir(), 'atom-cached-atom-shells')
|
||||
installDir = path.join(process.env.ProgramFiles, appName)
|
||||
else if process.platform is 'darwin'
|
||||
appName = 'Atom.app'
|
||||
tmpDir = '/tmp'
|
||||
buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'atom-build')
|
||||
symbolsDir = path.join(buildDir, 'Atom.breakpad.syms')
|
||||
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('/Applications', appName)
|
||||
else
|
||||
appName = 'Atom'
|
||||
tmpDir = '/tmp'
|
||||
buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'atom-build')
|
||||
symbolsDir = path.join(buildDir, 'Atom.breakpad.syms')
|
||||
shellAppDir = path.join(buildDir, appName)
|
||||
contentsDir = shellAppDir
|
||||
appDir = path.join(shellAppDir, 'resources', 'app')
|
||||
atomShellDownloadDir = '/tmp/atom-cached-atom-shells'
|
||||
installDir = process.env.INSTALL_PREFIX ? '/usr/local'
|
||||
|
||||
coffeeConfig =
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"grunt-contrib-coffee": "~0.9.0",
|
||||
"grunt-contrib-less": "~0.8.0",
|
||||
"grunt-cson": "0.8.0",
|
||||
"grunt-download-atom-shell": "git+https://atom-bot:467bac80a0017b96fb5be5cfc686f5e0cc607b10@github.com/atom/grunt-download-atom-shell#v0.6.1",
|
||||
"grunt-download-atom-shell": "~0.7.2",
|
||||
"grunt-lesslint": "0.13.0",
|
||||
"grunt-markdown": "~0.4.0",
|
||||
"grunt-peg": "~1.1.0",
|
||||
@@ -27,6 +27,8 @@
|
||||
"json-front-matter": "~0.1.3",
|
||||
"legal-eagle": "~0.4.0",
|
||||
"minidump": "0.5.x",
|
||||
"read-package-json": "1.1.8",
|
||||
"normalize-package-data": "0.2.12",
|
||||
"rcedit": "~0.1.2",
|
||||
"request": "~2.27.0",
|
||||
"rimraf": "~2.2.2",
|
||||
|
||||
@@ -5,7 +5,7 @@ module.exports = (grunt) ->
|
||||
{rm} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'partial-clean', 'Delete some of the build files', ->
|
||||
tmpdir = if process.platform is 'win32' then os.tmpdir() else '/tmp'
|
||||
tmpdir = os.tmpdir()
|
||||
|
||||
rm grunt.config.get('atom.buildDir')
|
||||
rm require('../src/coffee-cache').cacheDir
|
||||
|
||||
@@ -143,7 +143,7 @@ downloadFileFromRepo = ({repo, file}, callback) ->
|
||||
|
||||
downloadIncludes = (callback) ->
|
||||
includes = [
|
||||
{repo: 'atom-keymap', file: 'src/keymap.coffee'}
|
||||
{repo: 'atom-keymap', file: 'src/keymap-manager.coffee'}
|
||||
{repo: 'atom-keymap', file: 'src/key-binding.coffee'}
|
||||
{repo: 'first-mate', file: 'src/grammar.coffee'}
|
||||
{repo: 'first-mate', file: 'src/grammar-registry.coffee'}
|
||||
|
||||
@@ -17,7 +17,7 @@ module.exports = (grunt) ->
|
||||
|
||||
licenseText = getLicenseText(dependencyLicenses)
|
||||
if mode is 'save'
|
||||
targetPath = path.join(grunt.config.get('atom.appDir'), 'LICENSE')
|
||||
targetPath = path.join(grunt.config.get('atom.appDir'), 'LICENSE.md')
|
||||
fs.writeFileSync(targetPath, licenseText)
|
||||
else
|
||||
console.log licenseText
|
||||
@@ -26,7 +26,7 @@ module.exports = (grunt) ->
|
||||
getLicenseText = (dependencyLicenses) ->
|
||||
{keys} = require 'underscore-plus'
|
||||
text = """
|
||||
Copyright 2014 GitHub, Inc.
|
||||
#{fs.readFileSync('LICENSE.md', 'utf8')}
|
||||
|
||||
This application bundles the following third-party packages in accordance
|
||||
with the following licenses:\n\n
|
||||
|
||||
@@ -29,21 +29,14 @@ module.exports = (gruntObject) ->
|
||||
done = @async()
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
|
||||
createBuildRelease (error, release) ->
|
||||
zipApps buildDir, assets, (error) ->
|
||||
return done(error) if error?
|
||||
|
||||
zipApps buildDir, assets, (error) ->
|
||||
getAtomDraftRelease (error, release) ->
|
||||
return done(error) if error?
|
||||
uploadAssets release, buildDir, assets, (error) ->
|
||||
assetNames = (asset.assetName for asset in assets)
|
||||
deleteExistingAssets release, assetNames, (error) ->
|
||||
return done(error) if error?
|
||||
publishRelease release, (error) ->
|
||||
return done(error) if error?
|
||||
getAtomDraftRelease (error, release) ->
|
||||
return done(error) if error?
|
||||
assetNames = (asset.assetName for asset in assets)
|
||||
deleteExistingAssets release, assetNames, (error) ->
|
||||
return done(error) if error?
|
||||
uploadAssets(release, buildDir, assets, done)
|
||||
uploadAssets(release, buildDir, assets, done)
|
||||
|
||||
logError = (message, error, details) ->
|
||||
grunt.log.error(message)
|
||||
@@ -64,25 +57,6 @@ zipApps = (buildDir, assets, callback) ->
|
||||
tasks.push(zip.bind(this, buildDir, sourceName, assetName))
|
||||
async.parallel(tasks, callback)
|
||||
|
||||
getRelease = (callback) ->
|
||||
options =
|
||||
uri: 'https://api.github.com/repos/atom/atom-master-builds/releases'
|
||||
method: 'GET'
|
||||
headers: defaultHeaders
|
||||
json: true
|
||||
request options, (error, response, releases=[]) ->
|
||||
if error? or response.statusCode isnt 200
|
||||
logError('Fetching releases failed', error, releases)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
if releases.length > maxReleases
|
||||
deleteRelease(release) for release in releases[maxReleases..]
|
||||
|
||||
for release in releases when release.name is commitSha
|
||||
callback(null, release)
|
||||
return
|
||||
callback()
|
||||
|
||||
getAtomDraftRelease = (callback) ->
|
||||
atomRepo = new GitHub({repo: 'atom/atom', token})
|
||||
atomRepo.getReleases (error, releases=[]) ->
|
||||
@@ -125,35 +99,6 @@ deleteExistingAssets = (release, assetNames, callback) ->
|
||||
tasks.push(deleteAsset.bind(this, asset.url))
|
||||
async.parallel(tasks, callback)
|
||||
|
||||
createBuildRelease = (callback) ->
|
||||
getRelease (error, release) ->
|
||||
if error?
|
||||
callback(error)
|
||||
return
|
||||
|
||||
if release?
|
||||
deleteExistingAssets release, (error) ->
|
||||
callback(error, release)
|
||||
return
|
||||
|
||||
options =
|
||||
uri: 'https://api.github.com/repos/atom/atom-master-builds/releases'
|
||||
method: 'POST'
|
||||
headers: defaultHeaders
|
||||
json:
|
||||
tag_name: "v#{commitSha}"
|
||||
target_commitish: 'master'
|
||||
name: commitSha
|
||||
body: "Build of [atom@#{commitSha.substring(0, 7)}](https://github.com/atom/atom/commits/#{commitSha})"
|
||||
draft: true
|
||||
prerelease: true
|
||||
request options, (error, response, release={}) ->
|
||||
if error? or response.statusCode isnt 201
|
||||
logError('Creating release failed', error, release)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
callback(null, release)
|
||||
|
||||
uploadAssets = (release, buildDir, assets, callback) ->
|
||||
upload = (release, assetName, assetPath, callback) ->
|
||||
options =
|
||||
@@ -178,17 +123,3 @@ uploadAssets = (release, buildDir, assets, callback) ->
|
||||
assetPath = path.join(buildDir, assetName)
|
||||
tasks.push(upload.bind(this, release, assetName, assetPath))
|
||||
async.parallel(tasks, callback)
|
||||
|
||||
publishRelease = (release, callback) ->
|
||||
options =
|
||||
uri: release.url
|
||||
method: 'POST'
|
||||
headers: defaultHeaders
|
||||
json:
|
||||
draft: false
|
||||
request options, (error, response, body={}) ->
|
||||
if error? or response.statusCode isnt 200
|
||||
logError('Creating release failed', error, body)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
callback()
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
async = require 'async'
|
||||
request = require 'request'
|
||||
|
||||
# Configure and publish all packages in package.json to atom.io
|
||||
#
|
||||
# This task should be run whenever you want to be sure that atom.io contains
|
||||
# all the packages and versions referenced in Atom's package.json file.
|
||||
module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
baseUrl = "https://atom.io/api/packages"
|
||||
|
||||
packageExists = (packageName, token, callback) ->
|
||||
requestSettings =
|
||||
url: "#{baseUrl}/#{packageName}"
|
||||
json: true
|
||||
headers:
|
||||
authorization: token
|
||||
request.get requestSettings, (error, response, body={}) ->
|
||||
if error?
|
||||
callback(error)
|
||||
else
|
||||
callback(null, response.statusCode is 404)
|
||||
|
||||
createPackage = (packageName, token, callback) ->
|
||||
requestSettings =
|
||||
url: baseUrl
|
||||
json: true
|
||||
headers:
|
||||
authorization: token
|
||||
method: 'POST'
|
||||
body:
|
||||
repository: "atom/#{packageName}"
|
||||
|
||||
request.get requestSettings, (error, response, body={}) ->
|
||||
if error?
|
||||
callback(error)
|
||||
else if response.statusCode isnt 201
|
||||
message = body.message ? body.error ? body
|
||||
callback(new Error("Creating package failed: #{message}"))
|
||||
else
|
||||
callback()
|
||||
|
||||
createPackageVersion = (packageName, tag, token, callback) ->
|
||||
requestSettings =
|
||||
url: "#{baseUrl}/#{packageName}/versions"
|
||||
json: true
|
||||
method: 'POST'
|
||||
headers:
|
||||
authorization: token
|
||||
body:
|
||||
tag: tag
|
||||
|
||||
request.get requestSettings, (error, response, body={}) ->
|
||||
if error?
|
||||
callback(error)
|
||||
else if response.statusCode isnt 201
|
||||
message = body.message ? body.error ? body
|
||||
if message is 'Version exists'
|
||||
callback()
|
||||
else
|
||||
callback(new Error("Creating new version failed: #{message}"))
|
||||
else
|
||||
callback()
|
||||
|
||||
getToken = (callback) ->
|
||||
if token = process.env.ATOM_ACCESS_TOKEN
|
||||
callback(null, token)
|
||||
else
|
||||
spawn {cmd: 'security', args: ['-q', 'find-generic-password', '-ws', 'GitHub API Token']}, (error, result, code) ->
|
||||
token = result.toString() unless error?
|
||||
callback(error, token)
|
||||
|
||||
grunt.registerTask 'publish-packages', 'Publish all bundled packages', ->
|
||||
done = @async()
|
||||
|
||||
getToken (error, token) ->
|
||||
unless token
|
||||
grunt.log.error('Token not found in keychain or ATOM_ACCESS_TOKEN environment variable')
|
||||
done(false)
|
||||
|
||||
{packageDependencies} = grunt.file.readJSON('package.json') ? {}
|
||||
tasks = []
|
||||
for name, version of packageDependencies
|
||||
do (name, version) ->
|
||||
tasks.push (callback) ->
|
||||
grunt.verbose.writeln("Publishing #{name}@#{version}")
|
||||
tag = "v#{version}"
|
||||
packageExists name, token, (error, exists) ->
|
||||
if error?
|
||||
callback(error)
|
||||
return
|
||||
|
||||
if exists
|
||||
createPackage name, token, (error) ->
|
||||
if error?
|
||||
callback(error)
|
||||
else
|
||||
createPackageVersion(name, tag, token, callback)
|
||||
else
|
||||
createPackageVersion(name, tag, token, callback)
|
||||
|
||||
async.waterfall tasks, (error) ->
|
||||
if error?
|
||||
grunt.log.error(error.message)
|
||||
done(false)
|
||||
else
|
||||
done()
|
||||
@@ -5,14 +5,18 @@ module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
getVersion = (callback) ->
|
||||
if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH is 'master'
|
||||
{version} = require(path.join(grunt.config.get('atom.appDir'), 'package.json'))
|
||||
onBuildMachine = process.env.JANKY_SHA1 and process.env.JANKY_BRANCH is 'master'
|
||||
inRepository = fs.existsSync(path.resolve(__dirname, '..', '..', '.git'))
|
||||
{version} = require(path.join(grunt.config.get('atom.appDir'), 'package.json'))
|
||||
if onBuildMachine or not inRepository
|
||||
callback(null, version)
|
||||
else
|
||||
cmd = 'git'
|
||||
args = ['rev-parse', '--short', 'HEAD']
|
||||
spawn {cmd, args}, (error, {stdout}={}, code) ->
|
||||
callback(error, stdout?.trim?())
|
||||
commitHash = stdout?.trim?()
|
||||
combinedVersion = "#{version}-#{commitHash}"
|
||||
callback(error, combinedVersion)
|
||||
|
||||
grunt.registerTask 'set-version', 'Set the version in the plist and package.json', ->
|
||||
done = @async()
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# Welcome to the Atom API Documentation
|
||||
|
||||

|
||||

|
||||
|
||||
## FAQ
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ keystrokes pass through elements with the class `.editor`:
|
||||
|
||||
```coffee
|
||||
'.editor':
|
||||
'cmd-delete': 'editor:backspace-to-beginning-of-line'
|
||||
'alt-backspace': 'editor:backspace-to-beginning-of-word'
|
||||
'cmd-delete': 'editor:delete-to-beginning-of-line'
|
||||
'alt-backspace': 'editor:delete-to-beginning-of-word'
|
||||
'ctrl-A': 'editor:select-to-first-character-of-line'
|
||||
'ctrl-shift-e': 'editor:select-to-end-of-line'
|
||||
'cmd-left': 'editor:move-to-first-character-of-line'
|
||||
@@ -24,7 +24,7 @@ keystrokes pass through elements with the class `.editor`:
|
||||
Beneath the first selector are several bindings, mapping specific *keystroke
|
||||
patterns* to *commands*. When an element with the `.editor` class is focused and
|
||||
`cmd-delete` is pressed, an custom DOM event called
|
||||
`editor:backspace-to-beginning-of-line` is emitted on the `.editor` element.
|
||||
`editor:delete-to-beginning-of-line` is emitted on the `.editor` element.
|
||||
|
||||
The second selector group also targets editors, but only if they don't have the
|
||||
`.mini` class. In this example, the commands for code folding don't really make
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
## Developing Node Modules
|
||||
|
||||
Atom contains a number of packages that are Node modules instead of Atom packages. If you want to
|
||||
make changes to the Node modules, for instance `atom-keymap`, you have to link them into the
|
||||
development environment differently than you would a normal Atom package.
|
||||
|
||||
### Linking a Node Module Into Your Atom Dev Environment
|
||||
|
||||
Here are the steps to run a local version of a node module *not an apm* within Atom. We're using
|
||||
`atom-keymap` as an example:
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/atom/atom-keymap.git
|
||||
$ cd atom-keymap
|
||||
$ npm install
|
||||
$ npm link
|
||||
$ apm rebuild # This is the special step, it makes the npm work with Atom's version of Node
|
||||
$ cd WHERE-YOU-CLONED-ATOM
|
||||
$ npm link atom-keymap
|
||||
$ atom # Should work!
|
||||
```
|
||||
|
||||
After this, you'll have to `npm install` and `apm rebuild` when you make a change to the node
|
||||
module's code.
|
||||
@@ -0,0 +1,22 @@
|
||||
# FreeBSD
|
||||
|
||||
FreeBSD -RELEASE 64-bit is the recommended platform.
|
||||
|
||||
## Requirements
|
||||
|
||||
* FreeBSD
|
||||
* `pkg install node`
|
||||
* `pkg install npm`
|
||||
* `pkg install libgnome-keyring`
|
||||
* `npm config set python /usr/local/bin/python2 -g` to ensure that gyp uses Python 2
|
||||
|
||||
## Instructions
|
||||
|
||||
```sh
|
||||
git clone https://github.com/atom/atom
|
||||
cd atom
|
||||
script/build # Creates application at $TMPDIR/atom-build/Atom
|
||||
sudo script/grunt install # Installs command to /usr/local/bin/atom
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
@@ -0,0 +1,49 @@
|
||||
# Linux
|
||||
|
||||
Ubuntu LTS 12.04 64-bit is the recommended platform.
|
||||
|
||||
## Requirements
|
||||
|
||||
* OS with 64-bit architecture
|
||||
* [node.js](http://nodejs.org/download/) v0.10.x
|
||||
* [npm](http://www.npmjs.org/) v1.4.x
|
||||
* libgnome-keyring-dev `sudo apt-get install libgnome-keyring-dev` (refer to your distribution's manual on how to install packages if you are not on Debian or Ubuntu-based systems)
|
||||
* `npm config set python /usr/bin/python2 -g` to ensure that gyp uses Python 2
|
||||
|
||||
|
||||
## Instructions
|
||||
|
||||
```sh
|
||||
git clone https://github.com/atom/atom
|
||||
cd atom
|
||||
script/build # Creates application at $TMPDIR/atom-build/Atom
|
||||
sudo script/grunt install # Installs command to /usr/local/bin/atom
|
||||
script/grunt mkdeb # Generates a .deb package at $TMPDIR/atom-build
|
||||
```
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
### Exception: "TypeError: Unable to watch path"
|
||||
|
||||
If you get following error with a big traceback right after Atom starts:
|
||||
|
||||
```
|
||||
TypeError: Unable to watch path
|
||||
```
|
||||
|
||||
you have to increase number of watched files by inotify. For testing if
|
||||
this is the reason for this error you can issue
|
||||
|
||||
```sh
|
||||
sudo sysctl fs.inotify.max_user_watches=32768
|
||||
```
|
||||
|
||||
and restart Atom. If Atom now works fine, you can make this setting permanent:
|
||||
|
||||
```sh
|
||||
echo 32768 > /proc/sys/fs/inotify/max_user_watches
|
||||
```
|
||||
|
||||
See also https://github.com/atom/atom/issues/2082.
|
||||
@@ -0,0 +1,17 @@
|
||||
# OS X
|
||||
|
||||
## Requirements
|
||||
|
||||
* OS X 10.8 or later
|
||||
* [node.js](http://nodejs.org/download/) v0.10.x
|
||||
* Command Line Tools for [Xcode](https://developer.apple.com/xcode/downloads/) (run `xcode-select --install` to install)
|
||||
|
||||
## Instructions
|
||||
|
||||
```sh
|
||||
git clone https://github.com/atom/atom
|
||||
cd atom
|
||||
script/build # Creates application at /Applications/Atom.app
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
@@ -0,0 +1,35 @@
|
||||
# Windows
|
||||
|
||||
## Requirements
|
||||
|
||||
* Windows 7 or later
|
||||
* [Visual C++ 2010 Express](http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs#DownloadFamilies_4) with [SP1](http://www.microsoft.com/en-us/download/details.aspx?id=23691)
|
||||
* [node.js](http://nodejs.org/download/) v0.10.x
|
||||
* [Python](http://www.python.org/download/) v2.7.x
|
||||
* [GitHub for Windows](http://windows.github.com/)
|
||||
|
||||
## Instructions
|
||||
|
||||
```bat
|
||||
# Use the `Git Shell` app which was installed by GitHub for Windows. Also Make
|
||||
# sure you have logged into the GitHub for Windows GUI App.
|
||||
cd C:\
|
||||
git clone https://github.com/atom/atom/
|
||||
cd atom
|
||||
script\build
|
||||
```
|
||||
|
||||
## Why do I have to use GitHub for Windows?
|
||||
|
||||
You don't, You can use your existing Git! GitHub for Windows's Git Shell is just
|
||||
easier to set up. You need to have Posix tools in your `%PATH%` (i.e. `grep`,
|
||||
`sed`, et al.), which isn't the default configuration when you install Git. To
|
||||
fix this, you probably need to fiddle with your system PATH.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Errors
|
||||
* `node is not recognized`
|
||||
|
||||
* If you just installed node you need to restart your computer before node is
|
||||
available on your Path.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Contributing to Official Atom Packages
|
||||
|
||||
If you think you know which package is causing the issue you are reporting, feel
|
||||
free to open up the issue in that specific repository instead. When in doubt
|
||||
just open the issue here but be aware that it may get closed here and reopened
|
||||
in the proper package's repository.
|
||||
|
||||
## Hacking on Packages
|
||||
|
||||
### Cloning
|
||||
|
||||
The first step is creating your own clone. You can of course do this manually
|
||||
with git, or you can use the `apm develop` command to create a clone based on
|
||||
the package's `repository` field in the `package.json`.
|
||||
|
||||
For example, if you want to make changes to the `tree-view` package, run the
|
||||
following command:
|
||||
|
||||
```
|
||||
> apm develop tree-view
|
||||
Cloning https://github.com/atom/tree-view ✓
|
||||
Installing modules ✓
|
||||
~/.atom/dev/packages/tree-view -> ~/github/tree-view
|
||||
```
|
||||
|
||||
This clones the `tree-view` repository to `~/github`. If you prefer a different
|
||||
path, specify it via the `ATOM_REPOS_HOME` environment variable.
|
||||
|
||||
### Running in Development Mode
|
||||
|
||||
Editing a package in Atom is a bit of a circular experience: you're using Atom
|
||||
to modify itself. What happens if you temporarily break something? You don't
|
||||
want the version of Atom you're using to edit to become useless in the process.
|
||||
For this reason, you'll only want to load packages in **development mode** while
|
||||
you are working on them. You'll perform your editing in **stable mode**, only
|
||||
switching to development mode to test your changes.
|
||||
|
||||
To open a development mode window, use the "Application: Open Dev" command,
|
||||
which is normally bound to `cmd-shift-o`. You can also run dev mode from the
|
||||
command line with `atom --dev`.
|
||||
|
||||
To load your package in development mode, create a symlink to it in
|
||||
`~/.atom/dev/packages`. This occurs automatically when you clone the package
|
||||
with `apm develop`. You can also run `apm link --dev` and `apm unlink --dev`
|
||||
from the package directory to create and remove dev-mode symlinks.
|
||||
|
||||
### Installing Dependencies
|
||||
|
||||
Finally, you need to install the cloned package's dependencies by running
|
||||
`apm install` within the package directory. This step is also performed
|
||||
automatically the first time you run `apm develop`, but you'll want to keep
|
||||
dependencies up to date by running `apm update` after pulling upstream changes.
|
||||
@@ -1,142 +0,0 @@
|
||||
# Contributing to Atom Packages
|
||||
|
||||
The following is a set of guidelines for contributing to Atom packages, which
|
||||
are hosted in the [Atom Organization](https://github.com/atom) on GitHub. If
|
||||
you're unsure which package is causing your problem or if you're having an issue
|
||||
with Atom core, please use the feedback form in the application or email
|
||||
[atom@github.com](mailto:atom@github.com).
|
||||
|
||||
## Submitting Issues
|
||||
|
||||
* Include screenshots and animated GIFs whenever possible; they are immensely
|
||||
helpful.
|
||||
* Include the behavior you expected and other places you've seen that behavior
|
||||
such as Emacs, vi, Xcode, etc.
|
||||
* Check the dev tools (`alt-cmd-i`) for errors and stack traces to include.
|
||||
* Check Console.app for stack traces to include if reporting a crash.
|
||||
* Perform a cursory search to see if a similar issue has already been submitted.
|
||||
|
||||
## Hacking on Packages
|
||||
|
||||
### Cloning
|
||||
|
||||
The first step is creating your own clone. You can of course do this manually
|
||||
with git, or you can use the `apm develop` command to create a clone based on
|
||||
the package's `repository` field in the `package.json`.
|
||||
|
||||
For example, if you want to make changes to the `tree-view` package, run the
|
||||
following command:
|
||||
|
||||
```
|
||||
> apm develop tree-view
|
||||
Cloning https://github.com/atom/tree-view ✓
|
||||
Installing modules ✓
|
||||
~/.atom/dev/packages/tree-view -> ~/github/tree-view
|
||||
```
|
||||
|
||||
This clones the `tree-view` repository to `~/github`. If you prefer a different
|
||||
path, specify it via the `ATOM_REPOS_HOME` environment variable.
|
||||
|
||||
### Running in Development Mode
|
||||
|
||||
Editing a package in Atom is a bit of a circular experience: you're using Atom
|
||||
to modify itself. What happens if you temporarily break something? You don't
|
||||
want the version of Atom you're using to edit to become useless in the process.
|
||||
For this reason, you'll only want to load packages in **development mode** while
|
||||
you are working on them. You'll perform your editing in **stable mode**, only
|
||||
switching to development mode to test your changes.
|
||||
|
||||
To open a development mode window, use the "Application: Open Dev" command,
|
||||
which is normally bound to `cmd-shift-o`. You can also run dev mode from the
|
||||
command line with `atom --dev`.
|
||||
|
||||
To load your package in development mode, create a symlink to it in
|
||||
`~/.atom/dev/packages`. This occurs automatically when you clone the package
|
||||
with `apm develop`. You can also run `apm link --dev` and `apm unlink --dev`
|
||||
from the package directory to create and remove dev-mode symlinks.
|
||||
|
||||
### Installing Dependencies
|
||||
|
||||
Finally, you need to install the cloned package's dependencies by running
|
||||
`apm install` within the package directory. This step is also performed
|
||||
automatically the first time you run `apm develop`, but you'll want to keep
|
||||
dependencies up to date by running `apm update` after pulling upstream changes.
|
||||
|
||||
## Submitting Pull Requests
|
||||
|
||||
### Code Guidelines
|
||||
|
||||
* Include screenshots and animated GIFs in your pull request whenever possible.
|
||||
* Follow the [CoffeeScript](#coffeescript-styleguide),
|
||||
[JavaScript](https://github.com/styleguide/javascript),
|
||||
and [CSS](https://github.com/styleguide/css) styleguides.
|
||||
* Include thoughtfully-worded, well-structured
|
||||
[Jasmine](http://pivotal.github.com/jasmine) specs.
|
||||
* Document new code based on the
|
||||
[Documentation Styleguide](#documentation-styleguide)
|
||||
* End files with a newline.
|
||||
* Place requires 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)
|
||||
* Place class properties in the following order:
|
||||
* Class methods and properties (methods starting with a `@`)
|
||||
* Instance methods and properties
|
||||
* Avoid platform-dependent code:
|
||||
* Use `require('atom').fs.getHomeDirectory()` to get the home directory.
|
||||
* Use `path.join()` to concatenate filenames.
|
||||
* Use `os.tmpdir()` rather than `/tmp` when you need to reference the
|
||||
temporary directory.
|
||||
|
||||
### Commit Message Guidelines
|
||||
* Use the present tense ("Add feature" not "Added feature")
|
||||
* Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
|
||||
* Limit the first line to 72 characters or less
|
||||
* 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
|
||||
* :bulb: Check out the [Emoji Cheat Sheet](http://www.emoji-cheat-sheet.com)
|
||||
for more ideas.
|
||||
|
||||
## CoffeeScript Styleguide
|
||||
|
||||
* Use parentheses if it improves code clarity.
|
||||
* Prefer alphabetic keywords to symbolic keywords:
|
||||
* `a is b` instead of `a == b`
|
||||
* Avoid spaces inside the curly-braces of hash literals:
|
||||
* `{a: 1, b: 2}` instead of `{ a: 1, b: 2 }`
|
||||
* Set parameter defaults without spaces around the equal sign:
|
||||
* `clear = (count=1) ->` instead of `clear = (count = 1) ->`
|
||||
* Include a single line of whitespace between methods.
|
||||
|
||||
## Documentation Styleguide
|
||||
|
||||
* Use [TomDoc](http://tomdoc.org).
|
||||
* Use [Markdown](https://daringfireball.net/projects/markdown).
|
||||
* Reference methods and classes in markdown with the custom `{}` notation:
|
||||
* Reference classes with `{ClassName}`
|
||||
* Reference instance methods with `{ClassName::methodName}`
|
||||
* Reference class methods with `{ClassName.methodName}`
|
||||
|
||||
### Example
|
||||
|
||||
```coffee
|
||||
# Public: Disable the package with the given name.
|
||||
#
|
||||
# This method emits multiple events:
|
||||
#
|
||||
# * `package-will-be-disabled` - before the package is disabled.
|
||||
# * `package-disabled` - after the package is disabled.
|
||||
#
|
||||
# name - The {String} name of the package to disable.
|
||||
# options - The {Object} with disable options (default: {}):
|
||||
# :trackTime - `true` to track the amount of time disabling took.
|
||||
# :ignoreErrors - `true` to catch and ignore errors thrown.
|
||||
# callback - The {Function} to call after the package has been disabled.
|
||||
#
|
||||
# Returns `undefined`.
|
||||
disablePackage: (name, options, callback) ->
|
||||
```
|
||||
Link simbólico
+1
@@ -0,0 +1 @@
|
||||
../CONTRIBUTING.md
|
||||
@@ -20,7 +20,7 @@ apm help init
|
||||
|
||||
You should see a message print out with details about the `apm init` command.
|
||||
|
||||
If you do not, launch Atom and run the _Atom > Install Shell Commmands_ menu
|
||||
If you do not, launch Atom and run the _Atom > Install Shell Commands_ menu
|
||||
to install the `apm` and `atom` commands.
|
||||
|
||||
### Convert the Package
|
||||
@@ -49,4 +49,4 @@ the editor to see it in action!
|
||||
[plist]: http://en.wikipedia.org/wiki/Property_list
|
||||
[R]: http://en.wikipedia.org/wiki/R_(programming_language)
|
||||
[TextMate]: http://macromates.com
|
||||
[TextMateOrg]: https://github.com/textmate/r.tmbundle
|
||||
[TextMateOrg]: https://github.com/textmate
|
||||
|
||||
@@ -25,7 +25,7 @@ apm help init
|
||||
|
||||
You should see a message print out with details about the `apm init` command.
|
||||
|
||||
If you do not, launch Atom and run the _Atom > Install Shell Commmands_ menu
|
||||
If you do not, launch Atom and run the _Atom > Install Shell Commands_ menu
|
||||
to install the `apm` and `atom` commands.
|
||||
|
||||
You can now run `apm help init` to see all the options for initializing new
|
||||
|
||||
@@ -34,7 +34,7 @@ apm help install
|
||||
|
||||
You should see a message print out with details about the `apm install` command.
|
||||
|
||||
If you do not, launch Atom and run the _Atom > Install Shell Commmands_ menu
|
||||
If you do not, launch Atom and run the _Atom > Install Shell Commands_ menu
|
||||
to install the `apm` and `atom` commands.
|
||||
|
||||
You can also install packages by using the `apm install` command:
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* [Creating a Package](creating-a-package.md)
|
||||
* [Creating a Theme](creating-a-theme.md)
|
||||
* [Publishing a Package](publishing-a-package.md)
|
||||
* [Writing Specs](writing-specs.md)
|
||||
* [Converting a TextMate Bundle](converting-a-text-mate-bundle.md)
|
||||
* [Converting a TextMate Theme](converting-a-text-mate-theme.md)
|
||||
* [Contributing](contributing.md)
|
||||
@@ -12,6 +13,7 @@
|
||||
### Advanced Topics
|
||||
|
||||
* [Configuration](advanced/configuration.md)
|
||||
* [Developing Node Modules](advanced/node-modules.md)
|
||||
* [Keymaps](advanced/keymaps.md)
|
||||
* [Serialization](advanced/serialization.md)
|
||||
* [View System](advanced/view-system.md)
|
||||
|
||||
@@ -7,7 +7,7 @@ Publishing a package allows other people to install it and use it in Atom. It
|
||||
is a great way to share what you've made and get feedback and contributions from
|
||||
others.
|
||||
|
||||
This guide assumes your package's name is `my-package` and but you should pick a
|
||||
This guide assumes your package's name is `my-package` but you should pick a
|
||||
better name.
|
||||
|
||||
### Install apm
|
||||
@@ -24,7 +24,7 @@ apm help publish
|
||||
|
||||
You should see a message print out with details about the `apm publish` command.
|
||||
|
||||
If you do not, launch Atom and run the _Atom > Install Shell Commmands_ menu
|
||||
If you do not, launch Atom and run the _Atom > Install Shell Commands_ menu
|
||||
to install the `apm` and `atom` commands.
|
||||
|
||||
### Prepare Your Package
|
||||
@@ -42,7 +42,7 @@ If not, there are a few things you should check before publishing:
|
||||
* Your package is in a Git repository that has been pushed to
|
||||
[GitHub][github]. Follow [this guide][repo-guide] if your package isn't
|
||||
already on GitHub.
|
||||
|
||||
|
||||
### Publish Your Package
|
||||
|
||||
Before you publish a package it is a good idea to check ahead of time if
|
||||
@@ -59,7 +59,7 @@ Now let's review what the `apm publish` command does:
|
||||
3. Creates a new [Git tag][git-tag] for the version being published.
|
||||
4. Pushes the tag and current branch up to GitHub.
|
||||
5. Updates atom.io with the new version being published.
|
||||
|
||||
|
||||
Now run the following commands to publish your package:
|
||||
|
||||
```sh
|
||||
@@ -80,7 +80,7 @@ digit of the version before publishing so the published version will be `0.1.0`
|
||||
and the Git tag created will be `v0.1.0`.
|
||||
|
||||
In the future you can run `apm publish major` to publish the `1.0.0` version but
|
||||
since this was the first version being published it is a good idead to start
|
||||
since this was the first version being published it is a good idea to start
|
||||
with a minor release.
|
||||
|
||||
### Further Reading
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# Writting specs
|
||||
|
||||
Atom uses [Jasmine](http://jasmine.github.io/2.0/introduction.html) as its spec framework. Any new functionality should have specs to guard against regressions.
|
||||
|
||||
## Create a new spec
|
||||
|
||||
[Atom specs](https://github.com/atom/atom/tree/master/spec) and [package specs](https://github.com/atom/markdown-preview/tree/master/spec) are added to their respective `spec` directory. The example below creates a spec for Atom core.
|
||||
|
||||
0. Create a spec file
|
||||
|
||||
Spec files **must** end with `-spec` so add `sample-spec.coffee` to `atom/spec`.
|
||||
|
||||
0. Add one or more `describe` method
|
||||
|
||||
The `describe` method takes two arguments, a description and a function. If the description explains a behavior it typically begins with `when` if it is more like a unit test it begins with the method name.
|
||||
|
||||
```coffee
|
||||
describe "when a test is written", ->
|
||||
# contents
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```coffee
|
||||
describe "Editor::moveUp", ->
|
||||
# contents
|
||||
```
|
||||
|
||||
0. Add one or more `it` method
|
||||
|
||||
The `it` method also takes two arugments, a description and a function. Try and make the description flow with the `it` method. For example, a description of `this should work` doesn't read well as `it this should work`. But a description of `should work` sounds great as `it should work`.
|
||||
|
||||
```coffee
|
||||
describe "when a test is written", ->
|
||||
it "has some expectations that should pass", ->
|
||||
# Expectations
|
||||
```
|
||||
|
||||
0. Add one or more expectations
|
||||
|
||||
The best way to learn about expectations is to read the [jamsine documentation](http://jasmine.github.io/2.0/introduction.html#section-Expectations) about them. Below is a simple example.
|
||||
|
||||
```coffee
|
||||
describe "when a test is written", ->
|
||||
it "has some expectations that should pass", ->
|
||||
expect("apples").toEqual("apples")
|
||||
expect("oranges").not.toEqual("apples")
|
||||
```
|
||||
|
||||
## Runnings specs
|
||||
|
||||
Most of the time you'll want to run specs by triggering the `window:run-package-specs` command. This command is not only to run package specs, it is also for Atom core specs. This will run all the specs in the current project's spec directory. If you want to run the Atom core specs and **all** the default package specs trigger the `window:run-all-specs` command.
|
||||
|
||||
To run a limited subset of specs use the `fdescribe` or `fit` methods. You can use those to focus a single spec or several specs. In the example above, focusing an individual spec looks like this:
|
||||
|
||||
```coffee
|
||||
describe "when a test is written", ->
|
||||
fit "has some expectations that should pass", ->
|
||||
expect("apples").toEqual("apples")
|
||||
expect("oranges").not.toEqual("apples")
|
||||
```
|
||||
@@ -21,7 +21,7 @@ To begin, press `cmd-shift-P` to bring up the [Command
|
||||
Palette](https://github.com/atom/command-palette). Type "generate package" and
|
||||
select the "Package Generator: Generate Package" command. Now we need to name
|
||||
the package. Try to avoid naming your package with the *atom-* prefix, for
|
||||
example we are going to call this package _ascii-art_.
|
||||
example we are going to call this package _ascii-art_.
|
||||
|
||||
Atom will open a new window with the contents of our new _ascii-art_ package
|
||||
displayed in the Tree View. Because this window is opened **after** the package
|
||||
@@ -149,8 +149,10 @@ ASCII art professional!
|
||||
|
||||
* [Getting your project on GitHub guide](http://guides.github.com/overviews/desktop)
|
||||
|
||||
* [Writing specs](writing-specs.md) for your package
|
||||
|
||||
* [Creating a package guide](creating-a-package.html) for more information
|
||||
on the mechanics of packages
|
||||
|
||||
* [Publishing a package guide](publish-a-package.html) for more information
|
||||
* [Publishing a package guide](publishing-a-package.html) for more information
|
||||
on publishing your package to [atom.io](https://atom.io)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
storage
|
||||
compile-cache
|
||||
dev
|
||||
.npm
|
||||
.node-gyp
|
||||
@@ -1,9 +1,5 @@
|
||||
'.editor':
|
||||
# Platform Bindings
|
||||
'alt-left': 'editor:move-to-beginning-of-word'
|
||||
'alt-right': 'editor:move-to-end-of-word'
|
||||
'alt-shift-left': 'editor:select-to-beginning-of-word'
|
||||
'alt-shift-right': 'editor:select-to-end-of-word'
|
||||
'home': 'editor:move-to-first-character-of-line'
|
||||
'end': 'editor:move-to-end-of-screen-line'
|
||||
'shift-home': 'editor:select-to-first-character-of-line'
|
||||
|
||||
+12
-4
@@ -65,6 +65,8 @@
|
||||
'cmd-}': 'pane:show-next-item'
|
||||
'cmd-alt-left': 'pane:show-previous-item'
|
||||
'cmd-alt-right': 'pane:show-next-item'
|
||||
'ctrl-tab': 'pane:show-next-item'
|
||||
'ctrl-shift-tab': 'pane:show-previous-item'
|
||||
'cmd-=': 'window:increase-font-size'
|
||||
'cmd-+': 'window:increase-font-size'
|
||||
'cmd--': 'window:decrease-font-size'
|
||||
@@ -94,17 +96,23 @@
|
||||
'cmd-9': 'pane:show-item-9'
|
||||
|
||||
'.editor':
|
||||
# Platform Bindings
|
||||
'alt-left': 'editor:move-to-beginning-of-word'
|
||||
'alt-right': 'editor:move-to-end-of-word'
|
||||
'alt-shift-left': 'editor:select-to-beginning-of-word'
|
||||
'alt-shift-right': 'editor:select-to-end-of-word'
|
||||
|
||||
# Apple Specific
|
||||
'cmd-backspace': 'editor:backspace-to-beginning-of-line'
|
||||
'cmd-shift-backspace': 'editor:backspace-to-beginning-of-line'
|
||||
'cmd-delete': 'editor:backspace-to-beginning-of-line'
|
||||
'cmd-backspace': 'editor:delete-to-beginning-of-line'
|
||||
'cmd-shift-backspace': 'editor:delete-to-beginning-of-line'
|
||||
'cmd-delete': 'editor:delete-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-screen-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-backspace': 'editor:delete-to-beginning-of-word'
|
||||
'alt-delete': 'editor:delete-to-end-of-word'
|
||||
'ctrl-a': 'editor:move-to-beginning-of-line'
|
||||
'ctrl-e': 'editor:move-to-end-of-line'
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
'alt-F': 'editor:select-to-end-of-word'
|
||||
'alt-b': 'editor:move-to-beginning-of-word'
|
||||
'alt-B': 'editor:select-to-beginning-of-word'
|
||||
'alt-h': 'editor:backspace-to-beginning-of-word'
|
||||
'alt-h': 'editor:delete-to-beginning-of-word'
|
||||
'alt-d': 'editor:delete-to-end-of-word'
|
||||
|
||||
+11
-2
@@ -60,8 +60,17 @@
|
||||
'ctrl-k ctrl-right': 'window:focus-pane-on-right'
|
||||
|
||||
'.workspace .editor':
|
||||
# Windows specific
|
||||
'ctrl-delete': 'editor:backspace-to-beginning-of-word'
|
||||
# Platform Bindings
|
||||
'ctrl-left': 'editor:move-to-beginning-of-word'
|
||||
'ctrl-right': 'editor:move-to-end-of-word'
|
||||
'ctrl-shift-left': 'editor:select-to-beginning-of-word'
|
||||
'ctrl-shift-right': 'editor:select-to-end-of-word'
|
||||
'ctrl-backspace': 'editor:delete-to-beginning-of-word'
|
||||
'ctrl-delete': 'editor:delete-to-end-of-word'
|
||||
'ctrl-home': 'core:move-to-top'
|
||||
'ctrl-end': 'core:move-to-bottom'
|
||||
'ctrl-shift-home': 'core:select-to-top'
|
||||
'ctrl-shift-end': 'core:select-to-bottom'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-a': 'core:select-all'
|
||||
|
||||
+15
-2
@@ -1,4 +1,8 @@
|
||||
'body':
|
||||
# Platform Bindings
|
||||
'ctrl-pageup': 'pane:show-previous-item'
|
||||
'ctrl-pagedown': 'pane:show-next-item'
|
||||
|
||||
# Atom Specific
|
||||
'enter': 'core:confirm'
|
||||
'escape': 'core:cancel'
|
||||
@@ -10,6 +14,7 @@
|
||||
'ctrl-alt-i': 'window:toggle-dev-tools'
|
||||
'ctrl-alt-p': 'window:run-package-specs'
|
||||
'ctrl-alt-s': 'application:run-all-specs'
|
||||
'F11': 'window:toggle-full-screen'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-N': 'application:new-window'
|
||||
@@ -33,6 +38,7 @@
|
||||
'pageup': 'core:page-up'
|
||||
'pagedown': 'core:page-down'
|
||||
'backspace': 'core:backspace'
|
||||
'shift-backspace': 'core:backspace'
|
||||
'ctrl-tab': 'pane:show-next-item'
|
||||
'ctrl-shift-tab': 'pane:show-previous-item'
|
||||
'ctrl-shift-up': 'core:move-up'
|
||||
@@ -57,8 +63,15 @@
|
||||
'ctrl-k ctrl-right': 'window:focus-pane-on-right'
|
||||
|
||||
'.workspace .editor':
|
||||
# Windows specific
|
||||
'ctrl-delete': 'editor:backspace-to-beginning-of-word'
|
||||
# Platform Bindings
|
||||
'ctrl-left': 'editor:move-to-beginning-of-word'
|
||||
'ctrl-right': 'editor:move-to-end-of-word'
|
||||
'ctrl-shift-left': 'editor:select-to-beginning-of-word'
|
||||
'ctrl-shift-right': 'editor:select-to-end-of-word'
|
||||
'ctrl-backspace': 'editor:delete-to-beginning-of-word'
|
||||
'ctrl-delete': 'editor:delete-to-end-of-word'
|
||||
'ctrl-home': 'core:move-to-top'
|
||||
'ctrl-end': 'core:move-to-bottom'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-a': 'core:select-all'
|
||||
|
||||
@@ -134,6 +134,25 @@
|
||||
submenu: [
|
||||
{ label: 'Reload', command: 'window:reload' }
|
||||
{ label: 'Toggle Full Screen', command: 'window:toggle-full-screen' }
|
||||
{
|
||||
label: 'Panes'
|
||||
submenu: [
|
||||
{ label: 'Split Up', command: 'pane:split-up' }
|
||||
{ label: 'Split Down', command: 'pane:split-down' }
|
||||
{ label: 'Split Left', command: 'pane:split-left' }
|
||||
{ label: 'Split Right', command: 'pane:split-right' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Focus Next Pane', command: 'window:focus-next-pane' }
|
||||
{ label: 'Focus Previous Pane', command: 'window:focus-previous-pane' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Focus Pane Above', command: 'window:focus-pane-above' }
|
||||
{ label: 'Focus Pane Below', command: 'window:focus-pane-below' }
|
||||
{ label: 'Focus Pane On Left', command: 'window:focus-pane-on-left' }
|
||||
{ label: 'Focus Pane On Right', command: 'window:focus-pane-on-right' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Close Pane', command: 'pane:close' }
|
||||
]
|
||||
}
|
||||
{
|
||||
label: 'Developer'
|
||||
submenu: [
|
||||
@@ -166,6 +185,7 @@
|
||||
{
|
||||
label: 'Help'
|
||||
submenu: [
|
||||
{ label: 'Terms of Use', command: 'application:open-terms-of-use' }
|
||||
{ label: 'Documentation', command: 'application:open-documentation' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
|
||||
@@ -145,6 +145,7 @@
|
||||
{
|
||||
label: '&Help'
|
||||
submenu: [
|
||||
{ label: 'View &Terms of Use', command: 'application:open-terms-of-use' }
|
||||
{ label: 'View &License', command: 'application:open-license' }
|
||||
{ label: "VERSION", enabled: false }
|
||||
{ type: 'separator' }
|
||||
|
||||
@@ -88,6 +88,25 @@
|
||||
submenu: [
|
||||
{ label: '&Reload', command: 'window:reload' }
|
||||
{ label: 'Toggle &Full Screen', command: 'window:toggle-full-screen' }
|
||||
{
|
||||
label: 'Panes'
|
||||
submenu: [
|
||||
{ label: 'Split Up', command: 'pane:split-up' }
|
||||
{ label: 'Split Down', command: 'pane:split-down' }
|
||||
{ label: 'Split Left', command: 'pane:split-left' }
|
||||
{ label: 'Split Right', command: 'pane:split-right' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Focus Next Pane', command: 'window:focus-next-pane' }
|
||||
{ label: 'Focus Previous Pane', command: 'window:focus-previous-pane' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Focus Pane Above', command: 'window:focus-pane-above' }
|
||||
{ label: 'Focus Pane Below', command: 'window:focus-pane-below' }
|
||||
{ label: 'Focus Pane On Left', command: 'window:focus-pane-on-left' }
|
||||
{ label: 'Focus Pane On Right', command: 'window:focus-pane-on-right' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Close pane', command: 'pane:close' }
|
||||
]
|
||||
}
|
||||
{
|
||||
label: 'Developer'
|
||||
submenu: [
|
||||
@@ -146,6 +165,7 @@
|
||||
label: '&Help'
|
||||
submenu: [
|
||||
{ label: '&About Atom...', command: 'application:about' }
|
||||
{ label: 'View &Terms of Use', command: 'application:open-terms-of-use' }
|
||||
{ label: 'View &License', command: 'application:open-license' }
|
||||
{ label: "VERSION", enabled: false }
|
||||
{ label: "Install &update", command: 'application:install-update', visible: false }
|
||||
|
||||
+59
-54
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "0.92.0",
|
||||
"version": "0.98.0",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
@@ -11,49 +11,54 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/atom/atom/issues"
|
||||
},
|
||||
"license": "All Rights Reserved",
|
||||
"atomShellVersion": "0.11.10",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
|
||||
}
|
||||
],
|
||||
"atomShellVersion": "0.12.5",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"atom-keymap": "^0.18.0",
|
||||
"bootstrap": "git://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
|
||||
"atom-keymap": "^0.22.0",
|
||||
"bootstrap": "git+https://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
|
||||
"clear-cut": "0.4.0",
|
||||
"coffee-script": "1.7.0",
|
||||
"coffeestack": "0.7.0",
|
||||
"delegato": "1.x",
|
||||
"delegato": "^1",
|
||||
"emissary": "^1.2.1",
|
||||
"first-mate": "^1.5.2",
|
||||
"fs-plus": "^2.2.2",
|
||||
"first-mate": "^1.5.3",
|
||||
"fs-plus": "^2.2.3",
|
||||
"fstream": "0.1.24",
|
||||
"fuzzaldrin": "~1.1",
|
||||
"git-utils": "^1.2.2",
|
||||
"grim": "0.9.0",
|
||||
"fuzzaldrin": "^1.1",
|
||||
"git-utils": "^1.3",
|
||||
"grim": "0.10.0",
|
||||
"guid": "0.0.10",
|
||||
"jasmine-tagged": "^1.1.1",
|
||||
"less-cache": "0.12.0",
|
||||
"mixto": "1.x",
|
||||
"mixto": "^1",
|
||||
"mkdirp": "0.3.5",
|
||||
"nslog": "0.5.0",
|
||||
"oniguruma": "^1.0.6",
|
||||
"optimist": "0.4.0",
|
||||
"pathwatcher": "^1.2.1",
|
||||
"property-accessors": "1.x",
|
||||
"pathwatcher": "^1.3.1",
|
||||
"property-accessors": "^1",
|
||||
"q": "^1.0.1",
|
||||
"random-words": "0.0.1",
|
||||
"react": "^0.10.0",
|
||||
"reactionary": "^0.8.0",
|
||||
"runas": "0.5.x",
|
||||
"react-atom-fork": "^0.10.0",
|
||||
"reactionary-atom-fork": "^0.9.0",
|
||||
"runas": "^0.5",
|
||||
"scandal": "0.15.2",
|
||||
"scoped-property-store": "^0.8.0",
|
||||
"scrollbar-style": "^0.1.0",
|
||||
"scoped-property-store": "^0.9.0",
|
||||
"scrollbar-style": "^0.4.0",
|
||||
"season": "^1.0.2",
|
||||
"semver": "1.1.4",
|
||||
"serializable": "1.x",
|
||||
"space-pen": "3.1.1",
|
||||
"serializable": "^1",
|
||||
"space-pen": "3.2.0",
|
||||
"temp": "0.5.0",
|
||||
"text-buffer": "^2.1.0",
|
||||
"theorist": "1.x",
|
||||
"underscore-plus": "^1.2.1",
|
||||
"text-buffer": "^2.2.2",
|
||||
"theorist": "^1",
|
||||
"underscore-plus": "^1.3.0",
|
||||
"vm-compatibility-layer": "0.1.0"
|
||||
},
|
||||
"packageDependencies": {
|
||||
@@ -64,72 +69,72 @@
|
||||
"base16-tomorrow-dark-theme": "0.15.0",
|
||||
"solarized-dark-syntax": "0.14.0",
|
||||
"solarized-light-syntax": "0.7.0",
|
||||
"archive-view": "0.30.0",
|
||||
"autocomplete": "0.27.0",
|
||||
"autoflow": "0.16.0",
|
||||
"archive-view": "0.31.0",
|
||||
"autocomplete": "0.28.0",
|
||||
"autoflow": "0.17.0",
|
||||
"autosave": "0.13.0",
|
||||
"background-tips": "0.13.0",
|
||||
"bookmarks": "0.22.0",
|
||||
"bracket-matcher": "0.33.0",
|
||||
"bracket-matcher": "0.38.0",
|
||||
"command-palette": "0.21.0",
|
||||
"deprecation-cop": "0.5.0",
|
||||
"dev-live-reload": "0.30.0",
|
||||
"exception-reporting": "0.17.0",
|
||||
"feedback": "0.30.0",
|
||||
"find-and-replace": "0.99.0",
|
||||
"fuzzy-finder": "0.50.0",
|
||||
"feedback": "0.33.0",
|
||||
"find-and-replace": "0.106.0",
|
||||
"fuzzy-finder": "0.51.0",
|
||||
"git-diff": "0.28.0",
|
||||
"go-to-line": "0.19.0",
|
||||
"grammar-selector": "0.24.0",
|
||||
"go-to-line": "0.21.0",
|
||||
"grammar-selector": "0.27.0",
|
||||
"image-view": "0.33.0",
|
||||
"keybinding-resolver": "0.17.0",
|
||||
"link": "0.22.0",
|
||||
"markdown-preview": "0.65.0",
|
||||
"markdown-preview": "0.72.0",
|
||||
"metrics": "0.32.0",
|
||||
"open-on-github": "0.28.0",
|
||||
"package-generator": "0.30.0",
|
||||
"release-notes": "0.27.0",
|
||||
"settings-view": "0.111.0",
|
||||
"snippets": "0.41.0",
|
||||
"spell-check": "0.34.0",
|
||||
"release-notes": "0.31.0",
|
||||
"settings-view": "0.116.0",
|
||||
"snippets": "0.43.0",
|
||||
"spell-check": "0.35.0",
|
||||
"status-bar": "0.40.0",
|
||||
"styleguide": "0.29.0",
|
||||
"symbols-view": "0.49.0",
|
||||
"tabs": "0.37.0",
|
||||
"symbols-view": "0.53.0",
|
||||
"tabs": "0.40.0",
|
||||
"timecop": "0.18.0",
|
||||
"tree-view": "0.90.0",
|
||||
"tree-view": "0.93.0",
|
||||
"update-package-dependencies": "0.6.0",
|
||||
"welcome": "0.12.0",
|
||||
"welcome": "0.14.0",
|
||||
"whitespace": "0.22.0",
|
||||
"wrap-guide": "0.18.0",
|
||||
"language-c": "0.14.0",
|
||||
"language-c": "0.15.0",
|
||||
"language-coffee-script": "0.22.0",
|
||||
"language-css": "0.16.0",
|
||||
"language-gfm": "0.31.0",
|
||||
"language-gfm": "0.38.0",
|
||||
"language-git": "0.9.0",
|
||||
"language-go": "0.9.0",
|
||||
"language-html": "0.19.0",
|
||||
"language-go": "0.12.0",
|
||||
"language-html": "0.22.0",
|
||||
"language-hyperlink": "0.9.0",
|
||||
"language-java": "0.9.0",
|
||||
"language-javascript": "0.24.0",
|
||||
"language-java": "0.10.0",
|
||||
"language-javascript": "0.26.0",
|
||||
"language-json": "0.8.0",
|
||||
"language-less": "0.8.0",
|
||||
"language-less": "0.9.0",
|
||||
"language-make": "0.10.0",
|
||||
"language-objective-c": "0.11.0",
|
||||
"language-perl": "0.8.0",
|
||||
"language-php": "0.14.0",
|
||||
"language-property-list": "0.7.0",
|
||||
"language-python": "0.15.0",
|
||||
"language-ruby": "0.23.0",
|
||||
"language-ruby-on-rails": "0.12.0",
|
||||
"language-sass": "0.10.0",
|
||||
"language-python": "0.17.0",
|
||||
"language-ruby": "0.25.0",
|
||||
"language-ruby-on-rails": "0.14.0",
|
||||
"language-sass": "0.11.0",
|
||||
"language-shellscript": "0.8.0",
|
||||
"language-source": "0.7.0",
|
||||
"language-sql": "0.8.0",
|
||||
"language-text": "0.6.0",
|
||||
"language-todo": "0.10.0",
|
||||
"language-toml": "0.12.0",
|
||||
"language-xml": "0.11.0",
|
||||
"language-xml": "0.13.0",
|
||||
"language-yaml": "0.6.0"
|
||||
},
|
||||
"private": true,
|
||||
|
||||
Arquivo binário não exibido.
|
Antes Largura: | Altura: | Tamanho: 191 KiB Depois Largura: | Altura: | Tamanho: 284 KiB |
+267
-89
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
|
Antes Largura: | Altura: | Tamanho: 361 KiB Depois Largura: | Altura: | Tamanho: 141 KiB |
+15
-9
@@ -1,8 +1,18 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var nodeMinorVersion = process.versions.node.split('.')[1]
|
||||
if (nodeMinorVersion !== '10') {
|
||||
console.warn("You must run script/bootstrap and script/build with node v0.10.x");
|
||||
var fs = require('fs');
|
||||
|
||||
var nodeVersion = process.versions.node.split('.')
|
||||
var nodeMajorVersion = +nodeVersion[0]
|
||||
var nodeMinorVersion = +nodeVersion[1]
|
||||
if (nodeMajorVersion === 0 && nodeMinorVersion < 10) {
|
||||
console.warn("You must run script/bootstrap and script/build with node v0.10 or above");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Make sure python2.7 is installed
|
||||
if (process.platform == 'win32' && !fs.existsSync('C:\\Python27\\')) {
|
||||
console.warn("You must have Python 2.7 installed at 'C:\\Python27\\'");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -25,14 +35,13 @@ function executeCommands(commands, done, index) {
|
||||
done(null);
|
||||
}
|
||||
|
||||
var apmVendorPath = path.resolve(__dirname, '..', 'vendor', 'apm');
|
||||
var apmInstallPath = path.resolve(__dirname, '..', 'apm');
|
||||
if (!fs.existsSync(apmInstallPath))
|
||||
fs.mkdirSync(apmInstallPath);
|
||||
if (!fs.existsSync(path.join(apmInstallPath, 'node_modules')))
|
||||
fs.mkdirSync(path.join(apmInstallPath, 'node_modules'));
|
||||
|
||||
var apmPath = 'apm/node_modules/atom-package-manager/bin/apm'
|
||||
var apmPath = path.resolve(__dirname, '..', 'apm', 'node_modules', 'atom-package-manager', 'bin', 'apm')
|
||||
var apmFlags = process.env.JANKY_SHA1 || process.argv.indexOf('--no-color') !== -1 ? '--no-color' : '';
|
||||
|
||||
var npmPath = path.resolve(__dirname, '..', 'build', 'node_modules', '.bin', 'npm');
|
||||
@@ -42,11 +51,8 @@ var packagesToDedupe = ['fs-plus', 'humanize-plus', 'oniguruma', 'roaster', 'sea
|
||||
var echoNewLine = process.platform == 'win32' ? 'echo.' : 'echo';
|
||||
|
||||
var commands = [
|
||||
'git submodule --quiet sync',
|
||||
'git submodule --quiet update --recursive --init',
|
||||
{command: 'npm' + npmFlags + 'install --quiet', options: {cwd: path.resolve(__dirname, '..', 'build'), ignoreStdout: true}},
|
||||
{command: npmPath + npmFlags + 'install --quiet', options: {cwd: apmVendorPath, ignoreStdout: true}},
|
||||
{command: npmPath + npmFlags + 'install --quiet ' + apmVendorPath, options: {cwd: apmInstallPath, ignoreStdout: true}},
|
||||
{command: npmPath + npmFlags + 'install --quiet', options: {cwd: apmInstallPath, ignoreStdout: true}},
|
||||
echoNewLine,
|
||||
apmPath + ' clean ' + apmFlags,
|
||||
apmPath + ' install --quiet ' + apmFlags,
|
||||
|
||||
+1
-2
@@ -8,7 +8,7 @@ var productName = require('../package.json').productName;
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
var home = process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];
|
||||
var tmpdir = process.platform === 'win32' ? os.tmpdir() : '/tmp';
|
||||
var tmpdir = os.tmpdir();
|
||||
|
||||
// Windows: Use START as a way to ignore error if Atom.exe isnt running
|
||||
var killatom = process.platform === 'win32' ? 'START taskkill /F /IM ' + productName + '.exe' : 'pkill -9 ' + productName + ' || true';
|
||||
@@ -18,7 +18,6 @@ var commands = [
|
||||
[__dirname, '..', 'node_modules'],
|
||||
[__dirname, '..', 'build', 'node_modules'],
|
||||
[__dirname, '..', 'apm', 'node_modules'],
|
||||
[__dirname, '..', 'vendor', 'apm', 'node_modules'],
|
||||
[__dirname, '..', 'atom-shell'],
|
||||
[home, '.atom', '.node-gyp'],
|
||||
[home, '.atom', 'storage'],
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
@IF EXIST "%~dp0\node.exe" (
|
||||
"%~dp0\node.exe" "%~dp0\grunt" %*
|
||||
) ELSE (
|
||||
node "%~dp0\grunt" %*
|
||||
)
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/bin/bash
|
||||
# mkdeb version control-file-path deb-file-path
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT=`readlink -f "$0"`
|
||||
ROOT=`readlink -f $(dirname $SCRIPT)/..`
|
||||
cd $ROOT
|
||||
@@ -12,6 +14,7 @@ ICON_FILE="$4"
|
||||
DEB_PATH="$5"
|
||||
|
||||
TARGET_ROOT="`mktemp -d`"
|
||||
chmod 755 "$TARGET_ROOT"
|
||||
TARGET="$TARGET_ROOT/atom-$VERSION-amd64"
|
||||
|
||||
mkdir -p "$TARGET/usr"
|
||||
|
||||
+33
-30
@@ -113,18 +113,21 @@ describe "the `atom` global", ->
|
||||
promise
|
||||
|
||||
it "triggers the activation event on all handlers registered during activation", ->
|
||||
atom.workspaceView.openSync()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
eventHandler = jasmine.createSpy("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
|
||||
editorView.trigger 'activation-event'
|
||||
expect(mainModule.activationEventCallCount).toBe 2
|
||||
expect(eventHandler.callCount).toBe 2
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open()
|
||||
|
||||
runs ->
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
eventHandler = jasmine.createSpy("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
|
||||
editorView.trigger 'activation-event'
|
||||
expect(mainModule.activationEventCallCount).toBe 2
|
||||
expect(eventHandler.callCount).toBe 2
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
|
||||
it "activates the package immediately when the events are empty", ->
|
||||
mainModule = require './fixtures/packages/package-with-empty-activation-events/index'
|
||||
@@ -170,28 +173,28 @@ describe "the `atom` global", ->
|
||||
element2 = $$ -> @div class: 'test-2'
|
||||
element3 = $$ -> @div class: 'test-3'
|
||||
|
||||
expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)).toHaveLength 0
|
||||
expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', element2)).toHaveLength 0
|
||||
expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', element3)).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element2[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element3[0])).toHaveLength 0
|
||||
|
||||
atom.packages.activatePackage("package-with-keymaps")
|
||||
|
||||
expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)[0].command).toBe "test-1"
|
||||
expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', element2)[0].command).toBe "test-2"
|
||||
expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', element3)).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])[0].command).toBe "test-1"
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element2[0])[0].command).toBe "test-2"
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element3[0])).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(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])).toHaveLength 0
|
||||
|
||||
atom.packages.activatePackage("package-with-keymaps-manifest")
|
||||
|
||||
expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)[0].command).toBe 'keymap-1'
|
||||
expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-n', element1)[0].command).toBe 'keymap-2'
|
||||
expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-y', element3)).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])[0].command).toBe 'keymap-1'
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-n', target:element1[0])[0].command).toBe 'keymap-2'
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-y', target:element3[0])).toHaveLength 0
|
||||
|
||||
describe "menu loading", ->
|
||||
beforeEach ->
|
||||
@@ -377,8 +380,8 @@ describe "the `atom` global", ->
|
||||
|
||||
runs ->
|
||||
atom.packages.deactivatePackage('package-with-keymaps')
|
||||
expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', $$ -> @div class: 'test-1')).toHaveLength 0
|
||||
expect(atom.keymaps.keyBindingsForKeystrokeMatchingElement('ctrl-z', $$ -> @div class: 'test-2')).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:$$ -> @div class: 'test-1'[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:$$ -> @div class: 'test-2'[0])).toHaveLength 0
|
||||
|
||||
it "removes the package's stylesheets", ->
|
||||
waitsForPromise ->
|
||||
@@ -507,20 +510,20 @@ describe "the `atom` global", ->
|
||||
# enabling of theme
|
||||
pack = atom.packages.enablePackage(packageName)
|
||||
|
||||
activatedPackages = null
|
||||
waitsFor ->
|
||||
activatedPackages = atom.packages.getActivePackages()
|
||||
activatedPackages.length > 0
|
||||
pack in atom.packages.getActivePackages()
|
||||
|
||||
runs ->
|
||||
expect(activatedPackages).toContain(pack)
|
||||
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)
|
||||
|
||||
waitsFor ->
|
||||
not (pack in atom.packages.getActivePackages())
|
||||
|
||||
runs ->
|
||||
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
|
||||
|
||||
@@ -5,6 +5,10 @@ fs = require 'fs-plus'
|
||||
|
||||
describe "Config", ->
|
||||
dotAtomPath = path.join(temp.dir, 'dot-atom-dir')
|
||||
dotAtomPath = null
|
||||
|
||||
beforeEach ->
|
||||
dotAtomPath = temp.path('dot-atom-dir')
|
||||
|
||||
describe ".get(keyPath)", ->
|
||||
it "allows a key path's value to be read", ->
|
||||
@@ -258,8 +262,10 @@ describe "Config", ->
|
||||
|
||||
describe ".initializeConfigDirectory()", ->
|
||||
beforeEach ->
|
||||
if fs.existsSync(dotAtomPath)
|
||||
fs.removeSync(dotAtomPath)
|
||||
|
||||
atom.config.configDirPath = dotAtomPath
|
||||
expect(fs.existsSync(atom.config.configDirPath)).toBeFalsy()
|
||||
|
||||
afterEach ->
|
||||
fs.removeSync(dotAtomPath)
|
||||
|
||||
@@ -21,7 +21,7 @@ describe "DisplayBuffer", ->
|
||||
describe "::copy()", ->
|
||||
it "creates a new DisplayBuffer with the same initial state", ->
|
||||
marker1 = displayBuffer.markBufferRange([[1, 2], [3, 4]], id: 1)
|
||||
marker2 = displayBuffer.markBufferRange([[2, 3], [4, 5]], isReversed: true, id: 2)
|
||||
marker2 = displayBuffer.markBufferRange([[2, 3], [4, 5]], reversed: true, id: 2)
|
||||
marker3 = displayBuffer.markBufferPosition([5, 6], id: 3)
|
||||
displayBuffer.createFold(3, 5)
|
||||
|
||||
@@ -948,7 +948,7 @@ describe "DisplayBuffer", ->
|
||||
it "returns the start and end positions of the marker based on the line height and character widths assigned to the DisplayBuffer", ->
|
||||
marker = displayBuffer.markScreenRange([[5, 10], [6, 4]])
|
||||
|
||||
displayBuffer.setLineHeight(20)
|
||||
displayBuffer.setLineHeightInPixels(20)
|
||||
displayBuffer.setDefaultCharWidth(10)
|
||||
displayBuffer.setScopedCharWidths(["source.js", "keyword.control.js"], r: 11, e: 11, t: 11, u: 11, n: 11)
|
||||
|
||||
@@ -959,7 +959,7 @@ describe "DisplayBuffer", ->
|
||||
describe "::setScrollTop", ->
|
||||
beforeEach ->
|
||||
displayBuffer.manageScrollPosition = true
|
||||
displayBuffer.setLineHeight(10)
|
||||
displayBuffer.setLineHeightInPixels(10)
|
||||
|
||||
it "disallows negative values", ->
|
||||
displayBuffer.setHeight(displayBuffer.getScrollHeight() + 100)
|
||||
@@ -979,6 +979,7 @@ describe "DisplayBuffer", ->
|
||||
describe "::setScrollLeft", ->
|
||||
beforeEach ->
|
||||
displayBuffer.manageScrollPosition = true
|
||||
displayBuffer.setLineHeightInPixels(10)
|
||||
displayBuffer.setDefaultCharWidth(10)
|
||||
|
||||
it "disallows negative values", ->
|
||||
@@ -999,8 +1000,9 @@ describe "DisplayBuffer", ->
|
||||
describe "::scrollToScreenPosition(position)", ->
|
||||
it "sets the scroll top and scroll left so the given screen position is in view", ->
|
||||
displayBuffer.manageScrollPosition = true
|
||||
displayBuffer.setLineHeight(10)
|
||||
displayBuffer.setLineHeightInPixels(10)
|
||||
displayBuffer.setDefaultCharWidth(10)
|
||||
displayBuffer.setHorizontalScrollbarHeight(0)
|
||||
|
||||
displayBuffer.setHeight(50)
|
||||
displayBuffer.setWidth(50)
|
||||
|
||||
+370
-126
@@ -1,12 +1,16 @@
|
||||
{extend, flatten, toArray} = require 'underscore-plus'
|
||||
_ = require 'underscore-plus'
|
||||
{extend, flatten, toArray, last} = _
|
||||
|
||||
ReactEditorView = require '../src/react-editor-view'
|
||||
nbsp = String.fromCharCode(160)
|
||||
|
||||
describe "EditorComponent", ->
|
||||
[editor, wrapperView, component, node, verticalScrollbarNode, horizontalScrollbarNode] = []
|
||||
[lineHeightInPixels, charWidth, delayAnimationFrames, nextAnimationFrame] = []
|
||||
[contentNode, editor, wrapperView, component, node, verticalScrollbarNode, horizontalScrollbarNode] = []
|
||||
[lineHeightInPixels, charWidth, delayAnimationFrames, nextAnimationFrame, lineOverdrawMargin] = []
|
||||
|
||||
beforeEach ->
|
||||
lineOverdrawMargin = 2
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
@@ -22,67 +26,163 @@ describe "EditorComponent", ->
|
||||
else
|
||||
fn()
|
||||
|
||||
editor = atom.project.openSync('sample.js')
|
||||
wrapperView = new ReactEditorView(editor)
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample.js').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
contentNode = document.querySelector('#jasmine-content')
|
||||
contentNode.style.width = '1000px'
|
||||
|
||||
wrapperView = new ReactEditorView(editor, {lineOverdrawMargin})
|
||||
wrapperView.attachToDom()
|
||||
{component} = wrapperView
|
||||
component.setLineHeight(1.3)
|
||||
component.setFontSize(20)
|
||||
|
||||
lineHeightInPixels = editor.getLineHeight()
|
||||
lineHeightInPixels = editor.getLineHeightInPixels()
|
||||
charWidth = editor.getDefaultCharWidth()
|
||||
node = component.getDOMNode()
|
||||
verticalScrollbarNode = node.querySelector('.vertical-scrollbar')
|
||||
horizontalScrollbarNode = node.querySelector('.horizontal-scrollbar')
|
||||
|
||||
node.style.height = editor.getLineCount() * lineHeightInPixels + 'px'
|
||||
node.style.width = '1000px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
afterEach ->
|
||||
contentNode.style.width = ''
|
||||
|
||||
describe "line rendering", ->
|
||||
it "renders only the currently-visible lines", ->
|
||||
it "renders the currently-visible lines plus the overdraw margin", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
lines = node.querySelectorAll('.line')
|
||||
expect(lines.length).toBe 6
|
||||
expect(lines[0].textContent).toBe editor.lineForScreenRow(0).text
|
||||
expect(lines[5].textContent).toBe editor.lineForScreenRow(5).text
|
||||
linesNode = node.querySelector('.lines')
|
||||
expect(linesNode.style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)"
|
||||
expect(node.querySelectorAll('.line').length).toBe 6 + 2 # no margin above
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe editor.lineForScreenRow(0).text
|
||||
expect(component.lineNodeForScreenRow(0).offsetTop).toBe 0
|
||||
expect(component.lineNodeForScreenRow(5).textContent).toBe editor.lineForScreenRow(5).text
|
||||
expect(component.lineNodeForScreenRow(5).offsetTop).toBe 5 * lineHeightInPixels
|
||||
|
||||
verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels
|
||||
verticalScrollbarNode.scrollTop = 4.5 * lineHeightInPixels
|
||||
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
|
||||
expect(node.querySelector('.scroll-view-content').style['-webkit-transform']).toBe "translate3d(0px, #{-2.5 * lineHeightInPixels}px, 0)"
|
||||
expect(linesNode.style['-webkit-transform']).toBe "translate3d(0px, #{-4.5 * lineHeightInPixels}px, 0px)"
|
||||
expect(node.querySelectorAll('.line').length).toBe 6 + 4 # margin above and below
|
||||
expect(component.lineNodeForScreenRow(2).offsetTop).toBe 2 * lineHeightInPixels
|
||||
expect(component.lineNodeForScreenRow(2).textContent).toBe editor.lineForScreenRow(2).text
|
||||
expect(component.lineNodeForScreenRow(9).offsetTop).toBe 9 * lineHeightInPixels
|
||||
expect(component.lineNodeForScreenRow(9).textContent).toBe editor.lineForScreenRow(9).text
|
||||
|
||||
lineNodes = node.querySelectorAll('.line')
|
||||
expect(lineNodes.length).toBe 6
|
||||
expect(lineNodes[0].offsetTop).toBe 2 * lineHeightInPixels
|
||||
expect(lineNodes[0].textContent).toBe editor.lineForScreenRow(2).text
|
||||
expect(lineNodes[5].textContent).toBe editor.lineForScreenRow(7).text
|
||||
|
||||
it "updates absolute positions of subsequent lines when lines are inserted or removed", ->
|
||||
it "updates the top position of subsequent lines when lines are inserted or removed", ->
|
||||
editor.getBuffer().deleteRows(0, 1)
|
||||
lineNodes = node.querySelectorAll('.line')
|
||||
expect(lineNodes[0].offsetTop).toBe 0
|
||||
expect(lineNodes[1].offsetTop).toBe 1 * lineHeightInPixels
|
||||
expect(lineNodes[2].offsetTop).toBe 2 * lineHeightInPixels
|
||||
expect(component.lineNodeForScreenRow(0).offsetTop).toBe 0
|
||||
expect(component.lineNodeForScreenRow(1).offsetTop).toBe 1 * lineHeightInPixels
|
||||
expect(component.lineNodeForScreenRow(2).offsetTop).toBe 2 * lineHeightInPixels
|
||||
|
||||
editor.getBuffer().insert([0, 0], '\n\n')
|
||||
lineNodes = node.querySelectorAll('.line')
|
||||
expect(lineNodes[0].offsetTop).toBe 0
|
||||
expect(lineNodes[1].offsetTop).toBe 1 * lineHeightInPixels
|
||||
expect(lineNodes[2].offsetTop).toBe 2 * lineHeightInPixels
|
||||
expect(lineNodes[3].offsetTop).toBe 3 * lineHeightInPixels
|
||||
expect(lineNodes[4].offsetTop).toBe 4 * lineHeightInPixels
|
||||
expect(component.lineNodeForScreenRow(0).offsetTop).toBe 0 * lineHeightInPixels
|
||||
expect(component.lineNodeForScreenRow(1).offsetTop).toBe 1 * lineHeightInPixels
|
||||
expect(component.lineNodeForScreenRow(2).offsetTop).toBe 2 * lineHeightInPixels
|
||||
expect(component.lineNodeForScreenRow(3).offsetTop).toBe 3 * lineHeightInPixels
|
||||
expect(component.lineNodeForScreenRow(4).offsetTop).toBe 4 * lineHeightInPixels
|
||||
|
||||
it "updates the top position of lines when the line height changes", ->
|
||||
initialLineHeightInPixels = editor.getLineHeightInPixels()
|
||||
component.setLineHeight(2)
|
||||
|
||||
newLineHeightInPixels = editor.getLineHeightInPixels()
|
||||
expect(newLineHeightInPixels).not.toBe initialLineHeightInPixels
|
||||
expect(component.lineNodeForScreenRow(1).offsetTop).toBe 1 * newLineHeightInPixels
|
||||
|
||||
it "updates the top position of lines when the font size changes", ->
|
||||
initialLineHeightInPixels = editor.getLineHeightInPixels()
|
||||
component.setFontSize(10)
|
||||
|
||||
newLineHeightInPixels = editor.getLineHeightInPixels()
|
||||
expect(newLineHeightInPixels).not.toBe initialLineHeightInPixels
|
||||
expect(component.lineNodeForScreenRow(1).offsetTop).toBe 1 * newLineHeightInPixels
|
||||
|
||||
it "updates the top position of lines when the font family changes", ->
|
||||
# Can't find a font that changes the line height, but we think one might exist
|
||||
linesComponent = component.refs.scrollView.refs.lines
|
||||
spyOn(linesComponent, 'measureLineHeightInPixelsAndCharWidth').andCallFake -> editor.setLineHeightInPixels(10)
|
||||
|
||||
initialLineHeightInPixels = editor.getLineHeightInPixels()
|
||||
component.setFontFamily('sans-serif')
|
||||
|
||||
expect(linesComponent.measureLineHeightInPixelsAndCharWidth).toHaveBeenCalled()
|
||||
newLineHeightInPixels = editor.getLineHeightInPixels()
|
||||
expect(newLineHeightInPixels).not.toBe initialLineHeightInPixels
|
||||
expect(component.lineNodeForScreenRow(1).offsetTop).toBe 1 * newLineHeightInPixels
|
||||
|
||||
it "renders the .lines div at the full height of the editor if there aren't enough lines to scroll vertically", ->
|
||||
editor.setText('')
|
||||
node.style.height = '300px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
linesNode = node.querySelector('.lines')
|
||||
expect(linesNode.offsetHeight).toBe 300
|
||||
|
||||
describe "when showInvisibles is enabled", ->
|
||||
invisibles = null
|
||||
|
||||
beforeEach ->
|
||||
invisibles =
|
||||
eol: 'E'
|
||||
space: 'S'
|
||||
tab: 'T'
|
||||
cr: 'C'
|
||||
|
||||
atom.config.set("editor.showInvisibles", true)
|
||||
atom.config.set("editor.invisibles", invisibles)
|
||||
|
||||
it "re-renders the lines when the showInvisibles config option changes", ->
|
||||
editor.setText " a line with tabs\tand spaces "
|
||||
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab} and spaces#{invisibles.space}#{invisibles.eol}"
|
||||
atom.config.set("editor.showInvisibles", false)
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe " a line with tabs and spaces "
|
||||
atom.config.set("editor.showInvisibles", true)
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab} and spaces#{invisibles.space}#{invisibles.eol}"
|
||||
|
||||
it "displays spaces, tabs, and newlines as visible characters", ->
|
||||
editor.setText " a line with tabs\tand spaces "
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe "#{invisibles.space}a line with tabs#{invisibles.tab} and spaces#{invisibles.space}#{invisibles.eol}"
|
||||
|
||||
it "displays newlines as their own token outside of the other tokens' scopes", ->
|
||||
editor.setText "var"
|
||||
expect(component.lineNodeForScreenRow(0).innerHTML).toBe "<span class=\"source js\"><span class=\"storage modifier js\">var</span></span><span class=\"invisible-character\">#{invisibles.eol}</span>"
|
||||
|
||||
it "displays trailing carriage returns using a visible, non-empty value", ->
|
||||
editor.setText "a line that ends with a carriage return\r\n"
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe "a line that ends with a carriage return#{invisibles.cr}#{invisibles.eol}"
|
||||
|
||||
describe "when soft wrapping is enabled", ->
|
||||
beforeEach ->
|
||||
editor.setText "a line that wraps "
|
||||
editor.setSoftWrap(true)
|
||||
node.style.width = 15 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
it "doesn't show end of line invisibles at the end of wrapped lines", ->
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe "a line that "
|
||||
expect(component.lineNodeForScreenRow(1).textContent).toBe "wraps#{invisibles.space}#{invisibles.eol}"
|
||||
|
||||
describe "when indent guides are enabled", ->
|
||||
beforeEach ->
|
||||
component.setShowIndentGuide(true)
|
||||
|
||||
it "adds an 'indent-guide' class to spans comprising the leading whitespace", ->
|
||||
lines = node.querySelectorAll('.line')
|
||||
line1LeafNodes = getLeafNodes(lines[1])
|
||||
line1LeafNodes = getLeafNodes(component.lineNodeForScreenRow(1))
|
||||
expect(line1LeafNodes[0].textContent).toBe ' '
|
||||
expect(line1LeafNodes[0].classList.contains('indent-guide')).toBe true
|
||||
expect(line1LeafNodes[1].classList.contains('indent-guide')).toBe false
|
||||
|
||||
line2LeafNodes = getLeafNodes(lines[2])
|
||||
line2LeafNodes = getLeafNodes(component.lineNodeForScreenRow(2))
|
||||
expect(line2LeafNodes[0].textContent).toBe ' '
|
||||
expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe true
|
||||
expect(line2LeafNodes[1].textContent).toBe ' '
|
||||
@@ -92,8 +192,7 @@ describe "EditorComponent", ->
|
||||
it "renders leading whitespace spans with the 'indent-guide' class for empty lines", ->
|
||||
editor.getBuffer().insert([1, Infinity], '\n')
|
||||
|
||||
lines = node.querySelectorAll('.line')
|
||||
line2LeafNodes = getLeafNodes(lines[2])
|
||||
line2LeafNodes = getLeafNodes(component.lineNodeForScreenRow(2))
|
||||
|
||||
expect(line2LeafNodes.length).toBe 3
|
||||
expect(line2LeafNodes[0].textContent).toBe ' '
|
||||
@@ -105,8 +204,7 @@ describe "EditorComponent", ->
|
||||
|
||||
it "renders indent guides correctly on lines containing only whitespace", ->
|
||||
editor.getBuffer().insert([1, Infinity], '\n ')
|
||||
lines = node.querySelectorAll('.line')
|
||||
line2LeafNodes = getLeafNodes(lines[2])
|
||||
line2LeafNodes = getLeafNodes(component.lineNodeForScreenRow(2))
|
||||
expect(line2LeafNodes.length).toBe 3
|
||||
expect(line2LeafNodes[0].textContent).toBe ' '
|
||||
expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe true
|
||||
@@ -116,9 +214,8 @@ describe "EditorComponent", ->
|
||||
expect(line2LeafNodes[2].classList.contains('indent-guide')).toBe true
|
||||
|
||||
it "does not render indent guides in trailing whitespace for lines containing non whitespace characters", ->
|
||||
editor.getBuffer().setText (" hi ")
|
||||
lines = node.querySelectorAll('.line')
|
||||
line0LeafNodes = getLeafNodes(lines[0])
|
||||
editor.getBuffer().setText " hi "
|
||||
line0LeafNodes = getLeafNodes(component.lineNodeForScreenRow(0))
|
||||
expect(line0LeafNodes[0].textContent).toBe ' '
|
||||
expect(line0LeafNodes[0].classList.contains('indent-guide')).toBe true
|
||||
expect(line0LeafNodes[1].textContent).toBe ' '
|
||||
@@ -135,40 +232,39 @@ describe "EditorComponent", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
lines = node.querySelectorAll('.line-number')
|
||||
expect(lines.length).toBe 6
|
||||
expect(lines[0].textContent).toBe "#{nbsp}1"
|
||||
expect(lines[5].textContent).toBe "#{nbsp}6"
|
||||
expect(node.querySelectorAll('.line-number').length).toBe 6 + 2 + 1 # line overdraw margin below + dummy line number
|
||||
expect(component.lineNumberNodeForScreenRow(0).textContent).toBe "#{nbsp}1"
|
||||
expect(component.lineNumberNodeForScreenRow(5).textContent).toBe "#{nbsp}6"
|
||||
|
||||
verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels
|
||||
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
|
||||
expect(node.querySelector('.line-numbers').style['-webkit-transform']).toBe "translate3d(0, #{-2.5 * lineHeightInPixels}px, 0)"
|
||||
expect(node.querySelectorAll('.line-number').length).toBe 6 + 4 + 1 # line overdraw margin above/below + dummy line number
|
||||
|
||||
lineNumberNodes = node.querySelectorAll('.line-number')
|
||||
expect(lineNumberNodes.length).toBe 6
|
||||
expect(lineNumberNodes[0].offsetTop).toBe 2 * lineHeightInPixels
|
||||
expect(lineNumberNodes[5].offsetTop).toBe 7 * lineHeightInPixels
|
||||
expect(lineNumberNodes[0].textContent).toBe "#{nbsp}3"
|
||||
expect(lineNumberNodes[5].textContent).toBe "#{nbsp}8"
|
||||
expect(component.lineNumberNodeForScreenRow(2).textContent).toBe "#{nbsp}3"
|
||||
expect(component.lineNumberNodeForScreenRow(2).offsetTop).toBe 2 * lineHeightInPixels
|
||||
return
|
||||
expect(component.lineNumberNodeForScreenRow(7).textContent).toBe "#{nbsp}8"
|
||||
expect(component.lineNumberNodeForScreenRow(7).offsetTop).toBe 7 * lineHeightInPixels
|
||||
|
||||
it "updates absolute positions of subsequent line numbers when lines are inserted or removed", ->
|
||||
it "updates the translation of subsequent line numbers when lines are inserted or removed", ->
|
||||
editor.getBuffer().insert([0, 0], '\n\n')
|
||||
|
||||
lineNumberNodes = node.querySelectorAll('.line-number')
|
||||
expect(lineNumberNodes[0].offsetTop).toBe 0
|
||||
expect(lineNumberNodes[1].offsetTop).toBe 1 * lineHeightInPixels
|
||||
expect(lineNumberNodes[2].offsetTop).toBe 2 * lineHeightInPixels
|
||||
expect(lineNumberNodes[3].offsetTop).toBe 3 * lineHeightInPixels
|
||||
expect(lineNumberNodes[4].offsetTop).toBe 4 * lineHeightInPixels
|
||||
expect(component.lineNumberNodeForScreenRow(0).offsetTop).toBe 0
|
||||
expect(component.lineNumberNodeForScreenRow(1).offsetTop).toBe 1 * lineHeightInPixels
|
||||
expect(component.lineNumberNodeForScreenRow(2).offsetTop).toBe 2 * lineHeightInPixels
|
||||
expect(component.lineNumberNodeForScreenRow(3).offsetTop).toBe 3 * lineHeightInPixels
|
||||
expect(component.lineNumberNodeForScreenRow(4).offsetTop).toBe 4 * lineHeightInPixels
|
||||
|
||||
editor.getBuffer().insert([0, 0], '\n\n')
|
||||
lineNumberNodes = node.querySelectorAll('.line-number')
|
||||
expect(lineNumberNodes[0].offsetTop).toBe 0
|
||||
expect(lineNumberNodes[1].offsetTop).toBe 1 * lineHeightInPixels
|
||||
expect(lineNumberNodes[2].offsetTop).toBe 2 * lineHeightInPixels
|
||||
expect(lineNumberNodes[3].offsetTop).toBe 3 * lineHeightInPixels
|
||||
expect(lineNumberNodes[4].offsetTop).toBe 4 * lineHeightInPixels
|
||||
expect(component.lineNumberNodeForScreenRow(0).offsetTop).toBe 0
|
||||
expect(component.lineNumberNodeForScreenRow(1).offsetTop).toBe 1 * lineHeightInPixels
|
||||
expect(component.lineNumberNodeForScreenRow(2).offsetTop).toBe 2 * lineHeightInPixels
|
||||
expect(component.lineNumberNodeForScreenRow(3).offsetTop).toBe 3 * lineHeightInPixels
|
||||
expect(component.lineNumberNodeForScreenRow(4).offsetTop).toBe 4 * lineHeightInPixels
|
||||
expect(component.lineNumberNodeForScreenRow(5).offsetTop).toBe 5 * lineHeightInPixels
|
||||
expect(component.lineNumberNodeForScreenRow(6).offsetTop).toBe 6 * lineHeightInPixels
|
||||
|
||||
it "renders • characters for soft-wrapped lines", ->
|
||||
editor.setSoftWrap(true)
|
||||
@@ -176,29 +272,50 @@ describe "EditorComponent", ->
|
||||
node.style.width = 30 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
lines = node.querySelectorAll('.line-number')
|
||||
expect(lines.length).toBe 6
|
||||
expect(lines[0].textContent).toBe "#{nbsp}1"
|
||||
expect(lines[1].textContent).toBe "#{nbsp}•"
|
||||
expect(lines[2].textContent).toBe "#{nbsp}2"
|
||||
expect(lines[3].textContent).toBe "#{nbsp}•"
|
||||
expect(lines[4].textContent).toBe "#{nbsp}3"
|
||||
expect(lines[5].textContent).toBe "#{nbsp}•"
|
||||
expect(node.querySelectorAll('.line-number').length).toBe 6 + lineOverdrawMargin + 1 # 1 dummy line node
|
||||
expect(component.lineNumberNodeForScreenRow(0).textContent).toBe "#{nbsp}1"
|
||||
expect(component.lineNumberNodeForScreenRow(1).textContent).toBe "#{nbsp}•"
|
||||
expect(component.lineNumberNodeForScreenRow(2).textContent).toBe "#{nbsp}2"
|
||||
expect(component.lineNumberNodeForScreenRow(3).textContent).toBe "#{nbsp}•"
|
||||
expect(component.lineNumberNodeForScreenRow(4).textContent).toBe "#{nbsp}3"
|
||||
expect(component.lineNumberNodeForScreenRow(5).textContent).toBe "#{nbsp}•"
|
||||
|
||||
it "pads line numbers to be right-justified based on the maximum number of line number digits", ->
|
||||
editor.getBuffer().setText([1..10].join('\n'))
|
||||
for screenRow in [0..8]
|
||||
expect(component.lineNumberNodeForScreenRow(screenRow).textContent).toBe "#{nbsp}#{screenRow + 1}"
|
||||
expect(component.lineNumberNodeForScreenRow(9).textContent).toBe "10"
|
||||
|
||||
gutterNode = node.querySelector('.gutter')
|
||||
initialGutterWidth = gutterNode.offsetWidth
|
||||
|
||||
# Removes padding when the max number of digits goes down
|
||||
editor.getBuffer().delete([[1, 0], [2, 0]])
|
||||
for screenRow in [0..8]
|
||||
expect(component.lineNumberNodeForScreenRow(screenRow).textContent).toBe "#{screenRow + 1}"
|
||||
expect(gutterNode.offsetWidth).toBeLessThan initialGutterWidth
|
||||
|
||||
# Increases padding when the max number of digits goes up
|
||||
editor.getBuffer().insert([0, 0], '\n\n')
|
||||
for screenRow in [0..8]
|
||||
expect(component.lineNumberNodeForScreenRow(screenRow).textContent).toBe "#{nbsp}#{screenRow + 1}"
|
||||
expect(component.lineNumberNodeForScreenRow(9).textContent).toBe "10"
|
||||
expect(gutterNode.offsetWidth).toBe initialGutterWidth
|
||||
|
||||
describe "cursor rendering", ->
|
||||
it "renders the currently visible cursors", ->
|
||||
it "renders the currently visible cursors, translated relative to the scroll position", ->
|
||||
cursor1 = editor.getCursor()
|
||||
cursor1.setScreenPosition([0, 5])
|
||||
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
node.style.width = 20 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
cursorNodes = node.querySelectorAll('.cursor')
|
||||
expect(cursorNodes.length).toBe 1
|
||||
expect(cursorNodes[0].offsetHeight).toBe lineHeightInPixels
|
||||
expect(cursorNodes[0].offsetWidth).toBe charWidth
|
||||
expect(cursorNodes[0].offsetTop).toBe 0
|
||||
expect(cursorNodes[0].offsetLeft).toBe 5 * charWidth
|
||||
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate3d(#{5 * charWidth}px, #{0 * lineHeightInPixels}px, 0px)"
|
||||
|
||||
cursor2 = editor.addCursorAtScreenPosition([6, 11])
|
||||
cursor3 = editor.addCursorAtScreenPosition([4, 10])
|
||||
@@ -206,25 +323,23 @@ describe "EditorComponent", ->
|
||||
cursorNodes = node.querySelectorAll('.cursor')
|
||||
expect(cursorNodes.length).toBe 2
|
||||
expect(cursorNodes[0].offsetTop).toBe 0
|
||||
expect(cursorNodes[0].offsetLeft).toBe 5 * charWidth
|
||||
expect(cursorNodes[1].offsetTop).toBe 4 * lineHeightInPixels
|
||||
expect(cursorNodes[1].offsetLeft).toBe 10 * charWidth
|
||||
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate3d(#{5 * charWidth}px, #{0 * lineHeightInPixels}px, 0px)"
|
||||
expect(cursorNodes[1].style['-webkit-transform']).toBe "translate3d(#{10 * charWidth}px, #{4 * lineHeightInPixels}px, 0px)"
|
||||
|
||||
verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels
|
||||
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
horizontalScrollbarNode.scrollLeft = 3.5 * charWidth
|
||||
horizontalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
|
||||
cursorNodes = node.querySelectorAll('.cursor')
|
||||
expect(cursorNodes.length).toBe 2
|
||||
expect(cursorNodes[0].offsetTop).toBe 6 * lineHeightInPixels
|
||||
expect(cursorNodes[0].offsetLeft).toBe 11 * charWidth
|
||||
expect(cursorNodes[1].offsetTop).toBe 4 * lineHeightInPixels
|
||||
expect(cursorNodes[1].offsetLeft).toBe 10 * charWidth
|
||||
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate3d(#{(11 - 3.5) * charWidth}px, #{(6 - 2.5) * lineHeightInPixels}px, 0px)"
|
||||
expect(cursorNodes[1].style['-webkit-transform']).toBe "translate3d(#{(10 - 3.5) * charWidth}px, #{(4 - 2.5) * lineHeightInPixels}px, 0px)"
|
||||
|
||||
cursor3.destroy()
|
||||
cursorNodes = node.querySelectorAll('.cursor')
|
||||
expect(cursorNodes.length).toBe 1
|
||||
expect(cursorNodes[0].offsetTop).toBe 6 * lineHeightInPixels
|
||||
expect(cursorNodes[0].offsetLeft).toBe 11 * charWidth
|
||||
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate3d(#{(11 - 3.5) * charWidth}px, #{(6 - 2.5) * lineHeightInPixels}px, 0px)"
|
||||
|
||||
it "accounts for character widths when positioning cursors", ->
|
||||
atom.config.set('editor.fontFamily', 'sans-serif')
|
||||
@@ -233,7 +348,7 @@ describe "EditorComponent", ->
|
||||
cursor = node.querySelector('.cursor')
|
||||
cursorRect = cursor.getBoundingClientRect()
|
||||
|
||||
cursorLocationTextNode = node.querySelector('.storage.type.function.js').firstChild.firstChild
|
||||
cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.storage.type.function.js').firstChild
|
||||
range = document.createRange()
|
||||
range.setStart(cursorLocationTextNode, 0)
|
||||
range.setEnd(cursorLocationTextNode, 1)
|
||||
@@ -243,44 +358,22 @@ describe "EditorComponent", ->
|
||||
expect(cursorRect.width).toBe rangeRect.width
|
||||
|
||||
it "blinks cursors when they aren't moving", ->
|
||||
editor.addCursorAtScreenPosition([1, 0])
|
||||
[cursorNode1, cursorNode2] = node.querySelectorAll('.cursor')
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe false
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe false
|
||||
spyOn(_._, 'now').andCallFake -> window.now # Ensure _.debounce is based on our fake spec timeline
|
||||
cursorsNode = node.querySelector('.cursors')
|
||||
|
||||
expect(cursorsNode.classList.contains('blink-off')).toBe false
|
||||
advanceClock(component.props.cursorBlinkPeriod / 2)
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe true
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe true
|
||||
|
||||
expect(cursorsNode.classList.contains('blink-off')).toBe true
|
||||
advanceClock(component.props.cursorBlinkPeriod / 2)
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe false
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe false
|
||||
expect(cursorsNode.classList.contains('blink-off')).toBe false
|
||||
|
||||
advanceClock(component.props.cursorBlinkPeriod / 2)
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe true
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe true
|
||||
|
||||
# Stop blinking immediately when cursors move
|
||||
advanceClock(component.props.cursorBlinkPeriod / 4)
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe true
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe true
|
||||
|
||||
# Stop blinking for one full period after moving the cursor
|
||||
# Stop blinking after moving the cursor
|
||||
editor.moveCursorRight()
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe false
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe false
|
||||
|
||||
advanceClock(component.props.cursorBlinkResumeDelay / 2)
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe false
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe false
|
||||
|
||||
advanceClock(component.props.cursorBlinkResumeDelay / 2)
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe true
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe true
|
||||
expect(cursorsNode.classList.contains('blink-off')).toBe false
|
||||
|
||||
advanceClock(component.props.cursorBlinkResumeDelay)
|
||||
advanceClock(component.props.cursorBlinkPeriod / 2)
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe false
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe false
|
||||
expect(cursorsNode.classList.contains('blink-off')).toBe true
|
||||
|
||||
it "renders the hidden input field at the position of the last cursor if it is on screen", ->
|
||||
inputNode = node.querySelector('.hidden-input')
|
||||
@@ -307,13 +400,13 @@ describe "EditorComponent", ->
|
||||
|
||||
cursorNodes = node.querySelectorAll('.cursor')
|
||||
expect(cursorNodes.length).toBe 1
|
||||
expect(cursorNodes[0].offsetTop).toBe 6 * lineHeightInPixels
|
||||
expect(cursorNodes[0].offsetLeft).toBe 8 * charWidth
|
||||
expect(cursorNodes[0].style['-webkit-transform']).toBe "translate3d(#{8 * charWidth}px, #{6 * lineHeightInPixels}px, 0px)"
|
||||
|
||||
describe "selection rendering", ->
|
||||
scrollViewClientLeft = null
|
||||
[scrollViewNode, scrollViewClientLeft] = []
|
||||
|
||||
beforeEach ->
|
||||
scrollViewNode = node.querySelector('.scroll-view')
|
||||
scrollViewClientLeft = node.querySelector('.scroll-view').getBoundingClientRect().left
|
||||
|
||||
it "renders 1 region for 1-line selections", ->
|
||||
@@ -337,7 +430,7 @@ describe "EditorComponent", ->
|
||||
expect(region1Rect.top).toBe 1 * lineHeightInPixels
|
||||
expect(region1Rect.height).toBe 1 * lineHeightInPixels
|
||||
expect(region1Rect.left).toBe scrollViewClientLeft + 6 * charWidth
|
||||
expect(Math.ceil(region1Rect.right)).toBe node.clientWidth # TODO: Remove ceiling when react-wrapper is removed
|
||||
expect(region1Rect.right).toBe scrollViewNode.getBoundingClientRect().right
|
||||
|
||||
region2Rect = regions[1].getBoundingClientRect()
|
||||
expect(region2Rect.top).toBe 2 * lineHeightInPixels
|
||||
@@ -354,13 +447,13 @@ describe "EditorComponent", ->
|
||||
expect(region1Rect.top).toBe 1 * lineHeightInPixels
|
||||
expect(region1Rect.height).toBe 1 * lineHeightInPixels
|
||||
expect(region1Rect.left).toBe scrollViewClientLeft + 6 * charWidth
|
||||
expect(Math.ceil(region1Rect.right)).toBe node.clientWidth # TODO: Remove ceiling when react-wrapper is removed
|
||||
expect(region1Rect.right).toBe scrollViewNode.getBoundingClientRect().right
|
||||
|
||||
region2Rect = regions[1].getBoundingClientRect()
|
||||
expect(region2Rect.top).toBe 2 * lineHeightInPixels
|
||||
expect(region2Rect.height).toBe 3 * lineHeightInPixels
|
||||
expect(region2Rect.left).toBe scrollViewClientLeft + 0
|
||||
expect(Math.ceil(region2Rect.right)).toBe node.clientWidth # TODO: Remove ceiling when react-wrapper is removed
|
||||
expect(region2Rect.right).toBe scrollViewNode.getBoundingClientRect().right
|
||||
|
||||
region3Rect = regions[2].getBoundingClientRect()
|
||||
expect(region3Rect.top).toBe 5 * lineHeightInPixels
|
||||
@@ -368,9 +461,12 @@ describe "EditorComponent", ->
|
||||
expect(region3Rect.left).toBe scrollViewClientLeft + 0
|
||||
expect(region3Rect.width).toBe 10 * charWidth
|
||||
|
||||
it "does not render empty selections", ->
|
||||
expect(editor.getSelection().isEmpty()).toBe true
|
||||
expect(node.querySelectorAll('.selection').length).toBe 0
|
||||
it "does not render empty selections unless they are the first selection (to prevent a Chromium rendering artifact caused by removing it)", ->
|
||||
editor.addSelectionForBufferRange([[2, 2], [2, 2]])
|
||||
expect(editor.getSelection(0).isEmpty()).toBe true
|
||||
expect(editor.getSelection(1).isEmpty()).toBe true
|
||||
|
||||
expect(node.querySelectorAll('.selection').length).toBe 1
|
||||
|
||||
describe "mouse interactions", ->
|
||||
linesNode = null
|
||||
@@ -504,16 +600,16 @@ describe "EditorComponent", ->
|
||||
editor.setScrollTop(10)
|
||||
expect(verticalScrollbarNode.scrollTop).toBe 10
|
||||
|
||||
it "updates the horizontal scrollbar and scroll view content x transform based on the scrollLeft of the model", ->
|
||||
it "updates the horizontal scrollbar and the x transform of the lines based on the scrollLeft of the model", ->
|
||||
node.style.width = 30 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
scrollViewContentNode = node.querySelector('.scroll-view-content')
|
||||
expect(scrollViewContentNode.style['-webkit-transform']).toBe "translate3d(0px, 0px, 0)"
|
||||
linesNode = node.querySelector('.lines')
|
||||
expect(linesNode.style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)"
|
||||
expect(horizontalScrollbarNode.scrollLeft).toBe 0
|
||||
|
||||
editor.setScrollLeft(100)
|
||||
expect(scrollViewContentNode.style['-webkit-transform']).toBe "translate3d(-100px, 0px, 0)"
|
||||
expect(linesNode.style['-webkit-transform']).toBe "translate3d(-100px, 0px, 0px)"
|
||||
expect(horizontalScrollbarNode.scrollLeft).toBe 100
|
||||
|
||||
it "updates the scrollLeft of the model when the scrollLeft of the horizontal scrollbar changes", ->
|
||||
@@ -526,6 +622,107 @@ describe "EditorComponent", ->
|
||||
|
||||
expect(editor.getScrollLeft()).toBe 100
|
||||
|
||||
it "does not obscure the last line with the horizontal scrollbar", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
editor.setScrollBottom(editor.getScrollHeight())
|
||||
lastLineNode = component.lineNodeForScreenRow(editor.getLastScreenRow())
|
||||
bottomOfLastLine = lastLineNode.getBoundingClientRect().bottom
|
||||
topOfHorizontalScrollbar = horizontalScrollbarNode.getBoundingClientRect().top
|
||||
expect(bottomOfLastLine).toBe topOfHorizontalScrollbar
|
||||
|
||||
# Scroll so there's no space below the last line when the horizontal scrollbar disappears
|
||||
node.style.width = 100 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
bottomOfLastLine = lastLineNode.getBoundingClientRect().bottom
|
||||
bottomOfEditor = node.getBoundingClientRect().bottom
|
||||
expect(bottomOfLastLine).toBe bottomOfEditor
|
||||
|
||||
it "does not obscure the last character of the longest line with the vertical scrollbar", ->
|
||||
node.style.height = 7 * lineHeightInPixels + 'px'
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
editor.setScrollLeft(Infinity)
|
||||
|
||||
rightOfLongestLine = component.lineNodeForScreenRow(6).getBoundingClientRect().right
|
||||
leftOfVerticalScrollbar = verticalScrollbarNode.getBoundingClientRect().left
|
||||
expect(Math.round(rightOfLongestLine)).toBe leftOfVerticalScrollbar - 1 # Leave 1 px so the cursor is visible on the end of the line
|
||||
|
||||
it "only displays dummy scrollbars when scrollable in that direction", ->
|
||||
expect(verticalScrollbarNode.style.display).toBe 'none'
|
||||
expect(horizontalScrollbarNode.style.display).toBe 'none'
|
||||
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
node.style.width = '1000px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
expect(verticalScrollbarNode.style.display).toBe ''
|
||||
expect(horizontalScrollbarNode.style.display).toBe 'none'
|
||||
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
expect(verticalScrollbarNode.style.display).toBe ''
|
||||
expect(horizontalScrollbarNode.style.display).toBe ''
|
||||
|
||||
node.style.height = 20 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
expect(verticalScrollbarNode.style.display).toBe 'none'
|
||||
expect(horizontalScrollbarNode.style.display).toBe ''
|
||||
|
||||
it "makes the dummy scrollbar divs only as tall/wide as the actual scrollbars", ->
|
||||
node.style.height = 4 * lineHeightInPixels + 'px'
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
atom.themes.applyStylesheet "test", """
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
"""
|
||||
|
||||
scrollbarCornerNode = node.querySelector('.scrollbar-corner')
|
||||
expect(verticalScrollbarNode.offsetWidth).toBe 8
|
||||
expect(horizontalScrollbarNode.offsetHeight).toBe 8
|
||||
expect(scrollbarCornerNode.offsetWidth).toBe 8
|
||||
expect(scrollbarCornerNode.offsetHeight).toBe 8
|
||||
|
||||
it "assigns the bottom/right of the scrollbars to the width of the opposite scrollbar if it is visible", ->
|
||||
scrollbarCornerNode = node.querySelector('.scrollbar-corner')
|
||||
|
||||
expect(verticalScrollbarNode.style.bottom).toBe ''
|
||||
expect(horizontalScrollbarNode.style.right).toBe ''
|
||||
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
node.style.width = '1000px'
|
||||
component.measureHeightAndWidth()
|
||||
expect(verticalScrollbarNode.style.bottom).toBe ''
|
||||
expect(horizontalScrollbarNode.style.right).toBe verticalScrollbarNode.offsetWidth + 'px'
|
||||
expect(scrollbarCornerNode.style.display).toBe 'none'
|
||||
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
expect(verticalScrollbarNode.style.bottom).toBe horizontalScrollbarNode.offsetHeight + 'px'
|
||||
expect(horizontalScrollbarNode.style.right).toBe verticalScrollbarNode.offsetWidth + 'px'
|
||||
expect(scrollbarCornerNode.style.display).toBe ''
|
||||
|
||||
node.style.height = 20 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
expect(verticalScrollbarNode.style.bottom).toBe horizontalScrollbarNode.offsetHeight + 'px'
|
||||
expect(horizontalScrollbarNode.style.right).toBe ''
|
||||
expect(scrollbarCornerNode.style.display).toBe 'none'
|
||||
|
||||
it "accounts for the width of the gutter in the scrollWidth of the horizontal scrollbar", ->
|
||||
gutterNode = node.querySelector('.gutter')
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
expect(horizontalScrollbarNode.scrollWidth).toBe gutterNode.offsetWidth + editor.getScrollWidth()
|
||||
|
||||
describe "when a mousewheel event occurs on the editor", ->
|
||||
it "updates the horizontal or vertical scrollbar depending on which delta is greater (x or y)", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
@@ -543,6 +740,32 @@ describe "EditorComponent", ->
|
||||
expect(verticalScrollbarNode.scrollTop).toBe 10
|
||||
expect(horizontalScrollbarNode.scrollLeft).toBe 15
|
||||
|
||||
describe "when the mousewheel event's target is a line", ->
|
||||
it "keeps the line on the DOM if it is scrolled off-screen", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
node.style.width = 20 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
lineNode = node.querySelector('.line')
|
||||
wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -500)
|
||||
Object.defineProperty(wheelEvent, 'target', get: -> lineNode)
|
||||
node.dispatchEvent(wheelEvent)
|
||||
|
||||
expect(node.contains(lineNode)).toBe true
|
||||
|
||||
describe "when the mousewheel event's target is a line number", ->
|
||||
it "keeps the line number on the DOM if it is scrolled off-screen", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
node.style.width = 20 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
lineNumberNode = node.querySelectorAll('.line-number')[1]
|
||||
wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -500)
|
||||
Object.defineProperty(wheelEvent, 'target', get: -> lineNumberNode)
|
||||
node.dispatchEvent(wheelEvent)
|
||||
|
||||
expect(node.contains(lineNumberNode)).toBe true
|
||||
|
||||
describe "input events", ->
|
||||
inputNode = null
|
||||
|
||||
@@ -578,3 +801,24 @@ describe "EditorComponent", ->
|
||||
|
||||
expect(editor.consolidateSelections).toHaveBeenCalled()
|
||||
expect(event.abortKeyBinding).toHaveBeenCalled()
|
||||
|
||||
describe "hiding and showing the editor", ->
|
||||
describe "when fontSize, fontFamily, or lineHeight changes while the editor is hidden", ->
|
||||
it "does not attempt to measure the lineHeight and defaultCharWidth until the editor becomes visible again", ->
|
||||
wrapperView.hide()
|
||||
initialLineHeightInPixels = editor.getLineHeightInPixels()
|
||||
initialCharWidth = editor.getDefaultCharWidth()
|
||||
|
||||
component.setLineHeight(2)
|
||||
expect(editor.getLineHeightInPixels()).toBe initialLineHeightInPixels
|
||||
expect(editor.getDefaultCharWidth()).toBe initialCharWidth
|
||||
component.setFontSize(22)
|
||||
expect(editor.getLineHeightInPixels()).toBe initialLineHeightInPixels
|
||||
expect(editor.getDefaultCharWidth()).toBe initialCharWidth
|
||||
component.setFontFamily('monospace')
|
||||
expect(editor.getLineHeightInPixels()).toBe initialLineHeightInPixels
|
||||
expect(editor.getDefaultCharWidth()).toBe initialCharWidth
|
||||
|
||||
wrapperView.show()
|
||||
expect(editor.getLineHeightInPixels()).not.toBe initialLineHeightInPixels
|
||||
expect(editor.getDefaultCharWidth()).not.toBe initialCharWidth
|
||||
|
||||
+165
-73
@@ -7,9 +7,12 @@ describe "Editor", ->
|
||||
buffer.setText(buffer.getText().replace(/[ ]{2}/g, "\t"))
|
||||
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('sample.js', autoIndent: false)
|
||||
buffer = editor.buffer
|
||||
lineLengths = buffer.getLines().map (line) -> line.length
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample.js', autoIndent: false).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
buffer = editor.buffer
|
||||
lineLengths = buffer.getLines().map (line) -> line.length
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
@@ -17,7 +20,7 @@ describe "Editor", ->
|
||||
describe "when the editor is deserialized", ->
|
||||
it "restores selections and folds based on markers in the buffer", ->
|
||||
editor.setSelectedBufferRange([[1, 2], [3, 4]])
|
||||
editor.addSelectionForBufferRange([[5, 6], [7, 5]], isReversed: true)
|
||||
editor.addSelectionForBufferRange([[5, 6], [7, 5]], reversed: true)
|
||||
editor.foldBufferRow(4)
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
|
||||
@@ -31,15 +34,33 @@ describe "Editor", ->
|
||||
editor2.destroy()
|
||||
|
||||
describe "when the editor is constructed with an initialLine option", ->
|
||||
it "and positions the cursor on the specified line", ->
|
||||
editor = atom.project.openSync('sample.js', initialLine: 2)
|
||||
buffer = editor.buffer
|
||||
expect(editor.getCursor().getBufferPosition().row).toEqual 2
|
||||
it "positions the cursor on the specified line", ->
|
||||
editor = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.less', initialLine: 5).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
buffer = editor.buffer
|
||||
expect(editor.getCursor().getBufferPosition().row).toEqual 5
|
||||
expect(editor.getCursor().getBufferPosition().column).toEqual 0
|
||||
|
||||
describe "when the editor is constructed with an initialColumn option", ->
|
||||
it "positions the cursor on the specified column", ->
|
||||
editor = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.less', initialColumn: 8).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
buffer = editor.buffer
|
||||
expect(editor.getCursor().getBufferPosition().row).toEqual 0
|
||||
expect(editor.getCursor().getBufferPosition().column).toEqual 8
|
||||
|
||||
describe ".copy()", ->
|
||||
it "returns a different edit session with the same initial state", ->
|
||||
editor.setSelectedBufferRange([[1, 2], [3, 4]])
|
||||
editor.addSelectionForBufferRange([[5, 6], [7, 8]], isReversed: true)
|
||||
editor.addSelectionForBufferRange([[5, 6], [7, 8]], reversed: true)
|
||||
editor.foldBufferRow(4)
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
|
||||
@@ -57,21 +78,31 @@ describe "Editor", ->
|
||||
|
||||
describe "config defaults", ->
|
||||
it "uses the `editor.tabLength`, `editor.softWrap`, and `editor.softTabs` config values", ->
|
||||
editor1 = null
|
||||
editor2 = null
|
||||
atom.config.set('editor.tabLength', 4)
|
||||
atom.config.set('editor.softWrap', true)
|
||||
atom.config.set('editor.softTabs', false)
|
||||
editor1 = atom.project.openSync('a')
|
||||
expect(editor1.getTabLength()).toBe 4
|
||||
expect(editor1.getSoftWrap()).toBe true
|
||||
expect(editor1.getSoftTabs()).toBe false
|
||||
|
||||
atom.config.set('editor.tabLength', 100)
|
||||
atom.config.set('editor.softWrap', false)
|
||||
atom.config.set('editor.softTabs', true)
|
||||
editor2 = atom.project.openSync('b')
|
||||
expect(editor2.getTabLength()).toBe 100
|
||||
expect(editor2.getSoftWrap()).toBe false
|
||||
expect(editor2.getSoftTabs()).toBe true
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('a').then (o) -> editor1 = o
|
||||
|
||||
runs ->
|
||||
expect(editor1.getTabLength()).toBe 4
|
||||
expect(editor1.getSoftWrap()).toBe true
|
||||
expect(editor1.getSoftTabs()).toBe false
|
||||
|
||||
atom.config.set('editor.tabLength', 100)
|
||||
atom.config.set('editor.softWrap', false)
|
||||
atom.config.set('editor.softTabs', true)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('b').then (o) -> editor2 = o
|
||||
|
||||
runs ->
|
||||
expect(editor2.getTabLength()).toBe 100
|
||||
expect(editor2.getSoftWrap()).toBe false
|
||||
expect(editor2.getSoftTabs()).toBe true
|
||||
|
||||
describe "title", ->
|
||||
describe ".getTitle()", ->
|
||||
@@ -665,8 +696,9 @@ describe "Editor", ->
|
||||
editor.manageScrollPosition = true
|
||||
editor.setVerticalScrollMargin(2)
|
||||
editor.setHorizontalScrollMargin(2)
|
||||
editor.setLineHeight(10)
|
||||
editor.setLineHeightInPixels(10)
|
||||
editor.setDefaultCharWidth(10)
|
||||
editor.setHorizontalScrollbarHeight(0)
|
||||
editor.setHeight(5.5 * 10)
|
||||
editor.setWidth(5.5 * 10)
|
||||
|
||||
@@ -759,7 +791,7 @@ describe "Editor", ->
|
||||
expect(selection1.isReversed()).toBeFalsy()
|
||||
|
||||
it "merges selections when they intersect when moving up", ->
|
||||
editor.setSelectedBufferRanges([[[0,9], [0,13]], [[1,10], [1,20]]], isReversed: true)
|
||||
editor.setSelectedBufferRanges([[[0,9], [0,13]], [[1,10], [1,20]]], reversed: true)
|
||||
[selection1, selection2] = editor.getSelections()
|
||||
|
||||
editor.selectUp()
|
||||
@@ -770,7 +802,7 @@ describe "Editor", ->
|
||||
expect(selection1.isReversed()).toBeTruthy()
|
||||
|
||||
it "merges selections when they intersect when moving left", ->
|
||||
editor.setSelectedBufferRanges([[[0,9], [0,13]], [[0,14], [1,20]]], isReversed: true)
|
||||
editor.setSelectedBufferRanges([[[0,9], [0,13]], [[0,14], [1,20]]], reversed: true)
|
||||
[selection1, selection2] = editor.getSelections()
|
||||
|
||||
editor.selectLeft()
|
||||
@@ -1103,10 +1135,11 @@ describe "Editor", ->
|
||||
describe "when the 'autoscroll' option is true", ->
|
||||
it "autoscrolls to the selection", ->
|
||||
editor.manageScrollPosition = true
|
||||
editor.setLineHeight(10)
|
||||
editor.setLineHeightInPixels(10)
|
||||
editor.setDefaultCharWidth(10)
|
||||
editor.setHeight(50)
|
||||
editor.setWidth(50)
|
||||
editor.setHorizontalScrollbarHeight(0)
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
|
||||
editor.setSelectedBufferRange([[5, 6], [6, 8]], autoscroll: true)
|
||||
@@ -1339,10 +1372,14 @@ describe "Editor", ->
|
||||
expect(selection.isEmpty()).toBeTruthy()
|
||||
|
||||
it "does not share selections between different edit sessions for the same buffer", ->
|
||||
editor2 = atom.project.openSync('sample.js')
|
||||
editor.setSelectedBufferRanges([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
|
||||
editor2.setSelectedBufferRanges([[[8, 7], [6, 5]], [[4, 3], [2, 1]]])
|
||||
expect(editor2.getSelectedBufferRanges()).not.toEqual editor.getSelectedBufferRanges()
|
||||
editor2 = null
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample.js').then (o) -> editor2 = o
|
||||
|
||||
runs ->
|
||||
editor.setSelectedBufferRanges([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
|
||||
editor2.setSelectedBufferRanges([[[8, 7], [6, 5]], [[4, 3], [2, 1]]])
|
||||
expect(editor2.getSelectedBufferRanges()).not.toEqual editor.getSelectedBufferRanges()
|
||||
|
||||
describe "buffer manipulation", ->
|
||||
describe ".insertText(text)", ->
|
||||
@@ -1694,26 +1731,26 @@ describe "Editor", ->
|
||||
editor.backspace()
|
||||
expect(editor.lineForBufferRow(0)).toBe 'var = () {'
|
||||
|
||||
describe ".backspaceToBeginningOfWord()", ->
|
||||
describe ".deleteToBeginningOfWord()", ->
|
||||
describe "when no text is selected", ->
|
||||
it "deletes all text between the cursor and the beginning of the word", ->
|
||||
editor.setCursorBufferPosition([1, 24])
|
||||
editor.addCursorAtBufferPosition([3, 5])
|
||||
[cursor1, cursor2] = editor.getCursors()
|
||||
|
||||
editor.backspaceToBeginningOfWord()
|
||||
editor.deleteToBeginningOfWord()
|
||||
expect(buffer.lineForRow(1)).toBe ' var sort = function(ems) {'
|
||||
expect(buffer.lineForRow(3)).toBe ' ar pivot = items.shift(), current, left = [], right = [];'
|
||||
expect(cursor1.getBufferPosition()).toEqual [1, 22]
|
||||
expect(cursor2.getBufferPosition()).toEqual [3, 4]
|
||||
|
||||
editor.backspaceToBeginningOfWord()
|
||||
editor.deleteToBeginningOfWord()
|
||||
expect(buffer.lineForRow(1)).toBe ' var sort = functionems) {'
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.length <= 1) return itemsar pivot = items.shift(), current, left = [], right = [];'
|
||||
expect(cursor1.getBufferPosition()).toEqual [1, 21]
|
||||
expect(cursor2.getBufferPosition()).toEqual [2, 39]
|
||||
|
||||
editor.backspaceToBeginningOfWord()
|
||||
editor.deleteToBeginningOfWord()
|
||||
expect(buffer.lineForRow(1)).toBe ' var sort = ems) {'
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.length <= 1) return ar pivot = items.shift(), current, left = [], right = [];'
|
||||
expect(cursor1.getBufferPosition()).toEqual [1, 13]
|
||||
@@ -1721,24 +1758,24 @@ describe "Editor", ->
|
||||
|
||||
editor.setText(' var sort')
|
||||
editor.setCursorBufferPosition([0, 2])
|
||||
editor.backspaceToBeginningOfWord()
|
||||
editor.deleteToBeginningOfWord()
|
||||
expect(buffer.lineForRow(0)).toBe 'var sort'
|
||||
|
||||
describe "when text is selected", ->
|
||||
it "deletes only selected text", ->
|
||||
editor.setSelectedBufferRanges([[[1, 24], [1, 27]], [[2, 0], [2, 4]]])
|
||||
editor.backspaceToBeginningOfWord()
|
||||
editor.deleteToBeginningOfWord()
|
||||
expect(buffer.lineForRow(1)).toBe ' var sort = function(it) {'
|
||||
expect(buffer.lineForRow(2)).toBe 'if (items.length <= 1) return items;'
|
||||
|
||||
describe ".backspaceToBeginningOfLine()", ->
|
||||
describe ".deleteToBeginningOfLine()", ->
|
||||
describe "when no text is selected", ->
|
||||
it "deletes all text between the cursor and the beginning of the line", ->
|
||||
editor.setCursorBufferPosition([1, 24])
|
||||
editor.addCursorAtBufferPosition([2, 5])
|
||||
[cursor1, cursor2] = editor.getCursors()
|
||||
|
||||
editor.backspaceToBeginningOfLine()
|
||||
editor.deleteToBeginningOfLine()
|
||||
expect(buffer.lineForRow(1)).toBe 'ems) {'
|
||||
expect(buffer.lineForRow(2)).toBe 'f (items.length <= 1) return items;'
|
||||
expect(cursor1.getBufferPosition()).toEqual [1, 0]
|
||||
@@ -1747,13 +1784,13 @@ describe "Editor", ->
|
||||
describe "when at the beginning of the line", ->
|
||||
it "deletes the newline", ->
|
||||
editor.setCursorBufferPosition([2])
|
||||
editor.backspaceToBeginningOfLine()
|
||||
editor.deleteToBeginningOfLine()
|
||||
expect(buffer.lineForRow(1)).toBe ' var sort = function(items) { if (items.length <= 1) return items;'
|
||||
|
||||
describe "when text is selected", ->
|
||||
it "still deletes all text to begginning of the line", ->
|
||||
editor.setSelectedBufferRanges([[[1, 24], [1, 27]], [[2, 0], [2, 4]]])
|
||||
editor.backspaceToBeginningOfLine()
|
||||
editor.deleteToBeginningOfLine()
|
||||
expect(buffer.lineForRow(1)).toBe 'ems) {'
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.length <= 1) return items;'
|
||||
|
||||
@@ -2026,17 +2063,44 @@ describe "Editor", ->
|
||||
|
||||
describe ".copySelectedText()", ->
|
||||
it "copies selected text onto the clipboard", ->
|
||||
editor.setSelectedBufferRanges([[[0,4], [0,13]], [[1,6], [1, 10]], [[2,8], [2, 13]]])
|
||||
|
||||
editor.copySelectedText()
|
||||
expect(buffer.lineForRow(0)).toBe "var quicksort = function () {"
|
||||
expect(buffer.lineForRow(1)).toBe " var sort = function(items) {"
|
||||
expect(clipboard.readText()).toBe 'quicksort\nsort'
|
||||
expect(buffer.lineForRow(2)).toBe " if (items.length <= 1) return items;"
|
||||
expect(clipboard.readText()).toBe 'quicksort\nsort\nitems'
|
||||
expect(atom.clipboard.readWithMetadata().metadata.selections).toEqual([
|
||||
'quicksort'
|
||||
'sort'
|
||||
'items'
|
||||
])
|
||||
|
||||
describe ".pasteText()", ->
|
||||
it "pastes text into the buffer", ->
|
||||
atom.clipboard.write('first')
|
||||
editor.pasteText()
|
||||
expect(editor.buffer.lineForRow(0)).toBe "var first = function () {"
|
||||
expect(buffer.lineForRow(1)).toBe " var first = function(items) {"
|
||||
expect(editor.lineForBufferRow(0)).toBe "var first = function () {"
|
||||
expect(editor.lineForBufferRow(1)).toBe " var first = function(items) {"
|
||||
|
||||
describe 'when the clipboard has many selections', ->
|
||||
it "pastes each selection separately into the buffer", ->
|
||||
atom.clipboard.write('first\nsecond', {selections: ['first', 'second'] })
|
||||
editor.pasteText()
|
||||
expect(editor.lineForBufferRow(0)).toBe "var first = function () {"
|
||||
expect(editor.lineForBufferRow(1)).toBe " var second = function(items) {"
|
||||
|
||||
describe 'and the selections count does not match', ->
|
||||
it "pastes the whole text into the buffer", ->
|
||||
atom.clipboard.write('first\nsecond\nthird', {selections: ['first', 'second', 'third'] })
|
||||
editor.pasteText()
|
||||
expect(editor.lineForBufferRow(0)).toBe "var first"
|
||||
expect(editor.lineForBufferRow(1)).toBe "second"
|
||||
expect(editor.lineForBufferRow(2)).toBe "third = function () {"
|
||||
|
||||
expect(editor.lineForBufferRow(3)).toBe " var first"
|
||||
expect(editor.lineForBufferRow(4)).toBe "second"
|
||||
expect(editor.lineForBufferRow(5)).toBe "third = function(items) {"
|
||||
|
||||
describe ".indentSelectedRows()", ->
|
||||
describe "when nothing is selected", ->
|
||||
@@ -2225,10 +2289,14 @@ describe "Editor", ->
|
||||
|
||||
it "does not explode if the current language mode has no comment regex", ->
|
||||
editor.destroy()
|
||||
editor = atom.project.openSync(null, autoIndent: false)
|
||||
editor.setSelectedBufferRange([[4, 5], [4, 5]])
|
||||
editor.toggleLineCommentsInSelection()
|
||||
expect(buffer.lineForRow(4)).toBe " while(items.length > 0) {"
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(null, autoIndent: false).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
editor.setSelectedBufferRange([[4, 5], [4, 5]])
|
||||
editor.toggleLineCommentsInSelection()
|
||||
expect(buffer.lineForRow(4)).toBe " while(items.length > 0) {"
|
||||
|
||||
it "uncomments when the line lacks the trailing whitespace in the comment regex", ->
|
||||
editor.setCursorBufferPosition([10, 0])
|
||||
@@ -2547,14 +2615,21 @@ describe "Editor", ->
|
||||
|
||||
describe "soft-tabs detection", ->
|
||||
it "assigns soft / hard tabs based on the contents of the buffer, or uses the default if unknown", ->
|
||||
editor = atom.project.openSync('sample.js', softTabs: false)
|
||||
expect(editor.getSoftTabs()).toBeTruthy()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js', softTabs: false).then (editor) ->
|
||||
expect(editor.getSoftTabs()).toBeTruthy()
|
||||
|
||||
editor = atom.project.openSync('sample-with-tabs.coffee', softTabs: true)
|
||||
expect(editor.getSoftTabs()).toBeFalsy()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample-with-tabs.coffee', softTabs: true).then (editor) ->
|
||||
expect(editor.getSoftTabs()).toBeFalsy()
|
||||
|
||||
editor = atom.project.openSync(null, softTabs: false)
|
||||
expect(editor.getSoftTabs()).toBeFalsy()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample-with-tabs-and-initial-comment.js', softTabs: true).then (editor) ->
|
||||
expect(editor.getSoftTabs()).toBeFalsy()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(null, softTabs: false).then (editor) ->
|
||||
expect(editor.getSoftTabs()).toBeFalsy()
|
||||
|
||||
describe ".indentLevelForLine(line)", ->
|
||||
it "returns the indent level when the line has only leading whitespace", ->
|
||||
@@ -2578,16 +2653,21 @@ describe "Editor", ->
|
||||
|
||||
describe "when a better-matched grammar is added to syntax", ->
|
||||
it "switches to the better-matched grammar and re-tokenizes the buffer", ->
|
||||
editor.destroy()
|
||||
|
||||
jsGrammar = atom.syntax.selectGrammar('a.js')
|
||||
atom.syntax.removeGrammar(jsGrammar)
|
||||
|
||||
editor2 = atom.project.openSync('sample.js', autoIndent: false)
|
||||
expect(editor2.getGrammar()).toBe atom.syntax.nullGrammar
|
||||
expect(editor2.lineForScreenRow(0).tokens.length).toBe 1
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js', autoIndent: false).then (o) -> editor = o
|
||||
|
||||
atom.syntax.addGrammar(jsGrammar)
|
||||
expect(editor2.getGrammar()).toBe jsGrammar
|
||||
expect(editor2.lineForScreenRow(0).tokens.length).toBeGreaterThan 1
|
||||
runs ->
|
||||
expect(editor.getGrammar()).toBe atom.syntax.nullGrammar
|
||||
expect(editor.lineForScreenRow(0).tokens.length).toBe 1
|
||||
|
||||
atom.syntax.addGrammar(jsGrammar)
|
||||
expect(editor.getGrammar()).toBe jsGrammar
|
||||
expect(editor.lineForScreenRow(0).tokens.length).toBeGreaterThan 1
|
||||
|
||||
describe "auto-indent", ->
|
||||
copyText = (text, {startColumn}={}) ->
|
||||
@@ -2874,10 +2954,15 @@ describe "Editor", ->
|
||||
expect(editor.shouldPromptToSave()).toBeFalsy()
|
||||
buffer.setText('changed')
|
||||
expect(editor.shouldPromptToSave()).toBeTruthy()
|
||||
editor2 = atom.project.openSync('sample.js', autoIndent: false)
|
||||
expect(editor.shouldPromptToSave()).toBeFalsy()
|
||||
editor2.destroy()
|
||||
expect(editor.shouldPromptToSave()).toBeTruthy()
|
||||
|
||||
editor2 = null
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample.js', autoIndent: false).then (o) -> editor2 = o
|
||||
|
||||
runs ->
|
||||
expect(editor.shouldPromptToSave()).toBeFalsy()
|
||||
editor2.destroy()
|
||||
expect(editor.shouldPromptToSave()).toBeTruthy()
|
||||
|
||||
describe "when the edit session contains surrogate pair characters", ->
|
||||
it "correctly backspaces over them", ->
|
||||
@@ -2973,12 +3058,15 @@ describe "Editor", ->
|
||||
|
||||
describe "when the grammar is added", ->
|
||||
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor.setText("// http://github.com")
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js').then (o) -> editor = o
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " http://github.com"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
runs ->
|
||||
editor.setText("// http://github.com")
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " http://github.com"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-hyperlink')
|
||||
@@ -2990,12 +3078,15 @@ describe "Editor", ->
|
||||
|
||||
describe "when the grammar is updated", ->
|
||||
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor.setText("// SELECT * FROM OCTOCATS")
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js').then (o) -> editor = o
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
runs ->
|
||||
editor.setText("// SELECT * FROM OCTOCATS")
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('package-with-injection-selector')
|
||||
@@ -3032,10 +3123,11 @@ describe "Editor", ->
|
||||
describe ".scrollToCursorPosition()", ->
|
||||
it "scrolls the last cursor into view", ->
|
||||
editor.setCursorScreenPosition([8, 8])
|
||||
editor.setLineHeight(10)
|
||||
editor.setLineHeightInPixels(10)
|
||||
editor.setDefaultCharWidth(10)
|
||||
editor.setHeight(50)
|
||||
editor.setWidth(50)
|
||||
editor.setHorizontalScrollbarHeight(0)
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
expect(editor.getScrollLeft()).toBe 0
|
||||
|
||||
@@ -3047,7 +3139,7 @@ describe "Editor", ->
|
||||
it "scrolls one screen height up or down", ->
|
||||
editor.manageScrollPosition = true
|
||||
|
||||
editor.setLineHeight(10)
|
||||
editor.setLineHeightInPixels(10)
|
||||
editor.setHeight(50)
|
||||
expect(editor.getScrollHeight()).toBe 130
|
||||
|
||||
|
||||
+183
-96
@@ -7,28 +7,36 @@ path = require 'path'
|
||||
temp = require 'temp'
|
||||
|
||||
describe "EditorView", ->
|
||||
[buffer, editorView, editor, cachedLineHeight, cachedCharWidth] = []
|
||||
[buffer, editorView, editor, cachedEditor, cachedLineHeight, cachedCharWidth, fart] = []
|
||||
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
buffer = editor.buffer
|
||||
editorView = new EditorView(editor)
|
||||
editorView.lineOverdraw = 2
|
||||
editorView.isFocused = true
|
||||
editorView.enableKeymap()
|
||||
editorView.calculateHeightInLines = ->
|
||||
Math.ceil(@height() / @lineHeight)
|
||||
editorView.attachToDom = ({ heightInLines, widthInChars } = {}) ->
|
||||
heightInLines ?= @getEditor().getBuffer().getLineCount()
|
||||
@height(getLineHeight() * heightInLines)
|
||||
@width(getCharWidth() * widthInChars) if widthInChars
|
||||
$('#jasmine-content').append(this)
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js').then (o) -> editor = o
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-text', sync: true)
|
||||
atom.workspace.open('sample.less').then (o) -> cachedEditor = o
|
||||
|
||||
runs ->
|
||||
buffer = editor.buffer
|
||||
editorView = new EditorView(editor)
|
||||
editorView.lineOverdraw = 2
|
||||
editorView.isFocused = true
|
||||
editorView.enableKeymap()
|
||||
|
||||
editorView.calculateHeightInLines = ->
|
||||
Math.ceil(@height() / @lineHeight)
|
||||
|
||||
editorView.attachToDom = ({ heightInLines, widthInChars } = {}) ->
|
||||
heightInLines ?= @getEditor().getBuffer().getLineCount()
|
||||
@height(getLineHeight() * heightInLines)
|
||||
@width(getCharWidth() * widthInChars) if widthInChars
|
||||
$('#jasmine-content').append(this)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
atom.packages.activatePackage('language-text')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
getLineHeight = ->
|
||||
return cachedLineHeight if cachedLineHeight?
|
||||
@@ -41,7 +49,7 @@ describe "EditorView", ->
|
||||
cachedCharWidth
|
||||
|
||||
calcDimensions = ->
|
||||
editorForMeasurement = new EditorView(editor: atom.project.openSync('sample.js'))
|
||||
editorForMeasurement = new EditorView({editor: cachedEditor})
|
||||
editorForMeasurement.attachToDom()
|
||||
cachedLineHeight = editorForMeasurement.lineHeight
|
||||
cachedCharWidth = editorForMeasurement.charWidth
|
||||
@@ -105,18 +113,23 @@ describe "EditorView", ->
|
||||
|
||||
describe "when the editor's file is modified on disk", ->
|
||||
it "triggers an alert", ->
|
||||
fileChangeHandler = null
|
||||
filePath = path.join(temp.dir, 'atom-changed-file.txt')
|
||||
fs.writeFileSync(filePath, "")
|
||||
editor = atom.project.openSync(filePath)
|
||||
editorView.edit(editor)
|
||||
editor.insertText("now the buffer is modified")
|
||||
|
||||
fileChangeHandler = jasmine.createSpy('fileChange')
|
||||
editor.buffer.file.on 'contents-changed', fileChangeHandler
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath).then (o) -> editor = o
|
||||
|
||||
spyOn(atom, "confirm")
|
||||
runs ->
|
||||
editorView.edit(editor)
|
||||
editor.insertText("now the buffer is modified")
|
||||
|
||||
fs.writeFileSync(filePath, "a file change")
|
||||
fileChangeHandler = jasmine.createSpy('fileChange')
|
||||
editor.buffer.file.on 'contents-changed', fileChangeHandler
|
||||
|
||||
spyOn(atom, "confirm")
|
||||
|
||||
fs.writeFileSync(filePath, "a file change")
|
||||
|
||||
waitsFor "file to trigger contents-changed event", ->
|
||||
fileChangeHandler.callCount > 0
|
||||
@@ -133,8 +146,11 @@ describe "EditorView", ->
|
||||
[newEditor, newBuffer] = []
|
||||
|
||||
beforeEach ->
|
||||
newEditor = atom.project.openSync('two-hundred.txt')
|
||||
newBuffer = newEditor.buffer
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('two-hundred.txt').then (o) -> newEditor = o
|
||||
|
||||
runs ->
|
||||
newBuffer = newEditor.buffer
|
||||
|
||||
it "updates the rendered lines, cursors, selections, scroll position, and event subscriptions to match the given edit session", ->
|
||||
editorView.attachToDom(heightInLines: 5, widthInChars: 30)
|
||||
@@ -170,17 +186,24 @@ describe "EditorView", ->
|
||||
expect(editorView.lineElementForScreenRow(6).text()).toMatch /^ currentgoodbye/
|
||||
|
||||
it "triggers alert if edit session's buffer goes into conflict with changes on disk", ->
|
||||
contentsConflictedHandler = null
|
||||
filePath = path.join(temp.dir, 'atom-changed-file.txt')
|
||||
fs.writeFileSync(filePath, "")
|
||||
tempEditor = atom.project.openSync(filePath)
|
||||
editorView.edit(tempEditor)
|
||||
tempEditor.insertText("a buffer change")
|
||||
tempEditor = null
|
||||
|
||||
spyOn(atom, "confirm")
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath).then (o) -> tempEditor = o
|
||||
|
||||
runs ->
|
||||
editorView.edit(tempEditor)
|
||||
tempEditor.insertText("a buffer change")
|
||||
|
||||
spyOn(atom, "confirm")
|
||||
|
||||
contentsConflictedHandler = jasmine.createSpy("contentsConflictedHandler")
|
||||
tempEditor.on 'contents-conflicted', contentsConflictedHandler
|
||||
fs.writeFileSync(filePath, "a file change")
|
||||
|
||||
contentsConflictedHandler = jasmine.createSpy("contentsConflictedHandler")
|
||||
tempEditor.on 'contents-conflicted', contentsConflictedHandler
|
||||
fs.writeFileSync(filePath, "a file change")
|
||||
waitsFor ->
|
||||
contentsConflictedHandler.callCount > 0
|
||||
|
||||
@@ -281,26 +304,34 @@ describe "EditorView", ->
|
||||
it "emits event when editor view view receives a new buffer", ->
|
||||
eventHandler = jasmine.createSpy('eventHandler')
|
||||
editorView.on 'editor:path-changed', eventHandler
|
||||
editorView.edit(atom.project.openSync(filePath))
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath).then (editor) ->
|
||||
editorView.edit(editor)
|
||||
|
||||
runs ->
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
|
||||
it "stops listening to events on previously set buffers", ->
|
||||
eventHandler = jasmine.createSpy('eventHandler')
|
||||
oldBuffer = editor.getBuffer()
|
||||
newEditor = atom.project.openSync(filePath)
|
||||
editorView.on 'editor:path-changed', eventHandler
|
||||
newEditor = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath).then (o) -> newEditor = o
|
||||
|
||||
editorView.edit(newEditor)
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
runs ->
|
||||
editorView.on 'editor:path-changed', eventHandler
|
||||
|
||||
eventHandler.reset()
|
||||
oldBuffer.saveAs(path.join(temp.dir, 'atom-bad.txt'))
|
||||
expect(eventHandler).not.toHaveBeenCalled()
|
||||
editorView.edit(newEditor)
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
|
||||
eventHandler.reset()
|
||||
newEditor.getBuffer().saveAs(path.join(temp.dir, 'atom-new.txt'))
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
eventHandler.reset()
|
||||
oldBuffer.saveAs(path.join(temp.dir, 'atom-bad.txt'))
|
||||
expect(eventHandler).not.toHaveBeenCalled()
|
||||
|
||||
eventHandler.reset()
|
||||
newEditor.getBuffer().saveAs(path.join(temp.dir, 'atom-new.txt'))
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
|
||||
it "loads the grammar for the new path", ->
|
||||
expect(editor.getGrammar().name).toBe 'JavaScript'
|
||||
@@ -478,7 +509,7 @@ describe "EditorView", ->
|
||||
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[3, 10], [3, 12]]
|
||||
|
||||
describe "when clicking between a word and a non-word", ->
|
||||
describe "when double-clicking between a word and a non-word", ->
|
||||
it "selects the word", ->
|
||||
expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0)
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [1, 21], originalEvent: {detail: 1})
|
||||
@@ -501,6 +532,30 @@ describe "EditorView", ->
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
expect(editor.getSelectedText()).toBe "{"
|
||||
|
||||
describe "when double-clicking on whitespace", ->
|
||||
it "selects all adjacent whitespace", ->
|
||||
editor.setText(" some text ")
|
||||
editor.setCursorBufferPosition([0, 2])
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 2], originalEvent: {detail: 1})
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 2], originalEvent: {detail: 2})
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 3]]
|
||||
|
||||
editor.setCursorBufferPosition([0, 8])
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 8], originalEvent: {detail: 1})
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 8], originalEvent: {detail: 2})
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[0, 7], [0, 9]]
|
||||
|
||||
editor.setCursorBufferPosition([0, 14])
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 14], originalEvent: {detail: 1})
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 14], originalEvent: {detail: 2})
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[0, 13], [0, 17]]
|
||||
|
||||
describe "triple/quardruple/etc-click", ->
|
||||
it "selects the line under the cursor", ->
|
||||
expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0)
|
||||
@@ -1529,14 +1584,18 @@ describe "EditorView", ->
|
||||
|
||||
describe "when autoscrolling at the end of the document", ->
|
||||
it "renders lines properly", ->
|
||||
editorView.edit(atom.project.openSync('two-hundred.txt'))
|
||||
editorView.attachToDom(heightInLines: 5.5)
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('two-hundred.txt').then (editor) ->
|
||||
editorView.edit(editor)
|
||||
|
||||
expect(editorView.renderedLines.find('.line').length).toBe 8
|
||||
runs ->
|
||||
editorView.attachToDom(heightInLines: 5.5)
|
||||
|
||||
editor.moveCursorToBottom()
|
||||
expect(editorView.renderedLines.find('.line').length).toBe 8
|
||||
|
||||
expect(editorView.renderedLines.find('.line').length).toBe 8
|
||||
editor.moveCursorToBottom()
|
||||
|
||||
expect(editorView.renderedLines.find('.line').length).toBe 8
|
||||
|
||||
describe "when line has a character that could push it to be too tall (regression)", ->
|
||||
it "does renders the line at a consistent height", ->
|
||||
@@ -1744,6 +1803,13 @@ describe "EditorView", ->
|
||||
expect(editorView.renderedLines.find('.line:eq(10) .indent-guide').text()).toBe "#{eol} "
|
||||
expect(editorView.renderedLines.find('.line:eq(10) .invisible-character').text()).toBe eol
|
||||
|
||||
describe "when editor.showIndentGuide is set to false", ->
|
||||
it "does not render the indent guide on whitespace only lines (regression)", ->
|
||||
editorView.attachToDom()
|
||||
editor.setText(' ')
|
||||
atom.config.set('editor.showIndentGuide', false)
|
||||
expect(editorView.renderedLines.find('.line:eq(0) .indent-guide').length).toBe 0
|
||||
|
||||
describe "when soft-wrap is enabled", ->
|
||||
beforeEach ->
|
||||
jasmine.unspy(window, 'setTimeout')
|
||||
@@ -1784,10 +1850,14 @@ describe "EditorView", ->
|
||||
expect(editor.bufferPositionForScreenPosition(editor.getCursorScreenPosition())).toEqual [3, 60]
|
||||
|
||||
it "does not wrap the lines of any newly assigned buffers", ->
|
||||
otherEditor = atom.project.openSync()
|
||||
otherEditor.buffer.setText([1..100].join(''))
|
||||
editorView.edit(otherEditor)
|
||||
expect(editorView.renderedLines.find('.line').length).toBe(1)
|
||||
otherEditor = null
|
||||
waitsForPromise ->
|
||||
atom.workspace.open().then (o) -> otherEditor = o
|
||||
|
||||
runs ->
|
||||
otherEditor.buffer.setText([1..100].join(''))
|
||||
editorView.edit(otherEditor)
|
||||
expect(editorView.renderedLines.find('.line').length).toBe(1)
|
||||
|
||||
it "unwraps lines when softwrap is disabled", ->
|
||||
editorView.toggleSoftWrap()
|
||||
@@ -1816,15 +1886,15 @@ describe "EditorView", ->
|
||||
expect(editor.getCursorScreenPosition()).toEqual [11, 0]
|
||||
|
||||
it "calls .setWidthInChars() when the editor view is attached because now its dimensions are available to calculate it", ->
|
||||
otherEditor = new EditorView(editor: atom.project.openSync('sample.js'))
|
||||
spyOn(otherEditor, 'setWidthInChars')
|
||||
otherEditorView = new EditorView(editor)
|
||||
spyOn(otherEditorView, 'setWidthInChars')
|
||||
|
||||
otherEditor.editor.setSoftWrap(true)
|
||||
expect(otherEditor.setWidthInChars).not.toHaveBeenCalled()
|
||||
otherEditorView.editor.setSoftWrap(true)
|
||||
expect(otherEditorView.setWidthInChars).not.toHaveBeenCalled()
|
||||
|
||||
otherEditor.simulateDomAttachment()
|
||||
expect(otherEditor.setWidthInChars).toHaveBeenCalled()
|
||||
otherEditor.remove()
|
||||
otherEditorView.simulateDomAttachment()
|
||||
expect(otherEditorView.setWidthInChars).toHaveBeenCalled()
|
||||
otherEditorView.remove()
|
||||
|
||||
describe "when the editor view's width changes", ->
|
||||
it "updates the width in characters on the edit session", ->
|
||||
@@ -1975,15 +2045,19 @@ describe "EditorView", ->
|
||||
|
||||
describe "when the switching from an edit session for a long buffer to an edit session for a short buffer", ->
|
||||
it "updates the line numbers to reflect the shorter buffer", ->
|
||||
emptyEditor = atom.project.openSync(null)
|
||||
editorView.edit(emptyEditor)
|
||||
expect(editorView.gutter.lineNumbers.find('.line-number').length).toBe 1
|
||||
emptyEditor = null
|
||||
waitsForPromise ->
|
||||
atom.workspace.open().then (o) -> emptyEditor = o
|
||||
|
||||
editorView.edit(editor)
|
||||
expect(editorView.gutter.lineNumbers.find('.line-number').length).toBeGreaterThan 1
|
||||
runs ->
|
||||
editorView.edit(emptyEditor)
|
||||
expect(editorView.gutter.lineNumbers.find('.line-number').length).toBe 1
|
||||
|
||||
editorView.edit(emptyEditor)
|
||||
expect(editorView.gutter.lineNumbers.find('.line-number').length).toBe 1
|
||||
editorView.edit(editor)
|
||||
expect(editorView.gutter.lineNumbers.find('.line-number').length).toBeGreaterThan 1
|
||||
|
||||
editorView.edit(emptyEditor)
|
||||
expect(editorView.gutter.lineNumbers.find('.line-number').length).toBe 1
|
||||
|
||||
describe "when the editor view is mini", ->
|
||||
it "hides the gutter", ->
|
||||
@@ -2203,10 +2277,13 @@ describe "EditorView", ->
|
||||
|
||||
describe "folding", ->
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('two-hundred.txt')
|
||||
buffer = editor.buffer
|
||||
editorView.edit(editor)
|
||||
editorView.attachToDom()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('two-hundred.txt').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
buffer = editor.buffer
|
||||
editorView.edit(editor)
|
||||
editorView.attachToDom()
|
||||
|
||||
describe "when a fold-selection event is triggered", ->
|
||||
it "folds the lines covered by the selection into a single line with a fold class and marker", ->
|
||||
@@ -2260,7 +2337,7 @@ describe "EditorView", ->
|
||||
it "adds/removes the 'fold-selected' class to the fold's line element and hides the cursor if it is on the fold line", ->
|
||||
editor.createFold(2, 4)
|
||||
|
||||
editor.setSelectedBufferRange([[1, 0], [2, 0]], preserveFolds: true, isReversed: true)
|
||||
editor.setSelectedBufferRange([[1, 0], [2, 0]], preserveFolds: true, reversed: true)
|
||||
expect(editorView.lineElementForScreenRow(2)).toMatchSelector('.fold.fold-selected')
|
||||
|
||||
editor.setSelectedBufferRange([[1, 0], [1, 1]], preserveFolds: true)
|
||||
@@ -2341,8 +2418,11 @@ describe "EditorView", ->
|
||||
beforeEach ->
|
||||
filePath = atom.project.resolve('git/working-dir/file.txt')
|
||||
originalPathText = fs.readFileSync(filePath, 'utf8')
|
||||
editor = atom.project.openSync(filePath)
|
||||
editorView.edit(editor)
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
editorView.edit(editor)
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(filePath, originalPathText)
|
||||
@@ -2816,7 +2896,7 @@ describe "EditorView", ->
|
||||
|
||||
describe "when the escape key is pressed on the editor view", ->
|
||||
it "clears multiple selections if there are any, and otherwise allows other bindings to be handled", ->
|
||||
atom.keymaps.bindKeys 'name', '.editor', {'escape': 'test-event'}
|
||||
atom.keymaps.add 'name', '.editor': {'escape': 'test-event'}
|
||||
testEventHandler = jasmine.createSpy("testEventHandler")
|
||||
|
||||
editorView.on 'test-event', testEventHandler
|
||||
@@ -2833,22 +2913,26 @@ describe "EditorView", ->
|
||||
describe "when the editor view is attached but invisible", ->
|
||||
describe "when the editor view's text is changed", ->
|
||||
it "redraws the editor view when it is next shown", ->
|
||||
displayUpdatedHandler = null
|
||||
atom.workspaceView = new WorkspaceView
|
||||
atom.workspaceView.openSync('sample.js')
|
||||
atom.workspaceView.attachToDom()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open('sample.txt').then (o) -> editor = o
|
||||
|
||||
view = $$ -> @div id: 'view', tabindex: -1, 'View'
|
||||
editorView.getPane().activateItem(view)
|
||||
expect(editorView.isVisible()).toBeFalsy()
|
||||
runs ->
|
||||
atom.workspaceView.attachToDom()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
|
||||
editor.setText('hidden changes')
|
||||
editor.setCursorBufferPosition([0,4])
|
||||
view = $$ -> @div id: 'view', tabindex: -1, 'View'
|
||||
editorView.getPane().activateItem(view)
|
||||
expect(editorView.isVisible()).toBeFalsy()
|
||||
|
||||
displayUpdatedHandler = jasmine.createSpy("displayUpdatedHandler")
|
||||
editorView.on 'editor:display-updated', displayUpdatedHandler
|
||||
editorView.getPane().activateItem(editorView.getModel())
|
||||
expect(editorView.isVisible()).toBeTruthy()
|
||||
editor.setText('hidden changes')
|
||||
editor.setCursorBufferPosition([0,4])
|
||||
|
||||
displayUpdatedHandler = jasmine.createSpy("displayUpdatedHandler")
|
||||
editorView.on 'editor:display-updated', displayUpdatedHandler
|
||||
editorView.getPane().activateItem(editorView.getModel())
|
||||
expect(editorView.isVisible()).toBeTruthy()
|
||||
|
||||
waitsFor ->
|
||||
displayUpdatedHandler.callCount is 1
|
||||
@@ -2888,14 +2972,17 @@ describe "EditorView", ->
|
||||
describe "when the editor view is removed", ->
|
||||
it "fires a editor:will-be-removed event", ->
|
||||
atom.workspaceView = new WorkspaceView
|
||||
atom.workspaceView.openSync('sample.js')
|
||||
atom.workspaceView.attachToDom()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js')
|
||||
|
||||
willBeRemovedHandler = jasmine.createSpy('willBeRemovedHandler')
|
||||
editorView.on 'editor:will-be-removed', willBeRemovedHandler
|
||||
editorView.getPane().destroyActiveItem()
|
||||
expect(willBeRemovedHandler).toHaveBeenCalled()
|
||||
runs ->
|
||||
atom.workspaceView.attachToDom()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
|
||||
willBeRemovedHandler = jasmine.createSpy('willBeRemovedHandler')
|
||||
editorView.on 'editor:will-be-removed', willBeRemovedHandler
|
||||
editorView.getPane().destroyActiveItem()
|
||||
expect(willBeRemovedHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when setInvisibles is toggled (regression)", ->
|
||||
it "renders inserted newlines properly", ->
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Look, this is a comment. Don't go making assumtions that I want soft tabs
|
||||
* because this block comment has leading spaces, Geez.
|
||||
*/
|
||||
|
||||
if (beNice) {
|
||||
console.log('Thank you for being nice.');
|
||||
}
|
||||
+11
-5
@@ -229,8 +229,11 @@ describe "Git", ->
|
||||
[originalContent, editor] = []
|
||||
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
originalContent = editor.getText()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
originalContent = editor.getText()
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(editor.getPath(), originalContent)
|
||||
@@ -274,9 +277,12 @@ describe "Git", ->
|
||||
project2?.destroy()
|
||||
|
||||
it "subscribes to all the serialized buffers in the project", ->
|
||||
atom.project.openSync('sample.js')
|
||||
project2 = atom.project.testSerialization()
|
||||
buffer = project2.getBuffers()[0]
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js')
|
||||
|
||||
runs ->
|
||||
project2 = atom.project.testSerialization()
|
||||
buffer = project2.getBuffers()[0]
|
||||
|
||||
waitsFor ->
|
||||
buffer.loaded
|
||||
|
||||
@@ -6,8 +6,10 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "javascript", ->
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js', autoIndent: false).then (o) ->
|
||||
editor = o
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
@@ -112,8 +114,10 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "coffeescript", ->
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('coffee.coffee', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('coffee.coffee', autoIndent: false).then (o) ->
|
||||
editor = o
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-coffee-script')
|
||||
@@ -161,8 +165,10 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "css", ->
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('css.css', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('css.css', autoIndent: false).then (o) ->
|
||||
editor = o
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-css')
|
||||
@@ -178,13 +184,13 @@ describe "LanguageMode", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(2, 2)
|
||||
expect(buffer.lineForRow(0)).toBe "/*body {"
|
||||
expect(buffer.lineForRow(1)).toBe " font-size: 1234px;*/"
|
||||
expect(buffer.lineForRow(2)).toBe "/* width: 110%;*/"
|
||||
expect(buffer.lineForRow(2)).toBe " /*width: 110%;*/"
|
||||
expect(buffer.lineForRow(3)).toBe " font-weight: bold !important;"
|
||||
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 1)
|
||||
expect(buffer.lineForRow(0)).toBe "body {"
|
||||
expect(buffer.lineForRow(1)).toBe " font-size: 1234px;"
|
||||
expect(buffer.lineForRow(2)).toBe "/* width: 110%;*/"
|
||||
expect(buffer.lineForRow(2)).toBe " /*width: 110%;*/"
|
||||
expect(buffer.lineForRow(3)).toBe " font-weight: bold !important;"
|
||||
|
||||
it "uncomments lines with leading whitespace", ->
|
||||
@@ -204,8 +210,10 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "less", ->
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('sample.less', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.less', autoIndent: false).then (o) ->
|
||||
editor = o
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-less')
|
||||
@@ -220,9 +228,11 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "xml", ->
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('sample.xml', autoIndent: false)
|
||||
editor.setText("<!-- test -->")
|
||||
{buffer, languageMode} = editor
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.xml', autoIndent: false).then (o) ->
|
||||
editor = o
|
||||
editor.setText("<!-- test -->")
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-xml')
|
||||
@@ -234,8 +244,10 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "folding", ->
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js', autoIndent: false).then (o) ->
|
||||
editor = o
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
@@ -319,8 +331,10 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "folding with comments", ->
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('sample-with-comments.js', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample-with-comments.js', autoIndent: false).then (o) ->
|
||||
editor = o
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
@@ -385,7 +399,10 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "css", ->
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('css.css', autoIndent: true)
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('css.css', autoIndent: true).then (o) ->
|
||||
editor = o
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-source')
|
||||
|
||||
@@ -21,11 +21,16 @@ describe "PaneView", ->
|
||||
container = new PaneContainerView
|
||||
view1 = new TestView(id: 'view-1', text: 'View 1')
|
||||
view2 = new TestView(id: 'view-2', text: 'View 2')
|
||||
editor1 = atom.project.openSync('sample.js')
|
||||
editor2 = atom.project.openSync('sample.txt')
|
||||
pane = container.getRoot()
|
||||
paneModel = pane.model
|
||||
paneModel.addItems([view1, editor1, view2, editor2])
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js').then (o) -> editor1 = o
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt').then (o) -> editor2 = o
|
||||
|
||||
runs ->
|
||||
pane = container.getRoot()
|
||||
paneModel = pane.model
|
||||
paneModel.addItems([view1, editor1, view2, editor2])
|
||||
|
||||
afterEach ->
|
||||
atom.deserializers.remove(TestView)
|
||||
@@ -159,14 +164,20 @@ describe "PaneView", ->
|
||||
|
||||
describe "when an unmodifed buffer's path is deleted", ->
|
||||
it "removes the pane item", ->
|
||||
editor = null
|
||||
jasmine.unspy(window, 'setTimeout')
|
||||
filePath = temp.openSync('atom').path
|
||||
editor = atom.project.openSync(filePath)
|
||||
pane.activateItem(editor)
|
||||
expect(pane.items).toHaveLength(5)
|
||||
|
||||
fs.removeSync(filePath)
|
||||
waitsFor -> pane.items.length == 4
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
pane.activateItem(editor)
|
||||
expect(pane.items).toHaveLength(5)
|
||||
fs.removeSync(filePath)
|
||||
|
||||
waitsFor ->
|
||||
pane.items.length == 4
|
||||
|
||||
describe "when a pane is destroyed", ->
|
||||
[pane2, pane2Model] = []
|
||||
|
||||
+67
-75
@@ -18,62 +18,39 @@ describe "Project", ->
|
||||
deserializedProject?.destroy()
|
||||
|
||||
it "does not include unretained buffers in the serialized state", ->
|
||||
atom.project.bufferForPathSync('a')
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
waitsForPromise ->
|
||||
atom.project.bufferForPath('a')
|
||||
|
||||
deserializedProject = atom.project.testSerialization()
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
runs ->
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
deserializedProject = atom.project.testSerialization()
|
||||
expect(deserializedProject.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
|
||||
deserializedProject = atom.project.testSerialization()
|
||||
waitsForPromise ->
|
||||
atom.project.open('a')
|
||||
|
||||
expect(deserializedProject.getBuffers().length).toBe 1
|
||||
deserializedProject.getBuffers()[0].destroy()
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
runs ->
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
deserializedProject = atom.project.testSerialization()
|
||||
|
||||
expect(deserializedProject.getBuffers().length).toBe 1
|
||||
deserializedProject.getBuffers()[0].destroy()
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
|
||||
describe "when an editor 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
|
||||
atom.project.setPath(undefined)
|
||||
expect(atom.project.getPath()).toBeUndefined()
|
||||
editor = atom.project.openSync()
|
||||
editor.saveAs(tempFile)
|
||||
expect(atom.project.getPath()).toBe path.dirname(tempFile)
|
||||
editor = null
|
||||
|
||||
describe ".openSync(path)", ->
|
||||
[absolutePath, newBufferHandler] = []
|
||||
beforeEach ->
|
||||
absolutePath = require.resolve('./fixtures/dir/a')
|
||||
newBufferHandler = jasmine.createSpy('newBufferHandler')
|
||||
atom.project.on 'buffer-created', newBufferHandler
|
||||
waitsForPromise ->
|
||||
atom.project.open().then (o) -> editor = o
|
||||
|
||||
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'", ->
|
||||
editor = atom.project.openSync(absolutePath)
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
|
||||
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'", ->
|
||||
editor = atom.project.openSync('a')
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
|
||||
describe "when passed the path to a buffer that has already been opened", ->
|
||||
it "returns a new edit session containing previously opened buffer", ->
|
||||
editor = atom.project.openSync(absolutePath)
|
||||
newBufferHandler.reset()
|
||||
expect(atom.project.openSync(absolutePath).buffer).toBe editor.buffer
|
||||
expect(atom.project.openSync('a').buffer).toBe editor.buffer
|
||||
expect(newBufferHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe "when not passed a path", ->
|
||||
it "returns a new edit session and emits 'buffer-created'", ->
|
||||
editor = atom.project.openSync()
|
||||
expect(editor.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer)
|
||||
runs ->
|
||||
editor.saveAs(tempFile)
|
||||
expect(atom.project.getPath()).toBe path.dirname(tempFile)
|
||||
|
||||
describe ".open(path)", ->
|
||||
[absolutePath, newBufferHandler] = []
|
||||
@@ -106,14 +83,21 @@ describe "Project", ->
|
||||
describe "when passed the path to a buffer that is currently opened", ->
|
||||
it "returns a new edit session containing currently opened buffer", ->
|
||||
editor = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open(absolutePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
newBufferHandler.reset()
|
||||
expect(atom.project.openSync(absolutePath).buffer).toBe editor.buffer
|
||||
expect(atom.project.openSync('a').buffer).toBe editor.buffer
|
||||
expect(newBufferHandler).not.toHaveBeenCalled()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open(absolutePath).then ({buffer}) ->
|
||||
expect(buffer).toBe editor.buffer
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('a').then ({buffer}) ->
|
||||
expect(buffer).toBe editor.buffer
|
||||
expect(newBufferHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe "when not passed a path", ->
|
||||
it "returns a new edit session and emits 'buffer-created'", ->
|
||||
@@ -137,20 +121,6 @@ describe "Project", ->
|
||||
runs ->
|
||||
expect(totalBytes).toBe fs.statSync(filePath).size
|
||||
|
||||
describe ".bufferForPathSync(path)", ->
|
||||
describe "when opening a previously opened path", ->
|
||||
it "does not create a new buffer", ->
|
||||
buffer = atom.project.bufferForPathSync("a").retain()
|
||||
expect(atom.project.bufferForPathSync("a")).toBe buffer
|
||||
|
||||
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 = atom.project.bufferForPathSync("a").retain().release()
|
||||
expect(atom.project.bufferForPathSync("a").retain().release()).not.toBe buffer
|
||||
|
||||
describe ".bufferForPath(path)", ->
|
||||
[buffer] = []
|
||||
beforeEach ->
|
||||
@@ -240,10 +210,15 @@ describe "Project", ->
|
||||
|
||||
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()
|
||||
|
||||
editor = null
|
||||
results = []
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample.js').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor.isModified()).toBeFalsy()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.replace /items/gi, 'items', [filePath], (result) ->
|
||||
results.push(result)
|
||||
@@ -256,10 +231,12 @@ describe "Project", ->
|
||||
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')
|
||||
|
||||
editor = null
|
||||
results = []
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample-with-comments.js').then (o) -> editor = o
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.replace /items/gi, 'items', [commentFilePath], (result) ->
|
||||
results.push(result)
|
||||
@@ -269,11 +246,16 @@ describe "Project", ->
|
||||
expect(results[0].filePath).toBe commentFilePath
|
||||
|
||||
it "does NOT save when modified", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor.buffer.setTextInRange([[0,0],[0,0]], 'omg')
|
||||
expect(editor.isModified()).toBeTruthy()
|
||||
|
||||
editor = null
|
||||
results = []
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample.js').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
editor.buffer.setTextInRange([[0,0],[0,0]], 'omg')
|
||||
expect(editor.isModified()).toBeTruthy()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.replace /items/gi, 'okthen', [filePath], (result) ->
|
||||
results.push(result)
|
||||
@@ -437,9 +419,14 @@ describe "Project", ->
|
||||
expect(resultHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "scans buffer contents if the buffer is modified", ->
|
||||
editor = atom.project.openSync("a")
|
||||
editor.setText("Elephant")
|
||||
editor = null
|
||||
results = []
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('a').then (o) ->
|
||||
editor = o
|
||||
editor.setText("Elephant")
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.scan /a|Elephant/, (result) -> results.push result
|
||||
|
||||
@@ -450,9 +437,14 @@ describe "Project", ->
|
||||
expect(resultForA.matches[0].matchText).toBe 'Elephant'
|
||||
|
||||
it "ignores buffers outside the project", ->
|
||||
editor = atom.project.openSync(temp.openSync().path)
|
||||
editor.setText("Elephant")
|
||||
editor = null
|
||||
results = []
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open(temp.openSync().path).then (o) ->
|
||||
editor = o
|
||||
editor.setText("Elephant")
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.scan /Elephant/, (result) -> results.push result
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ describe "Selection", ->
|
||||
|
||||
describe "when only the selection's tail is moved (regression)", ->
|
||||
it "emits the 'screen-range-changed' event", ->
|
||||
selection.setBufferRange([[2, 0], [2, 10]], isReversed: true)
|
||||
selection.setBufferRange([[2, 0], [2, 10]], reversed: true)
|
||||
changeScreenRangeHandler = jasmine.createSpy('changeScreenRangeHandler')
|
||||
selection.on 'screen-range-changed', changeScreenRangeHandler
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ require '../vendor/jasmine-jquery'
|
||||
path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
Grim = require 'grim'
|
||||
KeymapManager = require '../src/keymap-extensions'
|
||||
{$, WorkspaceView, Workspace} = require 'atom'
|
||||
Config = require '../src/config'
|
||||
@@ -37,6 +38,7 @@ jasmine.getEnv().defaultTimeoutInterval = 5000
|
||||
specPackageName = null
|
||||
specPackagePath = null
|
||||
specProjectPath = null
|
||||
isCoreSpec = false
|
||||
|
||||
{specDirectory, resourcePath} = atom.getLoadSettings()
|
||||
|
||||
@@ -46,7 +48,10 @@ if specDirectory
|
||||
specPackageName = JSON.parse(fs.readFileSync(path.join(specPackagePath, 'package.json')))?.name
|
||||
specProjectPath = path.join(specDirectory, 'fixtures')
|
||||
|
||||
isCoreSpec = specDirectory == fs.realpathSync(__dirname)
|
||||
|
||||
beforeEach ->
|
||||
Grim.clearDeprecations() if isCoreSpec
|
||||
$.fx.off = true
|
||||
projectPath = specProjectPath ? path.join(@specDirectory, 'fixtures')
|
||||
atom.project = new Project(path: projectPath)
|
||||
@@ -124,6 +129,7 @@ afterEach ->
|
||||
jasmine.unspy(atom, 'saveSync')
|
||||
ensureNoPathSubscriptions()
|
||||
atom.syntax.off()
|
||||
ensureNoDeprecatedFunctionsCalled() if isCoreSpec
|
||||
waits(0) # yield to ui thread to make screen update more frequently
|
||||
|
||||
ensureNoPathSubscriptions = ->
|
||||
@@ -132,6 +138,27 @@ ensureNoPathSubscriptions = ->
|
||||
if watchedPaths.length > 0
|
||||
throw new Error("Leaking subscriptions for paths: " + watchedPaths.join(", "))
|
||||
|
||||
ensureNoDeprecatedFunctionsCalled = ->
|
||||
deprecations = Grim.getDeprecations()
|
||||
if deprecations.length > 0
|
||||
originalPrepareStackTrace = Error.prepareStackTrace
|
||||
Error.prepareStackTrace = (error, stack) ->
|
||||
output = []
|
||||
for deprecation in deprecations
|
||||
output.push "#{deprecation.originName} is deprecated. #{deprecation.message}"
|
||||
output.push _.multiplyString("-", output[output.length - 1].length)
|
||||
for stack in deprecation.getStacks()
|
||||
for {functionName, location} in stack
|
||||
output.push "#{functionName} -- #{location}"
|
||||
output.push ""
|
||||
output.join("\n")
|
||||
|
||||
error = new Error("Deprecated function(s) #{deprecations.map(({originName}) -> originName).join ', '}) were called.")
|
||||
error.stack
|
||||
Error.prepareStackTrace = originalPrepareStackTrace
|
||||
|
||||
throw error
|
||||
|
||||
emitObject = jasmine.StringPrettyPrinter.prototype.emitObject
|
||||
jasmine.StringPrettyPrinter.prototype.emitObject = (obj) ->
|
||||
if obj.inspect
|
||||
@@ -234,7 +261,9 @@ window.waitsForPromise = (args...) ->
|
||||
window.resetTimeouts = ->
|
||||
window.now = 0
|
||||
window.timeoutCount = 0
|
||||
window.intervalCount = 0
|
||||
window.timeouts = []
|
||||
window.intervalTimeouts = {}
|
||||
|
||||
window.fakeSetTimeout = (callback, ms) ->
|
||||
id = ++window.timeoutCount
|
||||
@@ -245,13 +274,15 @@ window.fakeClearTimeout = (idToClear) ->
|
||||
window.timeouts = window.timeouts.filter ([id]) -> id != idToClear
|
||||
|
||||
window.fakeSetInterval = (callback, ms) ->
|
||||
id = ++window.intervalCount
|
||||
action = ->
|
||||
callback()
|
||||
window.fakeSetTimeout(action, ms)
|
||||
window.fakeSetTimeout(action, ms)
|
||||
window.intervalTimeouts[id] = window.fakeSetTimeout(action, ms)
|
||||
window.intervalTimeouts[id] = window.fakeSetTimeout(action, ms)
|
||||
id
|
||||
|
||||
window.fakeClearInterval = (idToClear) ->
|
||||
window.fakeClearTimeout(idToClear)
|
||||
window.fakeClearTimeout(@intervalTimeouts[idToClear])
|
||||
|
||||
window.advanceClock = (delta=1) ->
|
||||
window.now += delta
|
||||
@@ -289,7 +320,11 @@ $.fn.resultOfTrigger = (type) ->
|
||||
event.result
|
||||
|
||||
$.fn.enableKeymap = ->
|
||||
@on 'keydown', (e) => atom.keymaps.handleKeyEvent(e)
|
||||
@on 'keydown', (e) ->
|
||||
originalEvent = e.originalEvent ? e
|
||||
Object.defineProperty(originalEvent, 'target', get: -> e.target) unless originalEvent.target?
|
||||
atom.keymaps.handleKeyboardEvent(originalEvent)
|
||||
not e.originalEvent.defaultPrevented
|
||||
|
||||
$.fn.attachToDom = ->
|
||||
@appendTo($('#jasmine-content'))
|
||||
|
||||
@@ -20,7 +20,7 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "theme getters and setters", ->
|
||||
beforeEach ->
|
||||
atom.packages.loadPackages(sync: true)
|
||||
atom.packages.loadPackages()
|
||||
|
||||
it 'getLoadedThemes get all the loaded themes', ->
|
||||
themes = themeManager.getLoadedThemes()
|
||||
@@ -131,15 +131,21 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "requireStylesheet(path)", ->
|
||||
it "synchronously loads css at the given path and installs a style tag for it in the head", ->
|
||||
themeManager.on 'stylesheets-changed', stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
themeManager.on 'stylesheet-added', stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
|
||||
cssPath = atom.project.resolve('css.css')
|
||||
lengthBefore = $('head style').length
|
||||
|
||||
themeManager.requireStylesheet(cssPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
|
||||
expect(stylesheetAddedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetsChangedHandler).toHaveBeenCalled()
|
||||
|
||||
element = $('head style[id*="css.css"]')
|
||||
expect(element.attr('id')).toBe themeManager.stringToId(cssPath)
|
||||
expect(element.text()).toBe fs.readFileSync(cssPath, 'utf8')
|
||||
expect(element[0].sheet).toBe stylesheetAddedHandler.argsForCall[0][0]
|
||||
|
||||
# doesn't append twice
|
||||
themeManager.requireStylesheet(cssPath)
|
||||
@@ -187,9 +193,18 @@ describe "ThemeManager", ->
|
||||
themeManager.requireStylesheet(cssPath)
|
||||
expect($(document.body).css('font-weight')).toBe("bold")
|
||||
|
||||
themeManager.on 'stylesheet-removed', stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
|
||||
themeManager.on 'stylesheets-changed', stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
|
||||
themeManager.removeStylesheet(cssPath)
|
||||
|
||||
expect($(document.body).css('font-weight')).not.toBe("bold")
|
||||
|
||||
expect(stylesheetRemovedHandler).toHaveBeenCalled()
|
||||
stylesheet = stylesheetRemovedHandler.argsForCall[0][0]
|
||||
expect(stylesheet instanceof CSSStyleSheet).toBe true
|
||||
expect(stylesheet.cssRules[0].selectorText).toBe 'body'
|
||||
|
||||
expect(stylesheetsChangedHandler).toHaveBeenCalled()
|
||||
|
||||
describe "base stylesheet loading", ->
|
||||
@@ -219,20 +234,21 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "when the user stylesheet changes", ->
|
||||
it "reloads it", ->
|
||||
[stylesheetRemovedHandler, stylesheetAddedHandler, stylesheetsChangedHandler] = []
|
||||
userStylesheetPath = path.join(temp.mkdirSync("atom"), 'styles.less')
|
||||
fs.writeFileSync(userStylesheetPath, 'body {border-style: dotted !important;}')
|
||||
|
||||
spyOn(themeManager, 'getUserStylesheetPath').andReturn userStylesheetPath
|
||||
themeManager.on 'stylesheets-changed', stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
|
||||
waitsForPromise ->
|
||||
themeManager.activateThemes()
|
||||
|
||||
runs ->
|
||||
expect($(document.body).css('border-style')).toBe 'dotted'
|
||||
expect(stylesheetsChangedHandler).toHaveBeenCalled()
|
||||
stylesheetsChangedHandler.reset()
|
||||
themeManager.on 'stylesheets-changed', stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
themeManager.on 'stylesheet-removed', stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
|
||||
themeManager.on 'stylesheet-added', stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
|
||||
spyOn(themeManager, 'loadUserStylesheet').andCallThrough()
|
||||
|
||||
expect($(document.body).css('border-style')).toBe 'dotted'
|
||||
fs.writeFileSync(userStylesheetPath, 'body {border-style: dashed}')
|
||||
|
||||
waitsFor ->
|
||||
@@ -240,7 +256,16 @@ describe "ThemeManager", ->
|
||||
|
||||
runs ->
|
||||
expect($(document.body).css('border-style')).toBe 'dashed'
|
||||
|
||||
expect(stylesheetRemovedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dotted'
|
||||
|
||||
expect(stylesheetAddedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetAddedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed'
|
||||
|
||||
expect(stylesheetsChangedHandler).toHaveBeenCalled()
|
||||
|
||||
stylesheetRemovedHandler.reset()
|
||||
stylesheetsChangedHandler.reset()
|
||||
fs.removeSync(userStylesheetPath)
|
||||
|
||||
@@ -248,16 +273,25 @@ describe "ThemeManager", ->
|
||||
themeManager.loadUserStylesheet.callCount is 2
|
||||
|
||||
runs ->
|
||||
expect(stylesheetRemovedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed'
|
||||
expect($(document.body).css('border-style')).toBe 'none'
|
||||
expect(stylesheetsChangedHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when a non-existent theme is present in the config", ->
|
||||
it "logs a warning but does not throw an exception (regression)", ->
|
||||
reloaded = false
|
||||
|
||||
waitsForPromise ->
|
||||
themeManager.activateThemes()
|
||||
|
||||
runs ->
|
||||
themeManager.once 'reloaded', -> reloaded = true
|
||||
spyOn(console, 'warn')
|
||||
expect(-> atom.config.set('core.themes', ['atom-light-ui', 'theme-really-does-not-exist'])).not.toThrow()
|
||||
|
||||
waitsFor -> reloaded
|
||||
|
||||
runs ->
|
||||
expect(console.warn.callCount).toBe 1
|
||||
expect(console.warn.argsForCall[0][0].length).toBeGreaterThan 0
|
||||
|
||||
@@ -6,7 +6,7 @@ describe "TokenizedLine", ->
|
||||
|
||||
describe "::getScopeTree()", ->
|
||||
it "returns a tree whose inner nodes are scopes and whose leaf nodes are tokens in those scopes", ->
|
||||
editor = atom.project.openSync('coffee.coffee')
|
||||
[tokens, tokenIndex] = []
|
||||
|
||||
ensureValidScopeTree = (scopeTree, scopes=[]) ->
|
||||
if scopeTree.children?
|
||||
@@ -16,7 +16,11 @@ describe "TokenizedLine", ->
|
||||
expect(scopeTree).toBe tokens[tokenIndex++]
|
||||
expect(scopes).toEqual scopeTree.scopes
|
||||
|
||||
tokenIndex = 0
|
||||
tokens = editor.lineForScreenRow(1).tokens
|
||||
scopeTree = editor.lineForScreenRow(1).getScopeTree()
|
||||
ensureValidScopeTree(scopeTree)
|
||||
waitsForPromise ->
|
||||
atom.project.open('coffee.coffee').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
tokenIndex = 0
|
||||
tokens = editor.lineForScreenRow(1).tokens
|
||||
scopeTree = editor.lineForScreenRow(1).getScopeTree()
|
||||
ensureValidScopeTree(scopeTree)
|
||||
|
||||
+37
-20
@@ -64,27 +64,41 @@ describe "Window", ->
|
||||
|
||||
describe "when pane items are are modified", ->
|
||||
it "prompts user to save and and calls workspaceView.confirmClose", ->
|
||||
editor = null
|
||||
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(atom.workspaceView.confirmClose).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open("sample.js").then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.workspaceView.confirmClose).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "prompts user to save and handler returns true if don't save", ->
|
||||
editor = null
|
||||
spyOn(atom, "confirm").andReturn(2)
|
||||
editor = atom.workspaceView.openSync("sample.js")
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open("sample.js").then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "prompts user to save and handler returns false if dialog is canceled", ->
|
||||
editor = null
|
||||
spyOn(atom, "confirm").andReturn(1)
|
||||
editor = atom.workspaceView.openSync("sample.js")
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open("sample.js").then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
describe ".unloadEditorWindow()", ->
|
||||
it "saves the serialized state of the window so it can be deserialized after reload", ->
|
||||
@@ -100,15 +114,18 @@ describe "Window", ->
|
||||
expect(atom.saveSync).toHaveBeenCalled()
|
||||
|
||||
it "unsubscribes from all buffers", ->
|
||||
atom.workspaceView.openSync('sample.js')
|
||||
buffer = atom.workspaceView.getActivePaneItem().buffer
|
||||
pane = atom.workspaceView.getActivePaneView()
|
||||
pane.splitRight(pane.copyActiveItem())
|
||||
expect(atom.workspaceView.find('.editor').length).toBe 2
|
||||
waitsForPromise ->
|
||||
atom.workspace.open("sample.js")
|
||||
|
||||
atom.unloadEditorWindow()
|
||||
runs ->
|
||||
buffer = atom.workspace.getActivePaneItem().buffer
|
||||
pane = atom.workspaceView.getActivePaneView()
|
||||
pane.splitRight(pane.copyActiveItem())
|
||||
expect(atom.workspaceView.find('.editor').length).toBe 2
|
||||
|
||||
expect(buffer.getSubscriptionCount()).toBe 0
|
||||
atom.unloadEditorWindow()
|
||||
|
||||
expect(buffer.getSubscriptionCount()).toBe 0
|
||||
|
||||
describe "drag and drop", ->
|
||||
buildDragEvent = (type, files) ->
|
||||
|
||||
+109
-80
@@ -37,12 +37,17 @@ describe "Workspace", ->
|
||||
describe "when called with a uri", ->
|
||||
describe "when the active pane already has an editor for the given uri", ->
|
||||
it "activates the existing editor on the active pane", ->
|
||||
editor1 = workspace.openSync('a')
|
||||
editor2 = workspace.openSync('b')
|
||||
|
||||
editor = null
|
||||
editor1 = null
|
||||
editor2 = null
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open('a').then (o) -> editor = o
|
||||
workspace.open('a').then (o) ->
|
||||
editor1 = o
|
||||
workspace.open('b').then (o) ->
|
||||
editor2 = o
|
||||
workspace.open('a').then (o) ->
|
||||
editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor).toBe editor1
|
||||
@@ -64,11 +69,21 @@ describe "Workspace", ->
|
||||
describe "when the 'searchAllPanes' option is true", ->
|
||||
describe "when an editor for the given uri is already open on an inactive pane", ->
|
||||
it "activates the existing editor on the inactive pane, then activates that pane", ->
|
||||
editor1 = workspace.openSync('a')
|
||||
pane1 = workspace.activePane
|
||||
pane2 = workspace.activePane.splitRight()
|
||||
editor2 = workspace.openSync('b')
|
||||
expect(workspace.activePaneItem).toBe editor2
|
||||
editor1 = null
|
||||
editor2 = null
|
||||
pane1 = workspace.getActivePane()
|
||||
pane2 = workspace.getActivePane().splitRight()
|
||||
|
||||
waitsForPromise ->
|
||||
pane1.activate()
|
||||
workspace.open('a').then (o) -> editor1 = o
|
||||
|
||||
waitsForPromise ->
|
||||
pane2.activate()
|
||||
workspace.open('b').then (o) -> editor2 = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePaneItem).toBe editor2
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open('a', searchAllPanes: true)
|
||||
@@ -112,6 +127,22 @@ describe "Workspace", ->
|
||||
expect(pane1.items).toEqual [editor]
|
||||
expect(pane2.items).toEqual []
|
||||
|
||||
describe "when a pane axis is the leftmost sibling of the current pane", ->
|
||||
it "opens the new item in the current pane", ->
|
||||
editor = null
|
||||
pane1 = workspace.activePane
|
||||
pane2 = pane1.splitLeft()
|
||||
pane3 = pane2.splitDown()
|
||||
pane1.activate()
|
||||
expect(workspace.activePane).toBe pane1
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open('a', split: 'left').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(pane1.items).toEqual [editor]
|
||||
|
||||
describe "when the 'split' option is 'right'", ->
|
||||
it "opens the editor in the rightmost pane of the current pane axis", ->
|
||||
editor = null
|
||||
@@ -136,6 +167,26 @@ describe "Workspace", ->
|
||||
expect(pane1.items).toEqual []
|
||||
expect(pane2.items).toEqual [editor]
|
||||
|
||||
describe "when a pane axis is the rightmost sibling of the current pane", ->
|
||||
it "opens the new item in a new pane split to the right of the current pane", ->
|
||||
editor = null
|
||||
pane1 = workspace.activePane
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitDown()
|
||||
pane1.activate()
|
||||
expect(workspace.activePane).toBe pane1
|
||||
pane4 = null
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open('a', split: 'right').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
pane4 = workspace.getPanes().filter((p) -> p != pane1)[0]
|
||||
expect(workspace.activePane).toBe pane4
|
||||
expect(pane4.items).toEqual [editor]
|
||||
expect(workspace.paneContainer.root.children[0]).toBe pane1
|
||||
expect(workspace.paneContainer.root.children[1]).toBe pane4
|
||||
|
||||
describe "when passed a path that matches a custom opener", ->
|
||||
it "returns the resource returned by the custom opener", ->
|
||||
fooOpener = (pathToOpen, options) -> { foo: pathToOpen, options } if pathToOpen?.match(/\.foo/)
|
||||
@@ -164,80 +215,55 @@ describe "Workspace", ->
|
||||
runs ->
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "::openSync(uri, options)", ->
|
||||
[activePane, initialItemCount] = []
|
||||
|
||||
beforeEach ->
|
||||
activePane = workspace.activePane
|
||||
spyOn(activePane, 'activate')
|
||||
initialItemCount = activePane.items.length
|
||||
|
||||
describe "when called without a uri", ->
|
||||
it "adds and activates an empty editor on the active pane", ->
|
||||
editor = workspace.openSync()
|
||||
expect(activePane.items.length).toBe initialItemCount + 1
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(activePane.activate).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a uri", ->
|
||||
describe "when the active pane already has an editor for the given uri", ->
|
||||
it "activates the existing editor on the active pane", ->
|
||||
editor1 = workspace.openSync('a')
|
||||
editor2 = workspace.openSync('b')
|
||||
expect(activePane.activeItem).toBe editor2
|
||||
expect(activePane.items.length).toBe 2
|
||||
|
||||
editor = workspace.openSync(editor1.getPath())
|
||||
expect(editor).toBe editor1
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(activePane.activate).toHaveBeenCalled()
|
||||
expect(activePane.items.length).toBe 2
|
||||
|
||||
describe "when the active pane does not have an editor for the given uri", ->
|
||||
it "adds and activates a new editor for the given path on the active pane", ->
|
||||
editor = workspace.openSync('a')
|
||||
expect(activePane.items.length).toBe 1
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(activePane.activate).toHaveBeenCalled()
|
||||
|
||||
describe "when the 'activatePane' option is false", ->
|
||||
it "does not activate the active pane", ->
|
||||
workspace.openSync('b', activatePane: false)
|
||||
expect(activePane.activate).not.toHaveBeenCalled()
|
||||
|
||||
describe "::reopenItemSync()", ->
|
||||
describe "::reopenItem()", ->
|
||||
it "opens the uri associated with the last closed pane that isn't currently open", ->
|
||||
pane = workspace.activePane
|
||||
workspace.openSync('a')
|
||||
workspace.openSync('b')
|
||||
workspace.openSync('file1')
|
||||
workspace.openSync()
|
||||
waitsForPromise ->
|
||||
workspace.open('a').then ->
|
||||
workspace.open('b').then ->
|
||||
workspace.open('file1').then ->
|
||||
workspace.open()
|
||||
|
||||
# does not reopen items with no uri
|
||||
expect(workspace.activePaneItem.getUri()).toBeUndefined()
|
||||
pane.destroyActiveItem()
|
||||
workspace.reopenItemSync()
|
||||
expect(workspace.activePaneItem.getUri()).not.toBeUndefined()
|
||||
runs ->
|
||||
# does not reopen items with no uri
|
||||
expect(workspace.activePaneItem.getUri()).toBeUndefined()
|
||||
pane.destroyActiveItem()
|
||||
|
||||
# destroy all items
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')
|
||||
pane.destroyActiveItem()
|
||||
waitsForPromise ->
|
||||
workspace.reopenItem()
|
||||
|
||||
# reopens items with uris
|
||||
expect(workspace.activePaneItem).toBeUndefined()
|
||||
workspace.reopenItemSync()
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')
|
||||
runs ->
|
||||
expect(workspace.activePaneItem.getUri()).not.toBeUndefined()
|
||||
|
||||
# destroy all items
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')
|
||||
pane.destroyActiveItem()
|
||||
|
||||
# reopens items with uris
|
||||
expect(workspace.activePaneItem).toBeUndefined()
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.reopenItem()
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')
|
||||
|
||||
# does not reopen items that are already open
|
||||
workspace.openSync('b')
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
|
||||
workspace.reopenItemSync()
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')
|
||||
waitsForPromise ->
|
||||
workspace.open('b')
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.reopenItem()
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')
|
||||
|
||||
describe "::increase/decreaseFontSize()", ->
|
||||
it "increases/decreases the font size without going below 1", ->
|
||||
@@ -272,11 +298,14 @@ describe "Workspace", ->
|
||||
|
||||
describe "when an editor is copied", ->
|
||||
it "emits an 'editor-created' event", ->
|
||||
editor = null
|
||||
handler = jasmine.createSpy('editorCreatedHandler')
|
||||
workspace.on 'editor-created', handler
|
||||
|
||||
editor1 = workspace.openSync("a")
|
||||
expect(handler.callCount).toBe 1
|
||||
waitsForPromise ->
|
||||
workspace.open("a").then (o) -> editor = o
|
||||
|
||||
editor2 = editor1.copy()
|
||||
expect(handler.callCount).toBe 2
|
||||
runs ->
|
||||
expect(handler.callCount).toBe 1
|
||||
editorCopy = editor.copy()
|
||||
expect(handler.callCount).toBe 2
|
||||
|
||||
@@ -15,9 +15,12 @@ describe "WorkspaceView", ->
|
||||
atom.workspace = new Workspace
|
||||
atom.workspaceView = new WorkspaceView(atom.workspace)
|
||||
atom.workspaceView.enableKeymap()
|
||||
atom.workspaceView.openSync(pathToOpen)
|
||||
atom.workspaceView.focus()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(pathToOpen)
|
||||
|
||||
|
||||
describe "@deserialize()", ->
|
||||
viewState = null
|
||||
|
||||
@@ -33,17 +36,21 @@ describe "WorkspaceView", ->
|
||||
describe "when the serialized WorkspaceView has an unsaved buffer", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
atom.workspaceView.attachToDom()
|
||||
atom.workspaceView.openSync()
|
||||
editorView1 = atom.workspaceView.getActiveView()
|
||||
buffer = editorView1.getEditor().getBuffer()
|
||||
editorView1.splitRight()
|
||||
expect(atom.workspaceView.getActivePaneView()).toBe atom.workspaceView.getPaneViews()[1]
|
||||
|
||||
simulateReload()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open()
|
||||
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 2
|
||||
expect(atom.workspaceView.getActivePaneView()).toBe atom.workspaceView.getPaneViews()[1]
|
||||
expect(atom.workspaceView.title).toBe "untitled - #{atom.project.getPath()}"
|
||||
runs ->
|
||||
editorView1 = atom.workspaceView.getActiveView()
|
||||
buffer = editorView1.getEditor().getBuffer()
|
||||
editorView1.splitRight()
|
||||
expect(atom.workspaceView.getActivePaneView()).toBe atom.workspaceView.getPaneViews()[1]
|
||||
|
||||
simulateReload()
|
||||
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 2
|
||||
expect(atom.workspaceView.getActivePaneView()).toBe atom.workspaceView.getPaneViews()[1]
|
||||
expect(atom.workspaceView.title).toBe "untitled - #{atom.project.getPath()}"
|
||||
|
||||
describe "when there are open editors", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
@@ -51,42 +58,56 @@ describe "WorkspaceView", ->
|
||||
pane1 = atom.workspaceView.getActivePaneView()
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitRight()
|
||||
pane4 = pane2.splitDown()
|
||||
pane2.activateItem(atom.project.openSync('b'))
|
||||
pane3.activateItem(atom.project.openSync('../sample.js'))
|
||||
pane3.activeItem.setCursorScreenPosition([2, 4])
|
||||
pane4.activateItem(atom.project.openSync('../sample.txt'))
|
||||
pane4.activeItem.setCursorScreenPosition([0, 2])
|
||||
pane2.focus()
|
||||
pane4 = null
|
||||
|
||||
simulateReload()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('b').then (editor) ->
|
||||
pane2.activateItem(editor)
|
||||
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 4
|
||||
editorView1 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(0)').view()
|
||||
editorView3 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(1)').view()
|
||||
editorView2 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(0)').view()
|
||||
editorView4 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(1)').view()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('../sample.js').then (editor) ->
|
||||
pane3.activateItem(editor)
|
||||
|
||||
expect(editorView1.getEditor().getPath()).toBe atom.project.resolve('a')
|
||||
expect(editorView2.getEditor().getPath()).toBe atom.project.resolve('b')
|
||||
expect(editorView3.getEditor().getPath()).toBe atom.project.resolve('../sample.js')
|
||||
expect(editorView3.getEditor().getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(editorView4.getEditor().getPath()).toBe atom.project.resolve('../sample.txt')
|
||||
expect(editorView4.getEditor().getCursorScreenPosition()).toEqual [0, 2]
|
||||
runs ->
|
||||
pane3.activeItem.setCursorScreenPosition([2, 4])
|
||||
pane4 = pane2.splitDown()
|
||||
|
||||
# ensure adjust pane dimensions is called
|
||||
expect(editorView1.width()).toBeGreaterThan 0
|
||||
expect(editorView2.width()).toBeGreaterThan 0
|
||||
expect(editorView3.width()).toBeGreaterThan 0
|
||||
expect(editorView4.width()).toBeGreaterThan 0
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('../sample.txt').then (editor) ->
|
||||
pane4.activateItem(editor)
|
||||
|
||||
# ensure correct editorView is focused again
|
||||
expect(editorView2.isFocused).toBeTruthy()
|
||||
expect(editorView1.isFocused).toBeFalsy()
|
||||
expect(editorView3.isFocused).toBeFalsy()
|
||||
expect(editorView4.isFocused).toBeFalsy()
|
||||
runs ->
|
||||
pane4.activeItem.setCursorScreenPosition([0, 2])
|
||||
pane2.focus()
|
||||
|
||||
expect(atom.workspaceView.title).toBe "#{path.basename(editorView2.getEditor().getPath())} - #{atom.project.getPath()}"
|
||||
simulateReload()
|
||||
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 4
|
||||
editorView1 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(0)').view()
|
||||
editorView3 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(1)').view()
|
||||
editorView2 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(0)').view()
|
||||
editorView4 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(1)').view()
|
||||
|
||||
expect(editorView1.getEditor().getPath()).toBe atom.project.resolve('a')
|
||||
expect(editorView2.getEditor().getPath()).toBe atom.project.resolve('b')
|
||||
expect(editorView3.getEditor().getPath()).toBe atom.project.resolve('../sample.js')
|
||||
expect(editorView3.getEditor().getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(editorView4.getEditor().getPath()).toBe atom.project.resolve('../sample.txt')
|
||||
expect(editorView4.getEditor().getCursorScreenPosition()).toEqual [0, 2]
|
||||
|
||||
# ensure adjust pane dimensions is called
|
||||
expect(editorView1.width()).toBeGreaterThan 0
|
||||
expect(editorView2.width()).toBeGreaterThan 0
|
||||
expect(editorView3.width()).toBeGreaterThan 0
|
||||
expect(editorView4.width()).toBeGreaterThan 0
|
||||
|
||||
# ensure correct editorView is focused again
|
||||
expect(editorView2).toHaveFocus()
|
||||
expect(editorView1).not.toHaveFocus()
|
||||
expect(editorView3).not.toHaveFocus()
|
||||
expect(editorView4).not.toHaveFocus()
|
||||
|
||||
expect(atom.workspaceView.title).toBe "#{path.basename(editorView2.getEditor().getPath())} - #{atom.project.getPath()}"
|
||||
|
||||
describe "where there are no open editors", ->
|
||||
it "constructs the view with no open editors", ->
|
||||
@@ -102,9 +123,9 @@ describe "WorkspaceView", ->
|
||||
it "hands off focus to the active pane", ->
|
||||
activePane = atom.workspaceView.getActivePaneView()
|
||||
$('body').focus()
|
||||
expect(activePane.hasFocus()).toBe false
|
||||
expect(activePane).not.toHaveFocus()
|
||||
atom.workspaceView.focus()
|
||||
expect(activePane.hasFocus()).toBe true
|
||||
expect(activePane).toHaveFocus()
|
||||
|
||||
describe "keymap wiring", ->
|
||||
commandHandler = null
|
||||
@@ -112,7 +133,7 @@ describe "WorkspaceView", ->
|
||||
commandHandler = jasmine.createSpy('commandHandler')
|
||||
atom.workspaceView.on('foo-command', commandHandler)
|
||||
|
||||
atom.keymaps.bindKeys('name', '*', 'x': 'foo-command')
|
||||
atom.keymaps.add('name', '*': {'x': 'foo-command'})
|
||||
|
||||
describe "when a keydown event is triggered in the WorkspaceView", ->
|
||||
it "triggers matching keybindings for that event", ->
|
||||
@@ -129,7 +150,8 @@ describe "WorkspaceView", ->
|
||||
|
||||
describe "when the project has a path", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView.openSync('b')
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('b')
|
||||
|
||||
describe "when there is an active pane item", ->
|
||||
it "sets the title to the pane item's title plus the project path", ->
|
||||
@@ -144,7 +166,7 @@ describe "WorkspaceView", ->
|
||||
|
||||
describe "when the active pane's item changes", ->
|
||||
it "updates the title to the new item's title plus the project path", ->
|
||||
atom.workspaceView.getActivePaneView().showNextItem()
|
||||
atom.workspaceView.getActivePaneView().activateNextItem()
|
||||
item = atom.workspace.getActivePaneItem()
|
||||
expect(atom.workspaceView.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
|
||||
@@ -159,7 +181,7 @@ describe "WorkspaceView", ->
|
||||
pane = atom.workspaceView.getActivePaneView()
|
||||
pane.splitRight()
|
||||
initialTitle = atom.workspaceView.title
|
||||
pane.showNextItem()
|
||||
pane.activateNextItem()
|
||||
expect(atom.workspaceView.title).toBe initialTitle
|
||||
|
||||
describe "when the root view is deserialized", ->
|
||||
@@ -246,10 +268,22 @@ describe "WorkspaceView", ->
|
||||
describe "core:close", ->
|
||||
it "closes the active pane item until all that remains is a single empty pane", ->
|
||||
atom.config.set('core.destroyEmptyPanes', true)
|
||||
atom.project.openSync('../sample.txt')
|
||||
expect(atom.workspaceView.getActivePaneView().getItems()).toHaveLength 1
|
||||
|
||||
paneView1 = atom.workspaceView.getActivePaneView()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
editorView.splitRight()
|
||||
paneView2 = atom.workspaceView.getActivePaneView()
|
||||
|
||||
expect(paneView1).not.toBe paneView2
|
||||
expect(atom.workspaceView.getPaneViews()).toHaveLength 2
|
||||
atom.workspaceView.trigger('core:close')
|
||||
|
||||
expect(atom.workspaceView.getActivePaneView().getItems()).toHaveLength 1
|
||||
expect(atom.workspaceView.getPaneViews()).toHaveLength 1
|
||||
atom.workspaceView.trigger('core:close')
|
||||
|
||||
expect(atom.workspaceView.getActivePaneView().getItems()).toHaveLength 0
|
||||
expect(atom.workspaceView.getPaneViews()).toHaveLength 1
|
||||
|
||||
describe "the scrollbar visibility class", ->
|
||||
it "has a class based on the style of the scrollbar", ->
|
||||
|
||||
+26
-23
@@ -24,7 +24,7 @@ WindowEventHandler = require './window-event-handler'
|
||||
# * `atom.config` - A {Config} instance
|
||||
# * `atom.contextMenu` - A {ContextMenuManager} instance
|
||||
# * `atom.deserializers` - A {DeserializerManager} instance
|
||||
# * `atom.keymaps` - A {Keymap} instance
|
||||
# * `atom.keymaps` - A {KeymapManager} instance
|
||||
# * `atom.menu` - A {MenuManager} instance
|
||||
# * `atom.packages` - A {PackageManager} instance
|
||||
# * `atom.project` - A {Project} instance
|
||||
@@ -38,8 +38,8 @@ class Atom extends Model
|
||||
|
||||
# Public: Load or create the Atom environment in the given mode.
|
||||
#
|
||||
# - mode: Pass 'editor' or 'spec' depending on the kind of environment you
|
||||
# want to build.
|
||||
# mode - Pass 'editor' or 'spec' depending on the kind of environment you
|
||||
# want to build.
|
||||
#
|
||||
# Returns an Atom instance, fully initialized
|
||||
@loadOrCreate: (mode) ->
|
||||
@@ -148,7 +148,7 @@ class Atom extends Model
|
||||
ThemeManager = require './theme-manager'
|
||||
ContextMenuManager = require './context-menu-manager'
|
||||
MenuManager = require './menu-manager'
|
||||
{devMode, resourcePath} = @getLoadSettings()
|
||||
{devMode, safeMode, resourcePath} = @getLoadSettings()
|
||||
configDirPath = @getConfigDirPath()
|
||||
|
||||
# Add 'src/exports' to module search path.
|
||||
@@ -163,7 +163,7 @@ class Atom extends Model
|
||||
@config = new Config({configDirPath, resourcePath})
|
||||
@keymaps = new KeymapManager({configDirPath, resourcePath})
|
||||
@keymap = @keymaps # Deprecated
|
||||
@packages = new PackageManager({devMode, configDirPath, resourcePath})
|
||||
@packages = new PackageManager({devMode, configDirPath, resourcePath, safeMode})
|
||||
@themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath})
|
||||
@contextMenu = new ContextMenuManager(devMode)
|
||||
@menu = new MenuManager({resourcePath})
|
||||
@@ -212,11 +212,11 @@ class Atom extends Model
|
||||
# in the dimensions parameter. If x or y are omitted the window will be
|
||||
# centered. If height or width are omitted only the position will be changed.
|
||||
#
|
||||
# * dimensions:
|
||||
# + x: The new x coordinate.
|
||||
# + y: The new y coordinate.
|
||||
# + width: The new width.
|
||||
# + height: The new height.
|
||||
# dimensions - An {Object} with the following keys:
|
||||
# :x - The new x coordinate.
|
||||
# :y - The new y coordinate.
|
||||
# :width - The new width.
|
||||
# :height - The new height.
|
||||
setWindowDimensions: ({x, y, width, height}) ->
|
||||
if width? and height?
|
||||
@setSize(width, height)
|
||||
@@ -340,7 +340,7 @@ class Atom extends Model
|
||||
# options - An {Object} with the following keys:
|
||||
# :pathsToOpen - An {Array} of {String} paths to open.
|
||||
open: (options) ->
|
||||
ipc.sendChannel('open', options)
|
||||
ipc.send('open', options)
|
||||
|
||||
# Public: Open a confirm dialog.
|
||||
#
|
||||
@@ -394,50 +394,50 @@ class Atom extends Model
|
||||
|
||||
# Public: Open the dev tools for the current window.
|
||||
openDevTools: ->
|
||||
ipc.sendChannel('call-window-method', 'openDevTools')
|
||||
ipc.send('call-window-method', 'openDevTools')
|
||||
|
||||
# Public: Toggle the visibility of the dev tools for the current window.
|
||||
toggleDevTools: ->
|
||||
ipc.sendChannel('call-window-method', 'toggleDevTools')
|
||||
ipc.send('call-window-method', 'toggleDevTools')
|
||||
|
||||
# Public: Execute code in dev tools.
|
||||
executeJavaScriptInDevTools: (code) ->
|
||||
ipc.sendChannel('call-window-method', 'executeJavaScriptInDevTools', code)
|
||||
ipc.send('call-window-method', 'executeJavaScriptInDevTools', code)
|
||||
|
||||
# Public: Reload the current window.
|
||||
reload: ->
|
||||
ipc.sendChannel('call-window-method', 'restart')
|
||||
ipc.send('call-window-method', 'restart')
|
||||
|
||||
# Public: Focus the current window.
|
||||
focus: ->
|
||||
ipc.sendChannel('call-window-method', 'focus')
|
||||
ipc.send('call-window-method', 'focus')
|
||||
$(window).focus()
|
||||
|
||||
# Public: Show the current window.
|
||||
show: ->
|
||||
ipc.sendChannel('call-window-method', 'show')
|
||||
ipc.send('call-window-method', 'show')
|
||||
|
||||
# Public: Hide the current window.
|
||||
hide: ->
|
||||
ipc.sendChannel('call-window-method', 'hide')
|
||||
ipc.send('call-window-method', 'hide')
|
||||
|
||||
# Public: Set the size of current window.
|
||||
#
|
||||
# width - The {Number} of pixels.
|
||||
# height - The {Number} of pixels.
|
||||
setSize: (width, height) ->
|
||||
ipc.sendChannel('call-window-method', 'setSize', width, height)
|
||||
ipc.send('call-window-method', 'setSize', width, height)
|
||||
|
||||
# Public: Set the position of current window.
|
||||
#
|
||||
# x - The {Number} of pixels.
|
||||
# y - The {Number} of pixels.
|
||||
setPosition: (x, y) ->
|
||||
ipc.sendChannel('call-window-method', 'setPosition', x, y)
|
||||
ipc.send('call-window-method', 'setPosition', x, y)
|
||||
|
||||
# Public: Move current window to the center of the screen.
|
||||
center: ->
|
||||
ipc.sendChannel('call-window-method', 'center')
|
||||
ipc.send('call-window-method', 'center')
|
||||
|
||||
# Schedule the window to be shown and focused on the next tick.
|
||||
#
|
||||
@@ -472,13 +472,16 @@ class Atom extends Model
|
||||
|
||||
# Public: Set the full screen state of the current window.
|
||||
setFullScreen: (fullScreen=false) ->
|
||||
ipc.sendChannel('call-window-method', 'setFullScreen', fullScreen)
|
||||
ipc.send('call-window-method', 'setFullScreen', fullScreen)
|
||||
if fullScreen then document.body.classList.add("fullscreen") else document.body.classList.remove("fullscreen")
|
||||
|
||||
# Public: Is the current window in full screen mode?
|
||||
isFullScreen: ->
|
||||
@getCurrentWindow().isFullScreen()
|
||||
|
||||
# Public: Get the version of the Atom application.
|
||||
#
|
||||
# Returns the version text {String}.
|
||||
getVersion: ->
|
||||
@constructor.getVersion()
|
||||
|
||||
@@ -488,7 +491,7 @@ class Atom extends Model
|
||||
|
||||
# Public: Get the directory path to Atom's configuration area.
|
||||
#
|
||||
# Returns the absolute path to ~/.atom
|
||||
# Returns the absolute path to `~/.atom`.
|
||||
getConfigDirPath: ->
|
||||
@constructor.getConfigDirPath()
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ class AtomApplication
|
||||
exit: (status) -> app.exit(status)
|
||||
|
||||
constructor: (options) ->
|
||||
{@resourcePath, @version, @devMode} = options
|
||||
{@resourcePath, @version, @devMode, @safeMode} = options
|
||||
global.atomApplication = this
|
||||
|
||||
@pidsToOpenWindows = {}
|
||||
@@ -77,15 +77,15 @@ class AtomApplication
|
||||
@openWithOptions(options)
|
||||
|
||||
# Opens a new window based on the options provided.
|
||||
openWithOptions: ({pathsToOpen, urlsToOpen, test, pidToKillWhenClosed, devMode, newWindow, specDirectory, logFile}) ->
|
||||
openWithOptions: ({pathsToOpen, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, specDirectory, logFile}) ->
|
||||
if test
|
||||
@runSpecs({exitWhenDone: true, @resourcePath, specDirectory, logFile})
|
||||
else if pathsToOpen.length > 0
|
||||
@openPaths({pathsToOpen, pidToKillWhenClosed, newWindow, devMode})
|
||||
@openPaths({pathsToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode})
|
||||
else if urlsToOpen.length > 0
|
||||
@openUrl({urlToOpen, devMode}) for urlToOpen in urlsToOpen
|
||||
@openUrl({urlToOpen, devMode, safeMode}) for urlToOpen in urlsToOpen
|
||||
else
|
||||
@openPath({pidToKillWhenClosed, newWindow, devMode}) # Always open a editor window if this is the first instance of Atom.
|
||||
@openPath({pidToKillWhenClosed, newWindow, devMode, safeMode}) # Always open a editor window if this is the first instance of Atom.
|
||||
|
||||
# Public: Removes the {AtomWindow} from the global window list.
|
||||
removeWindow: (window) ->
|
||||
@@ -140,11 +140,13 @@ class AtomApplication
|
||||
@on 'application:open-file', -> @promptForPath(type: 'file')
|
||||
@on 'application:open-folder', -> @promptForPath(type: 'folder')
|
||||
@on 'application:open-dev', -> @promptForPath(devMode: true)
|
||||
@on 'application:open-safe', -> @promptForPath(safeMode: true)
|
||||
@on 'application:inspect', ({x,y, atomWindow}) ->
|
||||
atomWindow ?= @focusedWindow()
|
||||
atomWindow?.browserWindow.inspectElement(x, y)
|
||||
|
||||
@on 'application:open-documentation', -> shell.openExternal('https://atom.io/docs/latest/?app')
|
||||
@on 'application:open-terms-of-use', -> shell.openExternal('https://atom.io/terms')
|
||||
@on 'application:install-update', -> @autoUpdateManager.install()
|
||||
@on 'application:check-for-update', => @autoUpdateManager.check()
|
||||
|
||||
@@ -166,6 +168,7 @@ class AtomApplication
|
||||
@openPathOnEvent('application:open-your-keymap', 'atom://.atom/keymap')
|
||||
@openPathOnEvent('application:open-your-snippets', 'atom://.atom/snippets')
|
||||
@openPathOnEvent('application:open-your-stylesheet', 'atom://.atom/stylesheet')
|
||||
@openPathOnEvent('application:open-license', path.join(@resourcePath, 'LICENSE.md'))
|
||||
|
||||
app.on 'window-all-closed', ->
|
||||
app.quit() if process.platform in ['win32', 'linux']
|
||||
@@ -184,14 +187,14 @@ class AtomApplication
|
||||
|
||||
app.on 'open-url', (event, urlToOpen) =>
|
||||
event.preventDefault()
|
||||
@openUrl({urlToOpen, @devMode})
|
||||
@openUrl({urlToOpen, @devMode, @safeMode})
|
||||
|
||||
app.on 'activate-with-no-open-windows', (event) =>
|
||||
event.preventDefault()
|
||||
@emit('application:new-window')
|
||||
|
||||
# A request from the associated render process to open a new render process.
|
||||
ipc.on 'open', (processId, routingId, options) =>
|
||||
ipc.on 'open', (event, options) =>
|
||||
if options?
|
||||
if options.pathsToOpen?.length > 0
|
||||
@openPaths(options)
|
||||
@@ -200,21 +203,21 @@ class AtomApplication
|
||||
else
|
||||
@promptForPath()
|
||||
|
||||
ipc.on 'update-application-menu', (processId, routingId, template, keystrokesByCommand) =>
|
||||
ipc.on 'update-application-menu', (event, template, keystrokesByCommand) =>
|
||||
@applicationMenu.update(template, keystrokesByCommand)
|
||||
|
||||
ipc.on 'run-package-specs', (processId, routingId, specDirectory) =>
|
||||
ipc.on 'run-package-specs', (event, specDirectory) =>
|
||||
@runSpecs({resourcePath: global.devResourcePath, specDirectory: specDirectory, exitWhenDone: false})
|
||||
|
||||
ipc.on 'command', (processId, routingId, command) =>
|
||||
ipc.on 'command', (event, command) =>
|
||||
@emit(command)
|
||||
|
||||
ipc.on 'window-command', (processId, routingId, command, args...) ->
|
||||
win = BrowserWindow.fromProcessIdAndRoutingId(processId, routingId)
|
||||
ipc.on 'window-command', (event, command, args...) ->
|
||||
win = BrowserWindow.fromWebContents(event.sender)
|
||||
win.emit(command, args...)
|
||||
|
||||
ipc.on 'call-window-method', (processId, routingId, method, args...) ->
|
||||
win = BrowserWindow.fromProcessIdAndRoutingId(processId, routingId)
|
||||
ipc.on 'call-window-method', (event, method, args...) ->
|
||||
win = BrowserWindow.fromWebContents(event.sender)
|
||||
win[method](args...)
|
||||
|
||||
# Public: Executes the given command.
|
||||
@@ -288,8 +291,10 @@ class AtomApplication
|
||||
# :pidToKillWhenClosed - The integer of the pid to kill
|
||||
# :newWindow - Boolean of whether this should be opened in a new window.
|
||||
# :devMode - Boolean to control the opened window's dev mode.
|
||||
openPaths: ({pathsToOpen, pidToKillWhenClosed, newWindow, devMode}) ->
|
||||
@openPath({pathToOpen, pidToKillWhenClosed, newWindow, devMode}) for pathToOpen in pathsToOpen ? []
|
||||
# :safeMode - Boolean to control the opened window's safe mode.
|
||||
openPaths: ({pathsToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode}) ->
|
||||
for pathToOpen in pathsToOpen ? []
|
||||
@openPath({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode})
|
||||
|
||||
# Public: Opens a single path, in an existing window if possible.
|
||||
#
|
||||
@@ -298,13 +303,16 @@ class AtomApplication
|
||||
# :pidToKillWhenClosed - The integer of the pid to kill
|
||||
# :newWindow - Boolean of whether this should be opened in a new window.
|
||||
# :devMode - Boolean to control the opened window's dev mode.
|
||||
# :safeMode - Boolean to control the opened window's safe mode.
|
||||
# :windowDimensions - Object with height and width keys.
|
||||
openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, windowDimensions}={}) ->
|
||||
openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions}={}) ->
|
||||
if pathToOpen
|
||||
[basename, initialLine] = path.basename(pathToOpen).split(':')
|
||||
if initialLine
|
||||
pathToOpen = "#{path.dirname(pathToOpen)}/#{basename}"
|
||||
initialLine -= 1 # Convert line numbers to a base of 0
|
||||
[basename, initialLine, initialColumn] = path.basename(pathToOpen).split(':')
|
||||
pathToOpen = path.join(path.dirname(pathToOpen), basename) if initialLine
|
||||
|
||||
# Convert line numbers to a base of 0
|
||||
initialLine -= 1 if initialLine
|
||||
initialColumn -= 1 if initialColumn
|
||||
|
||||
unless devMode
|
||||
existingWindow = @windowForPath(pathToOpen) unless pidToKillWhenClosed or newWindow
|
||||
@@ -320,12 +328,12 @@ class AtomApplication
|
||||
|
||||
bootstrapScript ?= require.resolve('../window-bootstrap')
|
||||
resourcePath ?= @resourcePath
|
||||
openedWindow = new AtomWindow({pathToOpen, initialLine, bootstrapScript, resourcePath, devMode, windowDimensions})
|
||||
openedWindow = new AtomWindow({pathToOpen, initialLine, initialColumn, bootstrapScript, resourcePath, devMode, safeMode, windowDimensions})
|
||||
|
||||
if pidToKillWhenClosed?
|
||||
@pidsToOpenWindows[pidToKillWhenClosed] = openedWindow
|
||||
|
||||
openedWindow.browserWindow.on 'destroyed', =>
|
||||
openedWindow.browserWindow.on 'closed', =>
|
||||
@killProcessForWindow(openedWindow)
|
||||
|
||||
# Kill all processes associated with opened windows.
|
||||
@@ -355,7 +363,8 @@ class AtomApplication
|
||||
# options -
|
||||
# :urlToOpen - The atom:// url to open.
|
||||
# :devMode - Boolean to control the opened window's dev mode.
|
||||
openUrl: ({urlToOpen, devMode}) ->
|
||||
# :safeMode - Boolean to control the opened window's safe mode.
|
||||
openUrl: ({urlToOpen, devMode, safeMode}) ->
|
||||
unless @packages?
|
||||
PackageManager = require '../package-manager'
|
||||
fs = require 'fs-plus'
|
||||
@@ -371,7 +380,7 @@ class AtomApplication
|
||||
packagePath = @packages.resolvePackagePath(packageName)
|
||||
bootstrapScript = path.resolve(packagePath, pack.urlMain)
|
||||
windowDimensions = @focusedWindow()?.getDimensions()
|
||||
new AtomWindow({bootstrapScript, @resourcePath, devMode, urlToOpen, windowDimensions})
|
||||
new AtomWindow({bootstrapScript, @resourcePath, devMode, safeMode, urlToOpen, windowDimensions})
|
||||
else
|
||||
console.log "Package '#{pack.name}' does not have a url main: #{urlToOpen}"
|
||||
else
|
||||
@@ -414,8 +423,10 @@ class AtomApplication
|
||||
# :type - A String which specifies the type of the dialog, could be 'file',
|
||||
# 'folder' or 'all'. The 'all' is only available on OS X.
|
||||
# :devMode - A Boolean which controls whether any newly opened windows
|
||||
# should be in dev mode or not.
|
||||
promptForPath: ({type, devMode}={}) ->
|
||||
# should be in dev mode or not.
|
||||
# :safeMode - A Boolean which controls whether any newly opened windows
|
||||
# should be in safe mode or not.
|
||||
promptForPath: ({type, devMode, safeMode}={}) ->
|
||||
type ?= 'all'
|
||||
properties =
|
||||
switch type
|
||||
@@ -424,4 +435,4 @@ class AtomApplication
|
||||
when 'all' then ['openFile', 'openDirectory']
|
||||
else throw new Error("#{type} is an invalid type for promptForPath")
|
||||
dialog.showOpenDialog title: 'Open', properties: properties.concat(['multiSelections', 'createDirectory']), (pathsToOpen) =>
|
||||
@openPaths({pathsToOpen, devMode})
|
||||
@openPaths({pathsToOpen, devMode, safeMode})
|
||||
|
||||
@@ -2,7 +2,6 @@ BrowserWindow = require 'browser-window'
|
||||
ContextMenu = require './context-menu'
|
||||
app = require 'app'
|
||||
dialog = require 'dialog'
|
||||
ipc = require 'ipc'
|
||||
path = require 'path'
|
||||
fs = require 'fs'
|
||||
url = require 'url'
|
||||
@@ -21,7 +20,7 @@ class AtomWindow
|
||||
isSpec: null
|
||||
|
||||
constructor: (settings={}) ->
|
||||
{@resourcePath, pathToOpen, initialLine, @isSpec, @exitWhenDone} = settings
|
||||
{@resourcePath, pathToOpen, initialLine, initialColumn, @isSpec, @exitWhenDone} = settings
|
||||
global.atomApplication.addWindow(this)
|
||||
|
||||
@browserWindow = new BrowserWindow show: false, title: 'Atom', icon: @constructor.iconPath
|
||||
@@ -48,7 +47,7 @@ class AtomWindow
|
||||
@browserWindow.loadUrl @getUrl(loadSettings)
|
||||
@browserWindow.focusOnWebView() if @isSpec
|
||||
|
||||
@openPath(pathToOpen, initialLine)
|
||||
@openPath(pathToOpen, initialLine, initialColumn)
|
||||
|
||||
getUrl: (loadSettingsObj) ->
|
||||
# Ignore the windowState when passing loadSettings via URL, since it could
|
||||
@@ -81,7 +80,7 @@ class AtomWindow
|
||||
false
|
||||
|
||||
handleEvents: ->
|
||||
@browserWindow.on 'destroyed', =>
|
||||
@browserWindow.on 'closed', =>
|
||||
global.atomApplication.removeWindow(this)
|
||||
|
||||
@browserWindow.on 'unresponsive', =>
|
||||
@@ -94,7 +93,7 @@ class AtomWindow
|
||||
detail: 'The editor is not responding. Would you like to force close it or just keep waiting?'
|
||||
@browserWindow.destroy() if chosen is 0
|
||||
|
||||
@browserWindow.on 'crashed', =>
|
||||
@browserWindow.webContents.on 'crashed', =>
|
||||
global.atomApplication.exit(100) if @exitWhenDone
|
||||
|
||||
chosen = dialog.showMessageBox @browserWindow,
|
||||
@@ -114,12 +113,12 @@ class AtomWindow
|
||||
@browserWindow.on 'blur', =>
|
||||
@browserWindow.focusOnWebView()
|
||||
|
||||
openPath: (pathToOpen, initialLine) ->
|
||||
openPath: (pathToOpen, initialLine, initialColumn) ->
|
||||
if @loaded
|
||||
@focus()
|
||||
@sendCommand('window:open-path', {pathToOpen, initialLine})
|
||||
@sendCommand('window:open-path', {pathToOpen, initialLine, initialColumn})
|
||||
else
|
||||
@browserWindow.once 'window:loaded', => @openPath(pathToOpen, initialLine)
|
||||
@browserWindow.once 'window:loaded', => @openPath(pathToOpen, initialLine, initialColumn)
|
||||
|
||||
sendCommand: (command, args...) ->
|
||||
if @isSpecWindow()
|
||||
@@ -136,7 +135,7 @@ class AtomWindow
|
||||
|
||||
sendCommandToBrowserWindow: (command, args...) ->
|
||||
action = if args[0]?.contextCommand then 'context-command' else 'command'
|
||||
ipc.sendChannel @browserWindow.getProcessId(), @browserWindow.getRoutingId(), action, command, args...
|
||||
@browserWindow.webContents.send action, command, args...
|
||||
|
||||
getDimensions: ->
|
||||
[x, y] = @browserWindow.getPosition()
|
||||
|
||||
@@ -42,7 +42,7 @@ start = ->
|
||||
app.removeListener 'open-url', addUrlToOpen
|
||||
|
||||
args.pathsToOpen = args.pathsToOpen.map (pathToOpen) ->
|
||||
path.resolve(args.executedFrom ? process.cwd(), pathToOpen)
|
||||
path.resolve(args.executedFrom ? process.cwd(), pathToOpen.toString())
|
||||
|
||||
require('coffee-script').register()
|
||||
if args.devMode
|
||||
@@ -73,6 +73,7 @@ parseCommandLine = ->
|
||||
options.alias('l', 'log-file').string('l').describe('l', 'Log all output to file.')
|
||||
options.alias('n', 'new-window').boolean('n').describe('n', 'Open a new window.')
|
||||
options.alias('s', 'spec-directory').string('s').describe('s', 'Set the spec directory (default: Atom\'s spec directory).')
|
||||
options.boolean('safe').describe('safe', 'Do not load packages from ~/.atom/packages or ~/.atom/dev/packages.')
|
||||
options.alias('t', 'test').boolean('t').describe('t', 'Run the specified specs and exit with error code on failures.')
|
||||
options.alias('v', 'version').boolean('v').describe('v', 'Print the version.')
|
||||
options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.')
|
||||
@@ -88,6 +89,7 @@ parseCommandLine = ->
|
||||
|
||||
executedFrom = args['executed-from']
|
||||
devMode = args['dev']
|
||||
safeMode = args['safe']
|
||||
pathsToOpen = args._
|
||||
pathsToOpen = [executedFrom] if executedFrom and pathsToOpen.length is 0
|
||||
test = args['test']
|
||||
@@ -107,6 +109,6 @@ parseCommandLine = ->
|
||||
catch
|
||||
resourcePath = path.dirname(path.dirname(__dirname))
|
||||
|
||||
{resourcePath, pathsToOpen, executedFrom, test, version, pidToKillWhenClosed, devMode, newWindow, specDirectory, logFile}
|
||||
{resourcePath, pathsToOpen, executedFrom, test, version, pidToKillWhenClosed, devMode, safeMode, newWindow, specDirectory, logFile}
|
||||
|
||||
start()
|
||||
|
||||
@@ -12,7 +12,7 @@ ChildProcess = require 'child_process'
|
||||
# args = ['-ef']
|
||||
# stdout = (output) -> console.log(output)
|
||||
# exit = (code) -> console.log("ps -ef exited with #{code}")
|
||||
# process = new BufferredProcess({command, args, stdout, exit})
|
||||
# process = new BufferedProcess({command, args, stdout, exit})
|
||||
# ```
|
||||
module.exports =
|
||||
class BufferedProcess
|
||||
@@ -39,7 +39,14 @@ class BufferedProcess
|
||||
# containing the exit status (optional).
|
||||
constructor: ({command, args, options, stdout, stderr, exit}={}) ->
|
||||
options ?= {}
|
||||
@process = ChildProcess.spawn(command, args, options)
|
||||
# Quick hack. Killing @process will only kill cmd.exe, and not the child
|
||||
# process and will just orphan it. Does not escape ^ (cmd's escape symbol).
|
||||
# Related to joyent/node#2318
|
||||
if process.platform is "win32"
|
||||
@process = ChildProcess.spawn(process.env.comspec || "cmd.exe",
|
||||
[ "/c", command ].concat(args), options)
|
||||
else
|
||||
@process = ChildProcess.spawn(command, args, options)
|
||||
@killed = false
|
||||
|
||||
stdoutClosed = true
|
||||
|
||||
+5
-3
@@ -222,11 +222,13 @@ class Config
|
||||
#
|
||||
# keyPath - The {String} name of the key to observe
|
||||
# options - An optional {Object} containing the `callNow` key.
|
||||
# callback - The {Function} that fires when the. It is given a single argument, `value`,
|
||||
# which is the new value of `keyPath`.
|
||||
# callback - The {Function} to call when the value of the key changes.
|
||||
# The first argument will be the new value of the key and the
|
||||
# second argument will be an {Object} with a `previous` property
|
||||
# that is the prior value of the key.
|
||||
#
|
||||
# Returns an {Object} with the following keys:
|
||||
# :off - A {Function} that unobserves the `keyPath` with called.
|
||||
# :off - A {Function} that unobserves the `keyPath` when called.
|
||||
observe: (keyPath, options={}, callback) ->
|
||||
if _.isFunction(options)
|
||||
callback = options
|
||||
|
||||
@@ -98,5 +98,6 @@ class ContextMenuManager
|
||||
showForEvent: (event) ->
|
||||
@activeElement = event.target
|
||||
menuTemplate = @combinedMenuTemplateForElement(event.target)
|
||||
return unless menuTemplate?.length > 0
|
||||
@executeBuildHandlers(event, menuTemplate)
|
||||
remote.getCurrentWindow().emit('context-menu', menuTemplate)
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
React = require 'react'
|
||||
{div} = require 'reactionary'
|
||||
React = require 'react-atom-fork'
|
||||
{div} = require 'reactionary-atom-fork'
|
||||
|
||||
module.exports =
|
||||
CursorComponent = React.createClass
|
||||
displayName: 'CursorComponent'
|
||||
|
||||
render: ->
|
||||
{top, left, height, width} = @props.cursor.getPixelRect()
|
||||
className = 'cursor'
|
||||
className += ' blink-off' if @props.blinkOff
|
||||
{cursor, scrollTop, scrollLeft} = @props
|
||||
{top, left, height, width} = cursor.getPixelRect()
|
||||
top -= scrollTop
|
||||
left -= scrollLeft
|
||||
WebkitTransform = "translate3d(#{left}px, #{top}px, 0px)"
|
||||
|
||||
div className: className, style: {top, left, height, width}
|
||||
div className: 'cursor', style: {height, width, WebkitTransform}
|
||||
|
||||
@@ -78,7 +78,11 @@ class CursorView extends View
|
||||
setVisible: (visible) ->
|
||||
unless @visible is visible
|
||||
@visible = visible
|
||||
@toggle(@visible)
|
||||
hiddenCursor = 'hidden-cursor'
|
||||
if visible
|
||||
@removeClass hiddenCursor
|
||||
else
|
||||
@addClass hiddenCursor
|
||||
|
||||
stopBlinking: ->
|
||||
@constructor.stopBlinking(this) if @blinking
|
||||
|
||||
+4
-4
@@ -138,13 +138,13 @@ class Cursor extends Model
|
||||
|
||||
# Public: Identifies if the cursor is surrounded by whitespace.
|
||||
#
|
||||
# "Surrounded" here means that all characters before and after the cursor is
|
||||
# whitespace.
|
||||
# "Surrounded" here means that the character directly before and after the
|
||||
# cursor are both whitespace.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
isSurroundedByWhitespace: ->
|
||||
{row, column} = @getBufferPosition()
|
||||
range = [[row, Math.min(0, column - 1)], [row, Math.max(0, column + 1)]]
|
||||
range = [[row, column - 1], [row, column + 1]]
|
||||
/^\s+$/.test @editor.getTextInBufferRange(range)
|
||||
|
||||
# Public: Returns whether the cursor is currently between a word and non-word
|
||||
@@ -465,7 +465,7 @@ class Cursor extends Model
|
||||
getCurrentParagraphBufferRange: ->
|
||||
@editor.languageMode.rowRangeForParagraphAtBufferRow(@getBufferRow())
|
||||
|
||||
# Public: Returns the characters preceeding the cursor in the current word.
|
||||
# Public: Returns the characters preceding the cursor in the current word.
|
||||
getCurrentWordPrefix: ->
|
||||
@editor.getTextInBufferRange([@getBeginningOfCurrentWordBufferPosition(), @getBufferPosition()])
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
React = require 'react'
|
||||
{div} = require 'reactionary'
|
||||
{debounce} = require 'underscore-plus'
|
||||
React = require 'react-atom-fork'
|
||||
{div} = require 'reactionary-atom-fork'
|
||||
{debounce, toArray} = require 'underscore-plus'
|
||||
SubscriberMixin = require './subscriber-mixin'
|
||||
CursorComponent = require './cursor-component'
|
||||
|
||||
|
||||
module.exports =
|
||||
CursorsComponent = React.createClass
|
||||
displayName: 'CursorsComponent'
|
||||
@@ -13,38 +12,44 @@ CursorsComponent = React.createClass
|
||||
cursorBlinkIntervalHandle: null
|
||||
|
||||
render: ->
|
||||
{editor} = @props
|
||||
blinkOff = @state.blinkCursorsOff
|
||||
{editor, scrollTop, scrollLeft} = @props
|
||||
{blinkOff} = @state
|
||||
|
||||
div className: 'cursors',
|
||||
className = 'cursors'
|
||||
className += ' blink-off' if blinkOff
|
||||
|
||||
div {className},
|
||||
if @isMounted()
|
||||
for selection in editor.getSelections()
|
||||
if selection.isEmpty() and editor.selectionIntersectsVisibleRowRange(selection)
|
||||
{cursor} = selection
|
||||
CursorComponent({key: cursor.id, cursor, blinkOff})
|
||||
CursorComponent({key: cursor.id, cursor, scrollTop, scrollLeft})
|
||||
|
||||
getInitialState: ->
|
||||
blinkCursorsOff: false
|
||||
blinkOff: false
|
||||
|
||||
componentDidMount: ->
|
||||
{editor} = @props
|
||||
@startBlinkingCursors()
|
||||
|
||||
componentWillUnmount: ->
|
||||
clearInterval(@cursorBlinkIntervalHandle)
|
||||
@stopBlinkingCursors()
|
||||
|
||||
componentWillUpdate: ({cursorsMoved}) ->
|
||||
@pauseCursorBlinking() if cursorsMoved
|
||||
|
||||
startBlinkingCursors: ->
|
||||
@cursorBlinkIntervalHandle = setInterval(@toggleCursorBlink, @props.cursorBlinkPeriod / 2)
|
||||
@toggleCursorBlinkHandle = setInterval(@toggleCursorBlink, @props.cursorBlinkPeriod / 2) if @isMounted()
|
||||
|
||||
startBlinkingCursorsAfterDelay: null # Created lazily
|
||||
|
||||
toggleCursorBlink: -> @setState(blinkCursorsOff: not @state.blinkCursorsOff)
|
||||
stopBlinkingCursors: ->
|
||||
clearInterval(@toggleCursorBlinkHandle)
|
||||
|
||||
toggleCursorBlink: ->
|
||||
@setState(blinkOff: not @state.blinkOff)
|
||||
|
||||
pauseCursorBlinking: ->
|
||||
@state.blinkCursorsOff = false
|
||||
clearInterval(@cursorBlinkIntervalHandle)
|
||||
@state.blinkOff = false
|
||||
@stopBlinkingCursors()
|
||||
@startBlinkingCursorsAfterDelay ?= debounce(@startBlinkingCursors, @props.cursorBlinkResumeDelay)
|
||||
@startBlinkingCursorsAfterDelay()
|
||||
|
||||
+78
-22
@@ -23,7 +23,7 @@ class DisplayBuffer extends Model
|
||||
manageScrollPosition: false
|
||||
softWrap: null
|
||||
editorWidthInChars: null
|
||||
lineHeight: null
|
||||
lineHeightInPixels: null
|
||||
defaultCharWidth: null
|
||||
height: null
|
||||
width: null
|
||||
@@ -32,6 +32,8 @@ class DisplayBuffer extends Model
|
||||
|
||||
verticalScrollMargin: 2
|
||||
horizontalScrollMargin: 6
|
||||
horizontalScrollbarHeight: 15
|
||||
verticalScrollbarWidth: 15
|
||||
|
||||
constructor: ({tabLength, @editorWidthInChars, @tokenizedBuffer, buffer}={}) ->
|
||||
super
|
||||
@@ -111,32 +113,83 @@ class DisplayBuffer extends Model
|
||||
getHorizontalScrollMargin: -> @horizontalScrollMargin
|
||||
setHorizontalScrollMargin: (@horizontalScrollMargin) -> @horizontalScrollMargin
|
||||
|
||||
getHeight: -> @height ? @getScrollHeight()
|
||||
getHorizontalScrollbarHeight: -> @horizontalScrollbarHeight
|
||||
setHorizontalScrollbarHeight: (@horizontalScrollbarHeight) -> @horizontalScrollbarHeight
|
||||
|
||||
getVerticalScrollbarWidth: -> @verticalScrollbarWidth
|
||||
setVerticalScrollbarWidth: (@verticalScrollbarWidth) -> @verticalScrollbarWidth
|
||||
|
||||
getHeight: ->
|
||||
if @height?
|
||||
@height
|
||||
else
|
||||
if @horizontallyScrollable()
|
||||
@getScrollHeight() + @getHorizontalScrollbarHeight()
|
||||
else
|
||||
@getScrollHeight()
|
||||
|
||||
setHeight: (@height) -> @height
|
||||
|
||||
getWidth: -> @width ? @getScrollWidth()
|
||||
getClientHeight: (reentrant) ->
|
||||
if @horizontallyScrollable(reentrant)
|
||||
@getHeight() - @getHorizontalScrollbarHeight()
|
||||
else
|
||||
@getHeight()
|
||||
|
||||
getClientWidth: (reentrant) ->
|
||||
if @verticallyScrollable(reentrant)
|
||||
@getWidth() - @getVerticalScrollbarWidth()
|
||||
else
|
||||
@getWidth()
|
||||
|
||||
horizontallyScrollable: (reentrant) ->
|
||||
return false unless @width?
|
||||
return false if @getSoftWrap()
|
||||
if reentrant
|
||||
@getScrollWidth() > @getWidth()
|
||||
else
|
||||
@getScrollWidth() > @getClientWidth(true)
|
||||
|
||||
verticallyScrollable: (reentrant) ->
|
||||
return false unless @height?
|
||||
if reentrant
|
||||
@getScrollHeight() > @getHeight()
|
||||
else
|
||||
@getScrollHeight() > @getClientHeight(true)
|
||||
|
||||
getWidth: ->
|
||||
if @width?
|
||||
@width
|
||||
else
|
||||
if @verticallyScrollable()
|
||||
@getScrollWidth() + @getVerticalScrollbarWidth()
|
||||
else
|
||||
@getScrollWidth()
|
||||
|
||||
setWidth: (newWidth) ->
|
||||
oldWidth = @width
|
||||
@width = newWidth
|
||||
@updateWrappedScreenLines() if newWidth isnt oldWidth and @softWrap
|
||||
@setScrollTop(@getScrollTop()) # Ensure scrollTop is still valid in case horizontal scrollbar disappeared
|
||||
@width
|
||||
|
||||
getScrollTop: -> @scrollTop
|
||||
setScrollTop: (scrollTop) ->
|
||||
if @manageScrollPosition
|
||||
@scrollTop = Math.max(0, Math.min(@getScrollHeight() - @getHeight(), scrollTop))
|
||||
@scrollTop = Math.max(0, Math.min(@getScrollHeight() - @getClientHeight(), scrollTop))
|
||||
else
|
||||
@scrollTop = scrollTop
|
||||
|
||||
getScrollBottom: -> @scrollTop + @height
|
||||
setScrollBottom: (scrollBottom) ->
|
||||
@setScrollTop(scrollBottom - @height)
|
||||
@setScrollTop(scrollBottom - @getClientHeight())
|
||||
@getScrollBottom()
|
||||
|
||||
getScrollLeft: -> @scrollLeft
|
||||
setScrollLeft: (scrollLeft) ->
|
||||
if @manageScrollPosition
|
||||
@scrollLeft = Math.max(0, Math.min(@getScrollWidth() - @getWidth(), scrollLeft))
|
||||
@scrollLeft = Math.max(0, Math.min(@getScrollWidth() - @getClientWidth(), scrollLeft))
|
||||
@scrollLeft
|
||||
else
|
||||
@scrollLeft = scrollLeft
|
||||
|
||||
@@ -145,12 +198,14 @@ class DisplayBuffer extends Model
|
||||
@setScrollLeft(scrollRight - @width)
|
||||
@getScrollRight()
|
||||
|
||||
getLineHeight: -> @lineHeight
|
||||
setLineHeight: (@lineHeight) -> @lineHeight
|
||||
getLineHeightInPixels: -> @lineHeightInPixels
|
||||
setLineHeightInPixels: (@lineHeightInPixels) -> @lineHeightInPixels
|
||||
|
||||
getDefaultCharWidth: -> @defaultCharWidth
|
||||
setDefaultCharWidth: (@defaultCharWidth) -> @defaultCharWidth
|
||||
|
||||
getCursorWidth: -> 1
|
||||
|
||||
getScopedCharWidth: (scopeNames, char) ->
|
||||
@getScopedCharWidths(scopeNames)[char]
|
||||
|
||||
@@ -172,21 +227,22 @@ class DisplayBuffer extends Model
|
||||
@charWidthsByScope = {}
|
||||
|
||||
getScrollHeight: ->
|
||||
unless @getLineHeight() > 0
|
||||
throw new Error("You must assign lineHeight before calling ::getScrollHeight()")
|
||||
unless @getLineHeightInPixels() > 0
|
||||
throw new Error("You must assign lineHeightInPixels before calling ::getScrollHeight()")
|
||||
|
||||
@getLineCount() * @getLineHeight()
|
||||
@getLineCount() * @getLineHeightInPixels()
|
||||
|
||||
getScrollWidth: ->
|
||||
@getMaxLineLength() * @getDefaultCharWidth()
|
||||
(@getMaxLineLength() * @getDefaultCharWidth()) + @getCursorWidth()
|
||||
|
||||
getVisibleRowRange: ->
|
||||
unless @getLineHeight() > 0
|
||||
throw new Error("You must assign a non-zero lineHeight before calling ::getVisibleRowRange()")
|
||||
unless @getLineHeightInPixels() > 0
|
||||
throw new Error("You must assign a non-zero lineHeightInPixels before calling ::getVisibleRowRange()")
|
||||
|
||||
heightInLines = Math.ceil(@getHeight() / @getLineHeightInPixels()) + 1
|
||||
startRow = Math.floor(@getScrollTop() / @getLineHeightInPixels())
|
||||
endRow = Math.min(@getLineCount(), startRow + heightInLines)
|
||||
|
||||
heightInLines = Math.ceil(@getHeight() / @getLineHeight()) + 1
|
||||
startRow = Math.floor(@getScrollTop() / @getLineHeight())
|
||||
endRow = Math.min(@getLineCount(), Math.ceil(startRow + heightInLines))
|
||||
[startRow, endRow]
|
||||
|
||||
intersectsVisibleRowRange: (startRow, endRow) ->
|
||||
@@ -198,7 +254,7 @@ class DisplayBuffer extends Model
|
||||
@intersectsVisibleRowRange(start.row, end.row + 1)
|
||||
|
||||
scrollToScreenRange: (screenRange) ->
|
||||
verticalScrollMarginInPixels = @getVerticalScrollMargin() * @getLineHeight()
|
||||
verticalScrollMarginInPixels = @getVerticalScrollMargin() * @getLineHeightInPixels()
|
||||
horizontalScrollMarginInPixels = @getHorizontalScrollMargin() * @getDefaultCharWidth()
|
||||
|
||||
{top, left, height, width} = @pixelRectForScreenRange(screenRange)
|
||||
@@ -229,11 +285,11 @@ class DisplayBuffer extends Model
|
||||
if screenRange.end.row > screenRange.start.row
|
||||
top = @pixelPositionForScreenPosition(screenRange.start).top
|
||||
left = 0
|
||||
height = (screenRange.end.row - screenRange.start.row + 1) * @getLineHeight()
|
||||
height = (screenRange.end.row - screenRange.start.row + 1) * @getLineHeightInPixels()
|
||||
width = @getScrollWidth()
|
||||
else
|
||||
{top, left} = @pixelPositionForScreenPosition(screenRange.start)
|
||||
height = @getLineHeight()
|
||||
height = @getLineHeightInPixels()
|
||||
width = @pixelPositionForScreenPosition(screenRange.end).left - left
|
||||
|
||||
{top, left, width, height}
|
||||
@@ -456,7 +512,7 @@ class DisplayBuffer extends Model
|
||||
targetColumn = screenPosition.column
|
||||
defaultCharWidth = @defaultCharWidth
|
||||
|
||||
top = targetRow * @lineHeight
|
||||
top = targetRow * @lineHeightInPixels
|
||||
left = 0
|
||||
column = 0
|
||||
for token in @lineForRow(targetRow).tokens
|
||||
@@ -471,7 +527,7 @@ class DisplayBuffer extends Model
|
||||
targetTop = pixelPosition.top
|
||||
targetLeft = pixelPosition.left
|
||||
defaultCharWidth = @defaultCharWidth
|
||||
row = Math.floor(targetTop / @getLineHeight())
|
||||
row = Math.floor(targetTop / @getLineHeightInPixels())
|
||||
row = Math.min(row, @getLastRow())
|
||||
row = Math.max(0, row)
|
||||
|
||||
|
||||
+215
-39
@@ -1,10 +1,12 @@
|
||||
React = require 'react'
|
||||
{div, span} = require 'reactionary'
|
||||
{debounce} = require 'underscore-plus'
|
||||
React = require 'react-atom-fork'
|
||||
{div, span} = require 'reactionary-atom-fork'
|
||||
{debounce, defaults} = require 'underscore-plus'
|
||||
scrollbarStyle = require 'scrollbar-style'
|
||||
|
||||
GutterComponent = require './gutter-component'
|
||||
EditorScrollViewComponent = require './editor-scroll-view-component'
|
||||
ScrollbarComponent = require './scrollbar-component'
|
||||
ScrollbarCornerComponent = require './scrollbar-corner-component'
|
||||
SubscriberMixin = require './subscriber-mixin'
|
||||
|
||||
module.exports =
|
||||
@@ -18,34 +20,52 @@ EditorComponent = React.createClass
|
||||
batchingUpdates: false
|
||||
updateRequested: false
|
||||
cursorsMoved: false
|
||||
preservedRowRange: null
|
||||
selectionChanged: false
|
||||
selectionAdded: false
|
||||
scrollingVertically: false
|
||||
gutterWidth: 0
|
||||
refreshingScrollbars: false
|
||||
measuringScrollbars: true
|
||||
pendingVerticalScrollDelta: 0
|
||||
pendingHorizontalScrollDelta: 0
|
||||
mouseWheelScreenRow: null
|
||||
|
||||
render: ->
|
||||
{focused, fontSize, lineHeight, fontFamily, showIndentGuide} = @state
|
||||
{focused, fontSize, lineHeight, fontFamily, showIndentGuide, showInvisibles, visible} = @state
|
||||
{editor, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props
|
||||
maxLineNumberDigits = editor.getScreenLineCount().toString().length
|
||||
invisibles = if showInvisibles then @state.invisibles else {}
|
||||
|
||||
if @isMounted()
|
||||
renderedRowRange = @getRenderedRowRange()
|
||||
scrollHeight = editor.getScrollHeight()
|
||||
scrollWidth = editor.getScrollWidth()
|
||||
scrollTop = editor.getScrollTop()
|
||||
scrollLeft = editor.getScrollLeft()
|
||||
lineHeightInPixels = editor.getLineHeight()
|
||||
lineHeightInPixels = editor.getLineHeightInPixels()
|
||||
scrollViewHeight = editor.getHeight()
|
||||
horizontalScrollbarHeight = editor.getHorizontalScrollbarHeight()
|
||||
verticalScrollbarWidth = editor.getVerticalScrollbarWidth()
|
||||
verticallyScrollable = editor.verticallyScrollable()
|
||||
horizontallyScrollable = editor.horizontallyScrollable()
|
||||
|
||||
className = 'editor editor-colors react'
|
||||
className += ' is-focused' if focused
|
||||
|
||||
div className: className, style: {fontSize, lineHeight, fontFamily}, tabIndex: -1,
|
||||
GutterComponent {
|
||||
editor, renderedRowRange, scrollTop, scrollHeight,
|
||||
lineHeight: lineHeightInPixels, @pendingChanges
|
||||
ref: 'gutter', editor, renderedRowRange, maxLineNumberDigits,
|
||||
scrollTop, scrollHeight, lineHeight, lineHeightInPixels, fontSize, fontFamily,
|
||||
@pendingChanges, onWidthChanged: @onGutterWidthChanged, @mouseWheelScreenRow
|
||||
}
|
||||
|
||||
EditorScrollViewComponent {
|
||||
ref: 'scrollView', editor, fontSize, fontFamily, showIndentGuide
|
||||
scrollHeight, scrollWidth, lineHeight: lineHeightInPixels,
|
||||
renderedRowRange, @pendingChanges, @scrollingVertically, @cursorsMoved,
|
||||
cursorBlinkPeriod, cursorBlinkResumeDelay, @onInputFocused, @onInputBlurred
|
||||
ref: 'scrollView', editor, fontSize, fontFamily, showIndentGuide,
|
||||
lineHeight, lineHeightInPixels, renderedRowRange, @pendingChanges,
|
||||
scrollTop, scrollLeft, scrollHeight, scrollWidth, @scrollingVertically,
|
||||
@cursorsMoved, @selectionChanged, @selectionAdded, cursorBlinkPeriod,
|
||||
cursorBlinkResumeDelay, @onInputFocused, @onInputBlurred, @mouseWheelScreenRow,
|
||||
invisibles, visible, scrollViewHeight
|
||||
}
|
||||
|
||||
ScrollbarComponent
|
||||
@@ -55,6 +75,10 @@ EditorComponent = React.createClass
|
||||
onScroll: @onVerticalScroll
|
||||
scrollTop: scrollTop
|
||||
scrollHeight: scrollHeight
|
||||
visible: verticallyScrollable and not @refreshingScrollbars and not @measuringScrollbars
|
||||
scrollableInOppositeDirection: horizontallyScrollable
|
||||
verticalScrollbarWidth: verticalScrollbarWidth
|
||||
horizontalScrollbarHeight: horizontalScrollbarHeight
|
||||
|
||||
ScrollbarComponent
|
||||
ref: 'horizontalScrollbar'
|
||||
@@ -62,20 +86,34 @@ EditorComponent = React.createClass
|
||||
orientation: 'horizontal'
|
||||
onScroll: @onHorizontalScroll
|
||||
scrollLeft: scrollLeft
|
||||
scrollWidth: scrollWidth
|
||||
scrollWidth: scrollWidth + @gutterWidth
|
||||
visible: horizontallyScrollable and not @refreshingScrollbars and not @measuringScrollbars
|
||||
scrollableInOppositeDirection: verticallyScrollable
|
||||
verticalScrollbarWidth: verticalScrollbarWidth
|
||||
horizontalScrollbarHeight: horizontalScrollbarHeight
|
||||
|
||||
# Also used to measure the height/width of scrollbars after the initial render
|
||||
ScrollbarCornerComponent
|
||||
ref: 'scrollbarCorner'
|
||||
visible: not @refreshingScrollbars and (@measuringScrollbars or horizontallyScrollable and verticallyScrollable)
|
||||
measuringScrollbars: @measuringScrollbars
|
||||
height: horizontalScrollbarHeight
|
||||
width: verticalScrollbarWidth
|
||||
|
||||
getRenderedRowRange: ->
|
||||
renderedRowRange = @props.editor.getVisibleRowRange()
|
||||
if @preservedRowRange?
|
||||
renderedRowRange[0] = Math.min(@preservedRowRange[0], renderedRowRange[0])
|
||||
renderedRowRange[1] = Math.max(@preservedRowRange[1], renderedRowRange[1])
|
||||
renderedRowRange
|
||||
{editor, lineOverdrawMargin} = @props
|
||||
[visibleStartRow, visibleEndRow] = editor.getVisibleRowRange()
|
||||
renderedStartRow = Math.max(0, visibleStartRow - lineOverdrawMargin)
|
||||
renderedEndRow = Math.min(editor.getScreenLineCount(), visibleEndRow + lineOverdrawMargin)
|
||||
[renderedStartRow, renderedEndRow]
|
||||
|
||||
getInitialState: -> {}
|
||||
getInitialState: ->
|
||||
visible: true
|
||||
|
||||
getDefaultProps: ->
|
||||
cursorBlinkPeriod: 800
|
||||
cursorBlinkResumeDelay: 200
|
||||
cursorBlinkResumeDelay: 100
|
||||
lineOverdrawMargin: 8
|
||||
|
||||
componentWillMount: ->
|
||||
@pendingChanges = []
|
||||
@@ -86,6 +124,9 @@ EditorComponent = React.createClass
|
||||
@observeEditor()
|
||||
@listenForDOMEvents()
|
||||
@listenForCommands()
|
||||
@measureScrollbars()
|
||||
@subscribe atom.themes, 'stylesheet-added stylsheet-removed', @onStylesheetsChanged
|
||||
@subscribe scrollbarStyle.changes, @refreshScrollbars
|
||||
@props.editor.setVisible(true)
|
||||
@requestUpdate()
|
||||
|
||||
@@ -99,6 +140,10 @@ EditorComponent = React.createClass
|
||||
componentDidUpdate: ->
|
||||
@pendingChanges.length = 0
|
||||
@cursorsMoved = false
|
||||
@selectionChanged = false
|
||||
@selectionAdded = false
|
||||
@refreshingScrollbars = false
|
||||
@measureScrollbars() if @measuringScrollbars
|
||||
@props.parentView.trigger 'editor:display-updated'
|
||||
|
||||
observeEditor: ->
|
||||
@@ -107,15 +152,14 @@ EditorComponent = React.createClass
|
||||
@subscribe editor, 'batched-updates-ended', @onBatchedUpdatesEnded
|
||||
@subscribe editor, 'screen-lines-changed', @onScreenLinesChanged
|
||||
@subscribe editor, 'cursors-moved', @onCursorsMoved
|
||||
@subscribe editor, 'selection-screen-range-changed', @requestUpdate
|
||||
@subscribe editor, 'selection-removed selection-screen-range-changed', @onSelectionChanged
|
||||
@subscribe editor, 'selection-added', @onSelectionAdded
|
||||
@subscribe editor, 'selection-removed', @onSelectionAdded
|
||||
@subscribe editor.$scrollTop.changes, @onScrollTopChanged
|
||||
@subscribe editor.$scrollLeft.changes, @requestUpdate
|
||||
@subscribe editor.$height.changes, @requestUpdate
|
||||
@subscribe editor.$width.changes, @requestUpdate
|
||||
@subscribe editor.$defaultCharWidth.changes, @requestUpdate
|
||||
@subscribe editor.$lineHeight.changes, @requestUpdate
|
||||
@subscribe editor.$lineHeightInPixels.changes, @requestUpdate
|
||||
|
||||
listenForDOMEvents: ->
|
||||
node = @getDOMNode()
|
||||
@@ -141,8 +185,8 @@ EditorComponent = React.createClass
|
||||
'editor:move-to-previous-word': => editor.moveCursorToPreviousWord()
|
||||
'editor:select-word': => editor.selectWord()
|
||||
'editor:consolidate-selections': @consolidateSelections
|
||||
'editor:backspace-to-beginning-of-word': => editor.backspaceToBeginningOfWord()
|
||||
'editor:backspace-to-beginning-of-line': => editor.backspaceToBeginningOfLine()
|
||||
'editor:delete-to-beginning-of-word': => editor.deleteToBeginningOfWord()
|
||||
'editor:delete-to-beginning-of-line': => editor.deleteToBeginningOfLine()
|
||||
'editor:delete-to-end-of-word': => editor.deleteToEndOfWord()
|
||||
'editor:delete-line': => editor.deleteLine()
|
||||
'editor:cut-to-end-of-line': => editor.cutToEndOfLine()
|
||||
@@ -218,6 +262,7 @@ EditorComponent = React.createClass
|
||||
'editor:scroll-to-cursor': => editor.scrollToCursorPosition()
|
||||
'core:page-up': => editor.pageUp()
|
||||
'core:page-down': => editor.pageDown()
|
||||
'benchmark:scroll': @runScrollBenchmark
|
||||
|
||||
addCommandListeners: (listenersByCommandName) ->
|
||||
{parentView} = @props
|
||||
@@ -228,7 +273,20 @@ EditorComponent = React.createClass
|
||||
observeConfig: ->
|
||||
@subscribe atom.config.observe 'editor.fontFamily', @setFontFamily
|
||||
@subscribe atom.config.observe 'editor.fontSize', @setFontSize
|
||||
@subscribe atom.config.observe 'editor.lineHeight', @setLineHeight
|
||||
@subscribe atom.config.observe 'editor.showIndentGuide', @setShowIndentGuide
|
||||
@subscribe atom.config.observe 'editor.invisibles', @setInvisibles
|
||||
@subscribe atom.config.observe 'editor.showInvisibles', @setShowInvisibles
|
||||
|
||||
measureScrollbars: ->
|
||||
@measuringScrollbars = false
|
||||
|
||||
{editor} = @props
|
||||
scrollbarCornerNode = @refs.scrollbarCorner.getDOMNode()
|
||||
width = (scrollbarCornerNode.offsetWidth - scrollbarCornerNode.clientWidth) or 15
|
||||
height = (scrollbarCornerNode.offsetHeight - scrollbarCornerNode.clientHeight) or 15
|
||||
editor.setVerticalScrollbarWidth(width)
|
||||
editor.setHorizontalScrollbarHeight(height)
|
||||
|
||||
setFontSize: (fontSize) ->
|
||||
@setState({fontSize})
|
||||
@@ -242,6 +300,25 @@ EditorComponent = React.createClass
|
||||
setShowIndentGuide: (showIndentGuide) ->
|
||||
@setState({showIndentGuide})
|
||||
|
||||
# Public: Defines which characters are invisible.
|
||||
#
|
||||
# invisibles - An {Object} defining the invisible characters:
|
||||
# :eol - The end of line invisible {String} (default: `\u00ac`).
|
||||
# :space - The space invisible {String} (default: `\u00b7`).
|
||||
# :tab - The tab invisible {String} (default: `\u00bb`).
|
||||
# :cr - The carriage return invisible {String} (default: `\u00a4`).
|
||||
setInvisibles: (invisibles={}) ->
|
||||
defaults invisibles,
|
||||
eol: '\u00ac'
|
||||
space: '\u00b7'
|
||||
tab: '\u00bb'
|
||||
cr: '\u00a4'
|
||||
|
||||
@setState({invisibles})
|
||||
|
||||
setShowInvisibles: (showInvisibles) ->
|
||||
@setState({showInvisibles})
|
||||
|
||||
onFocus: ->
|
||||
@refs.scrollView.focus()
|
||||
|
||||
@@ -276,21 +353,61 @@ EditorComponent = React.createClass
|
||||
@pendingScrollLeft = null
|
||||
|
||||
onMouseWheel: (event) ->
|
||||
event.preventDefault()
|
||||
screenRow = @screenRowForNode(event.target)
|
||||
@mouseWheelScreenRow = screenRow if screenRow?
|
||||
animationFramePending = @pendingHorizontalScrollDelta isnt 0 or @pendingVerticalScrollDelta isnt 0
|
||||
|
||||
# Only scroll in one direction at a time
|
||||
{wheelDeltaX, wheelDeltaY} = event
|
||||
if Math.abs(wheelDeltaX) > Math.abs(wheelDeltaY)
|
||||
@refs.horizontalScrollbar.getDOMNode().scrollLeft -= wheelDeltaX
|
||||
@pendingHorizontalScrollDelta -= wheelDeltaX
|
||||
else
|
||||
@refs.verticalScrollbar.getDOMNode().scrollTop -= wheelDeltaY
|
||||
@pendingVerticalScrollDelta -= wheelDeltaY
|
||||
|
||||
event.preventDefault()
|
||||
unless animationFramePending
|
||||
requestAnimationFrame =>
|
||||
{editor} = @props
|
||||
editor.setScrollTop(editor.getScrollTop() + @pendingVerticalScrollDelta)
|
||||
editor.setScrollLeft(editor.getScrollLeft() + @pendingHorizontalScrollDelta)
|
||||
@pendingVerticalScrollDelta = 0
|
||||
@pendingHorizontalScrollDelta = 0
|
||||
|
||||
clearPreservedRowRange: ->
|
||||
@preservedRowRange = null
|
||||
@scrollingVertically = false
|
||||
screenRowForNode: (node) ->
|
||||
while node isnt document
|
||||
if screenRow = node.dataset.screenRow
|
||||
return parseInt(screenRow)
|
||||
node = node.parentNode
|
||||
null
|
||||
|
||||
onStylesheetsChanged: (stylesheet) ->
|
||||
@refreshScrollbars() if @containsScrollbarSelector(stylesheet)
|
||||
|
||||
containsScrollbarSelector: (stylesheet) ->
|
||||
for rule in stylesheet.cssRules
|
||||
if rule.selectorText?.indexOf('scrollbar') > -1
|
||||
return true
|
||||
false
|
||||
|
||||
refreshScrollbars: ->
|
||||
# Believe it or not, proper handling of changes to scrollbar styles requires
|
||||
# three DOM updates.
|
||||
|
||||
# Scrollbar style changes won't apply to scrollbars that are already
|
||||
# visible, so first we need to hide scrollbars so we can redisplay them and
|
||||
# force Chromium to apply updates.
|
||||
@refreshingScrollbars = true
|
||||
@requestUpdate()
|
||||
|
||||
clearPreservedRowRangeAfterDelay: null # Created lazily
|
||||
# Next, we display only the scrollbar corner so we can measure the new
|
||||
# scrollbar dimensions. The ::measuringScrollbars property will be set back
|
||||
# to false after the scrollbars are measured.
|
||||
@measuringScrollbars = true
|
||||
@requestUpdate()
|
||||
|
||||
# Finally, we restore the scrollbars based on the newly-measured dimensions
|
||||
# if the editor's content and dimensions require them to be visible.
|
||||
@requestUpdate()
|
||||
|
||||
onBatchedUpdatesStarted: ->
|
||||
@batchingUpdates = true
|
||||
@@ -307,24 +424,38 @@ EditorComponent = React.createClass
|
||||
@pendingChanges.push(change)
|
||||
@requestUpdate() if editor.intersectsVisibleRowRange(change.start, change.end + 1) # TODO: Use closed-open intervals for change events
|
||||
|
||||
onSelectionChanged: (selection) ->
|
||||
{editor} = @props
|
||||
if editor.selectionIntersectsVisibleRowRange(selection)
|
||||
@selectionChanged = true
|
||||
@requestUpdate()
|
||||
|
||||
onSelectionAdded: (selection) ->
|
||||
{editor} = @props
|
||||
@requestUpdate() if editor.selectionIntersectsVisibleRowRange(selection)
|
||||
if editor.selectionIntersectsVisibleRowRange(selection)
|
||||
@selectionChanged = true
|
||||
@selectionAdded = true
|
||||
@requestUpdate()
|
||||
|
||||
onScrollTopChanged: ->
|
||||
@preservedRowRange = @getRenderedRowRange()
|
||||
@scrollingVertically = true
|
||||
@clearPreservedRowRangeAfterDelay ?= debounce(@clearPreservedRowRange, 200)
|
||||
@clearPreservedRowRangeAfterDelay()
|
||||
@requestUpdate()
|
||||
@stopScrollingAfterDelay ?= debounce(@onStoppedScrolling, 100)
|
||||
@stopScrollingAfterDelay()
|
||||
|
||||
onStoppedScrolling: ->
|
||||
@scrollingVertically = false
|
||||
@mouseWheelScreenRow = null
|
||||
@requestUpdate()
|
||||
|
||||
onSelectionRemoved: (selection) ->
|
||||
{editor} = @props
|
||||
@requestUpdate() if editor.selectionIntersectsVisibleRowRange(selection)
|
||||
stopScrollingAfterDelay: null # created lazily
|
||||
|
||||
onCursorsMoved: ->
|
||||
@cursorsMoved = true
|
||||
|
||||
onGutterWidthChanged: (@gutterWidth) ->
|
||||
@requestUpdate()
|
||||
|
||||
requestUpdate: ->
|
||||
if @batchingUpdates
|
||||
@updateRequested = true
|
||||
@@ -336,3 +467,48 @@ EditorComponent = React.createClass
|
||||
|
||||
consolidateSelections: (e) ->
|
||||
e.abortKeyBinding() unless @props.editor.consolidateSelections()
|
||||
|
||||
lineNodeForScreenRow: (screenRow) -> @refs.scrollView.lineNodeForScreenRow(screenRow)
|
||||
|
||||
lineNumberNodeForScreenRow: (screenRow) -> @refs.gutter.lineNumberNodeForScreenRow(screenRow)
|
||||
|
||||
hide: ->
|
||||
@setState(visible: false)
|
||||
|
||||
show: ->
|
||||
@setState(visible: true)
|
||||
|
||||
runScrollBenchmark: ->
|
||||
unless process.env.NODE_ENV is 'production'
|
||||
ReactPerf = require 'react-atom-fork/lib/ReactDefaultPerf'
|
||||
ReactPerf.start()
|
||||
|
||||
node = @getDOMNode()
|
||||
|
||||
scroll = (delta, done) ->
|
||||
dispatchMouseWheelEvent = ->
|
||||
node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -0, wheelDeltaY: -delta))
|
||||
|
||||
stopScrolling = ->
|
||||
clearInterval(interval)
|
||||
done?()
|
||||
|
||||
interval = setInterval(dispatchMouseWheelEvent, 10)
|
||||
setTimeout(stopScrolling, 500)
|
||||
|
||||
console.timeline('scroll')
|
||||
scroll 50, ->
|
||||
scroll 100, ->
|
||||
scroll 200, ->
|
||||
scroll 400, ->
|
||||
scroll 800, ->
|
||||
scroll 1600, ->
|
||||
console.timelineEnd('scroll')
|
||||
unless process.env.NODE_ENV is 'production'
|
||||
ReactPerf.stop()
|
||||
console.log "Inclusive"
|
||||
ReactPerf.printInclusive()
|
||||
console.log "Exclusive"
|
||||
ReactPerf.printExclusive()
|
||||
console.log "Wasted"
|
||||
ReactPerf.printWasted()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
React = require 'react'
|
||||
{div} = require 'reactionary'
|
||||
React = require 'react-atom-fork'
|
||||
{div} = require 'reactionary-atom-fork'
|
||||
{debounce} = require 'underscore-plus'
|
||||
|
||||
InputComponent = require './input-component'
|
||||
@@ -16,20 +16,15 @@ EditorScrollViewComponent = React.createClass
|
||||
overflowChangedWhilePaused: false
|
||||
|
||||
render: ->
|
||||
{editor, fontSize, fontFamily, lineHeight, showIndentGuide} = @props
|
||||
{scrollHeight, scrollWidth, renderedRowRange, pendingChanges, scrollingVertically} = @props
|
||||
{cursorBlinkPeriod, cursorBlinkResumeDelay, cursorsMoved, onInputFocused, onInputBlurred} = @props
|
||||
{editor, fontSize, fontFamily, lineHeight, lineHeightInPixels, showIndentGuide, invisibles, visible} = @props
|
||||
{renderedRowRange, pendingChanges, scrollTop, scrollLeft, scrollHeight, scrollWidth, scrollViewHeight, scrollingVertically, mouseWheelScreenRow} = @props
|
||||
{selectionChanged, selectionAdded, cursorBlinkPeriod, cursorBlinkResumeDelay, cursorsMoved, onInputFocused, onInputBlurred} = @props
|
||||
|
||||
if @isMounted()
|
||||
inputStyle = @getHiddenInputPosition()
|
||||
inputStyle.WebkitTransform = 'translateZ(0)'
|
||||
|
||||
contentStyle =
|
||||
height: scrollHeight
|
||||
minWidth: scrollWidth
|
||||
WebkitTransform: "translate3d(#{-editor.getScrollLeft()}px, #{-editor.getScrollTop()}px, 0)"
|
||||
|
||||
div className: 'scroll-view',
|
||||
div className: 'scroll-view', onMouseDown: @onMouseDown,
|
||||
InputComponent
|
||||
ref: 'input'
|
||||
className: 'hidden-input'
|
||||
@@ -38,14 +33,13 @@ EditorScrollViewComponent = React.createClass
|
||||
onFocus: onInputFocused
|
||||
onBlur: onInputBlurred
|
||||
|
||||
div className: 'scroll-view-content', style: contentStyle, onMouseDown: @onMouseDown,
|
||||
CursorsComponent({editor, cursorsMoved, cursorBlinkPeriod, cursorBlinkResumeDelay})
|
||||
LinesComponent {
|
||||
ref: 'lines', editor, fontSize, fontFamily, lineHeight, showIndentGuide,
|
||||
renderedRowRange, pendingChanges, scrollingVertically
|
||||
}
|
||||
div className: 'underlayer',
|
||||
SelectionsComponent({editor})
|
||||
CursorsComponent({editor, scrollTop, scrollLeft, cursorsMoved, selectionAdded, cursorBlinkPeriod, cursorBlinkResumeDelay})
|
||||
LinesComponent {
|
||||
ref: 'lines', editor, fontSize, fontFamily, lineHeight, lineHeightInPixels,
|
||||
showIndentGuide, renderedRowRange, pendingChanges, scrollTop, scrollLeft, scrollingVertically,
|
||||
selectionChanged, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles,
|
||||
visible, scrollViewHeight
|
||||
}
|
||||
|
||||
componentDidMount: ->
|
||||
@getDOMNode().addEventListener 'overflowchanged', @onOverflowChanged
|
||||
@@ -182,17 +176,21 @@ EditorScrollViewComponent = React.createClass
|
||||
measureHeightAndWidth: ->
|
||||
return unless @isMounted()
|
||||
|
||||
node = @getDOMNode()
|
||||
computedStyle = getComputedStyle(node)
|
||||
{editor} = @props
|
||||
node = @getDOMNode()
|
||||
editorNode = node.parentNode
|
||||
{position} = getComputedStyle(editorNode)
|
||||
{width, height} = editorNode.style
|
||||
|
||||
unless computedStyle.height is '0px'
|
||||
clientHeight = node.clientHeight
|
||||
if position is 'absolute' or height
|
||||
clientHeight = node.clientHeight
|
||||
editor.setHeight(clientHeight) if clientHeight > 0
|
||||
|
||||
unless computedStyle.width is '0px'
|
||||
if position is 'absolute' or width
|
||||
clientWidth = node.clientWidth
|
||||
editor.setWidth(clientWidth) if clientHeight > 0
|
||||
editor.setWidth(clientWidth) if clientWidth > 0
|
||||
|
||||
focus: ->
|
||||
@refs.input.focus()
|
||||
|
||||
lineNodeForScreenRow: (screenRow) -> @refs.lines.lineNodeForScreenRow(screenRow)
|
||||
|
||||
@@ -50,7 +50,7 @@ class EditorView extends View
|
||||
showLineNumbers: true
|
||||
autoIndent: true
|
||||
normalizeIndentOnPaste: true
|
||||
nonWordCharacters: "./\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
|
||||
nonWordCharacters: "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
|
||||
preferredLineLength: 80
|
||||
tabLength: 2
|
||||
softWrap: false
|
||||
@@ -154,8 +154,8 @@ class EditorView extends View
|
||||
'editor:move-to-previous-word': => @editor.moveCursorToPreviousWord()
|
||||
'editor:select-word': => @editor.selectWord()
|
||||
'editor:consolidate-selections': (event) => @consolidateSelections(event)
|
||||
'editor:backspace-to-beginning-of-word': => @editor.backspaceToBeginningOfWord()
|
||||
'editor:backspace-to-beginning-of-line': => @editor.backspaceToBeginningOfLine()
|
||||
'editor:delete-to-beginning-of-word': => @editor.deleteToBeginningOfWord()
|
||||
'editor:delete-to-beginning-of-line': => @editor.deleteToBeginningOfLine()
|
||||
'editor:delete-to-end-of-word': => @editor.deleteToEndOfWord()
|
||||
'editor:delete-line': => @editor.deleteLine()
|
||||
'editor:cut-to-end-of-line': => @editor.cutToEndOfLine()
|
||||
@@ -387,7 +387,7 @@ class EditorView extends View
|
||||
|
||||
screenPosition = @screenPositionFromMouseEvent(e)
|
||||
if clickCount == 1
|
||||
if e.metaKey
|
||||
if e.metaKey or (process.platform isnt 'darwin' and e.ctrlKey)
|
||||
@editor.addCursorAtScreenPosition(screenPosition)
|
||||
else if e.shiftKey
|
||||
@editor.selectToScreenPosition(screenPosition)
|
||||
@@ -460,6 +460,9 @@ class EditorView extends View
|
||||
@hiddenInput.val(lastInput)
|
||||
false
|
||||
|
||||
# Ignore paste event, on Linux is wrongly emitted when user presses ctrl-v.
|
||||
@on "paste", -> false
|
||||
|
||||
bringHiddenInputIntoView: ->
|
||||
@hiddenInput.css(top: @scrollTop(), left: @scrollLeft())
|
||||
|
||||
@@ -472,7 +475,7 @@ class EditorView extends View
|
||||
$(document).off 'mouseup', finalizeSelections
|
||||
|
||||
unless @editor.isDestroyed()
|
||||
@editor.mergeIntersectingSelections(isReversed: @editor.getLastSelection().isReversed())
|
||||
@editor.mergeIntersectingSelections(reversed: @editor.getLastSelection().isReversed())
|
||||
@editor.finalizeSelections()
|
||||
@syncCursorAnimations()
|
||||
|
||||
@@ -1487,7 +1490,7 @@ class EditorView extends View
|
||||
position = 0
|
||||
for token in tokens
|
||||
@updateScopeStack(line, scopeStack, token.scopes)
|
||||
hasIndentGuide = not mini and showIndentGuide and token.hasLeadingWhitespace or (token.hasTrailingWhitespace and lineIsWhitespaceOnly)
|
||||
hasIndentGuide = not mini and showIndentGuide and (token.hasLeadingWhitespace or (token.hasTrailingWhitespace and lineIsWhitespaceOnly))
|
||||
line.push(token.getValueAsHtml({invisibles, hasIndentGuide}))
|
||||
position += token.value.length
|
||||
|
||||
|
||||
+81
-34
@@ -109,8 +109,8 @@ TextMateScopeSelector = require('first-mate').ScopeSelector
|
||||
# - {::insertNewlineAbove}
|
||||
# - {::insertNewlineBelow}
|
||||
# - {::backspace}
|
||||
# - {::backspaceToBeginningOfWord}
|
||||
# - {::backspaceToBeginningOfLine}
|
||||
# - {::deleteToBeginningOfWord}
|
||||
# - {::deleteToBeginningOfLine}
|
||||
# - {::delete}
|
||||
# - {::deleteToEndOfWord}
|
||||
# - {::deleteLine}
|
||||
@@ -149,10 +149,10 @@ class Editor extends Model
|
||||
'autoDecreaseIndentForBufferRow', 'toggleLineCommentForBufferRow', 'toggleLineCommentsForBufferRows',
|
||||
toProperty: 'languageMode'
|
||||
|
||||
@delegatesProperties '$lineHeight', '$defaultCharWidth', '$height', '$width',
|
||||
@delegatesProperties '$lineHeightInPixels', '$defaultCharWidth', '$height', '$width',
|
||||
'$scrollTop', '$scrollLeft', 'manageScrollPosition', toProperty: 'displayBuffer'
|
||||
|
||||
constructor: ({@softTabs, initialLine, tabLength, softWrap, @displayBuffer, buffer, registerEditor, suppressCursorCreation}) ->
|
||||
constructor: ({@softTabs, initialLine, initialColumn, tabLength, softWrap, @displayBuffer, buffer, registerEditor, suppressCursorCreation}) ->
|
||||
super
|
||||
|
||||
@cursors = []
|
||||
@@ -160,7 +160,7 @@ class Editor extends Model
|
||||
|
||||
@displayBuffer ?= new DisplayBuffer({buffer, tabLength, softWrap})
|
||||
@buffer = @displayBuffer.buffer
|
||||
@softTabs = @buffer.usesSoftTabs() ? @softTabs ? atom.config.get('editor.softTabs') ? true
|
||||
@softTabs = @usesSoftTabs() ? @softTabs ? atom.config.get('editor.softTabs') ? true
|
||||
|
||||
for marker in @findMarkers(@getSelectionMarkerAttributes())
|
||||
marker.setAttributes(preserveFolds: true)
|
||||
@@ -170,11 +170,9 @@ class Editor extends Model
|
||||
@subscribeToDisplayBuffer()
|
||||
|
||||
if @getCursors().length is 0 and not suppressCursorCreation
|
||||
if initialLine
|
||||
position = [initialLine, 0]
|
||||
else
|
||||
position = [0, 0]
|
||||
@addCursorAtBufferPosition(position)
|
||||
initialLine = Math.max(parseInt(initialLine) or 0, 0)
|
||||
initialColumn = Math.max(parseInt(initialColumn) or 0, 0)
|
||||
@addCursorAtBufferPosition([initialLine, initialColumn])
|
||||
|
||||
@languageMode = new LanguageMode(this)
|
||||
|
||||
@@ -266,7 +264,7 @@ class Editor extends Model
|
||||
else
|
||||
'untitled'
|
||||
|
||||
# Controls visiblity based on the given {Boolean}.
|
||||
# Controls visibility based on the given {Boolean}.
|
||||
setVisible: (visible) -> @displayBuffer.setVisible(visible)
|
||||
|
||||
# Set the number of characters that can be displayed horizontally in the
|
||||
@@ -277,7 +275,7 @@ class Editor extends Model
|
||||
setEditorWidthInChars: (editorWidthInChars) ->
|
||||
@displayBuffer.setEditorWidthInChars(editorWidthInChars)
|
||||
|
||||
# Public: Sets the column at which columsn will soft wrap
|
||||
# Public: Sets the column at which column will soft wrap
|
||||
getSoftWrapColumn: -> @displayBuffer.getSoftWrapColumn()
|
||||
|
||||
# Public: Returns a {Boolean} indicating whether softTabs are enabled for this
|
||||
@@ -319,6 +317,19 @@ class Editor extends Model
|
||||
# Public: Set the on-screen length of tab characters.
|
||||
setTabLength: (tabLength) -> @displayBuffer.setTabLength(tabLength)
|
||||
|
||||
# Public: Determine if the buffer uses hard or soft tabs.
|
||||
#
|
||||
# Returns `true` if the first non-comment line with leading whitespace starts
|
||||
# with a space character. Returns `false` if it starts with a hard tab (`\t`).
|
||||
#
|
||||
# Returns a {Boolean},
|
||||
usesSoftTabs: ->
|
||||
for bufferRow in [0..@buffer.getLastRow()]
|
||||
continue if @displayBuffer.tokenizedBuffer.lineForScreenRow(bufferRow).isComment()
|
||||
if match = @buffer.lineForRow(bufferRow).match(/^\s/)
|
||||
return match[0][0] != '\t'
|
||||
undefined
|
||||
|
||||
# Public: Clip the given {Point} to a valid position in the buffer.
|
||||
#
|
||||
# If the given {Point} describes a position that is actually reachable by the
|
||||
@@ -466,7 +477,7 @@ class Editor extends Model
|
||||
# options - An options hash with an `includeNewline` key.
|
||||
#
|
||||
# Returns a {Range}.
|
||||
bufferRangeForBufferRow: (row, options) -> @buffer.rangeForRow(row, options)
|
||||
bufferRangeForBufferRow: (row, {includeNewline}={}) -> @buffer.rangeForRow(row, includeNewline)
|
||||
|
||||
# Public: Returns a {String} representing the contents of the line at the
|
||||
# given buffer row.
|
||||
@@ -565,8 +576,8 @@ class Editor extends Model
|
||||
|
||||
bufferRowForScreenRow: (row) -> @displayBuffer.bufferRowForScreenRow(row)
|
||||
|
||||
# Public: Get the syntactic scopes for the most the given position in buffer
|
||||
# coorditanates.
|
||||
# Public: Get the syntactic scopes for the given position in buffer
|
||||
# coordinates.
|
||||
#
|
||||
# For example, if called with a position inside the parameter list of an
|
||||
# anonymous CoffeeScript function, the method returns the following array:
|
||||
@@ -647,17 +658,27 @@ class Editor extends Model
|
||||
backspace: ->
|
||||
@mutateSelectedText (selection) -> selection.backspace()
|
||||
|
||||
# Deprecated: Use {::deleteToBeginningOfWord} instead.
|
||||
backspaceToBeginningOfWord: ->
|
||||
deprecate("Use Editor::deleteToBeginningOfWord() instead")
|
||||
@deleteToBeginningOfWord()
|
||||
|
||||
# Deprecated: Use {::deleteToBeginningOfLine} instead.
|
||||
backspaceToBeginningOfLine: ->
|
||||
deprecate("Use Editor::deleteToBeginningOfLine() instead")
|
||||
@deleteToBeginningOfLine()
|
||||
|
||||
# Public: For each selection, if the selection is empty, delete all characters
|
||||
# of the containing word that precede the cursor. Otherwise delete the
|
||||
# selected text.
|
||||
backspaceToBeginningOfWord: ->
|
||||
@mutateSelectedText (selection) -> selection.backspaceToBeginningOfWord()
|
||||
deleteToBeginningOfWord: ->
|
||||
@mutateSelectedText (selection) -> selection.deleteToBeginningOfWord()
|
||||
|
||||
# Public: For each selection, if the selection is empty, delete all characters
|
||||
# of the containing line that precede the cursor. Otherwise delete the
|
||||
# selected text.
|
||||
backspaceToBeginningOfLine: ->
|
||||
@mutateSelectedText (selection) -> selection.backspaceToBeginningOfLine()
|
||||
deleteToBeginningOfLine: ->
|
||||
@mutateSelectedText (selection) -> selection.deleteToBeginningOfLine()
|
||||
|
||||
# Public: For each selection, if the selection is empty, delete the character
|
||||
# preceding the cursor. Otherwise delete the selected text.
|
||||
@@ -727,13 +748,24 @@ class Editor extends Model
|
||||
# Public: For each selection, replace the selected text with the contents of
|
||||
# the clipboard.
|
||||
#
|
||||
# If the clipboard contains the same number of selections as the current
|
||||
# editor, each selection will be replaced with the content of the
|
||||
# corresponding clipboard selection text.
|
||||
#
|
||||
# options - See {Selection::insertText}.
|
||||
pasteText: (options={}) ->
|
||||
{text, metadata} = atom.clipboard.readWithMetadata()
|
||||
|
||||
containsNewlines = text.indexOf('\n') isnt -1
|
||||
|
||||
if atom.config.get('editor.normalizeIndentOnPaste') and metadata
|
||||
if metadata?.selections? and metadata.selections.length is @getSelections().length
|
||||
@mutateSelectedText (selection, index) ->
|
||||
text = metadata.selections[index]
|
||||
selection.insertText(text, options)
|
||||
|
||||
return
|
||||
|
||||
else if atom.config.get("editor.normalizeIndentOnPaste") and metadata?.indentBasis?
|
||||
if !@getCursor().hasPrecedingCharactersOnLine() or containsNewlines
|
||||
options.indentBasis ?= metadata.indentBasis
|
||||
|
||||
@@ -895,7 +927,7 @@ class Editor extends Model
|
||||
endRow = row
|
||||
|
||||
insertPosition = Point.fromObject([startRow - insertDelta])
|
||||
endPosition = Point.min([endRow + 1], @buffer.getEofPosition())
|
||||
endPosition = Point.min([endRow + 1], @buffer.getEndPosition())
|
||||
lines = @buffer.getTextInRange([[startRow], endPosition])
|
||||
if endPosition.row is lastRow and endPosition.column > 0 and not @buffer.lineEndingForRow(endPosition.row)
|
||||
lines = "#{lines}\n"
|
||||
@@ -954,7 +986,7 @@ class Editor extends Model
|
||||
lines = @buffer.getTextInRange([[startRow], endPosition])
|
||||
@buffer.deleteRows(startRow, endRow)
|
||||
|
||||
insertPosition = Point.min([startRow + insertDelta], @buffer.getEofPosition())
|
||||
insertPosition = Point.min([startRow + insertDelta], @buffer.getEndPosition())
|
||||
if insertPosition.row is @buffer.getLastRow() and insertPosition.column > 0
|
||||
lines = "\n#{lines}"
|
||||
|
||||
@@ -1009,7 +1041,7 @@ class Editor extends Model
|
||||
#
|
||||
# fn - A {Function} that will be called with each {Selection}.
|
||||
mutateSelectedText: (fn) ->
|
||||
@transact => fn(selection) for selection in @getSelections()
|
||||
@transact => fn(selection,index) for selection,index in @getSelections()
|
||||
|
||||
replaceSelectedText: (options={}, fn) ->
|
||||
{selectWordIfEmpty} = options
|
||||
@@ -1237,6 +1269,9 @@ class Editor extends Model
|
||||
# Returns: An {Array} of {Selection}s.
|
||||
getSelections: -> new Array(@selections...)
|
||||
|
||||
selectionsForScreenRows: (startRow, endRow) ->
|
||||
@getSelections().filter (selection) -> selection.intersectsScreenRowRange(startRow, endRow)
|
||||
|
||||
# Public: Get the most recent {Selection} or the selection at the given
|
||||
# index.
|
||||
#
|
||||
@@ -1271,7 +1306,7 @@ class Editor extends Model
|
||||
# Public: Determine if a given range in buffer coordinates intersects a
|
||||
# selection.
|
||||
#
|
||||
# bufferRange - A {Range} or range-comptatible {Array}.
|
||||
# bufferRange - A {Range} or range-compatible {Array}.
|
||||
#
|
||||
# Returns a {Boolean}.
|
||||
selectionIntersectsBufferRange: (bufferRange) ->
|
||||
@@ -1483,7 +1518,7 @@ class Editor extends Model
|
||||
selectToScreenPosition: (position) ->
|
||||
lastSelection = @getLastSelection()
|
||||
lastSelection.selectToScreenPosition(position)
|
||||
@mergeIntersectingSelections(isReversed: lastSelection.isReversed())
|
||||
@mergeIntersectingSelections(reversed: lastSelection.isReversed())
|
||||
|
||||
# Public: Move the cursor of each selection one character rightward while
|
||||
# preserving the selection's tail position.
|
||||
@@ -1545,28 +1580,28 @@ class Editor extends Model
|
||||
# cursor is already on the first character of the line, move it to the
|
||||
# beginning of the line.
|
||||
#
|
||||
# This method may merge selections that end up intesecting.
|
||||
# This method may merge selections that end up intersecting.
|
||||
selectToFirstCharacterOfLine: ->
|
||||
@expandSelectionsBackward (selection) => selection.selectToFirstCharacterOfLine()
|
||||
|
||||
# Public: Move the cursor of each selection to the end of its line while
|
||||
# preserving the selection's tail position.
|
||||
#
|
||||
# This method may merge selections that end up intesecting.
|
||||
# This method may merge selections that end up intersecting.
|
||||
selectToEndOfLine: ->
|
||||
@expandSelectionsForward (selection) => selection.selectToEndOfLine()
|
||||
|
||||
# Public: For each selection, move its cursor to the preceding word boundary
|
||||
# while maintaining the selection's tail position.
|
||||
#
|
||||
# This method may merge selections that end up intesecting.
|
||||
# This method may merge selections that end up intersecting.
|
||||
selectToPreviousWordBoundary: ->
|
||||
@expandSelectionsBackward (selection) => selection.selectToPreviousWordBoundary()
|
||||
|
||||
# Public: For each selection, move its cursor to the next word boundary while
|
||||
# maintaining the selection's tail position.
|
||||
#
|
||||
# This method may merge selections that end up intesecting.
|
||||
# This method may merge selections that end up intersecting.
|
||||
selectToNextWordBoundary: ->
|
||||
@expandSelectionsForward (selection) => selection.selectToNextWordBoundary()
|
||||
|
||||
@@ -1576,7 +1611,7 @@ class Editor extends Model
|
||||
selectLine: ->
|
||||
@expandSelectionsForward (selection) => selection.selectLine()
|
||||
|
||||
# Public: Add a similarly-shaped selection to the next elibible line below
|
||||
# Public: Add a similarly-shaped selection to the next eligible line below
|
||||
# each selection.
|
||||
#
|
||||
# Operates on all selections. If the selection is empty, adds an empty
|
||||
@@ -1587,7 +1622,7 @@ class Editor extends Model
|
||||
addSelectionBelow: ->
|
||||
@expandSelectionsForward (selection) => selection.addSelectionBelow()
|
||||
|
||||
# Public: Add a similarly-shaped selection to the next elibible line above
|
||||
# Public: Add a similarly-shaped selection to the next eligible line above
|
||||
# each selection.
|
||||
#
|
||||
# Operates on all selections. If the selection is empty, adds an empty
|
||||
@@ -1710,7 +1745,7 @@ class Editor extends Model
|
||||
# Calls the given function with each selection, then merges selections in the
|
||||
# reversed orientation
|
||||
expandSelectionsBackward: (fn) ->
|
||||
@mergeIntersectingSelections isReversed: true, =>
|
||||
@mergeIntersectingSelections reversed: true, =>
|
||||
fn(selection) for selection in @getSelections()
|
||||
|
||||
finalizeSelections: ->
|
||||
@@ -1824,8 +1859,8 @@ class Editor extends Model
|
||||
getHorizontalScrollMargin: -> @displayBuffer.getHorizontalScrollMargin()
|
||||
setHorizontalScrollMargin: (horizontalScrollMargin) -> @displayBuffer.setHorizontalScrollMargin(horizontalScrollMargin)
|
||||
|
||||
getLineHeight: -> @displayBuffer.getLineHeight()
|
||||
setLineHeight: (lineHeight) -> @displayBuffer.setLineHeight(lineHeight)
|
||||
getLineHeightInPixels: -> @displayBuffer.getLineHeightInPixels()
|
||||
setLineHeightInPixels: (lineHeightInPixels) -> @displayBuffer.setLineHeightInPixels(lineHeightInPixels)
|
||||
|
||||
getScopedCharWidth: (scopeNames, char) -> @displayBuffer.getScopedCharWidth(scopeNames, char)
|
||||
setScopedCharWidth: (scopeNames, char, width) -> @displayBuffer.setScopedCharWidth(scopeNames, char, width)
|
||||
@@ -1840,6 +1875,8 @@ class Editor extends Model
|
||||
setHeight: (height) -> @displayBuffer.setHeight(height)
|
||||
getHeight: -> @displayBuffer.getHeight()
|
||||
|
||||
getClientHeight: -> @displayBuffer.getClientHeight()
|
||||
|
||||
setWidth: (width) -> @displayBuffer.setWidth(width)
|
||||
getWidth: -> @displayBuffer.getWidth()
|
||||
|
||||
@@ -1878,6 +1915,16 @@ class Editor extends Model
|
||||
|
||||
scrollToBufferPosition: (bufferPosition) -> @displayBuffer.scrollToBufferPosition(bufferPosition)
|
||||
|
||||
horizontallyScrollable: -> @displayBuffer.horizontallyScrollable()
|
||||
|
||||
verticallyScrollable: -> @displayBuffer.verticallyScrollable()
|
||||
|
||||
getHorizontalScrollbarHeight: -> @displayBuffer.getHorizontalScrollbarHeight()
|
||||
setHorizontalScrollbarHeight: (height) -> @displayBuffer.setHorizontalScrollbarHeight(height)
|
||||
|
||||
getVerticalScrollbarWidth: -> @displayBuffer.getVerticalScrollbarWidth()
|
||||
setVerticalScrollbarWidth: (width) -> @displayBuffer.setVerticalScrollbarWidth(width)
|
||||
|
||||
# Deprecated: Call {::joinLines} instead.
|
||||
joinLine: ->
|
||||
deprecate("Use Editor::joinLines() instead")
|
||||
|
||||
+7
-4
@@ -13,7 +13,7 @@ Task = require './task'
|
||||
# `atom.project` global and calling `getRepo()`. Note that this will only be
|
||||
# available when the project is backed by a Git repository.
|
||||
#
|
||||
# This class handles submodules automically by taking a `path` argument to many
|
||||
# This class handles submodules automatically by taking a `path` argument to many
|
||||
# of the methods. This `path` argument will determine which underlying
|
||||
# repository is used.
|
||||
#
|
||||
@@ -135,6 +135,7 @@ class Git
|
||||
relativePath = @relativize(path)
|
||||
currentPathStatus = @statuses[relativePath] ? 0
|
||||
pathStatus = repo.getStatus(repo.relativize(path)) ? 0
|
||||
pathStatus = 0 if repo.isStatusIgnored(pathStatus)
|
||||
if pathStatus > 0
|
||||
@statuses[relativePath] = pathStatus
|
||||
else
|
||||
@@ -344,8 +345,10 @@ class Git
|
||||
# Refreshes the current git status in an outside process and asynchronously
|
||||
# updates the relevant properties.
|
||||
refreshStatus: ->
|
||||
handlerPath = require.resolve('./repository-status-handler')
|
||||
@statusTask = Task.once handlerPath, @getPath(), ({statuses, upstream, branch, submodules}) =>
|
||||
@handlerPath ?= require.resolve('./repository-status-handler')
|
||||
|
||||
@statusTask?.terminate()
|
||||
@statusTask = Task.once @handlerPath, @getPath(), ({statuses, upstream, branch, submodules}) =>
|
||||
statusesUnchanged = _.isEqual(statuses, @statuses) and
|
||||
_.isEqual(upstream, @upstream) and
|
||||
_.isEqual(branch, @branch) and
|
||||
@@ -357,6 +360,6 @@ class Git
|
||||
@submodules = submodules
|
||||
|
||||
for submodulePath, submoduleRepo of @getRepo().submodules
|
||||
submoduleRepo.upstream = submodules[submodulePath].upstream
|
||||
submoduleRepo.upstream = submodules[submodulePath]?.upstream ? {ahead: 0, behind: 0}
|
||||
|
||||
@emit 'statuses-changed' unless statusesUnchanged
|
||||
|
||||
@@ -1,79 +1,124 @@
|
||||
React = require 'react'
|
||||
{div} = require 'reactionary'
|
||||
{isEqual, isEqualForProperties, multiplyString} = require 'underscore-plus'
|
||||
React = require 'react-atom-fork'
|
||||
{div} = require 'reactionary-atom-fork'
|
||||
{isEqual, isEqualForProperties, multiplyString, toArray} = require 'underscore-plus'
|
||||
SubscriberMixin = require './subscriber-mixin'
|
||||
|
||||
WrapperDiv = document.createElement('div')
|
||||
Nbsp = String.fromCharCode(160)
|
||||
|
||||
module.exports =
|
||||
GutterComponent = React.createClass
|
||||
displayName: 'GutterComponent'
|
||||
mixins: [SubscriberMixin]
|
||||
|
||||
lastMeasuredWidth: null
|
||||
wrapCountsByScreenRow: null
|
||||
|
||||
render: ->
|
||||
{scrollHeight, scrollTop} = @props
|
||||
|
||||
style =
|
||||
height: scrollHeight
|
||||
WebkitTransform: "translate3d(0px, #{-scrollTop}px, 0px)"
|
||||
|
||||
div className: 'gutter',
|
||||
@renderLineNumbers() if @isMounted()
|
||||
div className: 'line-numbers', ref: 'lineNumbers', style: style,
|
||||
@renderDummyLineNode()
|
||||
@renderLineNumbers() if @isMounted()
|
||||
|
||||
renderDummyLineNode: ->
|
||||
{editor} = @props
|
||||
@renderLineNumber('dummy', null, editor.getLastBufferRow(), false)
|
||||
|
||||
renderLineNumbers: ->
|
||||
{editor, renderedRowRange, scrollTop, scrollHeight} = @props
|
||||
{editor, renderedRowRange, maxLineNumberDigits, lineHeightInPixels, mouseWheelScreenRow} = @props
|
||||
[startRow, endRow] = renderedRowRange
|
||||
charWidth = editor.getDefaultCharWidth()
|
||||
lineHeight = editor.getLineHeight()
|
||||
maxDigits = editor.getLastBufferRow().toString().length
|
||||
style =
|
||||
width: charWidth * (maxDigits + 1.5)
|
||||
height: scrollHeight
|
||||
WebkitTransform: "translate3d(0, #{-scrollTop}px, 0)"
|
||||
|
||||
lineNumbers = []
|
||||
tokenizedLines = editor.linesForScreenRows(startRow, endRow - 1)
|
||||
tokenizedLines.push({id: 0}) if tokenizedLines.length is 0
|
||||
for bufferRow, i in editor.bufferRowsForScreenRows(startRow, endRow - 1)
|
||||
if bufferRow is lastBufferRow
|
||||
lineNumber = '•'
|
||||
lastBufferRow = null
|
||||
wrapCount = 0
|
||||
|
||||
wrapCountsByScreenRow = {}
|
||||
lineNumberComponents =
|
||||
for bufferRow, i in editor.bufferRowsForScreenRows(startRow, endRow - 1)
|
||||
if bufferRow is lastBufferRow
|
||||
softWrapped = true
|
||||
key = "#{bufferRow}-#{++wrapCount}"
|
||||
else
|
||||
softWrapped = false
|
||||
key = bufferRow.toString()
|
||||
lastBufferRow = bufferRow
|
||||
wrapCount = 0
|
||||
|
||||
screenRow = startRow + i
|
||||
wrapCountsByScreenRow[screenRow] = wrapCount
|
||||
@renderLineNumber(key, screenRow, bufferRow, softWrapped)
|
||||
|
||||
# Preserve the mouse wheel target's screen row if it exists
|
||||
if mouseWheelScreenRow? and not (startRow <= mouseWheelScreenRow < endRow)
|
||||
screenRow = mouseWheelScreenRow
|
||||
bufferRow = editor.bufferRowForScreenRow(screenRow)
|
||||
wrapCount = @wrapCountsByScreenRow[screenRow]
|
||||
wrapCountsByScreenRow[screenRow] = wrapCount
|
||||
if softWrapped = (wrapCount > 0)
|
||||
key = "#{bufferRow}-#{wrapCount}"
|
||||
else
|
||||
lastBufferRow = bufferRow
|
||||
lineNumber = (bufferRow + 1).toString()
|
||||
key = bufferRow.toString()
|
||||
|
||||
key = tokenizedLines[i].id
|
||||
screenRow = startRow + i
|
||||
lineNumbers.push(LineNumberComponent({key, lineNumber, maxDigits, bufferRow, screenRow, lineHeight}))
|
||||
lastBufferRow = bufferRow
|
||||
lineNumberComponents.push(@renderLineNumber(key, screenRow, bufferRow, false, endRow))
|
||||
|
||||
div className: 'line-numbers', style: style,
|
||||
lineNumbers
|
||||
@wrapCountsByScreenRow = wrapCountsByScreenRow
|
||||
lineNumberComponents
|
||||
|
||||
renderLineNumber: (key, screenRow, bufferRow, softWrapped, screenRowOverride) ->
|
||||
{lineHeightInPixels, maxLineNumberDigits} = @props
|
||||
|
||||
if screenRow?
|
||||
style =
|
||||
position: 'absolute'
|
||||
top: (screenRowOverride ? screenRow) * lineHeightInPixels
|
||||
else
|
||||
style =
|
||||
visibility: 'hidden'
|
||||
|
||||
if softWrapped
|
||||
lineNumber = "•"
|
||||
else
|
||||
lineNumber = (bufferRow + 1).toString()
|
||||
|
||||
if lineNumber.length < maxLineNumberDigits
|
||||
padding = multiplyString(Nbsp, maxLineNumberDigits - lineNumber.length)
|
||||
lineNumber = padding + lineNumber
|
||||
|
||||
div key: key, className: 'line-number', 'data-screen-row': screenRow, style: style,
|
||||
lineNumber,
|
||||
div className: 'icon-right'
|
||||
|
||||
# Only update the gutter if the visible row range has changed or if a
|
||||
# non-zero-delta change to the screen lines has occurred within the current
|
||||
# visible row range.
|
||||
shouldComponentUpdate: (newProps) ->
|
||||
return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'scrollTop', 'lineHeight')
|
||||
return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'scrollTop', 'lineHeightInPixels', 'fontSize', 'maxLineNumberDigits', 'mouseWheelScreenRow')
|
||||
|
||||
{renderedRowRange, pendingChanges} = newProps
|
||||
for change in pendingChanges when change.screenDelta > 0 or change.bufferDelta > 0
|
||||
for change in pendingChanges when Math.abs(change.screenDelta) > 0 or Math.abs(change.bufferDelta) > 0
|
||||
return true unless change.end <= renderedRowRange.start or renderedRowRange.end <= change.start
|
||||
|
||||
false
|
||||
|
||||
LineNumberComponent = React.createClass
|
||||
displayName: 'LineNumberComponent'
|
||||
componentDidUpdate: (oldProps) ->
|
||||
@measureWidth() unless @lastMeasuredWidth? and isEqualForProperties(oldProps, @props, 'maxLineNumberDigits', 'fontSize', 'fontFamily')
|
||||
|
||||
render: ->
|
||||
{bufferRow, screenRow, lineHeight} = @props
|
||||
div
|
||||
className: "line-number line-number-#{bufferRow}"
|
||||
style: {top: screenRow * lineHeight}
|
||||
'data-buffer-row': bufferRow
|
||||
'data-screen-row': screenRow
|
||||
dangerouslySetInnerHTML: {__html: @buildInnerHTML()}
|
||||
measureWidth: ->
|
||||
lineNumberNode = @refs.lineNumbers.getDOMNode().firstChild
|
||||
width = lineNumberNode.offsetWidth
|
||||
if width isnt @lastMeasuredWidth
|
||||
@props.onWidthChanged(@lastMeasuredWidth = width)
|
||||
|
||||
buildInnerHTML: ->
|
||||
{lineNumber, maxDigits} = @props
|
||||
if lineNumber.length < maxDigits
|
||||
padding = multiplyString(' ', maxDigits - lineNumber.length)
|
||||
padding + lineNumber + @iconDivHTML
|
||||
else
|
||||
lineNumber + @iconDivHTML
|
||||
lineNumberNodeForScreenRow: (screenRow) ->
|
||||
{renderedRowRange} = @props
|
||||
[startRow, endRow] = renderedRowRange
|
||||
|
||||
iconDivHTML: '<div class="icon-right"></div>'
|
||||
unless startRow <= screenRow < endRow
|
||||
throw new Error("Requested screenRow #{screenRow} is not currently rendered")
|
||||
|
||||
shouldComponentUpdate: (newProps) ->
|
||||
not isEqualForProperties(newProps, @props, 'lineHeight', 'screenRow')
|
||||
@refs.lineNumbers.getDOMNode().children[screenRow - startRow + 1]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
punycode = require 'punycode'
|
||||
{last, isEqual} = require 'underscore-plus'
|
||||
React = require 'react'
|
||||
{input} = require 'reactionary'
|
||||
React = require 'react-atom-fork'
|
||||
{input} = require 'reactionary-atom-fork'
|
||||
|
||||
module.exports =
|
||||
InputComponent = React.createClass
|
||||
@@ -10,12 +10,13 @@ InputComponent = React.createClass
|
||||
render: ->
|
||||
{className, style, onFocus, onBlur} = @props
|
||||
|
||||
input {className, style, onFocus, onBlur}
|
||||
input {className, style, onFocus, onBlur, 'data-react-skip-selection-restoration': true}
|
||||
|
||||
getInitialState: ->
|
||||
{lastChar: ''}
|
||||
|
||||
componentDidMount: ->
|
||||
@getDOMNode().addEventListener 'paste', @onPaste
|
||||
@getDOMNode().addEventListener 'input', @onInput
|
||||
@getDOMNode().addEventListener 'compositionupdate', @onCompositionUpdate
|
||||
|
||||
@@ -32,6 +33,9 @@ InputComponent = React.createClass
|
||||
shouldComponentUpdate: (newProps) ->
|
||||
not isEqual(newProps.style, @props.style)
|
||||
|
||||
onPaste: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
onInput: (e) ->
|
||||
e.stopPropagation()
|
||||
valueCharCodes = punycode.ucs2.decode(@getDOMNode().value)
|
||||
|
||||
@@ -60,7 +60,8 @@ class LanguageMode
|
||||
buffer.setTextInRange([[end, endColumn], [end, endLength]], "")
|
||||
else
|
||||
buffer.transact ->
|
||||
buffer.insert([start, 0], commentStartString)
|
||||
indentLength = buffer.lineForRow(start).match(/^\s*/)?[0].length ? 0
|
||||
buffer.insert([start, indentLength], commentStartString)
|
||||
buffer.insert([end, buffer.lineLengthForRow(end)], commentEndString)
|
||||
else
|
||||
if shouldUncomment and start isnt end
|
||||
|
||||
+147
-56
@@ -1,34 +1,142 @@
|
||||
React = require 'react'
|
||||
{div, span} = require 'reactionary'
|
||||
{debounce, isEqual, isEqualForProperties, multiplyString} = require 'underscore-plus'
|
||||
React = require 'react-atom-fork'
|
||||
{div, span} = require 'reactionary-atom-fork'
|
||||
{debounce, isEqual, isEqualForProperties, multiplyString, toArray, clone} = require 'underscore-plus'
|
||||
{$$} = require 'space-pen'
|
||||
|
||||
SelectionsComponent = require './selections-component'
|
||||
|
||||
DummyLineNode = $$(-> @div className: 'line', style: 'position: absolute; visibility: hidden;', => @span 'x')[0]
|
||||
AcceptFilter = {acceptNode: -> NodeFilter.FILTER_ACCEPT}
|
||||
WrapperDiv = document.createElement('div')
|
||||
|
||||
module.exports =
|
||||
LinesComponent = React.createClass
|
||||
displayName: 'LinesComponent'
|
||||
|
||||
measureWhenShown: false
|
||||
|
||||
render: ->
|
||||
{editor, scrollTop, scrollLeft, scrollHeight, scrollWidth, lineHeightInPixels, scrollViewHeight} = @props
|
||||
|
||||
if @isMounted()
|
||||
{editor, renderedRowRange, lineHeight, showIndentGuide} = @props
|
||||
[startRow, endRow] = renderedRowRange
|
||||
style =
|
||||
height: Math.max(scrollHeight, scrollViewHeight)
|
||||
width: scrollWidth
|
||||
WebkitTransform: "translate3d(#{-scrollLeft}px, #{-scrollTop}px, 0px)"
|
||||
|
||||
lines =
|
||||
for tokenizedLine, i in editor.linesForScreenRows(startRow, endRow - 1)
|
||||
LineComponent({key: tokenizedLine.id, tokenizedLine, showIndentGuide, lineHeight, screenRow: startRow + i})
|
||||
div {className: 'lines', style},
|
||||
@renderLines() if @isMounted()
|
||||
SelectionsComponent({key: 'selections', editor, lineHeightInPixels})
|
||||
|
||||
div {className: 'lines'}, lines
|
||||
renderLines: ->
|
||||
{editor, renderedRowRange, lineHeightInPixels, showIndentGuide, mini, invisibles, mouseWheelScreenRow} = @props
|
||||
[startRow, endRow] = renderedRowRange
|
||||
|
||||
lineComponents =
|
||||
for line, i in editor.linesForScreenRows(startRow, endRow - 1)
|
||||
@renderLine(line, startRow + i)
|
||||
|
||||
if mouseWheelScreenRow? and not (startRow <= mouseWheelScreenRow < endRow)
|
||||
lineComponents.push(@renderLine(editor.lineForScreenRow(mouseWheelScreenRow), mouseWheelScreenRow, endRow))
|
||||
|
||||
lineComponents
|
||||
|
||||
renderLine: (line, screenRow, screenRowOverride) ->
|
||||
{lineHeightInPixels} = @props
|
||||
|
||||
style =
|
||||
position: "absolute"
|
||||
top: (screenRowOverride ? screenRow) * lineHeightInPixels
|
||||
|
||||
@lineInnerHTMLByLineId[line.id] ?= @buildLineInnerHTML(line)
|
||||
innerHTML = @lineInnerHTMLByLineId[line.id]
|
||||
|
||||
div key: line.id, className: "line", style: style, 'data-screen-row': screenRow, dangerouslySetInnerHTML: {__html: innerHTML}
|
||||
|
||||
buildLineInnerHTML: (line) ->
|
||||
if line.text is ""
|
||||
@buildEmptyLineInnerHTML(line)
|
||||
else
|
||||
@buildNonEmptyLineInnerHTML(line)
|
||||
|
||||
buildEmptyLineInnerHTML: (line) ->
|
||||
{showIndentGuide} = @props
|
||||
{indentLevel, tabLength} = line
|
||||
|
||||
if showIndentGuide and indentLevel > 0
|
||||
indentSpan = "<span class='indent-guide'>#{multiplyString(' ', tabLength)}</span>"
|
||||
multiplyString(indentSpan, indentLevel + 1)
|
||||
else
|
||||
" "
|
||||
|
||||
buildNonEmptyLineInnerHTML: (line) ->
|
||||
{invisibles, mini, showIndentGuide, invisibles} = @props
|
||||
{tokens, text} = line
|
||||
innerHTML = ""
|
||||
|
||||
scopeStack = []
|
||||
firstTrailingWhitespacePosition = text.search(/\s*$/)
|
||||
lineIsWhitespaceOnly = firstTrailingWhitespacePosition is 0
|
||||
for token in tokens
|
||||
innerHTML += @updateScopeStack(scopeStack, token.scopes)
|
||||
hasIndentGuide = not mini and showIndentGuide and token.hasLeadingWhitespace or (token.hasTrailingWhitespace and lineIsWhitespaceOnly)
|
||||
innerHTML += token.getValueAsHtml({invisibles, hasIndentGuide})
|
||||
|
||||
innerHTML += @popScope(scopeStack) while scopeStack.length > 0
|
||||
innerHTML += @buildEndOfLineHTML(line)
|
||||
innerHTML
|
||||
|
||||
buildEndOfLineHTML: (line) ->
|
||||
{invisibles, mini} = @props
|
||||
return '' if mini or line.isSoftWrapped()
|
||||
|
||||
html = ''
|
||||
if invisibles.cr? and line.lineEnding is '\r\n'
|
||||
html += "<span class='invisible-character'>#{invisibles.cr}</span>"
|
||||
if invisibles.eol?
|
||||
html += "<span class='invisible-character'>#{invisibles.eol}</span>"
|
||||
|
||||
html
|
||||
|
||||
updateScopeStack: (scopeStack, desiredScopes) ->
|
||||
html = ""
|
||||
|
||||
# Find a common prefix
|
||||
for scope, i in desiredScopes
|
||||
break unless scopeStack[i]?.scope is desiredScopes[i]
|
||||
|
||||
# Pop scopes until we're at the common prefx
|
||||
until scopeStack.length is i
|
||||
html += @popScope(scopeStack)
|
||||
|
||||
# Push onto common prefix until scopeStack equals desiredScopes
|
||||
for j in [i...desiredScopes.length]
|
||||
html += @pushScope(scopeStack, desiredScopes[j])
|
||||
|
||||
html
|
||||
|
||||
popScope: (scopeStack) ->
|
||||
scopeStack.pop()
|
||||
"</span>"
|
||||
|
||||
pushScope: (scopeStack, scope) ->
|
||||
scopeStack.push(scope)
|
||||
"<span class=\"#{scope.replace(/\.+/g, ' ')}\">"
|
||||
|
||||
componentWillMount: ->
|
||||
@measuredLines = new WeakSet
|
||||
@lineInnerHTMLByLineId = {}
|
||||
|
||||
componentDidMount: ->
|
||||
@measureLineHeightAndCharWidth()
|
||||
@measureLineHeightInPixelsAndCharWidth()
|
||||
|
||||
shouldComponentUpdate: (newProps) ->
|
||||
return true unless isEqualForProperties(newProps, @props, 'renderedRowRange', 'fontSize', 'fontFamily', 'lineHeight', 'showIndentGuide')
|
||||
return true if newProps.selectionChanged
|
||||
return true unless isEqualForProperties(newProps, @props,
|
||||
'renderedRowRange', 'fontSize', 'fontFamily', 'lineHeight', 'lineHeightInPixels',
|
||||
'scrollTop', 'scrollLeft', 'showIndentGuide', 'scrollingVertically', 'invisibles',
|
||||
'visible', 'scrollViewHeight', 'mouseWheelScreenRow'
|
||||
)
|
||||
|
||||
{renderedRowRange, pendingChanges} = newProps
|
||||
for change in pendingChanges
|
||||
@@ -36,20 +144,44 @@ LinesComponent = React.createClass
|
||||
|
||||
false
|
||||
|
||||
componentWillUpdate: (newProps) ->
|
||||
unless isEqualForProperties(newProps, @props, 'invisibles', 'showIndentGuide')
|
||||
@lineInnerHTMLByLineId = {}
|
||||
|
||||
componentDidUpdate: (prevProps) ->
|
||||
@measureLineHeightAndCharWidth() unless isEqualForProperties(prevProps, @props, 'fontSize', 'fontFamily', 'lineHeight')
|
||||
@measureLineHeightInPixelsAndCharWidthIfNeeded(prevProps)
|
||||
@clearScopedCharWidths() unless isEqualForProperties(prevProps, @props, 'fontSize', 'fontFamily')
|
||||
@measureCharactersInNewLines() unless @props.scrollingVertically
|
||||
|
||||
measureLineHeightAndCharWidth: ->
|
||||
lineNodeForScreenRow: (screenRow) ->
|
||||
{renderedRowRange} = @props
|
||||
[startRow, endRow] = renderedRowRange
|
||||
|
||||
unless startRow <= screenRow < endRow
|
||||
throw new Error("Requested screenRow #{screenRow} is not currently rendered")
|
||||
|
||||
@getDOMNode().children[screenRow - startRow]
|
||||
|
||||
measureLineHeightInPixelsAndCharWidthIfNeeded: (prevProps) ->
|
||||
{visible} = @props
|
||||
|
||||
unless isEqualForProperties(prevProps, @props, 'fontSize', 'fontFamily', 'lineHeight')
|
||||
if visible
|
||||
@measureLineHeightInPixelsAndCharWidth()
|
||||
else
|
||||
@measureWhenShown = true
|
||||
@measureLineHeightInPixelsAndCharWidth() if visible and not prevProps.visible and @measureWhenShown
|
||||
|
||||
measureLineHeightInPixelsAndCharWidth: ->
|
||||
@measureWhenShown = false
|
||||
node = @getDOMNode()
|
||||
node.appendChild(DummyLineNode)
|
||||
lineHeight = DummyLineNode.getBoundingClientRect().height
|
||||
lineHeightInPixels = DummyLineNode.getBoundingClientRect().height
|
||||
charWidth = DummyLineNode.firstChild.getBoundingClientRect().width
|
||||
node.removeChild(DummyLineNode)
|
||||
|
||||
{editor} = @props
|
||||
editor.setLineHeight(lineHeight)
|
||||
editor.setLineHeightInPixels(lineHeightInPixels)
|
||||
editor.setDefaultCharWidth(charWidth)
|
||||
|
||||
measureCharactersInNewLines: ->
|
||||
@@ -97,44 +229,3 @@ LinesComponent = React.createClass
|
||||
clearScopedCharWidths: ->
|
||||
@measuredLines.clear()
|
||||
@props.editor.clearScopedCharWidths()
|
||||
|
||||
|
||||
LineComponent = React.createClass
|
||||
displayName: 'LineComponent'
|
||||
|
||||
render: ->
|
||||
{screenRow, lineHeight} = @props
|
||||
|
||||
style =
|
||||
top: screenRow * lineHeight
|
||||
position: 'absolute'
|
||||
|
||||
div className: 'line', style: style, 'data-screen-row': screenRow, dangerouslySetInnerHTML: {__html: @buildInnerHTML()}
|
||||
|
||||
buildInnerHTML: ->
|
||||
if @props.tokenizedLine.text.length is 0
|
||||
@buildEmptyLineHTML()
|
||||
else
|
||||
@buildScopeTreeHTML(@props.tokenizedLine.getScopeTree())
|
||||
|
||||
buildEmptyLineHTML: ->
|
||||
{showIndentGuide, tokenizedLine} = @props
|
||||
{indentLevel, tabLength} = tokenizedLine
|
||||
|
||||
if showIndentGuide and indentLevel > 0
|
||||
indentSpan = "<span class='indent-guide'>#{multiplyString(' ', tabLength)}</span>"
|
||||
multiplyString(indentSpan, indentLevel + 1)
|
||||
else
|
||||
" "
|
||||
|
||||
buildScopeTreeHTML: (scopeTree) ->
|
||||
if scopeTree.children?
|
||||
html = "<span class='#{scopeTree.scope.replace(/\./g, ' ')}'>"
|
||||
html += @buildScopeTreeHTML(child) for child in scopeTree.children
|
||||
html += "</span>"
|
||||
html
|
||||
else
|
||||
"<span>#{scopeTree.getValueAsHtml({hasIndentGuide: @props.showIndentGuide})}</span>"
|
||||
|
||||
shouldComponentUpdate: (newProps) ->
|
||||
not isEqualForProperties(newProps, @props, 'showIndentGuide', 'lineHeight', 'screenRow')
|
||||
|
||||
@@ -58,7 +58,7 @@ class MenuManager
|
||||
testBody = document.createElement('body')
|
||||
testBody.classList.add(@classesForElement(document.body)...)
|
||||
|
||||
testWorkspace = document.createElement('body')
|
||||
testWorkspace = document.createElement('div')
|
||||
workspaceClasses = @classesForElement(document.body.querySelector('.workspace'))
|
||||
workspaceClasses = ['workspace'] if workspaceClasses.length is 0
|
||||
testWorkspace.classList.add(workspaceClasses...)
|
||||
@@ -69,7 +69,12 @@ class MenuManager
|
||||
@testEditor.classList.add('editor')
|
||||
testWorkspace.appendChild(@testEditor)
|
||||
|
||||
@testEditor.webkitMatchesSelector(selector)
|
||||
element = @testEditor
|
||||
while element
|
||||
return true if element.webkitMatchesSelector(selector)
|
||||
element = element.parentElement
|
||||
|
||||
false
|
||||
|
||||
# Public: Refreshes the currently visible menu.
|
||||
update: ->
|
||||
@@ -112,7 +117,7 @@ class MenuManager
|
||||
|
||||
sendToBrowserProcess: (template, keystrokesByCommand) ->
|
||||
keystrokesByCommand = @filterMultipleKeystroke(keystrokesByCommand)
|
||||
ipc.sendChannel 'update-application-menu', template, keystrokesByCommand
|
||||
ipc.send 'update-application-menu', template, keystrokesByCommand
|
||||
|
||||
normalizeLabel: (label) ->
|
||||
return undefined unless label?
|
||||
|
||||
@@ -27,10 +27,12 @@ module.exports =
|
||||
class PackageManager
|
||||
Emitter.includeInto(this)
|
||||
|
||||
constructor: ({configDirPath, devMode, @resourcePath}) ->
|
||||
@packageDirPaths = [path.join(configDirPath, "packages")]
|
||||
if devMode
|
||||
@packageDirPaths.unshift(path.join(configDirPath, "dev", "packages"))
|
||||
constructor: ({configDirPath, devMode, safeMode, @resourcePath}) ->
|
||||
@packageDirPaths = []
|
||||
unless safeMode
|
||||
if devMode
|
||||
@packageDirPaths.push(path.join(configDirPath, "dev", "packages"))
|
||||
@packageDirPaths.push(path.join(configDirPath, "packages"))
|
||||
|
||||
@loadedPackages = {}
|
||||
@activePackages = {}
|
||||
@@ -41,7 +43,9 @@ class PackageManager
|
||||
|
||||
# Public: Get the path to the apm command
|
||||
getApmPath: ->
|
||||
@apmPath ?= path.resolve(__dirname, '..', 'apm', 'node_modules', 'atom-package-manager', 'bin', 'apm')
|
||||
commandName = 'apm'
|
||||
commandName += '.cmd' if process.platform is 'win32'
|
||||
@apmPath ?= path.resolve(__dirname, '..', 'apm', 'node_modules', 'atom-package-manager', 'bin', commandName)
|
||||
|
||||
# Public: Get the paths being used to look for packages.
|
||||
#
|
||||
|
||||
@@ -10,7 +10,7 @@ Pane = require './pane'
|
||||
# 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.
|
||||
# building a package that deals with switching between panes or items.
|
||||
module.exports =
|
||||
class PaneView extends View
|
||||
Delegator.includeInto(this)
|
||||
@@ -103,7 +103,7 @@ class PaneView extends View
|
||||
|
||||
# Deprecated: Use ::activateNextItem
|
||||
showNextItem: ->
|
||||
deprecate("Use PaneView::destroyItem instead")
|
||||
deprecate("Use PaneView::activateNextItem instead")
|
||||
@activateNextItem()
|
||||
|
||||
# Deprecated: Use ::activatePreviousItem
|
||||
@@ -149,7 +149,7 @@ class PaneView extends View
|
||||
hasFocus = @hasFocus()
|
||||
item.on? 'title-changed', @activeItemTitleChanged
|
||||
view = @viewForItem(item)
|
||||
@itemViews.children().not(view).hide()
|
||||
otherView.hide() for otherView in @itemViews.children().not(view).views()
|
||||
@itemViews.append(view) unless view.parent().is(@itemViews)
|
||||
view.show() if @attached
|
||||
view.focus() if hasFocus
|
||||
|
||||
+13
-5
@@ -339,18 +339,26 @@ class Pane extends Model
|
||||
newPane.activate()
|
||||
newPane
|
||||
|
||||
# If the parent is a horizontal axis, returns its first child;
|
||||
# otherwise this pane.
|
||||
# If the parent is a horizontal axis, returns its first child if it is a pane;
|
||||
# otherwise returns this pane.
|
||||
findLeftmostSibling: ->
|
||||
if @parent.orientation is 'horizontal'
|
||||
@parent.children[0]
|
||||
[leftmostSibling] = @parent.children
|
||||
if leftmostSibling instanceof PaneAxis
|
||||
this
|
||||
else
|
||||
leftmostSibling
|
||||
else
|
||||
this
|
||||
|
||||
# If the parent is a horizontal axis, returns its last child;
|
||||
# If the parent is a horizontal axis, returns its last child if it is a pane;
|
||||
# otherwise returns a new pane created by splitting this pane rightward.
|
||||
findOrCreateRightmostSibling: ->
|
||||
if @parent.orientation is 'horizontal'
|
||||
last(@parent.children)
|
||||
rightmostSibling = last(@parent.children)
|
||||
if rightmostSibling instanceof PaneAxis
|
||||
@splitRight()
|
||||
else
|
||||
rightmostSibling
|
||||
else
|
||||
@splitRight()
|
||||
|
||||
@@ -165,9 +165,8 @@ class Project extends Model
|
||||
bufferForId: (id) ->
|
||||
_.find @buffers, (buffer) -> buffer.id is id
|
||||
|
||||
# DEPRECATED
|
||||
# Still needed when deserializing a tokenized buffer
|
||||
buildBufferSync: (absoluteFilePath) ->
|
||||
deprecate("Use Project::buildBuffer instead")
|
||||
buffer = new TextBuffer({filePath: absoluteFilePath})
|
||||
@addBuffer(buffer)
|
||||
buffer.loadSync()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{View, $} = require 'space-pen'
|
||||
React = require 'react'
|
||||
React = require 'react-atom-fork'
|
||||
EditorComponent = require './editor-component'
|
||||
{defaults} = require 'underscore-plus'
|
||||
|
||||
module.exports =
|
||||
class ReactEditorView extends View
|
||||
@@ -8,12 +9,12 @@ class ReactEditorView extends View
|
||||
|
||||
focusOnAttach: false
|
||||
|
||||
constructor: (@editor) ->
|
||||
constructor: (@editor, @props) ->
|
||||
super
|
||||
|
||||
getEditor: -> @editor
|
||||
|
||||
Object.defineProperty @::, 'lineHeight', get: -> @editor.getLineHeight()
|
||||
Object.defineProperty @::, 'lineHeight', get: -> @editor.getLineHeightInPixels()
|
||||
Object.defineProperty @::, 'charWidth', get: -> @editor.getDefaultCharWidth()
|
||||
|
||||
scrollTop: (scrollTop) ->
|
||||
@@ -37,11 +38,12 @@ class ReactEditorView extends View
|
||||
afterAttach: (onDom) ->
|
||||
return unless onDom
|
||||
@attached = true
|
||||
@component = React.renderComponent(EditorComponent({@editor, parentView: this}), @element)
|
||||
props = defaults({@editor, parentView: this}, @props)
|
||||
@component = React.renderComponent(EditorComponent(props), @element)
|
||||
|
||||
node = @component.getDOMNode()
|
||||
|
||||
@underlayer = $(node).find('.underlayer')
|
||||
@underlayer = $(node).find('.selections')
|
||||
|
||||
@gutter = $(node).find('.gutter')
|
||||
@gutter.removeClassFromAllLines = (klass) =>
|
||||
@@ -64,7 +66,8 @@ class ReactEditorView extends View
|
||||
|
||||
appendToLinesView: (view) ->
|
||||
view.css('position', 'absolute')
|
||||
@find('.scroll-view-content').prepend(view)
|
||||
view.css('z-index', 1)
|
||||
@find('.lines').prepend(view)
|
||||
|
||||
beforeRemove: ->
|
||||
React.unmountComponentAtNode(@element)
|
||||
@@ -79,3 +82,11 @@ class ReactEditorView extends View
|
||||
@component.onFocus()
|
||||
else
|
||||
@focusOnAttach = true
|
||||
|
||||
hide: ->
|
||||
super
|
||||
@component.hide()
|
||||
|
||||
show: ->
|
||||
super
|
||||
@component.show()
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
React = require 'react'
|
||||
{div} = require 'reactionary'
|
||||
{isEqualForProperties} = require 'underscore-plus'
|
||||
React = require 'react-atom-fork'
|
||||
{div} = require 'reactionary-atom-fork'
|
||||
{extend, isEqualForProperties} = require 'underscore-plus'
|
||||
|
||||
module.exports =
|
||||
ScrollbarComponent = React.createClass
|
||||
render: ->
|
||||
{orientation, className, scrollHeight, scrollWidth} = @props
|
||||
displayName: 'ScrollbarComponent'
|
||||
|
||||
div {className, @onScroll},
|
||||
render: ->
|
||||
{orientation, className, scrollHeight, scrollWidth, visible} = @props
|
||||
{scrollableInOppositeDirection, horizontalScrollbarHeight, verticalScrollbarWidth} = @props
|
||||
|
||||
style = {}
|
||||
style.display = 'none' unless visible
|
||||
switch orientation
|
||||
when 'vertical'
|
||||
style.width = verticalScrollbarWidth
|
||||
style.bottom = horizontalScrollbarHeight if scrollableInOppositeDirection
|
||||
when 'horizontal'
|
||||
style.height = horizontalScrollbarHeight
|
||||
style.right = verticalScrollbarWidth if scrollableInOppositeDirection
|
||||
|
||||
div {className, style, @onScroll},
|
||||
switch orientation
|
||||
when 'vertical'
|
||||
div className: 'scrollbar-content', style: {height: scrollHeight}
|
||||
@@ -21,11 +34,13 @@ ScrollbarComponent = React.createClass
|
||||
throw new Error("Must specify an orientation property of 'vertical' or 'horizontal'")
|
||||
|
||||
shouldComponentUpdate: (newProps) ->
|
||||
return true if newProps.visible isnt @props.visible
|
||||
|
||||
switch @props.orientation
|
||||
when 'vertical'
|
||||
not isEqualForProperties(newProps, @props, 'scrollHeight', 'scrollTop')
|
||||
not isEqualForProperties(newProps, @props, 'scrollHeight', 'scrollTop', 'scrollableInOppositeDirection')
|
||||
when 'horizontal'
|
||||
not isEqualForProperties(newProps, @props, 'scrollWidth', 'scrollLeft')
|
||||
not isEqualForProperties(newProps, @props, 'scrollWidth', 'scrollLeft', 'scrollableInOppositeDirection')
|
||||
|
||||
componentDidUpdate: ->
|
||||
{orientation, scrollTop, scrollLeft} = @props
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
React = require 'react-atom-fork'
|
||||
{div} = require 'reactionary-atom-fork'
|
||||
|
||||
module.exports =
|
||||
ScrollbarCornerComponent = React.createClass
|
||||
displayName: 'ScrollbarCornerComponent'
|
||||
|
||||
render: ->
|
||||
{visible, measuringScrollbars, width, height} = @props
|
||||
|
||||
if measuringScrollbars
|
||||
height = 25
|
||||
width = 25
|
||||
|
||||
display = 'none' unless visible
|
||||
|
||||
div className: 'scrollbar-corner', style: {display, width, height},
|
||||
div style:
|
||||
height: height + 1
|
||||
width: width + 1
|
||||
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