Comparar commits
772 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| d8d378e083 | |||
| 8cf9e1990b | |||
| 389b5c7891 | |||
| 6bf97f7a1a | |||
| d0639393ca | |||
| 7ae6cba337 | |||
| a3e85d6758 | |||
| decce0e3a1 | |||
| aa2868efbf | |||
| 7dcafb44f1 | |||
| f9c975bba7 | |||
| 1c6f7b00eb | |||
| fc1709f113 | |||
| 35c2ef09a0 | |||
| dbbf310e36 | |||
| ee1c17d787 | |||
| fe3ea229a2 | |||
| 381ebe1a38 | |||
| 965a65d6f8 | |||
| 25b641125d | |||
| 53aff00218 | |||
| 9f8e1fbb80 | |||
| 5e7a26dc9d | |||
| 7ca6277403 | |||
| 774e86da8d | |||
| 893d349594 | |||
| f7d18154e7 | |||
| 4f5098f0a3 | |||
| e04c6b4732 | |||
| f31cc1b0da | |||
| 3b6711d83d | |||
| 776e8a308c | |||
| d62ac36062 | |||
| 1734182191 | |||
| e0b54d8501 | |||
| 66661f2d10 | |||
| cae5cdc81c | |||
| 8e6dcf43b0 | |||
| 15bb3ee31d | |||
| 7afb25cead | |||
| b0ddab842b | |||
| a2d08547b6 | |||
| a6aa94110c | |||
| 8f5018f925 | |||
| a47feb9dd8 | |||
| 9ef4ea180b | |||
| 24be115cf6 | |||
| d61504873a | |||
| ae7047571f | |||
| 40fdce9c00 | |||
| d3ede06cad | |||
| 31d25209a0 | |||
| 1d2ce26b81 | |||
| 56095187e6 | |||
| 58af226359 | |||
| 88cb7e2f96 | |||
| 728aacd3fb | |||
| 4f56bc32dc | |||
| d16bd0d7eb | |||
| b6fc17e363 | |||
| b6056a44e2 | |||
| 6ce4efa3ce | |||
| 0c0f143f91 | |||
| 2a5ac13e4c | |||
| 6cc6c81493 | |||
| a215cd77ed | |||
| ddad8ef45c | |||
| c1ce1e624d | |||
| d611dbb36a | |||
| 646d8a5966 | |||
| 7be8bd7118 | |||
| e98b43e479 | |||
| c167166add | |||
| 68964f1ca1 | |||
| fbcb152107 | |||
| c51ede98fc | |||
| 51183f4be5 | |||
| bf16527aa6 | |||
| 2cce36694d | |||
| 11fbb33a24 | |||
| 2e5c844d46 | |||
| 0b088e8b72 | |||
| 1479197ffb | |||
| c79c7b7a98 | |||
| dfb72c287b | |||
| 1db983a0a0 | |||
| 3ead596a5d | |||
| b8a2139c05 | |||
| 99af9e2c33 | |||
| b7bd11a883 | |||
| 1ac40b2673 | |||
| 5434a26636 | |||
| 50445a73bd | |||
| ff9c031f57 | |||
| c1ebbf36be | |||
| 8717547db4 | |||
| e99996d5b4 | |||
| 7c4a32fffa | |||
| 196729e6d2 | |||
| db94aa94eb | |||
| f670673baf | |||
| f5bd6a6baf | |||
| 9e20cefd4b | |||
| 3f8d7d54cf | |||
| b5703ff57a | |||
| f97db691c3 | |||
| 41ab48bc6e | |||
| ae1b640725 | |||
| dc49875af9 | |||
| a36454c86c | |||
| 8fa68c488d | |||
| a25cf0a899 | |||
| 6e1efdf2ee | |||
| 8e095d7b5b | |||
| 9899e7f2e3 | |||
| a62a0f4bb7 | |||
| dac792a243 | |||
| 31b3d8f967 | |||
| 55fb816998 | |||
| ce527aea10 | |||
| a7a9301f25 | |||
| 0f551d4b11 | |||
| 35a7bb115d | |||
| 553abc2009 | |||
| 0ad1aeea43 | |||
| 7e52c86095 | |||
| 950338ed22 | |||
| 8ed3b7a250 | |||
| 390c36ca15 | |||
| 1df2e50bbf | |||
| 749dba1ac7 | |||
| a28566a559 | |||
| 64f1c8b80e | |||
| b47f6265c7 | |||
| 9511c952af | |||
| be0877327e | |||
| 3134bfda95 | |||
| 1c177aa206 | |||
| ec65def5d3 | |||
| 3aefa53b33 | |||
| 4fd07a4cf3 | |||
| 03463da729 | |||
| 1cc5ef3479 | |||
| 77f78d0a11 | |||
| 8f98f2368b | |||
| f467e3eed4 | |||
| 3052fe3f3b | |||
| 1bce626324 | |||
| e222998f82 | |||
| 2aca16dc6b | |||
| 653a2623bf | |||
| 03ce9e0ec8 | |||
| 0245ec28eb | |||
| 94f86cb461 | |||
| b4871fddfb | |||
| a598dcc259 | |||
| 918c86476c | |||
| 4d65a220e2 | |||
| 7697f19c3d | |||
| 6ad6409efe | |||
| b6fcc35131 | |||
| bd873dc851 | |||
| 4c2931f6b5 | |||
| 1704c78eea | |||
| bcad8c1b3e | |||
| 81d9193bf4 | |||
| 7514f70434 | |||
| 12a8688a9b | |||
| 0524a724e1 | |||
| 805c7ae301 | |||
| 3f3dabed41 | |||
| 516035a82c | |||
| 08686ee769 | |||
| 5b479ad5f5 | |||
| d4366aa09e | |||
| 3f549bbada | |||
| 1187d50b81 | |||
| a1d4c2a4c7 | |||
| afe386ce40 | |||
| d31669c67f | |||
| 5259a1ced4 | |||
| 437b0decab | |||
| 61ee1be2cb | |||
| 2548891b99 | |||
| 115a7e1dfb | |||
| 5d2be8d5c5 | |||
| 6d08ade20c | |||
| 0043072ecf | |||
| 89c57b6d52 | |||
| f2a08cd178 | |||
| df524e4803 | |||
| b8c4b83653 | |||
| 2df2254227 | |||
| f4d256eef2 | |||
| cb109dc09f | |||
| 919181067a | |||
| 48d252e118 | |||
| 75a07ac722 | |||
| e06bbfac6b | |||
| 4265cfc61e | |||
| 57531d75fe | |||
| 5c1d9a6a2e | |||
| 883009a3bd | |||
| 3ed5e64a01 | |||
| 0fdceb8474 | |||
| 041ec8c7cf | |||
| 07e64152be | |||
| 87d2e51adb | |||
| 30582c69e8 | |||
| 6e34562d94 | |||
| 3da933372f | |||
| 3601baeb80 | |||
| 64b4adf6c8 | |||
| d2d63d3241 | |||
| 4840987082 | |||
| 249a14704b | |||
| cf64d80abe | |||
| 5e511ca6c6 | |||
| c4a547a9ea | |||
| b936e126da | |||
| 5418e74fb9 | |||
| 40a2ed3703 | |||
| 67c8de623d | |||
| bf7c14e6f3 | |||
| 512a50814b | |||
| 3c251dbe56 | |||
| eae33be515 | |||
| a343e4b9f7 | |||
| 5f359a4271 | |||
| ae1dd6e876 | |||
| d1bd2113bf | |||
| c9ae9e11c1 | |||
| b10b8d80ae | |||
| 7f57a094f6 | |||
| b7df08cbdd | |||
| 89dc5f26ad | |||
| 2c60b0463e | |||
| 3093198662 | |||
| 7b8ef2f6e3 | |||
| c56ac70181 | |||
| d77c914fd8 | |||
| 8e91e503c9 | |||
| 97c9f05447 | |||
| 2715bec4c8 | |||
| 45717d7431 | |||
| afd61a369b | |||
| 86082d19e1 | |||
| b7d6825287 | |||
| 1fe13fbad8 | |||
| 1f21e4178d | |||
| aef75238ec | |||
| 419b1ec348 | |||
| bb31af5e8c | |||
| befedac546 | |||
| be0dcb840e | |||
| b521af9b4a | |||
| 61cb22840e | |||
| eaeb5b3b13 | |||
| 466a50ff37 | |||
| 4f2d935a1d | |||
| b19126024a | |||
| 56eb1fc6e4 | |||
| 07a2a6710d | |||
| cbedd221f3 | |||
| d684911fce | |||
| b625965982 | |||
| 9688469501 | |||
| f9cb9b7ea2 | |||
| 6448258f4e | |||
| 9f1a3c6eff | |||
| 21c2bf4861 | |||
| b9753a0785 | |||
| ff27ac5670 | |||
| 720679b41b | |||
| 248843583e | |||
| 1f3ef99390 | |||
| e74b56c50e | |||
| c924d55a26 | |||
| 4964a067d0 | |||
| 264196a11f | |||
| bfa70b4ba6 | |||
| 7e5d554bd8 | |||
| a90039baab | |||
| 55228f9667 | |||
| 6295c2ddc4 | |||
| 5ca6d01911 | |||
| 6d9fed6644 | |||
| 3c8bfb8bc8 | |||
| 00987f3642 | |||
| 6e070ae057 | |||
| ac70bfc456 | |||
| fd0f323666 | |||
| 0e31557abf | |||
| e7c1d20d8e | |||
| e8c8f0de0d | |||
| ea0773e8fc | |||
| 037306a30c | |||
| 9ea94cb6cc | |||
| 97fe09f157 | |||
| 5b149bd41a | |||
| 8a899f2116 | |||
| dfa2f46ced | |||
| 2b2d1d87f7 | |||
| 3784b77b95 | |||
| 7f418e2de4 | |||
| 78da19f479 | |||
| 4e18ebe843 | |||
| 152a8b311f | |||
| 64470a3c7d | |||
| fae035731f | |||
| 446a48ca00 | |||
| ce9b34c9eb | |||
| d1ed176550 | |||
| 652219b8cf | |||
| e6229f6145 | |||
| 9c066d93fa | |||
| e754689014 | |||
| de96de2de1 | |||
| 6d89c893b7 | |||
| 16df6a32d6 | |||
| a337384e7b | |||
| cdb6de05b3 | |||
| 1b262eadaa | |||
| fd5ea8a0b1 | |||
| fe31d2d28a | |||
| 40b66e8eb3 | |||
| 71e83962ac | |||
| 5316d586d1 | |||
| f79039e4f7 | |||
| f4d6b69002 | |||
| 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 | |||
| fe4016a49d | |||
| 31b06b2e47 | |||
| cf6d83a896 | |||
| 084e4ab830 | |||
| 5a53b34697 | |||
| 8f3b76d3c9 | |||
| f5df111b5c | |||
| bb2527bbb8 | |||
| eb31c6c85f | |||
| 546f81f2d0 |
@@ -1,6 +1,7 @@
|
||||
*.swp
|
||||
*~
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
.project
|
||||
.svn
|
||||
.nvm-version
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
ca =
|
||||
cache = ~/.atom/.npm
|
||||
|
||||
+76
-47
@@ -1,77 +1,106 @@
|
||||
# :tada: Contributing to Atom :tada:
|
||||
# Contributing to Atom
|
||||
|
||||
These are just guidelines, not rules, use your best judgement and feel free
|
||||
to propose changes to this document in a pull request.
|
||||
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
|
||||
|
||||
## 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.
|
||||
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 open an issue on the [main atom repository](https://github.com/atom/atom/issues).
|
||||
These are just guidelines, not rules, use your best judgement and feel free to
|
||||
propose changes to this document in a pull request.
|
||||
|
||||
## Submitting Issues
|
||||
|
||||
* Include the version of Atom you are using and the OS.
|
||||
* 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 to include. If the dev tools
|
||||
are open _before_ the error is triggered, a full stack trace for the error
|
||||
will be logged. If you can reproduce the error, use this approach to get the
|
||||
full stack trace and include it in the issue.
|
||||
* 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
|
||||
* :checkered_flag: `:checkered_flag:` when fixing something on Windows
|
||||
* :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
|
||||
|
||||
|
||||
+7
-15
@@ -1,15 +1,8 @@
|
||||

|
||||
|
||||
Atom is a hackable text editor for the 21st century.
|
||||
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.
|
||||
|
||||
Atom is open source and built on top of [atom-shell](http://github.com/atom/atom-shell).
|
||||
|
||||
Atom is designed to be customizable, but also usable without needing to edit a config file.
|
||||
|
||||
Atom is modern, approachable, and hackable to the core.
|
||||
|
||||
Visit [atom.io](http://atom.io)
|
||||
to learn more.
|
||||
Visit [atom.io](https://atom.io) to learn more.
|
||||
|
||||
## Installing
|
||||
|
||||
@@ -19,11 +12,10 @@ Atom will automatically update when a new release is available.
|
||||
|
||||
## Building
|
||||
|
||||
```sh
|
||||
git clone git@github.com:atom/atom.git
|
||||
cd atom
|
||||
script/build # Creates application at /Applications/Atom.app
|
||||
```
|
||||
* [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)
|
||||
|
||||
## Developing
|
||||
Check out the [guides](https://atom.io/docs/latest) and the [API reference](atom.io/docs/api).
|
||||
Check out the [guides](https://atom.io/docs/latest) and the [API reference](https://atom.io/docs/api).
|
||||
|
||||
+1
-1
@@ -6,6 +6,6 @@
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "0.45.0"
|
||||
"atom-package-manager": "0.68.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": "~0.7.0",
|
||||
"grunt-download-atom-shell": "~0.8.0",
|
||||
"grunt-lesslint": "0.13.0",
|
||||
"grunt-markdown": "~0.4.0",
|
||||
"grunt-peg": "~1.1.0",
|
||||
@@ -26,7 +26,9 @@
|
||||
"harmony-collections": "~0.3.8",
|
||||
"json-front-matter": "~0.1.3",
|
||||
"legal-eagle": "~0.4.0",
|
||||
"minidump": "0.5.x",
|
||||
"minidump": "~0.6",
|
||||
"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'}
|
||||
|
||||
@@ -26,7 +26,7 @@ module.exports = (grunt) ->
|
||||
getLicenseText = (dependencyLicenses) ->
|
||||
{keys} = require 'underscore-plus'
|
||||
text = """
|
||||
#{fs.readFileSync('LICENSE.md')}
|
||||
#{fs.readFileSync('LICENSE.md', 'utf8')}
|
||||
|
||||
This application bundles the following third-party packages in accordance
|
||||
with the following licenses:\n\n
|
||||
|
||||
@@ -13,9 +13,15 @@ module.exports = (grunt) ->
|
||||
grunt.registerTask 'mkdeb', 'Create debian package', ->
|
||||
done = @async()
|
||||
|
||||
if process.arch is 'ia32'
|
||||
arch = 'i386'
|
||||
else if process.arch is 'x64'
|
||||
arch = 'amd64'
|
||||
else
|
||||
return done("Unsupported arch #{process.arch}")
|
||||
|
||||
{name, version, description} = grunt.file.readJSON('package.json')
|
||||
section = 'devel'
|
||||
arch = 'amd64'
|
||||
maintainer = 'GitHub <atom@github.com>'
|
||||
data = {name, version, description, section, arch, maintainer}
|
||||
|
||||
@@ -27,5 +33,5 @@ module.exports = (grunt) ->
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
|
||||
cmd = path.join('script', 'mkdeb')
|
||||
args = [version, control, desktop, icon, buildDir]
|
||||
args = [version, arch, control, desktop, icon, buildDir]
|
||||
spawn({cmd, args}, 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()
|
||||
|
||||
@@ -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
|
||||
@@ -91,6 +91,16 @@ the current keystroke sequence and continue searching from its parent. If you
|
||||
want to remove a binding from a keymap you don't control, such as keymaps in
|
||||
Atom core or in packages, use the `unset!` directive.
|
||||
|
||||
For example, the following code removes the keybinding for `a` in the Tree View,
|
||||
which is normally used to trigger the `tree-view:add-file` command:
|
||||
|
||||
```coffee
|
||||
'.tree-view':
|
||||
'a': 'unset!'
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Forcing Chromium's Native Keystroke Handling
|
||||
|
||||
If you want to force the native browser behavior for a given keystroke, use the
|
||||
|
||||
@@ -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,216 @@
|
||||
## Atom.io package and update API
|
||||
|
||||
This guide describes the web API used by [apm](https://github.com/atom/apm) and
|
||||
Atom. The vast majority of use cases are met by the `apm` command-line tool,
|
||||
which does other useful things like incrementing your version in `package.json`
|
||||
and making sure you have pushed your git tag. In fact, Atom itself shells out to
|
||||
`apm` rather than hitting the API directly. If you're curious about how Atom
|
||||
uses `apm`, see the [PackageManager class](https://github.com/atom/settings-view/blob/master/lib/package-manager.coffee)
|
||||
in the `settings-view` package.
|
||||
|
||||
### Authorization
|
||||
|
||||
For calls to the API that require authentication, provide a valid token from your
|
||||
[Atom.io account page](https://atom.io/account) in the `Authorization` header.
|
||||
|
||||
### Media type
|
||||
|
||||
All requests that take parameters require `application/json`.
|
||||
|
||||
## Resources
|
||||
|
||||
### Packages
|
||||
|
||||
#### GET /api/packages
|
||||
|
||||
Returns a list of all packages in the following format:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"releases": {
|
||||
"latest": "0.6.0"
|
||||
},
|
||||
"name": "thedaniel-test-package",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thedaniel/test-package"
|
||||
}
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
#### GET /api/packages/:package_name
|
||||
|
||||
Returns package details and versions for a single package
|
||||
|
||||
Parameters:
|
||||
|
||||
- **engine** (optional) - Only show packages with versions compatible with this
|
||||
Atom version. Must be valid [SemVer](http://semver.org).
|
||||
|
||||
Returns:
|
||||
|
||||
```json
|
||||
{
|
||||
"releases": {
|
||||
"latest": "0.6.0"
|
||||
},
|
||||
"name": "thedaniel-test-package",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thedaniel/test-package"
|
||||
},
|
||||
"versions": [
|
||||
(see single version output below)
|
||||
...,
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### POST /api/packages
|
||||
|
||||
Create a new package; requires authentication.
|
||||
|
||||
The name and version will be fetched from the `package.json`
|
||||
file in the specified repository. The authenticating user *must* have access
|
||||
to the indicated repository.
|
||||
|
||||
When a package is created, a release hook is registered with GitHub for package
|
||||
version creation.
|
||||
|
||||
Parameters:
|
||||
|
||||
- **repository** - String. The repository containing the plugin, in the form "owner/repo"
|
||||
|
||||
Returns:
|
||||
|
||||
- **201** - Successfully created, returns created package.
|
||||
- **400** - Repository is inaccessible, nonexistent, not an atom package. Possible
|
||||
error messages include:
|
||||
- That repo does not exist, isn't an atom package, or atombot does not have access
|
||||
- The package.json at owner/repo isn't valid
|
||||
- **409** - A package by that name already exists
|
||||
|
||||
|
||||
#### DELETE /api/packages/:package_name
|
||||
|
||||
Delete a package; requires authentication.
|
||||
|
||||
Returns:
|
||||
|
||||
- **204** - Success
|
||||
- **400** - Repository is inaccessible
|
||||
- **401** - Unauthorized
|
||||
|
||||
### Package Versions
|
||||
|
||||
#### GET /api/packages/:package_name/versions/:version_name
|
||||
|
||||
Returns `package.json` with `dist` key added for e.g. tarball download:
|
||||
|
||||
```json
|
||||
{
|
||||
"bugs": {
|
||||
"url": "https://github.com/thedaniel/test-package/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "~0.2.6",
|
||||
"pegjs": "~0.7.0",
|
||||
"season": "~0.13.0"
|
||||
},
|
||||
"description": "Expand snippets matching the current prefix with `tab`.",
|
||||
"dist": {
|
||||
"tarball": "https://codeload.github.com/..."
|
||||
},
|
||||
"engines": {
|
||||
"atom": "*"
|
||||
},
|
||||
"main": "./lib/snippets",
|
||||
"name": "thedaniel-test-package",
|
||||
"publishConfig": {
|
||||
"registry": "https://...",
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thedaniel/test-package.git"
|
||||
},
|
||||
"version": "0.6.0"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Creating a new package version
|
||||
|
||||
#### POST /api/packages/:package_name/versions
|
||||
|
||||
Creates a new package version from a git tag; requires authentication.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- **tag** - A git tag for the version you'd like to create. It's important to note
|
||||
that the version name will not be taken from the tag, but from the `version`
|
||||
key in the `package.json` file at that ref. The authenticating user *must* have
|
||||
access to the package repository.
|
||||
|
||||
#### Returns
|
||||
|
||||
- **201** - Successfully created. Returns created version.
|
||||
- **400** - Git tag not found / Repository inaccessible
|
||||
- **409** - Version exists
|
||||
|
||||
### Delete a version
|
||||
|
||||
#### DELETE /api/packages/:package_name/versions/:version_name
|
||||
|
||||
Deletes a package version; requires authentication.
|
||||
|
||||
Note that a version cannot be republished with a different tag if it is deleted.
|
||||
If you need to delete the latest version of a package for e.g. security reasons,
|
||||
you'll need to increment the version when republishing.
|
||||
|
||||
Returns 204 No Content
|
||||
|
||||
|
||||
### Stars
|
||||
|
||||
#### GET /api/users/:login/stars
|
||||
|
||||
List a user's starred packages.
|
||||
|
||||
Return value is similar to **GET /api/packages**
|
||||
|
||||
#### GET /api/stars
|
||||
|
||||
List the authenticated user's starred packages; requires authentication.
|
||||
|
||||
Return value is similar to **GET /api/packages**
|
||||
|
||||
#### POST /api/packages/:name/star
|
||||
|
||||
Star a package; requires authentication.
|
||||
|
||||
Returns a package.
|
||||
|
||||
#### DELETE /api/packages/:name/star
|
||||
|
||||
Unstar a package; requires authentication.
|
||||
|
||||
Returns 204 No Content.
|
||||
|
||||
### Atom updates
|
||||
|
||||
#### GET /api/updates
|
||||
|
||||
Atom update feed, following the format expected by [Squirrel](https://github.com/Squirrel/).
|
||||
|
||||
Returns:
|
||||
|
||||
```
|
||||
{
|
||||
"name": "0.96.0",
|
||||
"notes": "[HTML release notes]"
|
||||
"pub_date": "2014-05-19T15:52:06.000Z",
|
||||
"url": "https://www.atom.io/api/updates/download"
|
||||
}
|
||||
```
|
||||
@@ -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,56 @@
|
||||
# Linux
|
||||
|
||||
Ubuntu LTS 12.04 64-bit is the recommended platform.
|
||||
|
||||
## Requirements
|
||||
|
||||
* OS with 64-bit or 32-bit architecture
|
||||
* [node.js](http://nodejs.org/download/) v0.10.x
|
||||
* [npm](http://www.npmjs.org/) v1.4.x
|
||||
* libgnome-keyring-dev
|
||||
* on Ubuntu/Debian: `sudo apt-get install libgnome-keyring-dev`
|
||||
* on Fedora: `sudo yum --assumeyes install libgnome-keyring-devel`
|
||||
* on other distributions refer to the manual on how to install packages
|
||||
* `npm config set python /usr/bin/python2 -g` to ensure that gyp uses Python 2
|
||||
* This command may require `sudo` depending on how you have
|
||||
[configured npm](https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager#ubuntu-mint-elementary-os).
|
||||
|
||||
|
||||
## 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.
|
||||
|
||||
### Linux build error reports in atom/atom
|
||||
* Use [this search](https://github.com/atom/atom/search?q=label%3Abuild-error+label%3Alinux&type=Issues) to get a list of reports about build errors on Linux.
|
||||
@@ -0,0 +1,20 @@
|
||||
# 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.git
|
||||
cd atom
|
||||
script/build # Creates application at /Applications/Atom.app
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### OSX build error reports in atom/atom
|
||||
* Use [this search](https://github.com/atom/atom/search?q=label%3Abuild-error+label%3Aos-x&type=Issues) to get a list of reports about build errors on OSX.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Windows
|
||||
|
||||
## Requirements
|
||||
|
||||
### On Windows 7
|
||||
* [Visual C++ 2010 Express](http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs#DownloadFamilies_4)
|
||||
* [Visual Studio 2010 Service Pack 1](http://www.microsoft.com/en-us/download/details.aspx?id=23691)
|
||||
* [node.js](http://nodejs.org/download/) v0.10.x
|
||||
* For 64-bit builds of node and native modules you **must** have the
|
||||
[Windows 7 64-bit SDK](http://www.microsoft.com/en-us/download/details.aspx?id=8279).
|
||||
You may also need the [compiler update for the Windows SDK 7.1](http://www.microsoft.com/en-us/download/details.aspx?id=4422)
|
||||
|
||||
* [Python](http://www.python.org/download/) v2.7.x
|
||||
* [GitHub for Windows](http://windows.github.com/)
|
||||
|
||||
### On Windows 8
|
||||
* [Visual Studio Express 2013 for Windows Desktop](http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs#DownloadFamilies_2)
|
||||
* [node.js](http://nodejs.org/download/) v0.10.x
|
||||
* [Python](http://www.python.org/download/) v2.7.x (required by [node-gyp](https://github.com/TooTallNate/node-gyp))
|
||||
* [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 # Creates application in the `Program Files` directory
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
|
||||
* `script/build` outputs only the Node and Python versions before returning
|
||||
|
||||
* Try moving the repository to `C:\atom`. Most likely, the path is too long.
|
||||
See [issue #2200](https://github.com/atom/atom/issues/2200).
|
||||
|
||||
### Windows build error reports in atom/atom
|
||||
* Use [this search](https://github.com/atom/atom/search?q=label%3Abuild-error+label%3Awindows&type=Issues) to get a list of reports about build errors on Windows.
|
||||
@@ -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
|
||||
|
||||
@@ -225,6 +225,20 @@ elements until reaching the top of the DOM tree.
|
||||
In the example above, the `Add file` item will only appear when the focused item
|
||||
or one of its parents has the `tree-view` class applied to it.
|
||||
|
||||
You can also add separators and submenus to your context menus. To add a
|
||||
submenu, pass in another object instead of a command. To add a separator, use
|
||||
`-` for the name and command of the item.
|
||||
|
||||
```coffeescript
|
||||
'context-menu':
|
||||
'.workspace':
|
||||
'Inspect Element': 'core:inspect'
|
||||
'-': '-'
|
||||
'Text':
|
||||
'Select All': 'core:select-all'
|
||||
'Deleted Selected Text': 'core:delete'
|
||||
```
|
||||
|
||||
## Snippets
|
||||
|
||||
An extension can supply language snippets in the _snippets_ directory which
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# Debugging
|
||||
|
||||
Atom provides several tools to help you understand unexpected behavior and debug problems. This guide describes some of those tools and a few approaches to help you debug and provide more helpful information when [submitting issues].
|
||||
|
||||
## Update to the latest version
|
||||
|
||||
You might be running into an issue which was already fixed in a more recent version of Atom than the one you're using.
|
||||
|
||||
If you're building Atom from source, pull down the latest version of master and [re-build][building atom].
|
||||
|
||||
If you're using released version, check which version of Atom you're using:
|
||||
|
||||
```shell
|
||||
$ atom --version
|
||||
0.99.0
|
||||
```
|
||||
|
||||
Head on over to the [list of releases][atom releases] and see if there's a more recent release. You can update to the most recent release by downloading Atom from the releases page, or with the in-app auto-updater. The in-app auto-updater checks for and downloads a new version after you restart Atom, or if you use the Atom > Check for Update menu option.
|
||||
|
||||
## Check Atom and package settings
|
||||
|
||||
In some cases, unexpected behavior might be caused by misconfigured or unconfigured settings in Atom or in one of the packages.
|
||||
|
||||
Open Atom's Settings View with `cmd-,` or the Atom > Preferences menu option.
|
||||
|
||||
![Settings View]
|
||||
|
||||
Check Atom's settings in the Settings pane, there's a description of each configuration option [here][customizing guide]. For example, if you want Atom to use hard tabs (real tabs) and not soft tabs (spaces), disable the "Soft Tabs" option.
|
||||
|
||||
Since Atom ships with a set of packages and you can install additional packages yourself, check the list of packages and their settings. For example, if you'd like to get rid of the vertical line in the middle of the editor, disable the [Wrap Guide package]. And if you don't like it when Atom strips trailing whitespace or ensures that there's a single trailing newline in the file, you can configure that in the [Whitespace packages'][whitespace package] settings.
|
||||
|
||||
![Package Settings]
|
||||
|
||||
## Check the keybindings
|
||||
|
||||
If a command is not executing when you hit a keystroke or the wrong command is executing, there might be an issue with the keybindings for that keystroke. Atom ships with the [Keybinding resolver][keybinding resolver package], a neat package which helps you understand which keybindings are executed.
|
||||
|
||||
Show the keybinding resolver with <code>cmd-.</code> or with "Key Binding Resolver: Show" from the Command palette. With the keybinding resolver shown, hit a keystroke:
|
||||
|
||||
![Keybinding Resolver]
|
||||
|
||||
The keybinding resolver shows you a list of keybindings that exist for the keystroke, where each item in the list has the following:
|
||||
* the command for the keybinding,
|
||||
* the CSS selector used to define the context in which the keybinding is valid, and
|
||||
* the file in which the keybinding is defined.
|
||||
|
||||
Of all the keybinding that are listed (grey color), at most one keybinding is matched and executed (green color). If the command you wanted to trigger isn't listed, then a keybinding for that command hasn't been defined. More keybindings are provided by [packages] and you can [define your own keybindings][customizing keybindings].
|
||||
|
||||
If multiple keybindings are matched, Atom determines which keybinding will be executed based on the [specificity of the selectors and the order in which they were loaded][specificity and order]. If the command you wanted to trigger is listed in the Keybinding resolver, but wasn't the one that was executed, this is normally explained by one of two causes:
|
||||
* the keystroke was not used in the context defined by the keybinding's selector. For example, you can't trigger the "Tree View: Add File" command if the Tree View is not focused, or
|
||||
* there is another keybinding that took precedence. This often happens when you install a package which defines keybinding that conflict with existing keybindings. If the package's keybindings have selectors with higher specificity or were loaded later, they'll have priority over existing ones.
|
||||
|
||||
Atom loads core Atom keybindings and package keybindings first, and user-defined keybindings after last. Since user-defined keybindings are loaded last, you can use your `keymap.cson` file to tweak the keybindings and sort out problems like these. For example, you can remove keybindings with [the `unset!` directive][unset directive].
|
||||
|
||||
If you notice that a package's keybindings are taking precedence over core Atom keybindings, it might be a good idea to report the issue on the package's GitHub repository.
|
||||
|
||||
## Check if the problem shows up in safe mode
|
||||
|
||||
A large part of Atom's functionality comes from packages you can install. In some cases, these packages might be causing unexpected behavior, problems, or performance issues.
|
||||
|
||||
To determine if a package you installed is causing problems, start Atom from the terminal in safe mode:
|
||||
|
||||
```
|
||||
$ atom --safe
|
||||
```
|
||||
|
||||
This starts Atom, but does not load packages from `~/.atom/packages` or `~/.atom/dev/packages`. If you can no longer reproduce the problem in safe mode, it's likely it was caused by one of the packages.
|
||||
|
||||
To figure out which package is causing trouble, start Atom normally again and open Settings (`cmd-,`). Since Settings allow you to disable each installed package, you can disable packages one by one until you can no longer reproduce the issue. Restart (`cmd-q`) or reload (`cmd-ctrl-alt-l`) Atom after you disable each package to make sure it's completely gone.
|
||||
|
||||
When you find the problematic package, you can disable or uninstall the package, and consider creating an issue on the package's GitHub repository.
|
||||
|
||||
## Check your config files
|
||||
|
||||
You might have defined some custom functionality or styles in Atom's [Init script or Stylesheet]. In some situations, these personal hacks might be causing problems so try clearing those files and restarting Atom.
|
||||
|
||||
## Check for errors in the developer tools
|
||||
|
||||
When an error is thrown in Atom, the developer tools are automatically shown with the error logged in the Console tab. However, if the dev tools are open before the error is triggered, a full stack trace for the error will be logged:
|
||||
|
||||
![devtools error]
|
||||
|
||||
If you can reproduce the error, use this approach to get the full stack trace. The stack trace might point to a problem in your [Init script][init script or stylesheet] or a specific package you installed, which you can then disable and report an issue on its GitHub repository.
|
||||
|
||||
[submitting issues]: https://github.com/atom/atom/blob/master/CONTRIBUTING.md#submitting-issues
|
||||
[building atom]: https://github.com/atom/atom#building
|
||||
[atom releases]: https://github.com/atom/atom/releases
|
||||
[customizing guide]: https://atom.io/docs/latest/customizing-atom#configuration-key-reference
|
||||
[settings view]: https://f.cloud.github.com/assets/671378/2241795/ba4827d8-9ce4-11e3-93a8-6666ee100917.png
|
||||
[package settings]: https://cloud.githubusercontent.com/assets/38924/3173588/7e5f6b0c-ebe8-11e3-9ec3-e8d140967e79.png
|
||||
[wrap guide package]: https://atom.io/packages/wrap-guide
|
||||
[whitespace package]: https://atom.io/packages/whitespace
|
||||
[keybinding resolver package]: https://atom.io/packages/keybinding-resolver
|
||||
[keybinding resolver]: https://f.cloud.github.com/assets/671378/2241702/5dd5a102-9cde-11e3-9e3f-1d999930492f.png
|
||||
[customizing keybindings]: https://atom.io/docs/latest/customizing-atom#customizing-key-bindings
|
||||
[packages]: https://atom.io/packages
|
||||
[specificity and order]: https://atom.io/docs/latest/advanced/keymaps#specificity-and-cascade-order
|
||||
[unset directive]: https://atom.io/docs/latest/advanced/keymaps#removing-bindings
|
||||
[init script or stylesheet]: https://atom.io/docs/latest/customizing-atom#quick-personal-hacks
|
||||
[devtools error]: https://cloud.githubusercontent.com/assets/38924/3177710/11b4e510-ec13-11e3-96db-a2e8a7891773.png
|
||||
@@ -5,13 +5,16 @@
|
||||
* [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)
|
||||
* [Debugging](debugging.md)
|
||||
|
||||
### 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
|
||||
@@ -75,18 +75,34 @@ credentials are stored securely in your [keychain][keychain] once you login.
|
||||
:tada: Your package is now published and available on atom.io. Head on over to
|
||||
`http://atom.io/packages/my-package` to see your package's page.
|
||||
|
||||
With `apm publish`, you can bump the version and publish by using
|
||||
```sh
|
||||
apm publish <version-type>
|
||||
```
|
||||
where `<version-type>` can be `major`, `minor` and `patch`.
|
||||
|
||||
The `major` option to the publish command tells apm to increment the first
|
||||
digit of the version before publishing so the published version will be `1.0.0`
|
||||
and the Git tag created will be `v1.0.0`.
|
||||
|
||||
The `minor` option to the publish command tells apm to increment the second
|
||||
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
|
||||
with a minor release.
|
||||
The `patch` option to the publish command tells apm to increment the third
|
||||
digit of the version before publishing so the published version will be `0.0.1`
|
||||
and the Git tag created will be `v0.0.1`.
|
||||
|
||||
Use `major` when you make a huge change, like a rewrite, or a large change to the functionality or interface.
|
||||
Use `minor` when adding or removing a feature.
|
||||
Use `patch` when you make a small change like a bug fix that does not add or remove features.
|
||||
|
||||
### Further Reading
|
||||
|
||||
* Check out [semantic versioning][semver] to learn more about versioning your
|
||||
package releases.
|
||||
* Consult the [Atom.io package API docs][apm-rest-api] to learn more about how
|
||||
`apm` works.
|
||||
|
||||
[atomio]: https://atom.io
|
||||
[github]: https://github.com
|
||||
@@ -95,3 +111,4 @@ with a minor release.
|
||||
[repo-guide]: http://guides.github.com/overviews/desktop
|
||||
[semver]: http://semver.org
|
||||
[your-first-package]: your-first-package.html
|
||||
[apm-rest-api]: apm-rest-api.md
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# Writing 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` methods
|
||||
|
||||
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 arguments, 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 [jasmine 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")
|
||||
```
|
||||
|
||||
## Running 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")
|
||||
```
|
||||
@@ -149,6 +149,8 @@ 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
|
||||
|
||||
|
||||
+2
-4
@@ -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'
|
||||
@@ -16,6 +12,7 @@
|
||||
# Sublime Parity
|
||||
'tab': 'editor:indent'
|
||||
'enter': 'editor:newline'
|
||||
'num-enter': 'editor:newline'
|
||||
'shift-tab': 'editor:outdent-selected-rows'
|
||||
'ctrl-K': 'editor:delete-line'
|
||||
|
||||
@@ -30,6 +27,7 @@
|
||||
'tab': 'core:focus-next'
|
||||
'shift-tab': 'core:focus-previous'
|
||||
'enter': 'native!'
|
||||
'num-enter': 'native!'
|
||||
'backspace': 'native!'
|
||||
'shift-backspace': 'native!'
|
||||
'delete': 'native!'
|
||||
|
||||
+14
-4
@@ -21,6 +21,7 @@
|
||||
'cmd-O': 'application:open-dev'
|
||||
'cmd-alt-ctrl-s': 'application:run-all-specs'
|
||||
'enter': 'core:confirm'
|
||||
'num-enter': 'core:confirm'
|
||||
'escape': 'core:cancel'
|
||||
'up': 'core:move-up'
|
||||
'down': 'core:move-down'
|
||||
@@ -53,6 +54,7 @@
|
||||
'shift-left': 'core:select-left'
|
||||
'shift-right': 'core:select-right'
|
||||
'delete': 'core:delete'
|
||||
'shift-delete': 'core:delete'
|
||||
'pageup': 'core:page-up'
|
||||
'pagedown': 'core:page-down'
|
||||
'backspace': 'core:backspace'
|
||||
@@ -65,6 +67,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 +98,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'
|
||||
|
||||
+27
-4
@@ -1,13 +1,14 @@
|
||||
'body':
|
||||
# Atom Specific
|
||||
'enter': 'core:confirm'
|
||||
'num-enter': 'core:confirm'
|
||||
'escape': 'core:cancel'
|
||||
'up': 'core:move-up'
|
||||
'down': 'core:move-down'
|
||||
'left': 'core:move-left'
|
||||
'right': 'core:move-right'
|
||||
'ctrl-alt-r': 'window:reload'
|
||||
'ctrl-alt-i': 'window:toggle-dev-tools'
|
||||
'ctrl-shift-i': 'window:toggle-dev-tools'
|
||||
'ctrl-alt-p': 'window:run-package-specs'
|
||||
'ctrl-alt-s': 'application:run-all-specs'
|
||||
'ctrl-shift-o': 'application:open-dev'
|
||||
@@ -28,14 +29,18 @@
|
||||
'ctrl-x': 'core:cut'
|
||||
'ctrl-c': 'core:copy'
|
||||
'ctrl-v': 'core:paste'
|
||||
'ctrl-insert': 'core:copy'
|
||||
'shift-insert': 'core:paste'
|
||||
'shift-up': 'core:select-up'
|
||||
'shift-down': 'core:select-down'
|
||||
'shift-left': 'core:select-left'
|
||||
'shift-right': 'core:select-right'
|
||||
'delete': 'core:delete'
|
||||
'shift-delete': 'core:delete'
|
||||
'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'
|
||||
@@ -58,14 +63,32 @@
|
||||
'ctrl-k ctrl-down': 'window:focus-pane-below'
|
||||
'ctrl-k ctrl-left': 'window:focus-pane-on-left'
|
||||
'ctrl-k ctrl-right': 'window:focus-pane-on-right'
|
||||
'alt-1': 'pane:show-item-1'
|
||||
'alt-2': 'pane:show-item-2'
|
||||
'alt-3': 'pane:show-item-3'
|
||||
'alt-4': 'pane:show-item-4'
|
||||
'alt-5': 'pane:show-item-5'
|
||||
'alt-6': 'pane:show-item-6'
|
||||
'alt-7': 'pane:show-item-7'
|
||||
'alt-8': 'pane:show-item-8'
|
||||
'alt-9': 'pane:show-item-9'
|
||||
|
||||
'.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'
|
||||
'ctrl-alt-p': 'editor:log-cursor-scope'
|
||||
'ctrl-alt-shift-p': 'editor:log-cursor-scope'
|
||||
'ctrl-k ctrl-u': 'editor:upper-case'
|
||||
'ctrl-k ctrl-l': 'editor:lower-case'
|
||||
|
||||
|
||||
+22
-3
@@ -1,6 +1,11 @@
|
||||
'body':
|
||||
# Platform Bindings
|
||||
'ctrl-pageup': 'pane:show-previous-item'
|
||||
'ctrl-pagedown': 'pane:show-next-item'
|
||||
|
||||
# Atom Specific
|
||||
'enter': 'core:confirm'
|
||||
'num-enter': 'core:confirm'
|
||||
'escape': 'core:cancel'
|
||||
'up': 'core:move-up'
|
||||
'down': 'core:move-down'
|
||||
@@ -10,6 +15,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'
|
||||
@@ -25,18 +31,24 @@
|
||||
'ctrl-x': 'core:cut'
|
||||
'ctrl-c': 'core:copy'
|
||||
'ctrl-v': 'core:paste'
|
||||
'ctrl-insert': 'core:copy'
|
||||
'shift-insert': 'core:paste'
|
||||
'shift-up': 'core:select-up'
|
||||
'shift-down': 'core:select-down'
|
||||
'shift-left': 'core:select-left'
|
||||
'shift-right': 'core:select-right'
|
||||
'delete': 'core:delete'
|
||||
'shift-delete': 'core:delete'
|
||||
'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'
|
||||
'ctrl-shift-down': 'core:move-down'
|
||||
'ctrl-alt-up': 'editor:add-selection-above'
|
||||
'ctrl-alt-down': 'editor:add-selection-below'
|
||||
'ctrl-=': 'window:increase-font-size'
|
||||
'ctrl-+': 'window:increase-font-size'
|
||||
'ctrl--': 'window:decrease-font-size'
|
||||
@@ -57,12 +69,19 @@
|
||||
'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'
|
||||
'ctrl-alt-p': 'editor:log-cursor-scope'
|
||||
'ctrl-alt-shift-p': 'editor:log-cursor-scope'
|
||||
'ctrl-k ctrl-u': 'editor:upper-case'
|
||||
'ctrl-k ctrl-l': 'editor:lower-case'
|
||||
|
||||
|
||||
@@ -185,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' }
|
||||
|
||||
+2
-3
@@ -8,7 +8,7 @@
|
||||
{ label: 'Open Folder...', command: 'application:open-folder' }
|
||||
{ label: 'Reopen Last &Item', command: 'pane:reopen-closed-item' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Preferences...', command: 'application:show-settings' }
|
||||
{ label: 'Se&ttings', command: 'application:show-settings' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Save', command: 'core:save' }
|
||||
{ label: 'Save &As...', command: 'core:save-as' }
|
||||
@@ -164,10 +164,9 @@
|
||||
{
|
||||
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 }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Documentation', command: 'application:open-documentation' }
|
||||
{ type: 'separator' }
|
||||
|
||||
+60
-60
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "0.93.0",
|
||||
"version": "0.103.0",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
@@ -17,124 +17,124 @@
|
||||
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
|
||||
}
|
||||
],
|
||||
"atomShellVersion": "0.12.0",
|
||||
"atomShellVersion": "0.13.0",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"atom-keymap": "^0.19.0",
|
||||
"bootstrap": "git://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
|
||||
"atom-keymap": "^0.26.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",
|
||||
"emissary": "^1.2.1",
|
||||
"first-mate": "^1.5.3",
|
||||
"fs-plus": "^2.2.2",
|
||||
"first-mate": "^1.7",
|
||||
"fs-plus": "^2.2.3",
|
||||
"fstream": "0.1.24",
|
||||
"fuzzaldrin": "^1.1",
|
||||
"git-utils": "^1.3",
|
||||
"grim": "0.10.0",
|
||||
"guid": "0.0.10",
|
||||
"jasmine-tagged": "^1.1.1",
|
||||
"jasmine-tagged": "^1.1.2",
|
||||
"less-cache": "0.12.0",
|
||||
"mixto": "^1",
|
||||
"mkdirp": "0.3.5",
|
||||
"nslog": "0.5.0",
|
||||
"oniguruma": "^1.0.6",
|
||||
"optimist": "0.4.0",
|
||||
"pathwatcher": "^1.2.1",
|
||||
"pathwatcher": "^1.3.2",
|
||||
"property-accessors": "^1",
|
||||
"q": "^1.0.1",
|
||||
"random-words": "0.0.1",
|
||||
"react": "^0.10.0",
|
||||
"reactionary": "^0.8.0",
|
||||
"react-atom-fork": "^0.10.0",
|
||||
"reactionary-atom-fork": "^0.9.0",
|
||||
"runas": "^0.5",
|
||||
"scandal": "0.15.2",
|
||||
"scoped-property-store": "^0.9.0",
|
||||
"scrollbar-style": "^0.1.0",
|
||||
"scrollbar-style": "^0.4.0",
|
||||
"season": "^1.0.2",
|
||||
"semver": "1.1.4",
|
||||
"serializable": "^1",
|
||||
"space-pen": "3.1.1",
|
||||
"space-pen": "3.2.0",
|
||||
"temp": "0.5.0",
|
||||
"text-buffer": "^2.2.0",
|
||||
"text-buffer": "^2.2.2",
|
||||
"theorist": "^1",
|
||||
"underscore-plus": "^1.2.1",
|
||||
"underscore-plus": "^1.4.1",
|
||||
"vm-compatibility-layer": "0.1.0"
|
||||
},
|
||||
"packageDependencies": {
|
||||
"atom-dark-syntax": "0.15.0",
|
||||
"atom-dark-ui": "0.26.0",
|
||||
"atom-dark-syntax": "0.16.0",
|
||||
"atom-dark-ui": "0.29.0",
|
||||
"atom-light-syntax": "0.17.0",
|
||||
"atom-light-ui": "0.24.0",
|
||||
"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",
|
||||
"atom-light-ui": "0.25.0",
|
||||
"base16-tomorrow-dark-theme": "0.16.0",
|
||||
"solarized-dark-syntax": "0.17.0",
|
||||
"solarized-light-syntax": "0.8.0",
|
||||
"archive-view": "0.31.0",
|
||||
"autocomplete": "0.28.0",
|
||||
"autoflow": "0.17.0",
|
||||
"autosave": "0.13.0",
|
||||
"background-tips": "0.13.0",
|
||||
"background-tips": "0.14.0",
|
||||
"bookmarks": "0.22.0",
|
||||
"bracket-matcher": "0.33.0",
|
||||
"bracket-matcher": "0.43.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.100.0",
|
||||
"fuzzy-finder": "0.50.0",
|
||||
"deprecation-cop": "0.6.0",
|
||||
"dev-live-reload": "0.31.0",
|
||||
"exception-reporting": "0.18.0",
|
||||
"feedback": "0.33.0",
|
||||
"find-and-replace": "0.114.0",
|
||||
"fuzzy-finder": "0.54.0",
|
||||
"git-diff": "0.28.0",
|
||||
"go-to-line": "0.19.0",
|
||||
"grammar-selector": "0.26.0",
|
||||
"image-view": "0.33.0",
|
||||
"keybinding-resolver": "0.17.0",
|
||||
"go-to-line": "0.22.0",
|
||||
"grammar-selector": "0.27.0",
|
||||
"image-view": "0.34.0",
|
||||
"keybinding-resolver": "0.18.0",
|
||||
"link": "0.22.0",
|
||||
"markdown-preview": "0.69.0",
|
||||
"markdown-preview": "0.74.0",
|
||||
"metrics": "0.32.0",
|
||||
"open-on-github": "0.28.0",
|
||||
"package-generator": "0.30.0",
|
||||
"release-notes": "0.28.0",
|
||||
"settings-view": "0.114.0",
|
||||
"snippets": "0.43.0",
|
||||
"spell-check": "0.34.0",
|
||||
"release-notes": "0.32.0",
|
||||
"settings-view": "0.119.0",
|
||||
"snippets": "0.45.0",
|
||||
"spell-check": "0.36.0",
|
||||
"status-bar": "0.40.0",
|
||||
"styleguide": "0.29.0",
|
||||
"symbols-view": "0.50.0",
|
||||
"tabs": "0.39.0",
|
||||
"timecop": "0.18.0",
|
||||
"tree-view": "0.92.0",
|
||||
"symbols-view": "0.55.0",
|
||||
"tabs": "0.41.0",
|
||||
"timecop": "0.19.0",
|
||||
"tree-view": "0.98.0",
|
||||
"update-package-dependencies": "0.6.0",
|
||||
"welcome": "0.12.0",
|
||||
"welcome": "0.16.0",
|
||||
"whitespace": "0.22.0",
|
||||
"wrap-guide": "0.18.0",
|
||||
"language-c": "0.15.0",
|
||||
"language-c": "0.16.0",
|
||||
"language-coffee-script": "0.22.0",
|
||||
"language-css": "0.16.0",
|
||||
"language-gfm": "0.33.0",
|
||||
"language-css": "0.17.0",
|
||||
"language-gfm": "0.39.0",
|
||||
"language-git": "0.9.0",
|
||||
"language-go": "0.10.0",
|
||||
"language-html": "0.19.0",
|
||||
"language-hyperlink": "0.9.0",
|
||||
"language-go": "0.12.0",
|
||||
"language-html": "0.22.0",
|
||||
"language-hyperlink": "0.10.0",
|
||||
"language-java": "0.10.0",
|
||||
"language-javascript": "0.24.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-perl": "0.9.0",
|
||||
"language-php": "0.15.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.18.0",
|
||||
"language-ruby": "0.27.0",
|
||||
"language-ruby-on-rails": "0.14.0",
|
||||
"language-sass": "0.13.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.14.0",
|
||||
"language-yaml": "0.6.0"
|
||||
},
|
||||
"private": true,
|
||||
|
||||
Arquivo binário não exibido.
|
Antes Largura: | Altura: | Tamanho: 361 KiB Depois Largura: | Altura: | Tamanho: 141 KiB |
+31
-22
@@ -1,13 +1,8 @@
|
||||
#!/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");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var safeExec = require('./utils/child-process-wrapper.js').safeExec;
|
||||
var fs = require('fs');
|
||||
var verifyRequirements = require('./utils/verify-requirements');
|
||||
var safeExec = require('./utils/child-process-wrapper.js').safeExec;
|
||||
var path = require('path');
|
||||
|
||||
// Executes an array of commands one by one.
|
||||
@@ -21,33 +16,47 @@ function executeCommands(commands, done, index) {
|
||||
command = command.command;
|
||||
}
|
||||
safeExec(command, options, executeCommands.bind(this, commands, done, index + 1));
|
||||
} else
|
||||
}
|
||||
else
|
||||
done(null);
|
||||
}
|
||||
|
||||
var apmInstallPath = path.resolve(__dirname, '..', 'apm');
|
||||
if (!fs.existsSync(apmInstallPath))
|
||||
function bootstrap() {
|
||||
var apmInstallPath = path.resolve(__dirname, '..', 'apm');
|
||||
if (!fs.existsSync(apmInstallPath))
|
||||
fs.mkdirSync(apmInstallPath);
|
||||
if (!fs.existsSync(path.join(apmInstallPath, 'node_modules')))
|
||||
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 apmFlags = process.env.JANKY_SHA1 || process.argv.indexOf('--no-color') !== -1 ? '--no-color' : '';
|
||||
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');
|
||||
var npmFlags = ' --userconfig=' + path.resolve('.npmrc') + ' ';
|
||||
var npmPath = path.resolve(__dirname, '..', 'build', 'node_modules', '.bin', 'npm');
|
||||
var initialNpmCommand = fs.existsSync(npmPath) ? npmPath : 'npm';
|
||||
var npmFlags = ' --userconfig=' + path.resolve('.npmrc') + ' ';
|
||||
|
||||
var packagesToDedupe = ['fs-plus', 'humanize-plus', 'oniguruma', 'roaster', 'season'];
|
||||
var echoNewLine = process.platform == 'win32' ? 'echo.' : 'echo';
|
||||
var packagesToDedupe = ['fs-plus', 'humanize-plus', 'oniguruma', 'roaster', 'season'];
|
||||
var echoNewLine = process.platform == 'win32' ? 'echo.' : 'echo';
|
||||
|
||||
var commands = [
|
||||
{command: 'npm' + npmFlags + 'install --quiet', options: {cwd: path.resolve(__dirname, '..', 'build'), ignoreStdout: true}},
|
||||
var commands = [
|
||||
{command: initialNpmCommand + npmFlags + 'install --quiet', options: {cwd: path.resolve(__dirname, '..', 'build'), ignoreStdout: true}},
|
||||
{command: npmPath + npmFlags + 'install --quiet', options: {cwd: apmInstallPath, ignoreStdout: true}},
|
||||
echoNewLine,
|
||||
apmPath + ' clean ' + apmFlags,
|
||||
apmPath + ' install --quiet ' + apmFlags,
|
||||
apmPath + ' dedupe --quiet ' + apmFlags + ' ' + packagesToDedupe.join(' '),
|
||||
];
|
||||
];
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
executeCommands(commands, process.exit);
|
||||
process.chdir(path.dirname(__dirname));
|
||||
executeCommands(commands, process.exit);
|
||||
}
|
||||
|
||||
verifyRequirements(function(error, successMessage) {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(successMessage);
|
||||
bootstrap();
|
||||
});
|
||||
|
||||
+15
-13
@@ -28,17 +28,19 @@ function readEnvironmentVariables() {
|
||||
}
|
||||
|
||||
readEnvironmentVariables();
|
||||
cp.safeExec.bind(global, 'node script/bootstrap', function(error) {
|
||||
if (error)
|
||||
process.exit(1);
|
||||
require('fs-plus').removeSync.bind(global, path.join(homeDir, '.atom'))
|
||||
var async = require('async');
|
||||
var gruntPath = path.join('build', 'node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
|
||||
var tasks = [
|
||||
cp.safeExec.bind(global, 'git clean -dff'),
|
||||
cp.safeExec.bind(global, gruntPath + ' ci --gruntfile build/Gruntfile.coffee --stack --no-color'),
|
||||
]
|
||||
async.series(tasks, function(error) {
|
||||
process.exit(error ? 1 : 0);
|
||||
});
|
||||
cp.safeExec.bind(global, 'npm install npm', {cwd: path.resolve(__dirname, '..', 'build')}, function() {
|
||||
cp.safeExec.bind(global, 'node script/bootstrap', function(error) {
|
||||
if (error)
|
||||
process.exit(1);
|
||||
require('fs-plus').removeSync.bind(global, path.join(homeDir, '.atom'))
|
||||
var async = require('async');
|
||||
var gruntPath = path.join('build', 'node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
|
||||
var tasks = [
|
||||
cp.safeExec.bind(global, 'git clean -dff'),
|
||||
cp.safeExec.bind(global, gruntPath + ' ci --gruntfile build/Gruntfile.coffee --stack --no-color'),
|
||||
]
|
||||
async.series(tasks, function(error) {
|
||||
process.exit(error ? 1 : 0);
|
||||
});
|
||||
})();
|
||||
})();
|
||||
|
||||
+12
-4
@@ -1,14 +1,15 @@
|
||||
#!/usr/bin/env node
|
||||
var cp = require('./utils/child-process-wrapper.js');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var os = require('os');
|
||||
|
||||
var removeCommand = process.platform === 'win32' ? 'del /F /Q /S ' : 'rm -rf ';
|
||||
var removeCommand = process.platform === 'win32' ? 'rmdir /S /Q ' : 'rm -rf ';
|
||||
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';
|
||||
@@ -30,8 +31,15 @@ var run = function() {
|
||||
var next = commands.shift();
|
||||
if (!next)
|
||||
process.exit(0);
|
||||
if (Array.isArray(next))
|
||||
next = removeCommand + path.resolve.apply(path.resolve, next);
|
||||
|
||||
if (Array.isArray(next)) {
|
||||
var pathToRemove = path.resolve.apply(path.resolve, next);
|
||||
if (fs.existsSync(pathToRemove))
|
||||
next = removeCommand + pathToRemove;
|
||||
else
|
||||
return run();
|
||||
}
|
||||
|
||||
cp.safeExec(next, run);
|
||||
};
|
||||
run();
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
#!/usr/bin/env node
|
||||
var cp = require('./utils/child-process-wrapper.js');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
// node build/node_modules/.bin/grunt "$@"
|
||||
var gruntPath = path.resolve(__dirname, '..', 'build', 'node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
|
||||
|
||||
if (!fs.existsSync(gruntPath)) {
|
||||
console.error('Grunt command does not exist at: ' + gruntPath);
|
||||
console.error('Run script/bootstrap to install Grunt');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var args = ['--gruntfile', path.resolve('build', 'Gruntfile.coffee')];
|
||||
args = args.concat(process.argv.slice(2));
|
||||
cp.safeSpawn(gruntPath, args, process.exit);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
@IF EXIST "%~dp0\node.exe" (
|
||||
"%~dp0\node.exe" "%~dp0\grunt" %*
|
||||
) ELSE (
|
||||
node "%~dp0\grunt" %*
|
||||
)
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env coffee
|
||||
|
||||
path = require 'path'
|
||||
CommandInstaller = require '../src/command-installer'
|
||||
|
||||
callback = (error) ->
|
||||
console.warn error.message if error?
|
||||
|
||||
CommandInstaller.installAtomCommand(path.resolve(__dirname, '..'), callback)
|
||||
CommandInstaller.installApmCommand(path.resolve(__dirname, '..'), callback)
|
||||
+12
-8
@@ -1,31 +1,35 @@
|
||||
#!/bin/bash
|
||||
# mkdeb version control-file-path deb-file-path
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT=`readlink -f "$0"`
|
||||
ROOT=`readlink -f $(dirname $SCRIPT)/..`
|
||||
cd $ROOT
|
||||
|
||||
VERSION="$1"
|
||||
CONTROL_FILE="$2"
|
||||
DESKTOP_FILE="$3"
|
||||
ICON_FILE="$4"
|
||||
DEB_PATH="$5"
|
||||
ARCH="$2"
|
||||
CONTROL_FILE="$3"
|
||||
DESKTOP_FILE="$4"
|
||||
ICON_FILE="$5"
|
||||
DEB_PATH="$6"
|
||||
|
||||
TARGET_ROOT="`mktemp -d`"
|
||||
TARGET="$TARGET_ROOT/atom-$VERSION-amd64"
|
||||
chmod 755 "$TARGET_ROOT"
|
||||
TARGET="$TARGET_ROOT/atom-$VERSION-$ARCH"
|
||||
|
||||
mkdir -p "$TARGET/usr"
|
||||
env INSTALL_PREFIX="$TARGET/usr" script/grunt install
|
||||
|
||||
mkdir -p "$TARGET/DEBIAN"
|
||||
mv "$CONTROL_FILE" "$TARGET/DEBIAN/control"
|
||||
cp "$CONTROL_FILE" "$TARGET/DEBIAN/control"
|
||||
|
||||
mkdir -p "$TARGET/usr/share/applications"
|
||||
mv "$DESKTOP_FILE" "$TARGET/usr/share/applications"
|
||||
cp "$DESKTOP_FILE" "$TARGET/usr/share/applications"
|
||||
|
||||
mkdir -p "$TARGET/usr/share/pixmaps"
|
||||
cp "$ICON_FILE" "$TARGET/usr/share/pixmaps"
|
||||
|
||||
dpkg-deb -b "$TARGET"
|
||||
mv "$TARGET_ROOT/atom-$VERSION-amd64.deb" "$DEB_PATH"
|
||||
mv "$TARGET_ROOT/atom-$VERSION-$ARCH.deb" "$DEB_PATH"
|
||||
rm -rf $TARGET_ROOT
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var childProcess = require('child_process');
|
||||
|
||||
var pythonExecutable = process.env.PYTHON;
|
||||
|
||||
module.exports = function(cb) {
|
||||
verifyNode(function(error, nodeSuccessMessage) {
|
||||
if (error) {
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
verifyPython27(function(error, pythonSuccessMessage) {
|
||||
cb(error, (nodeSuccessMessage + "\n" + pythonSuccessMessage).trim());
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
function verifyNode(cb) {
|
||||
var nodeVersion = process.versions.node;
|
||||
var versionArray = nodeVersion.split('.');
|
||||
var nodeMajorVersion = +versionArray[0];
|
||||
var nodeMinorVersion = +versionArray[1];
|
||||
if (nodeMajorVersion === 0 && nodeMinorVersion < 10) {
|
||||
error = "node v0.10 is required to build Atom.";
|
||||
cb(error);
|
||||
}
|
||||
else {
|
||||
cb(null, "Node: v" + nodeVersion);
|
||||
}
|
||||
}
|
||||
|
||||
function verifyPython27(cb) {
|
||||
if (process.platform == 'win32') {
|
||||
if (!pythonExecutable) {
|
||||
var systemDrive = process.env.SystemDrive || 'C:\\';
|
||||
pythonExecutable = path.join(systemDrive, 'Python27', 'python.exe');
|
||||
|
||||
if (!fs.existsSync(pythonExecutable)) {
|
||||
pythonExecutable = 'python';
|
||||
}
|
||||
}
|
||||
|
||||
checkPythonVersion(pythonExecutable, cb);
|
||||
}
|
||||
else {
|
||||
cb(null, '');
|
||||
}
|
||||
}
|
||||
|
||||
function checkPythonVersion (python, cb) {
|
||||
var pythonHelpMessage = "Set the PYTHON env var to '/path/to/Python27/python.exe' if your python is installed in a non-default location.";
|
||||
|
||||
childProcess.execFile(python, ['-c', 'import platform; print(platform.python_version());'], { env: process.env }, function (err, stdout) {
|
||||
if (err) {
|
||||
error = "Python 2.7 is required to build Atom. An error (" + err + ") occured when checking the version of '" + python + "'. ";
|
||||
error += pythonHelpMessage;
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
var version = stdout.trim();
|
||||
if (~version.indexOf('+')) {
|
||||
version = version.replace(/\+/g, '');
|
||||
}
|
||||
if (~version.indexOf('rc')) {
|
||||
version = version.replace(/rc(.*)$/ig, '');
|
||||
}
|
||||
|
||||
// Atom requires python 2.7 or higher (but not python 3) for node-gyp
|
||||
var versionArray = version.split('.').map(function(num) { return +num; });
|
||||
var goodPythonVersion = (versionArray[0] === 2 && versionArray[1] >= 7);
|
||||
if (!goodPythonVersion) {
|
||||
error = "Python 2.7 is required to build Atom. '" + python + "' returns version " + version + ". ";
|
||||
error += pythonHelpMessage;
|
||||
cb(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Finally, if we've gotten this far, callback to resume the install process.
|
||||
cb(null, "Python: v" + version);
|
||||
});
|
||||
}
|
||||
@@ -7,8 +7,8 @@ sourceMaps = {}
|
||||
formatStackTrace = (spec, message='', stackTrace) ->
|
||||
return stackTrace unless stackTrace
|
||||
|
||||
jasminePattern = /^\s*at\s+.*\(?.*\/jasmine(-[^\/]*)?\.js:\d+:\d+\)?\s*$/
|
||||
firstJasmineLinePattern = /^\s*at \/.*\/jasmine(-[^\/]*)?\.js:\d+:\d+\)?\s*$/
|
||||
jasminePattern = /^\s*at\s+.*\(?.*[/\\]jasmine(-[^/\\]*)?\.js:\d+:\d+\)?\s*$/
|
||||
firstJasmineLinePattern = /^\s*at [/\\].*[/\\]jasmine(-[^/\\]*)?\.js:\d+:\d+\)?\s*$/
|
||||
convertedLines = []
|
||||
for line in stackTrace.split('\n')
|
||||
convertedLines.push(line) unless jasminePattern.test(line)
|
||||
@@ -23,7 +23,7 @@ formatStackTrace = (spec, message='', stackTrace) ->
|
||||
|
||||
for line, index in lines
|
||||
# Remove prefix of lines matching: at [object Object].<anonymous> (path:1:2)
|
||||
prefixMatch = line.match(/at \[object Object\]\.<anonymous> \(([^\)]+)\)/)
|
||||
prefixMatch = line.match(/at \[object Object\]\.<anonymous> \(([^)]+)\)/)
|
||||
line = "at #{prefixMatch[1]}" if prefixMatch
|
||||
|
||||
# Relativize locations to spec directory
|
||||
|
||||
@@ -380,8 +380,8 @@ describe "the `atom` global", ->
|
||||
|
||||
runs ->
|
||||
atom.packages.deactivatePackage('package-with-keymaps')
|
||||
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
|
||||
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 ->
|
||||
@@ -510,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)
|
||||
|
||||
@@ -17,6 +17,22 @@ describe "ContextMenuManager", ->
|
||||
expect(contextMenu.definitions['.selector'][0].label).toEqual 'label'
|
||||
expect(contextMenu.definitions['.selector'][0].command).toEqual 'command'
|
||||
|
||||
it "loads submenus", ->
|
||||
contextMenu.add 'file-path',
|
||||
'.selector':
|
||||
'parent':
|
||||
'child-1': 'child-1:trigger'
|
||||
'child-2': 'child-2:trigger'
|
||||
'parent-2': 'parent-2:trigger'
|
||||
|
||||
expect(contextMenu.definitions['.selector'].length).toBe 2
|
||||
expect(contextMenu.definitions['.selector'][0].label).toEqual 'parent'
|
||||
expect(contextMenu.definitions['.selector'][0].submenu.length).toBe 2
|
||||
expect(contextMenu.definitions['.selector'][0].submenu[0].label).toBe 'child-1'
|
||||
expect(contextMenu.definitions['.selector'][0].submenu[0].command).toBe 'child-1:trigger'
|
||||
expect(contextMenu.definitions['.selector'][0].submenu[1].label).toBe 'child-2'
|
||||
expect(contextMenu.definitions['.selector'][0].submenu[1].command).toBe 'child-2:trigger'
|
||||
|
||||
describe 'dev mode', ->
|
||||
it 'loads', ->
|
||||
contextMenu.add 'file-path',
|
||||
@@ -105,7 +121,7 @@ describe "ContextMenuManager", ->
|
||||
expect(menu[2].command).toEqual 'dev-command'
|
||||
expect(menu[3]).toBeUndefined()
|
||||
|
||||
describe "#executeBuildHandlers", ->
|
||||
describe "executeBuildHandlers", ->
|
||||
menuTemplate = [
|
||||
label: 'label'
|
||||
executeAtBuild: ->
|
||||
@@ -119,4 +135,3 @@ describe "ContextMenuManager", ->
|
||||
|
||||
expect(buildFn).toHaveBeenCalled()
|
||||
expect(buildFn.mostRecentCall.args[0]).toBe event
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
+524
-165
@@ -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')
|
||||
|
||||
@@ -26,66 +30,159 @@ describe "EditorComponent", ->
|
||||
atom.project.open('sample.js').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
wrapperView = new ReactEditorView(editor)
|
||||
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.measureScrollView()
|
||||
|
||||
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()
|
||||
component.measureScrollView()
|
||||
|
||||
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.lines
|
||||
spyOn(linesComponent, 'measureLineHeightAndDefaultCharWidth').andCallFake -> editor.setLineHeightInPixels(10)
|
||||
|
||||
initialLineHeightInPixels = editor.getLineHeightInPixels()
|
||||
component.setFontFamily('sans-serif')
|
||||
|
||||
expect(linesComponent.measureLineHeightAndDefaultCharWidth).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.measureScrollView()
|
||||
|
||||
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.measureScrollView()
|
||||
|
||||
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 ' '
|
||||
@@ -95,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 ' '
|
||||
@@ -108,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
|
||||
@@ -119,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 ' '
|
||||
@@ -136,98 +230,116 @@ describe "EditorComponent", ->
|
||||
describe "gutter rendering", ->
|
||||
it "renders the currently-visible line numbers", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureScrollView()
|
||||
|
||||
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)
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
node.style.width = 30 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureScrollView()
|
||||
|
||||
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'
|
||||
component.measureHeightAndWidth()
|
||||
node.style.width = 20 * lineHeightInPixels + 'px'
|
||||
component.measureScrollView()
|
||||
|
||||
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])
|
||||
cursor2 = editor.addCursorAtScreenPosition([8, 11])
|
||||
cursor3 = editor.addCursorAtScreenPosition([4, 10])
|
||||
|
||||
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.scrollTop = 4.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, #{(8 - 4.5) * lineHeightInPixels}px, 0px)"
|
||||
expect(cursorNodes[1].style['-webkit-transform']).toBe "translate3d(#{(10 - 3.5) * charWidth}px, #{(4 - 4.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')
|
||||
@@ -236,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)
|
||||
@@ -246,63 +358,23 @@ 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(cursorsNode.classList.contains('blink-off')).toBe true
|
||||
|
||||
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 false
|
||||
|
||||
advanceClock(component.props.cursorBlinkPeriod / 2)
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe false
|
||||
expect(cursorNode2.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
|
||||
|
||||
it "renders the hidden input field at the position of the last cursor if it is on screen", ->
|
||||
inputNode = node.querySelector('.hidden-input')
|
||||
node.style.height = 5 * lineHeightInPixels + 'px'
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, 0]
|
||||
editor.setScrollTop(3 * lineHeightInPixels)
|
||||
editor.setScrollLeft(3 * charWidth)
|
||||
expect(inputNode.offsetTop).toBe 0
|
||||
expect(inputNode.offsetLeft).toBe 0
|
||||
|
||||
editor.setCursorBufferPosition([5, 5])
|
||||
cursorRect = editor.getCursor().getPixelRect()
|
||||
cursorTop = cursorRect.top
|
||||
cursorLeft = cursorRect.left
|
||||
expect(inputNode.offsetTop).toBe cursorTop - editor.getScrollTop()
|
||||
expect(inputNode.offsetLeft).toBe cursorLeft - editor.getScrollLeft()
|
||||
expect(cursorsNode.classList.contains('blink-off')).toBe true
|
||||
|
||||
it "does not render cursors that are associated with non-empty selections", ->
|
||||
editor.setSelectedScreenRange([[0, 4], [4, 6]])
|
||||
@@ -310,13 +382,33 @@ 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)"
|
||||
|
||||
it "updates cursor positions when the line height changes", ->
|
||||
editor.setCursorBufferPosition([1, 10])
|
||||
component.setLineHeight(2)
|
||||
cursorNode = node.querySelector('.cursor')
|
||||
expect(cursorNode.style['-webkit-transform']).toBe "translate3d(#{10 * editor.getDefaultCharWidth()}px, #{editor.getLineHeightInPixels()}px, 0px)"
|
||||
|
||||
it "updates cursor positions when the font size changes", ->
|
||||
editor.setCursorBufferPosition([1, 10])
|
||||
component.setFontSize(10)
|
||||
cursorNode = node.querySelector('.cursor')
|
||||
expect(cursorNode.style['-webkit-transform']).toBe "translate3d(#{10 * editor.getDefaultCharWidth()}px, #{editor.getLineHeightInPixels()}px, 0px)"
|
||||
|
||||
it "updates cursor positions when the font family changes", ->
|
||||
editor.setCursorBufferPosition([1, 10])
|
||||
component.setFontFamily('sans-serif')
|
||||
cursorNode = node.querySelector('.cursor')
|
||||
|
||||
{left} = editor.pixelPositionForScreenPosition([1, 10])
|
||||
expect(cursorNode.style['-webkit-transform']).toBe "translate3d(#{left}px, #{editor.getLineHeightInPixels()}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", ->
|
||||
@@ -340,7 +432,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
|
||||
@@ -357,13 +449,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
|
||||
@@ -371,9 +463,74 @@ 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
|
||||
|
||||
it "updates selections when the line height changes", ->
|
||||
editor.setSelectedBufferRange([[1, 6], [1, 10]])
|
||||
component.setLineHeight(2)
|
||||
selectionNode = node.querySelector('.region')
|
||||
expect(selectionNode.offsetTop).toBe editor.getLineHeightInPixels()
|
||||
|
||||
it "updates selections when the font size changes", ->
|
||||
editor.setSelectedBufferRange([[1, 6], [1, 10]])
|
||||
component.setFontSize(10)
|
||||
selectionNode = node.querySelector('.region')
|
||||
expect(selectionNode.offsetTop).toBe editor.getLineHeightInPixels()
|
||||
expect(selectionNode.offsetLeft).toBe 6 * editor.getDefaultCharWidth()
|
||||
|
||||
it "updates selections when the font family changes", ->
|
||||
editor.setSelectedBufferRange([[1, 6], [1, 10]])
|
||||
component.setFontFamily('sans-serif')
|
||||
selectionNode = node.querySelector('.region')
|
||||
expect(selectionNode.offsetTop).toBe editor.getLineHeightInPixels()
|
||||
expect(selectionNode.offsetLeft).toBe editor.pixelPositionForScreenPosition([1, 6]).left
|
||||
|
||||
describe "hidden input field", ->
|
||||
it "renders the hidden input field at the position of the last cursor if the cursor is on screen and the editor is focused", ->
|
||||
editor.setVerticalScrollMargin(0)
|
||||
editor.setHorizontalScrollMargin(0)
|
||||
|
||||
inputNode = node.querySelector('.hidden-input')
|
||||
node.style.height = 5 * lineHeightInPixels + 'px'
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureScrollView()
|
||||
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, 0]
|
||||
editor.setScrollTop(3 * lineHeightInPixels)
|
||||
editor.setScrollLeft(3 * charWidth)
|
||||
|
||||
expect(inputNode.offsetTop).toBe 0
|
||||
expect(inputNode.offsetLeft).toBe 0
|
||||
|
||||
# In bounds, not focused
|
||||
editor.setCursorBufferPosition([5, 4])
|
||||
expect(inputNode.offsetTop).toBe 0
|
||||
expect(inputNode.offsetLeft).toBe 0
|
||||
|
||||
# In bounds and focused
|
||||
inputNode.focus()
|
||||
expect(inputNode.offsetTop).toBe (5 * lineHeightInPixels) - editor.getScrollTop()
|
||||
expect(inputNode.offsetLeft).toBe (4 * charWidth) - editor.getScrollLeft()
|
||||
|
||||
# In bounds, not focused
|
||||
inputNode.blur()
|
||||
expect(inputNode.offsetTop).toBe 0
|
||||
expect(inputNode.offsetLeft).toBe 0
|
||||
|
||||
# Out of bounds, not focused
|
||||
editor.setCursorBufferPosition([1, 2])
|
||||
expect(inputNode.offsetTop).toBe 0
|
||||
expect(inputNode.offsetLeft).toBe 0
|
||||
|
||||
# Out of bounds, focused
|
||||
inputNode.focus()
|
||||
expect(inputNode.offsetTop).toBe 0
|
||||
expect(inputNode.offsetLeft).toBe 0
|
||||
|
||||
describe "mouse interactions", ->
|
||||
linesNode = null
|
||||
@@ -387,7 +544,7 @@ describe "EditorComponent", ->
|
||||
it "moves the cursor to the nearest screen position", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureScrollView()
|
||||
editor.setScrollTop(3.5 * lineHeightInPixels)
|
||||
editor.setScrollLeft(2 * charWidth)
|
||||
|
||||
@@ -500,28 +657,28 @@ describe "EditorComponent", ->
|
||||
describe "scrolling", ->
|
||||
it "updates the vertical scrollbar when the scrollTop is changed in the model", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureScrollView()
|
||||
|
||||
expect(verticalScrollbarNode.scrollTop).toBe 0
|
||||
|
||||
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()
|
||||
component.measureScrollView()
|
||||
|
||||
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", ->
|
||||
node.style.width = 30 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
component.measureScrollView()
|
||||
|
||||
expect(editor.getScrollLeft()).toBe 0
|
||||
horizontalScrollbarNode.scrollLeft = 100
|
||||
@@ -529,22 +686,194 @@ 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.measureScrollView()
|
||||
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.measureScrollView()
|
||||
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.measureScrollView()
|
||||
|
||||
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.measureScrollView()
|
||||
|
||||
expect(verticalScrollbarNode.style.display).toBe ''
|
||||
expect(horizontalScrollbarNode.style.display).toBe 'none'
|
||||
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureScrollView()
|
||||
|
||||
expect(verticalScrollbarNode.style.display).toBe ''
|
||||
expect(horizontalScrollbarNode.style.display).toBe ''
|
||||
|
||||
node.style.height = 20 * lineHeightInPixels + 'px'
|
||||
component.measureScrollView()
|
||||
|
||||
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.measureScrollView()
|
||||
|
||||
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.measureScrollView()
|
||||
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.measureScrollView()
|
||||
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.measureScrollView()
|
||||
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.measureScrollView()
|
||||
|
||||
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)", ->
|
||||
|
||||
describe "mousewheel events", ->
|
||||
it "updates the scrollLeft or scrollTop on mousewheel events depending on which delta is greater (x or y)", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
node.style.width = 20 * charWidth + 'px'
|
||||
component.measureScrollView()
|
||||
|
||||
expect(verticalScrollbarNode.scrollTop).toBe 0
|
||||
expect(horizontalScrollbarNode.scrollLeft).toBe 0
|
||||
|
||||
node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -5, wheelDeltaY: -10))
|
||||
expect(verticalScrollbarNode.scrollTop).toBe 10
|
||||
expect(horizontalScrollbarNode.scrollLeft).toBe 0
|
||||
|
||||
node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -15, wheelDeltaY: -5))
|
||||
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()
|
||||
component.measureScrollView()
|
||||
|
||||
expect(verticalScrollbarNode.scrollTop).toBe 0
|
||||
expect(horizontalScrollbarNode.scrollLeft).toBe 0
|
||||
lineNode = node.querySelector('.line')
|
||||
wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -500)
|
||||
Object.defineProperty(wheelEvent, 'target', get: -> lineNode)
|
||||
node.dispatchEvent(wheelEvent)
|
||||
|
||||
node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -5, wheelDeltaY: -10))
|
||||
expect(verticalScrollbarNode.scrollTop).toBe 10
|
||||
expect(horizontalScrollbarNode.scrollLeft).toBe 0
|
||||
expect(node.contains(lineNode)).toBe true
|
||||
|
||||
node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -15, wheelDeltaY: -5))
|
||||
expect(verticalScrollbarNode.scrollTop).toBe 10
|
||||
expect(horizontalScrollbarNode.scrollLeft).toBe 15
|
||||
it "does not set the mouseWheelScreenRow if scrolling horizontally", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
node.style.width = 20 * charWidth + 'px'
|
||||
component.measureScrollView()
|
||||
|
||||
lineNode = node.querySelector('.line')
|
||||
wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 10, wheelDeltaY: 0)
|
||||
Object.defineProperty(wheelEvent, 'target', get: -> lineNode)
|
||||
node.dispatchEvent(wheelEvent)
|
||||
|
||||
expect(component.mouseWheelScreenRow).toBe null
|
||||
|
||||
it "clears the mouseWheelScreenRow after a delay even if the event does not cause scrolling", ->
|
||||
spyOn(_._, 'now').andCallFake -> window.now # Ensure _.debounce is based on our fake spec timeline
|
||||
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
|
||||
lineNode = node.querySelector('.line')
|
||||
wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: 10)
|
||||
Object.defineProperty(wheelEvent, 'target', get: -> lineNode)
|
||||
node.dispatchEvent(wheelEvent)
|
||||
|
||||
expect(editor.getScrollTop()).toBe 0
|
||||
|
||||
expect(component.mouseWheelScreenRow).toBe 0
|
||||
advanceClock(component.mouseWheelScreenRowClearDelay)
|
||||
expect(component.mouseWheelScreenRow).toBe null
|
||||
|
||||
it "does not preserve the line if it is on screen", ->
|
||||
expect(node.querySelectorAll('.line-number').length).toBe 14 # dummy line
|
||||
lineNodes = node.querySelectorAll('.line')
|
||||
expect(lineNodes.length).toBe 13
|
||||
lineNode = lineNodes[0]
|
||||
|
||||
wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: 100) # goes nowhere, we're already at scrollTop 0
|
||||
Object.defineProperty(wheelEvent, 'target', get: -> lineNode)
|
||||
node.dispatchEvent(wheelEvent)
|
||||
|
||||
expect(component.mouseWheelScreenRow).toBe 0
|
||||
editor.insertText("hello")
|
||||
expect(node.querySelectorAll('.line-number').length).toBe 14 # dummy line
|
||||
expect(node.querySelectorAll('.line').length).toBe 13
|
||||
|
||||
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.measureScrollView()
|
||||
|
||||
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
|
||||
@@ -581,3 +910,33 @@ 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
|
||||
|
||||
describe "when lines are changed while the editor is hidden", ->
|
||||
it "does not measure new characters until the editor is shown again", ->
|
||||
editor.setText('')
|
||||
wrapperView.hide()
|
||||
editor.setText('var z = 1')
|
||||
editor.setCursorBufferPosition([0, Infinity])
|
||||
wrapperView.show()
|
||||
expect(node.querySelector('.cursor').style['-webkit-transform']).toBe "translate3d(#{9 * charWidth}px, 0px, 0px)"
|
||||
|
||||
+102
-17
@@ -622,6 +622,38 @@ describe "Editor", ->
|
||||
editor.moveCursorToBeginningOfNextWord()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [11, 9]
|
||||
|
||||
describe ".moveCursorToBeginningOfNextParagraph()", ->
|
||||
it "moves the cursor before the first line of the next paragraph", ->
|
||||
editor.setCursorBufferPosition [0,6]
|
||||
cursor = editor.getCursor()
|
||||
|
||||
editor.moveCursorToBeginningOfNextParagraph()
|
||||
|
||||
expect(cursor.getBufferPosition()).toEqual { row : 10, column : 0 }
|
||||
|
||||
editor.setText("")
|
||||
editor.setCursorBufferPosition [0,0]
|
||||
cursor = editor.getCursor()
|
||||
editor.moveCursorToBeginningOfNextParagraph()
|
||||
|
||||
expect(cursor.getBufferPosition()).toEqual [0, 0]
|
||||
|
||||
describe ".moveCursorToBeginningOfPreviousParagraph()", ->
|
||||
it "moves the cursor before the first line of the pevious paragraph", ->
|
||||
editor.setCursorBufferPosition [10,0]
|
||||
cursor = editor.getCursor()
|
||||
|
||||
editor.moveCursorToBeginningOfPreviousParagraph()
|
||||
|
||||
expect(cursor.getBufferPosition()).toEqual { row : 0, column : 0 }
|
||||
|
||||
editor.setText("")
|
||||
editor.setCursorBufferPosition [0,0]
|
||||
cursor = editor.getCursor()
|
||||
editor.moveCursorToBeginningOfPreviousParagraph()
|
||||
|
||||
expect(cursor.getBufferPosition()).toEqual [0, 0]
|
||||
|
||||
describe ".getCurrentParagraphBufferRange()", ->
|
||||
it "returns the buffer range of the current paragraph, delimited by blank lines or the beginning / end of the file", ->
|
||||
buffer.setText """
|
||||
@@ -696,8 +728,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)
|
||||
|
||||
@@ -1134,10 +1167,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)
|
||||
@@ -1582,6 +1616,13 @@ describe "Editor", ->
|
||||
expect(editor.lineForBufferRow(1)).toBe ' '
|
||||
expect(editor.lineForBufferRow(2)).toBe '}'
|
||||
|
||||
describe "when a new line is appended before a closing tag (e.g. by pressing enter before a selection)", ->
|
||||
it "moves the line down and keeps the indentation level the same when editor.autoIndent is true", ->
|
||||
atom.config.set('editor.autoIndent', true)
|
||||
editor.setCursorBufferPosition([9,2])
|
||||
editor.insertNewline()
|
||||
expect(editor.lineForBufferRow(10)).toBe ' };'
|
||||
|
||||
describe ".backspace()", ->
|
||||
describe "when there is a single cursor", ->
|
||||
changeScreenRangeHandler = null
|
||||
@@ -1729,26 +1770,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]
|
||||
@@ -1756,24 +1797,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]
|
||||
@@ -1782,13 +1823,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;'
|
||||
|
||||
@@ -2061,17 +2102,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", ->
|
||||
@@ -2594,6 +2662,10 @@ describe "Editor", ->
|
||||
atom.workspace.open('sample-with-tabs.coffee', softTabs: true).then (editor) ->
|
||||
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()
|
||||
@@ -2811,6 +2883,18 @@ describe "Editor", ->
|
||||
expect(editor.lineForBufferRow(4)).toBe " }"
|
||||
expect(editor.lineForBufferRow(5)).toBe " i=1"
|
||||
|
||||
describe "soft and hard tabs", ->
|
||||
it "resets the tab style when tokenization is complete", ->
|
||||
editor.destroy()
|
||||
atom.project.open('sample-with-tabs-and-leading-comment.coffee').then (o) -> editor = o
|
||||
expect(editor.softTabs).toBe true
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-coffee-script')
|
||||
|
||||
runs ->
|
||||
expect(editor.softTabs).toBe false
|
||||
|
||||
describe ".destroy()", ->
|
||||
it "destroys all markers associated with the edit session", ->
|
||||
expect(buffer.getMarkerCount()).toBeGreaterThan 0
|
||||
@@ -3090,10 +3174,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
|
||||
|
||||
@@ -3105,7 +3190,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
|
||||
|
||||
|
||||
@@ -33,10 +33,10 @@ describe "EditorView", ->
|
||||
$('#jasmine-content').append(this)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-text', sync: true)
|
||||
atom.packages.activatePackage('language-text')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
getLineHeight = ->
|
||||
return cachedLineHeight if cachedLineHeight?
|
||||
@@ -1803,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')
|
||||
@@ -2406,20 +2413,21 @@ describe "EditorView", ->
|
||||
expect(editorView.getFirstVisibleScreenRow()).toBe(0)
|
||||
|
||||
describe ".checkoutHead()", ->
|
||||
[filePath, originalPathText] = []
|
||||
[filePath] = []
|
||||
|
||||
beforeEach ->
|
||||
filePath = atom.project.resolve('git/working-dir/file.txt')
|
||||
originalPathText = fs.readFileSync(filePath, 'utf8')
|
||||
workingDirPath = temp.mkdirSync('atom-working-dir')
|
||||
fs.copySync(path.join(__dirname, 'fixtures', 'git', 'working-dir'), workingDirPath)
|
||||
fs.renameSync(path.join(workingDirPath, 'git.git'), path.join(workingDirPath, '.git'))
|
||||
atom.project.setPath(workingDirPath)
|
||||
filePath = atom.project.resolve('file.txt')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
editorView.edit(editor)
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(filePath, originalPathText)
|
||||
|
||||
it "restores the contents of the editor view to the HEAD revision", ->
|
||||
editor.setText('')
|
||||
editor.save()
|
||||
@@ -2433,7 +2441,7 @@ describe "EditorView", ->
|
||||
fileChangeHandler.callCount > 0
|
||||
|
||||
runs ->
|
||||
expect(editor.getText()).toBe(originalPathText)
|
||||
expect(editor.getText()).toBe('undefined')
|
||||
|
||||
describe ".pixelPositionForBufferPosition(position)", ->
|
||||
describe "when the editor view is detached", ->
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
undefined
|
||||
Arquivo binário não exibido.
BIN
Arquivo binário não exibido.
BIN
Arquivo binário não exibido.
BIN
Arquivo binário não exibido.
BIN
Arquivo binário não exibido.
BIN
Arquivo binário não exibido.
+1
@@ -0,0 +1 @@
|
||||
x�ŽInB1³ö)úσ„¢°fÉ Úîvb�ù‘c†ãóQn�e=©žª,½· ÚÄ·9˜¡r&V!ªÆq¢êuH¢.Éâ)i6Ί|™àtuÞº�}-(+lUΉŒf_pUµQIcx�ßË€ßÚŽxoåÔ`wzáï}~ulçmYú(+•�QJ¤ëºvNþÿƒØµñž·ó1ÅiHL¢
|
||||
BIN
Arquivo binário não exibido.
BIN
Arquivo binário não exibido.
BIN
Arquivo binário não exibido.
@@ -1 +1 @@
|
||||
ef046e9eecaa5255ea5e9817132d4001724d6ae1
|
||||
8a9c86f1cb1f14b8f436eb91f4b052c8802ca99e
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Full of text
|
||||
@@ -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.');
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
# This is a comment
|
||||
if this.studyingEconomics
|
||||
buy() while supply > demand
|
||||
sell() until supply > demand
|
||||
+55
-96
@@ -4,6 +4,12 @@ fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
Task = require '../src/task'
|
||||
|
||||
copyRepository = ->
|
||||
workingDirPath = temp.mkdirSync('atom-working-dir')
|
||||
fs.copySync(path.join(__dirname, 'fixtures', 'git', 'working-dir'), workingDirPath)
|
||||
fs.renameSync(path.join(workingDirPath, 'git.git'), path.join(workingDirPath, '.git'))
|
||||
workingDirPath
|
||||
|
||||
describe "Git", ->
|
||||
repo = null
|
||||
|
||||
@@ -41,17 +47,13 @@ describe "Git", ->
|
||||
expect(repo.isPathIgnored('b.txt')).toBeFalsy()
|
||||
|
||||
describe ".isPathModified(path)", ->
|
||||
[repo, filePath, newPath, originalPathText] = []
|
||||
[repo, filePath, newPath] = []
|
||||
|
||||
beforeEach ->
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'working-dir'))
|
||||
filePath = require.resolve('./fixtures/git/working-dir/file.txt')
|
||||
newPath = path.join(__dirname, 'fixtures', 'git', 'working-dir', 'new-path.txt')
|
||||
originalPathText = fs.readFileSync(filePath, 'utf8')
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(filePath, originalPathText)
|
||||
fs.removeSync(newPath) if fs.existsSync(newPath)
|
||||
workingDirPath = copyRepository()
|
||||
repo = new Git(workingDirPath)
|
||||
filePath = path.join(workingDirPath, 'a.txt')
|
||||
newPath = path.join(workingDirPath, 'new-path.txt')
|
||||
|
||||
describe "when the path is unstaged", ->
|
||||
it "returns false if the path has not been modified", ->
|
||||
@@ -72,14 +74,12 @@ describe "Git", ->
|
||||
[filePath, newPath] = []
|
||||
|
||||
beforeEach ->
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'working-dir'))
|
||||
filePath = require.resolve('./fixtures/git/working-dir/file.txt')
|
||||
newPath = path.join(__dirname, 'fixtures', 'git', 'working-dir', 'new-path.txt')
|
||||
workingDirPath = copyRepository()
|
||||
repo = new Git(workingDirPath)
|
||||
filePath = path.join(workingDirPath, 'a.txt')
|
||||
newPath = path.join(workingDirPath, 'new-path.txt')
|
||||
fs.writeFileSync(newPath, "i'm new here")
|
||||
|
||||
afterEach ->
|
||||
fs.removeSync(newPath) if fs.existsSync(newPath)
|
||||
|
||||
describe "when the path is unstaged", ->
|
||||
it "returns true if the path is new", ->
|
||||
expect(repo.isPathNew(newPath)).toBeTruthy()
|
||||
@@ -88,48 +88,35 @@ describe "Git", ->
|
||||
expect(repo.isPathNew(filePath)).toBeFalsy()
|
||||
|
||||
describe ".checkoutHead(path)", ->
|
||||
[path1, path2, originalPath1Text, originalPath2Text] = []
|
||||
[filePath] = []
|
||||
|
||||
beforeEach ->
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'working-dir'))
|
||||
path1 = require.resolve('./fixtures/git/working-dir/file.txt')
|
||||
originalPath1Text = fs.readFileSync(path1, 'utf8')
|
||||
path2 = require.resolve('./fixtures/git/working-dir/other.txt')
|
||||
originalPath2Text = fs.readFileSync(path2, 'utf8')
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(path1, originalPath1Text)
|
||||
fs.writeFileSync(path2, originalPath2Text)
|
||||
workingDirPath = copyRepository()
|
||||
repo = new Git(workingDirPath)
|
||||
filePath = path.join(workingDirPath, 'a.txt')
|
||||
|
||||
it "no longer reports a path as modified after checkout", ->
|
||||
expect(repo.isPathModified(path1)).toBeFalsy()
|
||||
fs.writeFileSync(path1, '')
|
||||
expect(repo.isPathModified(path1)).toBeTruthy()
|
||||
expect(repo.checkoutHead(path1)).toBeTruthy()
|
||||
expect(repo.isPathModified(path1)).toBeFalsy()
|
||||
expect(repo.isPathModified(filePath)).toBeFalsy()
|
||||
fs.writeFileSync(filePath, 'ch ch changes')
|
||||
expect(repo.isPathModified(filePath)).toBeTruthy()
|
||||
expect(repo.checkoutHead(filePath)).toBeTruthy()
|
||||
expect(repo.isPathModified(filePath)).toBeFalsy()
|
||||
|
||||
it "restores the contents of the path to the original text", ->
|
||||
fs.writeFileSync(path1, '')
|
||||
expect(repo.checkoutHead(path1)).toBeTruthy()
|
||||
expect(fs.readFileSync(path1, 'utf8')).toBe(originalPath1Text)
|
||||
|
||||
it "only restores the path specified", ->
|
||||
fs.writeFileSync(path2, 'path 2 is edited')
|
||||
expect(repo.isPathModified(path2)).toBeTruthy()
|
||||
expect(repo.checkoutHead(path1)).toBeTruthy()
|
||||
expect(fs.readFileSync(path2, 'utf8')).toBe('path 2 is edited')
|
||||
expect(repo.isPathModified(path2)).toBeTruthy()
|
||||
fs.writeFileSync(filePath, 'ch ch changes')
|
||||
expect(repo.checkoutHead(filePath)).toBeTruthy()
|
||||
expect(fs.readFileSync(filePath, 'utf8')).toBe ''
|
||||
|
||||
it "fires a status-changed event if the checkout completes successfully", ->
|
||||
fs.writeFileSync(path1, '')
|
||||
repo.getPathStatus(path1)
|
||||
fs.writeFileSync(filePath, 'ch ch changes')
|
||||
repo.getPathStatus(filePath)
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
repo.on 'status-changed', statusHandler
|
||||
repo.checkoutHead(path1)
|
||||
repo.checkoutHead(filePath)
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler.argsForCall[0][0..1]).toEqual [path1, 0]
|
||||
expect(statusHandler.argsForCall[0][0..1]).toEqual [filePath, 0]
|
||||
|
||||
repo.checkoutHead(path1)
|
||||
repo.checkoutHead(filePath)
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
|
||||
describe ".destroy()", ->
|
||||
@@ -138,32 +125,13 @@ describe "Git", ->
|
||||
repo.destroy()
|
||||
expect(-> repo.getShortHead()).toThrow()
|
||||
|
||||
describe ".getDiffStats(path)", ->
|
||||
[filePath, originalPathText] = []
|
||||
|
||||
beforeEach ->
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'working-dir'))
|
||||
filePath = require.resolve('./fixtures/git/working-dir/file.txt')
|
||||
originalPathText = fs.readFileSync(filePath, 'utf8')
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(filePath, originalPathText)
|
||||
|
||||
it "returns the number of lines added and deleted", ->
|
||||
expect(repo.getDiffStats(filePath)).toEqual {added: 0, deleted: 0}
|
||||
fs.writeFileSync(filePath, "#{originalPathText} edited line")
|
||||
expect(repo.getDiffStats(filePath)).toEqual {added: 1, deleted: 1}
|
||||
|
||||
describe ".getPathStatus(path)", ->
|
||||
[filePath, originalPathText] = []
|
||||
[filePath] = []
|
||||
|
||||
beforeEach ->
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'working-dir'))
|
||||
filePath = require.resolve('./fixtures/git/working-dir/file.txt')
|
||||
originalPathText = fs.readFileSync(filePath, 'utf8')
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(filePath, originalPathText)
|
||||
workingDirectory = copyRepository()
|
||||
repo = new Git(workingDirectory)
|
||||
filePath = path.join(workingDirectory, 'file.txt')
|
||||
|
||||
it "trigger a status-changed event when the new status differs from the last cached one", ->
|
||||
statusHandler = jasmine.createSpy("statusHandler")
|
||||
@@ -178,16 +146,13 @@ describe "Git", ->
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
|
||||
describe ".getDirectoryStatus(path)", ->
|
||||
[directoryPath, filePath, originalPathText] = []
|
||||
[directoryPath, filePath] = []
|
||||
|
||||
beforeEach ->
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'working-dir'))
|
||||
directoryPath = path.join(__dirname, 'fixtures', 'git', 'working-dir', 'dir')
|
||||
filePath = require.resolve('./fixtures/git/working-dir/dir/b.txt')
|
||||
originalPathText = fs.readFileSync(filePath, 'utf8')
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(filePath, originalPathText)
|
||||
workingDirectory = copyRepository()
|
||||
repo = new Git(workingDirectory)
|
||||
directoryPath = path.join(workingDirectory, 'dir')
|
||||
filePath = path.join(directoryPath, 'b.txt')
|
||||
|
||||
it "gets the status based on the files inside the directory", ->
|
||||
expect(repo.isStatusModified(repo.getDirectoryStatus(directoryPath))).toBe false
|
||||
@@ -199,18 +164,15 @@ describe "Git", ->
|
||||
[newPath, modifiedPath, cleanPath, originalModifiedPathText] = []
|
||||
|
||||
beforeEach ->
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'working-dir'))
|
||||
modifiedPath = atom.project.resolve('git/working-dir/file.txt')
|
||||
originalModifiedPathText = fs.readFileSync(modifiedPath, 'utf8')
|
||||
newPath = atom.project.resolve('git/working-dir/untracked.txt')
|
||||
cleanPath = atom.project.resolve('git/working-dir/other.txt')
|
||||
workingDirectory = copyRepository()
|
||||
repo = new Git(workingDirectory)
|
||||
modifiedPath = path.join(workingDirectory, 'file.txt')
|
||||
newPath = path.join(workingDirectory, 'untracked.txt')
|
||||
cleanPath = path.join(workingDirectory, 'other.txt')
|
||||
fs.writeFileSync(cleanPath, 'Full of text')
|
||||
fs.writeFileSync(newPath, '')
|
||||
newPath = fs.absolute newPath # specs could be running under symbol path.
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(modifiedPath, originalModifiedPathText)
|
||||
fs.removeSync(newPath) if fs.existsSync(newPath)
|
||||
|
||||
it "returns status information for all new and modified files", ->
|
||||
fs.writeFileSync(modifiedPath, 'making this path modified')
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
@@ -226,17 +188,13 @@ describe "Git", ->
|
||||
expect(repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))).toBeTruthy()
|
||||
|
||||
describe "buffer events", ->
|
||||
[originalContent, editor] = []
|
||||
[editor] = []
|
||||
|
||||
beforeEach ->
|
||||
atom.project.setPath(copyRepository())
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
originalContent = editor.getText()
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(editor.getPath(), originalContent)
|
||||
atom.workspace.open('other.txt').then (o) -> editor = o
|
||||
|
||||
it "emits a status-changed event when a buffer is saved", ->
|
||||
editor.insertNewline()
|
||||
@@ -270,15 +228,16 @@ describe "Git", ->
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
|
||||
describe "when a project is deserialized", ->
|
||||
[originalContent, buffer, project2] = []
|
||||
[buffer, project2] = []
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(buffer.getPath(), originalContent)
|
||||
project2?.destroy()
|
||||
|
||||
it "subscribes to all the serialized buffers in the project", ->
|
||||
atom.project.setPath(copyRepository())
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js')
|
||||
atom.workspace.open('file.txt')
|
||||
|
||||
runs ->
|
||||
project2 = atom.project.testSerialization()
|
||||
|
||||
@@ -166,7 +166,8 @@ describe "PaneView", ->
|
||||
it "removes the pane item", ->
|
||||
editor = null
|
||||
jasmine.unspy(window, 'setTimeout')
|
||||
filePath = temp.openSync('atom').path
|
||||
filePath = path.join(temp.mkdirSync(), 'file.txt')
|
||||
fs.writeFileSync(filePath, '')
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath).then (o) -> editor = o
|
||||
|
||||
@@ -156,11 +156,20 @@ describe "Project", ->
|
||||
expect(atom.project.resolve('a')).toBe absolutePath
|
||||
expect(atom.project.resolve(absolutePath + '/../a')).toBe absolutePath
|
||||
expect(atom.project.resolve('a/../a')).toBe absolutePath
|
||||
expect(atom.project.resolve()).toBeUndefined()
|
||||
|
||||
describe "when passed a uri with a scheme", ->
|
||||
it "does not modify uris that begin with a scheme", ->
|
||||
expect(atom.project.resolve('http://zombo.com')).toBe 'http://zombo.com'
|
||||
|
||||
describe "when the project has no path", ->
|
||||
it "returns undefined for relative URIs", ->
|
||||
atom.project.setPath()
|
||||
expect(atom.project.resolve('test.txt')).toBeUndefined()
|
||||
expect(atom.project.resolve('http://github.com')).toBe 'http://github.com'
|
||||
absolutePath = fs.absolute(__dirname)
|
||||
expect(atom.project.resolve(absolutePath)).toBe absolutePath
|
||||
|
||||
describe ".setPath(path)", ->
|
||||
describe "when path is a file", ->
|
||||
it "sets its path to the files parent directory and updates the root directory", ->
|
||||
|
||||
@@ -14,8 +14,8 @@ try
|
||||
|
||||
{runSpecSuite} = require './jasmine-helper'
|
||||
|
||||
# Add 'src/exports' to module search path.
|
||||
exportsPath = path.resolve(atom.getLoadSettings().resourcePath, 'exports')
|
||||
# Add 'exports' to module search path.
|
||||
exportsPath = path.join(atom.getLoadSettings().resourcePath, 'exports')
|
||||
require('module').globalPaths.push(exportsPath)
|
||||
# Still set NODE_PATH since tasks may need it.
|
||||
process.env.NODE_PATH = exportsPath
|
||||
|
||||
@@ -261,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
|
||||
@@ -272,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
|
||||
|
||||
@@ -27,10 +27,8 @@ setSpecDirectory = (specDirectory) ->
|
||||
runAllSpecs = ->
|
||||
{resourcePath} = atom.getLoadSettings()
|
||||
|
||||
# Only run core specs when resource path is the Atom repository
|
||||
if Git.exists(resourcePath)
|
||||
requireSpecs(path.join(resourcePath, 'spec'))
|
||||
setSpecType('core')
|
||||
requireSpecs(path.join(resourcePath, 'spec'))
|
||||
setSpecType('core')
|
||||
|
||||
fixturesPackagesPath = path.join(__dirname, 'fixtures', 'packages')
|
||||
packagePaths = atom.packages.getAvailablePackageNames().map (packageName) ->
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -385,7 +385,58 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokens[1].value).toBeTruthy()
|
||||
expect(tokens[2].value).toBe 'xyz'
|
||||
|
||||
describe "when the grammar is tokenized", ->
|
||||
it "emits the `tokenized` event", ->
|
||||
editor = null
|
||||
tokenizedHandler = jasmine.createSpy("tokenized handler")
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample.js').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
|
||||
tokenizedBuffer.on 'tokenized', tokenizedHandler
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedHandler.callCount).toBe(1)
|
||||
|
||||
it "doesn't re-emit the `tokenized` event when it is re-tokenized", ->
|
||||
editor = null
|
||||
tokenizedHandler = jasmine.createSpy("tokenized handler")
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample.js').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
tokenizedBuffer.on 'tokenized', tokenizedHandler
|
||||
editor.getBuffer().insert([0, 0], "'")
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the grammar is updated because a grammar it includes is activated", ->
|
||||
it "re-emits the `tokenized` event", ->
|
||||
editor = null
|
||||
tokenizedBuffer = null
|
||||
tokenizedHandler = jasmine.createSpy("tokenized handler")
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('coffee.coffee').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
|
||||
tokenizedBuffer.on 'tokenized', tokenizedHandler
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
tokenizedHandler.reset()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-coffee-script')
|
||||
|
||||
runs ->
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedHandler.callCount).toBe(1)
|
||||
|
||||
it "retokenizes the buffer", ->
|
||||
|
||||
waitsForPromise ->
|
||||
|
||||
+14
-11
@@ -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) ->
|
||||
@@ -151,8 +151,8 @@ class Atom extends Model
|
||||
{devMode, safeMode, resourcePath} = @getLoadSettings()
|
||||
configDirPath = @getConfigDirPath()
|
||||
|
||||
# Add 'src/exports' to module search path.
|
||||
exportsPath = path.resolve(resourcePath, 'exports')
|
||||
# Add 'exports' to module search path.
|
||||
exportsPath = path.join(resourcePath, 'exports')
|
||||
require('module').globalPaths.push(exportsPath)
|
||||
# Still set NODE_PATH since tasks may need it.
|
||||
process.env.NODE_PATH = exportsPath
|
||||
@@ -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)
|
||||
@@ -473,12 +473,15 @@ class Atom extends Model
|
||||
# Public: Set the full screen state of the current window.
|
||||
setFullScreen: (fullScreen=false) ->
|
||||
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()
|
||||
|
||||
|
||||
@@ -60,6 +60,10 @@ class AtomApplication
|
||||
|
||||
constructor: (options) ->
|
||||
{@resourcePath, @version, @devMode, @safeMode} = options
|
||||
|
||||
# Normalize to make sure drive letter case is consistent on Windows
|
||||
@resourcePath = path.normalize(@resourcePath) if @resourcePath
|
||||
|
||||
global.atomApplication = this
|
||||
|
||||
@pidsToOpenWindows = {}
|
||||
@@ -146,6 +150,7 @@ class AtomApplication
|
||||
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()
|
||||
|
||||
@@ -305,13 +310,7 @@ class AtomApplication
|
||||
# :safeMode - Boolean to control the opened window's safe mode.
|
||||
# :windowDimensions - Object with height and width keys.
|
||||
openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions}={}) ->
|
||||
if pathToOpen
|
||||
[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
|
||||
{pathToOpen, initialLine, initialColumn} = @locationForPathToOpen(pathToOpen)
|
||||
|
||||
unless devMode
|
||||
existingWindow = @windowForPath(pathToOpen) unless pidToKillWhenClosed or newWindow
|
||||
@@ -414,6 +413,20 @@ class AtomApplication
|
||||
isSpec = true
|
||||
new AtomWindow({bootstrapScript, @resourcePath, isSpec})
|
||||
|
||||
locationForPathToOpen: (pathToOpen) ->
|
||||
return {pathToOpen} unless pathToOpen
|
||||
return {pathToOpen} if fs.existsSync(pathToOpen)
|
||||
|
||||
[fileToOpen, initialLine, initialColumn] = path.basename(pathToOpen).split(':')
|
||||
return {pathToOpen} unless initialLine
|
||||
return {pathToOpen} unless parseInt(initialLine) > 0
|
||||
|
||||
# Convert line numbers to a base of 0
|
||||
initialLine -= 1 if initialLine
|
||||
initialColumn -= 1 if initialColumn
|
||||
pathToOpen = path.join(path.dirname(pathToOpen), fileToOpen)
|
||||
{pathToOpen, initialLine, initialColumn}
|
||||
|
||||
# Opens a native dialog to prompt the user for a path.
|
||||
#
|
||||
# Once paths are selected, they're opened in a new or existing {AtomWindow}s.
|
||||
|
||||
@@ -21,6 +21,10 @@ class AtomWindow
|
||||
|
||||
constructor: (settings={}) ->
|
||||
{@resourcePath, pathToOpen, initialLine, initialColumn, @isSpec, @exitWhenDone} = settings
|
||||
|
||||
# Normalize to make sure drive letter case is consistent on Windows
|
||||
@resourcePath = path.normalize(@resourcePath) if @resourcePath
|
||||
|
||||
global.atomApplication.addWindow(this)
|
||||
|
||||
@browserWindow = new BrowserWindow show: false, title: 'Atom', icon: @constructor.iconPath
|
||||
@@ -29,6 +33,7 @@ class AtomWindow
|
||||
loadSettings = _.extend({}, settings)
|
||||
loadSettings.windowState ?= '{}'
|
||||
loadSettings.appVersion = app.getVersion()
|
||||
loadSettings.resourcePath = @resourcePath
|
||||
|
||||
# Only send to the first non-spec window created
|
||||
if @constructor.includeShellLoadTime and not @isSpec
|
||||
@@ -109,9 +114,15 @@ class AtomWindow
|
||||
new ContextMenu(menuTemplate, this)
|
||||
|
||||
if @isSpec
|
||||
# Workaround for https://github.com/atom/atom-shell/issues/380
|
||||
# Don't focus the window when it is being blurred during close or
|
||||
# else the app will crash on Windows.
|
||||
if process.platform is 'win32'
|
||||
@browserWindow.on 'close', => @isWindowClosing = true
|
||||
|
||||
# Spec window's web view should always have focus
|
||||
@browserWindow.on 'blur', =>
|
||||
@browserWindow.focusOnWebView()
|
||||
@browserWindow.focusOnWebView() unless @isWindowClosing
|
||||
|
||||
openPath: (pathToOpen, initialLine, initialColumn) ->
|
||||
if @loaded
|
||||
|
||||
@@ -19,4 +19,6 @@ class ContextMenu
|
||||
do (item) =>
|
||||
item.click = =>
|
||||
global.atomApplication.sendCommandToWindow(item.command, @atomWindow, item.commandOptions)
|
||||
else if item.submenu
|
||||
@createClickHandlers(item.submenu)
|
||||
item
|
||||
|
||||
@@ -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
|
||||
@@ -54,7 +54,9 @@ start = ->
|
||||
AtomApplication.open(args)
|
||||
console.log("App load time: #{Date.now() - global.shellStartTime}ms") unless args.test
|
||||
|
||||
global.devResourcePath = path.join(app.getHomeDir(), 'github', 'atom')
|
||||
global.devResourcePath = process.env.ATOM_DEV_RESOURCE_PATH ? path.join(app.getHomeDir(), 'github', 'atom')
|
||||
# Normalize to make sure drive letter case is consistent on Windows
|
||||
global.devResourcePath = path.normalize(global.devResourcePath) if global.devResourcePath
|
||||
|
||||
setupCrashReporter = ->
|
||||
crashReporter.start(productName: 'Atom', companyName: 'GitHub')
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
_ = require 'underscore-plus'
|
||||
ChildProcess = require 'child_process'
|
||||
|
||||
# Public: A wrapper which provides standard error/output line buffering for
|
||||
@@ -12,7 +13,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 +40,19 @@ 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"
|
||||
# Quote all arguments and escapes inner quotes
|
||||
cmdArgs = args.map (arg) -> "\"#{arg.replace(/"/g, '\\"')}\""
|
||||
cmdArgs.unshift("\"#{command}\"")
|
||||
cmdArgs = ['/s', '/c', "\"#{cmdArgs.join(' ')}\""]
|
||||
cmdOptions = _.clone(options)
|
||||
cmdOptions.windowsVerbatimArguments = true
|
||||
@process = ChildProcess.spawn(process.env.comspec or 'cmd.exe', cmdArgs, cmdOptions)
|
||||
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
|
||||
|
||||
@@ -33,8 +33,21 @@ class ContextMenuManager
|
||||
# Returns nothing.
|
||||
add: (name, object, {devMode}={}) ->
|
||||
for selector, items of object
|
||||
for label, command of items
|
||||
@addBySelector(selector, {label, command}, {devMode})
|
||||
for label, commandOrSubmenu of items
|
||||
if typeof commandOrSubmenu is 'object'
|
||||
submenu = []
|
||||
for submenuLabel, command of commandOrSubmenu
|
||||
submenu.push(@buildMenuItem(submenuLabel, command))
|
||||
@addBySelector(selector, {label: label, submenu: submenu}, {devMode})
|
||||
else
|
||||
menuItem = @buildMenuItem(label, commandOrSubmenu)
|
||||
@addBySelector(selector, menuItem, {devMode})
|
||||
|
||||
buildMenuItem: (label, command) ->
|
||||
if label is command is '-'
|
||||
{type: 'separator'}
|
||||
else
|
||||
{label, command}
|
||||
|
||||
# Registers a command to be displayed when the relevant item is right
|
||||
# clicked.
|
||||
@@ -98,5 +111,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
|
||||
{editor, screenRange, scrollTop, scrollLeft} = @props
|
||||
{top, left, height, width} = editor.pixelRectForScreenRange(screenRange)
|
||||
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
|
||||
|
||||
+38
-1
@@ -457,6 +457,43 @@ class Cursor extends Model
|
||||
getCurrentLineBufferRange: (options) ->
|
||||
@editor.bufferRangeForBufferRow(@getBufferRow(), options)
|
||||
|
||||
# Public: Moves the cursor to the beginning of the next paragraph
|
||||
moveToBeginningOfNextParagraph: ->
|
||||
if position = @getBeginningOfNextParagraphBufferPosition()
|
||||
@setBufferPosition(position)
|
||||
|
||||
# Public: Moves the cursor to the beginning of the previous paragraph
|
||||
moveToBeginningOfPreviousParagraph: ->
|
||||
if position = @getBeginningOfPreviousParagraphBufferPosition()
|
||||
@setBufferPosition(position)
|
||||
|
||||
getBeginningOfNextParagraphBufferPosition: (editor) ->
|
||||
start = @getBufferPosition()
|
||||
eof = @editor.getEofBufferPosition()
|
||||
scanRange = [start, eof]
|
||||
|
||||
{row, column} = eof
|
||||
position = new Point(row, column - 1)
|
||||
|
||||
@editor.scanInBufferRange /^\n*$/g, scanRange, ({range, stop}) =>
|
||||
if !range.start.isEqual(start)
|
||||
position = range.start
|
||||
stop()
|
||||
@editor.screenPositionForBufferPosition(position)
|
||||
|
||||
getBeginningOfPreviousParagraphBufferPosition: (editor) ->
|
||||
start = @editor.getCursorBufferPosition()
|
||||
|
||||
{row, column} = start
|
||||
scanRange = [[row-1, column], [0,0]]
|
||||
position = new Point(0, 0)
|
||||
zero = new Point(0,0)
|
||||
@editor.backwardsScanInBufferRange /^\n*$/g, scanRange, ({range, stop}) =>
|
||||
if !range.start.isEqual(zero)
|
||||
position = range.start
|
||||
stop()
|
||||
@editor.screenPositionForBufferPosition(position)
|
||||
|
||||
# Public: Retrieves the range for the current paragraph.
|
||||
#
|
||||
# A paragraph is defined as a block of text surrounded by empty lines.
|
||||
@@ -465,7 +502,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, isEqualForProperties, isEqual} = require 'underscore-plus'
|
||||
SubscriberMixin = require './subscriber-mixin'
|
||||
CursorComponent = require './cursor-component'
|
||||
|
||||
|
||||
module.exports =
|
||||
CursorsComponent = React.createClass
|
||||
displayName: 'CursorsComponent'
|
||||
@@ -13,38 +12,46 @@ CursorsComponent = React.createClass
|
||||
cursorBlinkIntervalHandle: null
|
||||
|
||||
render: ->
|
||||
{editor} = @props
|
||||
blinkOff = @state.blinkCursorsOff
|
||||
{editor, cursorScreenRanges, 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})
|
||||
for key, screenRange of cursorScreenRanges
|
||||
CursorComponent({key, editor, screenRange, scrollTop, scrollLeft})
|
||||
|
||||
getInitialState: ->
|
||||
blinkCursorsOff: false
|
||||
blinkOff: false
|
||||
|
||||
componentDidMount: ->
|
||||
{editor} = @props
|
||||
@startBlinkingCursors()
|
||||
|
||||
componentWillUnmount: ->
|
||||
clearInterval(@cursorBlinkIntervalHandle)
|
||||
@stopBlinkingCursors()
|
||||
|
||||
componentWillUpdate: ({cursorsMoved}) ->
|
||||
@pauseCursorBlinking() if cursorsMoved
|
||||
shouldComponentUpdate: (newProps, newState) ->
|
||||
not newState.blinkOff is @state.blinkOff or
|
||||
not isEqualForProperties(newProps, @props, 'cursorScreenRanges', 'scrollTop', 'scrollLeft', 'lineHeightInPixels', 'defaultCharWidth')
|
||||
|
||||
componentWillUpdate: (newProps) ->
|
||||
@pauseCursorBlinking() if @props.cursorScreenRanges and not isEqual(newProps.cursorScreenRanges, @props.cursorScreenRanges)
|
||||
|
||||
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()
|
||||
|
||||
+79
-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
|
||||
@@ -44,6 +46,7 @@ class DisplayBuffer extends Model
|
||||
@updateAllScreenLines()
|
||||
@createFoldForMarker(marker) for marker in @buffer.findMarkers(@getFoldMarkerAttributes())
|
||||
@subscribe @tokenizedBuffer, 'grammar-changed', (grammar) => @emit 'grammar-changed', grammar
|
||||
@subscribe @tokenizedBuffer, 'tokenized', => @emit 'tokenized'
|
||||
@subscribe @tokenizedBuffer, 'changed', @handleTokenizedBufferChange
|
||||
@subscribe @buffer, 'markers-updated', @handleBufferMarkersUpdated
|
||||
@subscribe @buffer, 'marker-created', @handleBufferMarkerCreated
|
||||
@@ -111,32 +114,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 +199,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 +228,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 +255,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 +286,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 +513,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 +528,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)
|
||||
|
||||
|
||||
+492
-70
@@ -1,10 +1,15 @@
|
||||
React = require 'react'
|
||||
{div, span} = require 'reactionary'
|
||||
{debounce} = require 'underscore-plus'
|
||||
React = require 'react-atom-fork'
|
||||
{div, span} = require 'reactionary-atom-fork'
|
||||
{debounce, defaults, isEqualForProperties} = require 'underscore-plus'
|
||||
scrollbarStyle = require 'scrollbar-style'
|
||||
{Range, Point} = require 'text-buffer'
|
||||
|
||||
GutterComponent = require './gutter-component'
|
||||
EditorScrollViewComponent = require './editor-scroll-view-component'
|
||||
InputComponent = require './input-component'
|
||||
CursorsComponent = require './cursors-component'
|
||||
LinesComponent = require './lines-component'
|
||||
ScrollbarComponent = require './scrollbar-component'
|
||||
ScrollbarCornerComponent = require './scrollbar-corner-component'
|
||||
SubscriberMixin = require './subscriber-mixin'
|
||||
|
||||
module.exports =
|
||||
@@ -18,35 +23,76 @@ 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
|
||||
mouseWheelScreenRowClearDelay: 150
|
||||
scrollViewMeasurementRequested: false
|
||||
overflowChangedEventsPaused: false
|
||||
overflowChangedWhilePaused: false
|
||||
measureLineHeightAndDefaultCharWidthWhenShown: false
|
||||
|
||||
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()
|
||||
[renderedStartRow, renderedEndRow] = renderedRowRange
|
||||
cursorScreenRanges = @getCursorScreenRanges(renderedRowRange)
|
||||
selectionScreenRanges = @getSelectionScreenRanges(renderedRowRange)
|
||||
scrollHeight = editor.getScrollHeight()
|
||||
scrollWidth = editor.getScrollWidth()
|
||||
scrollTop = editor.getScrollTop()
|
||||
scrollLeft = editor.getScrollLeft()
|
||||
lineHeightInPixels = editor.getLineHeight()
|
||||
lineHeightInPixels = editor.getLineHeightInPixels()
|
||||
defaultCharWidth = editor.getDefaultCharWidth()
|
||||
scrollViewHeight = editor.getHeight()
|
||||
horizontalScrollbarHeight = editor.getHorizontalScrollbarHeight()
|
||||
verticalScrollbarWidth = editor.getVerticalScrollbarWidth()
|
||||
verticallyScrollable = editor.verticallyScrollable()
|
||||
horizontallyScrollable = editor.horizontallyScrollable()
|
||||
hiddenInputStyle = @getHiddenInputPosition()
|
||||
hiddenInputStyle.WebkitTransform = 'translateZ(0)'
|
||||
if @mouseWheelScreenRow? and not (renderedStartRow <= @mouseWheelScreenRow < renderedEndRow)
|
||||
mouseWheelScreenRow = @mouseWheelScreenRow
|
||||
|
||||
className = 'editor editor-colors react'
|
||||
className = 'editor-contents editor-colors'
|
||||
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, lineHeightInPixels, @pendingChanges, mouseWheelScreenRow
|
||||
}
|
||||
|
||||
EditorScrollViewComponent {
|
||||
ref: 'scrollView', editor, fontSize, fontFamily, showIndentGuide
|
||||
scrollHeight, scrollWidth, lineHeight: lineHeightInPixels,
|
||||
renderedRowRange, @pendingChanges, @scrollingVertically, @cursorsMoved,
|
||||
cursorBlinkPeriod, cursorBlinkResumeDelay, @onInputFocused, @onInputBlurred
|
||||
}
|
||||
div ref: 'scrollView', className: 'scroll-view', onMouseDown: @onMouseDown,
|
||||
InputComponent
|
||||
ref: 'input'
|
||||
className: 'hidden-input'
|
||||
style: hiddenInputStyle
|
||||
onInput: @onInput
|
||||
onFocus: @onInputFocused
|
||||
onBlur: @onInputBlurred
|
||||
|
||||
CursorsComponent {
|
||||
editor, scrollTop, scrollLeft, cursorScreenRanges, cursorBlinkPeriod, cursorBlinkResumeDelay,
|
||||
lineHeightInPixels, defaultCharWidth
|
||||
}
|
||||
LinesComponent {
|
||||
ref: 'lines', editor, lineHeightInPixels, defaultCharWidth,
|
||||
showIndentGuide, renderedRowRange, @pendingChanges, scrollTop, scrollLeft, @scrollingVertically,
|
||||
selectionScreenRanges, scrollHeight, scrollWidth, mouseWheelScreenRow, invisibles,
|
||||
visible, scrollViewHeight
|
||||
}
|
||||
|
||||
ScrollbarComponent
|
||||
ref: 'verticalScrollbar'
|
||||
@@ -55,6 +101,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 +112,27 @@ 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
|
||||
|
||||
getRenderedRowRange: ->
|
||||
renderedRowRange = @props.editor.getVisibleRowRange()
|
||||
if @preservedRowRange?
|
||||
renderedRowRange[0] = Math.min(@preservedRowRange[0], renderedRowRange[0])
|
||||
renderedRowRange[1] = Math.max(@preservedRowRange[1], renderedRowRange[1])
|
||||
renderedRowRange
|
||||
# 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
|
||||
|
||||
getInitialState: -> {}
|
||||
getInitialState: ->
|
||||
visible: true
|
||||
|
||||
getDefaultProps: ->
|
||||
cursorBlinkPeriod: 800
|
||||
cursorBlinkResumeDelay: 200
|
||||
cursorBlinkResumeDelay: 100
|
||||
lineOverdrawMargin: 8
|
||||
|
||||
componentWillMount: ->
|
||||
@pendingChanges = []
|
||||
@@ -83,45 +140,116 @@ EditorComponent = React.createClass
|
||||
@observeConfig()
|
||||
|
||||
componentDidMount: ->
|
||||
{editor} = @props
|
||||
|
||||
@observeEditor()
|
||||
@listenForDOMEvents()
|
||||
@listenForCommands()
|
||||
@props.editor.setVisible(true)
|
||||
@requestUpdate()
|
||||
|
||||
@subscribe atom.themes, 'stylesheet-added stylsheet-removed', @onStylesheetsChanged
|
||||
@subscribe scrollbarStyle.changes, @refreshScrollbars
|
||||
|
||||
editor.setVisible(true)
|
||||
|
||||
editor.batchUpdates =>
|
||||
@measureLineHeightAndDefaultCharWidth()
|
||||
@measureScrollView()
|
||||
@measureScrollbars()
|
||||
|
||||
componentWillUnmount: ->
|
||||
@unsubscribe()
|
||||
@getDOMNode().removeEventListener 'mousewheel', @onMouseWheel
|
||||
window.removeEventListener('resize', @onWindowResize)
|
||||
|
||||
componentWillUpdate: ->
|
||||
@props.parentView.trigger 'cursor:moved' if @cursorsMoved
|
||||
|
||||
componentDidUpdate: ->
|
||||
componentDidUpdate: (prevProps, prevState) ->
|
||||
@pendingChanges.length = 0
|
||||
@cursorsMoved = false
|
||||
@refreshingScrollbars = false
|
||||
@measureScrollbars() if @measuringScrollbars
|
||||
@measureLineHeightAndCharWidthsIfNeeded(prevState)
|
||||
@pauseOverflowChangedEvents()
|
||||
@props.parentView.trigger 'editor:display-updated'
|
||||
|
||||
requestUpdate: ->
|
||||
if @batchingUpdates
|
||||
@updateRequested = true
|
||||
else
|
||||
@forceUpdate()
|
||||
|
||||
getRenderedRowRange: ->
|
||||
{editor, lineOverdrawMargin} = @props
|
||||
[visibleStartRow, visibleEndRow] = editor.getVisibleRowRange()
|
||||
renderedStartRow = Math.max(0, visibleStartRow - lineOverdrawMargin)
|
||||
renderedEndRow = Math.min(editor.getScreenLineCount(), visibleEndRow + lineOverdrawMargin)
|
||||
[renderedStartRow, renderedEndRow]
|
||||
|
||||
getHiddenInputPosition: ->
|
||||
{editor} = @props
|
||||
{focused} = @state
|
||||
return {top: 0, left: 0} unless @isMounted() and focused and editor.getCursor()?
|
||||
|
||||
{top, left, height, width} = editor.getCursor().getPixelRect()
|
||||
width = 2 if width is 0 # Prevent autoscroll at the end of longest line
|
||||
top -= editor.getScrollTop()
|
||||
left -= editor.getScrollLeft()
|
||||
top = Math.max(0, Math.min(editor.getHeight() - height, top))
|
||||
left = Math.max(0, Math.min(editor.getWidth() - width, left))
|
||||
{top, left}
|
||||
|
||||
getCursorScreenRanges: (renderedRowRange) ->
|
||||
{editor} = @props
|
||||
[renderedStartRow, renderedEndRow] = renderedRowRange
|
||||
|
||||
cursorScreenRanges = {}
|
||||
for selection in editor.getSelections() when selection.isEmpty()
|
||||
{cursor} = selection
|
||||
screenRange = cursor.getScreenRange()
|
||||
if renderedStartRow <= screenRange.start.row < renderedEndRow
|
||||
cursorScreenRanges[cursor.id] = screenRange
|
||||
cursorScreenRanges
|
||||
|
||||
getSelectionScreenRanges: (renderedRowRange) ->
|
||||
{editor} = @props
|
||||
[renderedStartRow, renderedEndRow] = renderedRowRange
|
||||
|
||||
selectionScreenRanges = {}
|
||||
for selection, index in editor.getSelections()
|
||||
screenRange = selection.getScreenRange()
|
||||
|
||||
if not screenRange.isEmpty() and screenRange.intersectsRowRange(renderedStartRow, renderedEndRow)
|
||||
selectionScreenRanges[selection.id] = screenRange
|
||||
|
||||
else if index is 0 # Rendering artifacts occur on the lines GPU layer if we remove the last selection
|
||||
selectionScreenRanges[selection.id] = new Range(new Point(renderedStartRow, 0), new Point(renderedStartRow, 0))
|
||||
|
||||
selectionScreenRanges
|
||||
|
||||
observeEditor: ->
|
||||
{editor} = @props
|
||||
@subscribe editor, 'batched-updates-started', @onBatchedUpdatesStarted
|
||||
@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()
|
||||
node.addEventListener 'mousewheel', @onMouseWheel
|
||||
node.addEventListener 'focus', @onFocus # For some reason, React's built in focus events seem to bubble
|
||||
|
||||
scrollViewNode = @refs.scrollView.getDOMNode()
|
||||
scrollViewNode.addEventListener 'overflowchanged', @onScrollViewOverflowChanged
|
||||
scrollViewNode.addEventListener 'scroll', @onScrollViewScroll
|
||||
window.addEventListener('resize', @onWindowResize)
|
||||
|
||||
listenForCommands: ->
|
||||
{parentView, editor, mini} = @props
|
||||
|
||||
@@ -141,11 +269,13 @@ 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()
|
||||
'editor:move-to-beginning-of-next-paragraph': => editor.moveCursorToBeginningOfNextParagraph()
|
||||
'editor:move-to-beginning-of-previous-paragraph': => editor.moveCursorToBeginningOfPreviousParagraph()
|
||||
'editor:move-to-beginning-of-screen-line': => editor.moveCursorToBeginningOfScreenLine()
|
||||
'editor:move-to-beginning-of-line': => editor.moveCursorToBeginningOfLine()
|
||||
'editor:move-to-end-of-screen-line': => editor.moveCursorToEndOfScreenLine()
|
||||
@@ -218,6 +348,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,22 +359,13 @@ 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
|
||||
|
||||
setFontSize: (fontSize) ->
|
||||
@setState({fontSize})
|
||||
|
||||
setLineHeight: (lineHeight) ->
|
||||
@setState({lineHeight})
|
||||
|
||||
setFontFamily: (fontFamily) ->
|
||||
@setState({fontFamily})
|
||||
|
||||
setShowIndentGuide: (showIndentGuide) ->
|
||||
@setState({showIndentGuide})
|
||||
@subscribe atom.config.observe 'editor.invisibles', @setInvisibles
|
||||
@subscribe atom.config.observe 'editor.showInvisibles', @setShowInvisibles
|
||||
|
||||
onFocus: ->
|
||||
@refs.scrollView.focus()
|
||||
@refs.input.focus()
|
||||
|
||||
onInputFocused: ->
|
||||
@setState(focused: true)
|
||||
@@ -276,21 +398,73 @@ EditorComponent = React.createClass
|
||||
@pendingScrollLeft = null
|
||||
|
||||
onMouseWheel: (event) ->
|
||||
event.preventDefault()
|
||||
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
|
||||
# Scrolling horizontally
|
||||
@pendingHorizontalScrollDelta -= wheelDeltaX
|
||||
else
|
||||
@refs.verticalScrollbar.getDOMNode().scrollTop -= wheelDeltaY
|
||||
# Scrolling vertically
|
||||
@pendingVerticalScrollDelta -= wheelDeltaY
|
||||
@mouseWheelScreenRow = @screenRowForNode(event.target)
|
||||
@clearMouseWheelScreenRowAfterDelay ?= debounce(@clearMouseWheelScreenRow, @mouseWheelScreenRowClearDelay)
|
||||
@clearMouseWheelScreenRowAfterDelay()
|
||||
|
||||
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
|
||||
@requestUpdate()
|
||||
onScrollViewOverflowChanged: ->
|
||||
if @overflowChangedEventsPaused
|
||||
@overflowChangedWhilePaused = true
|
||||
else
|
||||
@requestScrollViewMeasurement()
|
||||
|
||||
clearPreservedRowRangeAfterDelay: null # Created lazily
|
||||
onWindowResize: ->
|
||||
@requestScrollViewMeasurement()
|
||||
|
||||
onScrollViewScroll: ->
|
||||
console.warn "EditorScrollView scroll position changed, and it shouldn't have. If you can reproduce this, please report it."
|
||||
scrollViewNode = @refs.scrollView.getDOMNode()
|
||||
scrollViewNode.scrollTop = 0
|
||||
scrollViewNode.scrollLeft = 0
|
||||
|
||||
onInput: (char, replaceLastCharacter) ->
|
||||
{editor} = @props
|
||||
|
||||
if replaceLastCharacter
|
||||
editor.transact ->
|
||||
editor.selectLeft()
|
||||
editor.insertText(char)
|
||||
else
|
||||
editor.insertText(char)
|
||||
|
||||
onMouseDown: (event) ->
|
||||
{editor} = @props
|
||||
{detail, shiftKey, metaKey} = event
|
||||
screenPosition = @screenPositionForMouseEvent(event)
|
||||
|
||||
if shiftKey
|
||||
editor.selectToScreenPosition(screenPosition)
|
||||
else if metaKey
|
||||
editor.addCursorAtScreenPosition(screenPosition)
|
||||
else
|
||||
editor.setCursorScreenPosition(screenPosition)
|
||||
switch detail
|
||||
when 2 then editor.selectWord()
|
||||
when 3 then editor.selectLine()
|
||||
|
||||
@selectToMousePositionUntilMouseUp(event)
|
||||
|
||||
onStylesheetsChanged: (stylesheet) ->
|
||||
@refreshScrollbars() if @containsScrollbarSelector(stylesheet)
|
||||
|
||||
onBatchedUpdatesStarted: ->
|
||||
@batchingUpdates = true
|
||||
@@ -307,32 +481,280 @@ 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()
|
||||
@onStoppedScrollingAfterDelay ?= debounce(@onStoppedScrolling, 100)
|
||||
@onStoppedScrollingAfterDelay()
|
||||
|
||||
onStoppedScrolling: ->
|
||||
@scrollingVertically = false
|
||||
@mouseWheelScreenRow = null
|
||||
@requestUpdate()
|
||||
|
||||
onSelectionRemoved: (selection) ->
|
||||
{editor} = @props
|
||||
@requestUpdate() if editor.selectionIntersectsVisibleRowRange(selection)
|
||||
onStoppedScrollingAfterDelay: null # created lazily
|
||||
|
||||
onCursorsMoved: ->
|
||||
@cursorsMoved = true
|
||||
|
||||
requestUpdate: ->
|
||||
if @batchingUpdates
|
||||
@updateRequested = true
|
||||
else
|
||||
@forceUpdate()
|
||||
selectToMousePositionUntilMouseUp: (event) ->
|
||||
{editor} = @props
|
||||
dragging = false
|
||||
lastMousePosition = {}
|
||||
|
||||
measureHeightAndWidth: ->
|
||||
@refs.scrollView.measureHeightAndWidth()
|
||||
animationLoop = =>
|
||||
requestAnimationFrame =>
|
||||
if dragging
|
||||
@selectToMousePosition(lastMousePosition)
|
||||
animationLoop()
|
||||
|
||||
onMouseMove = (event) ->
|
||||
lastMousePosition.clientX = event.clientX
|
||||
lastMousePosition.clientY = event.clientY
|
||||
|
||||
# Start the animation loop when the mouse moves prior to a mouseup event
|
||||
unless dragging
|
||||
dragging = true
|
||||
animationLoop()
|
||||
|
||||
# Stop dragging when cursor enters dev tools because we can't detect mouseup
|
||||
onMouseUp() if event.which is 0
|
||||
|
||||
onMouseUp = ->
|
||||
dragging = false
|
||||
window.removeEventListener('mousemove', onMouseMove)
|
||||
window.removeEventListener('mouseup', onMouseUp)
|
||||
editor.finalizeSelections()
|
||||
|
||||
window.addEventListener('mousemove', onMouseMove)
|
||||
window.addEventListener('mouseup', onMouseUp)
|
||||
|
||||
selectToMousePosition: (event) ->
|
||||
@props.editor.selectToScreenPosition(@screenPositionForMouseEvent(event))
|
||||
|
||||
requestScrollViewMeasurement: ->
|
||||
return if @measurementPending
|
||||
|
||||
@scrollViewMeasurementRequested = true
|
||||
requestAnimationFrame =>
|
||||
@scrollViewMeasurementRequested = false
|
||||
@measureScrollView()
|
||||
|
||||
# Measure explicitly-styled height and width and relay them to the model. If
|
||||
# these values aren't explicitly styled, we assume the editor is unconstrained
|
||||
# and use the scrollHeight / scrollWidth as its height and width in
|
||||
# calculations.
|
||||
measureScrollView: ->
|
||||
return unless @isMounted()
|
||||
|
||||
{editor} = @props
|
||||
editorNode = @getDOMNode()
|
||||
scrollViewNode = @refs.scrollView.getDOMNode()
|
||||
{position} = getComputedStyle(editorNode)
|
||||
{width, height} = editorNode.style
|
||||
|
||||
if position is 'absolute' or height
|
||||
clientHeight = scrollViewNode.clientHeight
|
||||
editor.setHeight(clientHeight) if clientHeight > 0
|
||||
|
||||
if position is 'absolute' or width
|
||||
clientWidth = scrollViewNode.clientWidth
|
||||
editor.setWidth(clientWidth) if clientWidth > 0
|
||||
|
||||
measureLineHeightAndCharWidthsIfNeeded: (prevState) ->
|
||||
if not isEqualForProperties(prevState, @state, 'lineHeight', 'fontSize', 'fontFamily')
|
||||
{editor} = @props
|
||||
|
||||
editor.batchUpdates =>
|
||||
oldDefaultCharWidth = editor.getDefaultCharWidth()
|
||||
|
||||
if @state.visible
|
||||
@measureLineHeightAndDefaultCharWidth()
|
||||
else
|
||||
@measureLineHeightAndDefaultCharWidthWhenShown = true
|
||||
|
||||
unless oldDefaultCharWidth is editor.getDefaultCharWidth()
|
||||
@remeasureCharacterWidths()
|
||||
@measureGutter()
|
||||
|
||||
else if @measureLineHeightAndDefaultCharWidthWhenShown and @state.visible and not prevState.visible
|
||||
@measureLineHeightAndDefaultCharWidth()
|
||||
|
||||
measureLineHeightAndDefaultCharWidth: ->
|
||||
@measureLineHeightAndDefaultCharWidthWhenShown = false
|
||||
@refs.lines.measureLineHeightAndDefaultCharWidth()
|
||||
|
||||
remeasureCharacterWidths: ->
|
||||
@refs.lines.remeasureCharacterWidths()
|
||||
|
||||
measureGutter: ->
|
||||
oldGutterWidth = @gutterWidth
|
||||
@gutterWidth = @refs.gutter.getDOMNode().offsetWidth
|
||||
@requestUpdate() if @gutterWidth isnt oldGutterWidth
|
||||
|
||||
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)
|
||||
|
||||
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()
|
||||
|
||||
# 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()
|
||||
|
||||
pauseOverflowChangedEvents: ->
|
||||
@overflowChangedEventsPaused = true
|
||||
@resumeOverflowChangedEventsAfterDelay ?= debounce(@resumeOverflowChangedEvents, 500)
|
||||
@resumeOverflowChangedEventsAfterDelay()
|
||||
|
||||
resumeOverflowChangedEvents: ->
|
||||
if @overflowChangedWhilePaused
|
||||
@overflowChangedWhilePaused = false
|
||||
@requestScrollViewMeasurement()
|
||||
|
||||
resumeOverflowChangedEventsAfterDelay: null
|
||||
|
||||
clearMouseWheelScreenRow: ->
|
||||
if @mouseWheelScreenRow?
|
||||
@mouseWheelScreenRow = null
|
||||
@requestUpdate()
|
||||
|
||||
clearMouseWheelScreenRowAfterDelay: null # created lazily
|
||||
|
||||
consolidateSelections: (e) ->
|
||||
e.abortKeyBinding() unless @props.editor.consolidateSelections()
|
||||
|
||||
lineNodeForScreenRow: (screenRow) -> @refs.lines.lineNodeForScreenRow(screenRow)
|
||||
|
||||
lineNumberNodeForScreenRow: (screenRow) -> @refs.gutter.lineNumberNodeForScreenRow(screenRow)
|
||||
|
||||
screenRowForNode: (node) ->
|
||||
while node isnt document
|
||||
if screenRow = node.dataset.screenRow
|
||||
return parseInt(screenRow)
|
||||
node = node.parentNode
|
||||
null
|
||||
|
||||
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()
|
||||
|
||||
setFontSize: (fontSize) ->
|
||||
@setState({fontSize})
|
||||
|
||||
setLineHeight: (lineHeight) ->
|
||||
@setState({lineHeight})
|
||||
|
||||
setFontFamily: (fontFamily) ->
|
||||
@setState({fontFamily})
|
||||
|
||||
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})
|
||||
|
||||
screenPositionForMouseEvent: (event) ->
|
||||
pixelPosition = @pixelPositionForMouseEvent(event)
|
||||
@props.editor.screenPositionForPixelPosition(pixelPosition)
|
||||
|
||||
pixelPositionForMouseEvent: (event) ->
|
||||
{editor} = @props
|
||||
{clientX, clientY} = event
|
||||
|
||||
scrollViewClientRect = @refs.scrollView.getDOMNode().getBoundingClientRect()
|
||||
top = clientY - scrollViewClientRect.top + editor.getScrollTop()
|
||||
left = clientX - scrollViewClientRect.left + editor.getScrollLeft()
|
||||
{top, left}
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
React = require 'react'
|
||||
{div} = require 'reactionary'
|
||||
{debounce} = require 'underscore-plus'
|
||||
|
||||
InputComponent = require './input-component'
|
||||
LinesComponent = require './lines-component'
|
||||
CursorsComponent = require './cursors-component'
|
||||
SelectionsComponent = require './selections-component'
|
||||
|
||||
module.exports =
|
||||
EditorScrollViewComponent = React.createClass
|
||||
displayName: 'EditorScrollViewComponent'
|
||||
|
||||
measurementPending: false
|
||||
overflowChangedEventsPaused: false
|
||||
overflowChangedWhilePaused: false
|
||||
|
||||
render: ->
|
||||
{editor, fontSize, fontFamily, lineHeight, showIndentGuide} = @props
|
||||
{scrollHeight, scrollWidth, renderedRowRange, pendingChanges, scrollingVertically} = @props
|
||||
{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',
|
||||
InputComponent
|
||||
ref: 'input'
|
||||
className: 'hidden-input'
|
||||
style: inputStyle
|
||||
onInput: @onInput
|
||||
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})
|
||||
|
||||
componentDidMount: ->
|
||||
@getDOMNode().addEventListener 'overflowchanged', @onOverflowChanged
|
||||
window.addEventListener('resize', @onWindowResize)
|
||||
|
||||
@measureHeightAndWidth()
|
||||
|
||||
componentDidUnmount: ->
|
||||
window.removeEventListener('resize', @onWindowResize)
|
||||
|
||||
componentDidUpdate: ->
|
||||
@pauseOverflowChangedEvents()
|
||||
|
||||
onOverflowChanged: ->
|
||||
if @overflowChangedEventsPaused
|
||||
@overflowChangedWhilePaused = true
|
||||
else
|
||||
@requestMeasurement()
|
||||
|
||||
onWindowResize: ->
|
||||
@requestMeasurement()
|
||||
|
||||
pauseOverflowChangedEvents: ->
|
||||
@overflowChangedEventsPaused = true
|
||||
@resumeOverflowChangedEventsAfterDelay ?= debounce(@resumeOverflowChangedEvents, 500)
|
||||
@resumeOverflowChangedEventsAfterDelay()
|
||||
|
||||
resumeOverflowChangedEvents: ->
|
||||
if @overflowChangedWhilePaused
|
||||
@overflowChangedWhilePaused = false
|
||||
@requestMeasurement()
|
||||
|
||||
resumeOverflowChangedEventsAfterDelay: null
|
||||
|
||||
requestMeasurement: ->
|
||||
return if @measurementPending
|
||||
|
||||
@measurementPending = true
|
||||
requestAnimationFrame =>
|
||||
@measurementPending = false
|
||||
@measureHeightAndWidth()
|
||||
|
||||
onInput: (char, replaceLastCharacter) ->
|
||||
{editor} = @props
|
||||
|
||||
if replaceLastCharacter
|
||||
editor.transact ->
|
||||
editor.selectLeft()
|
||||
editor.insertText(char)
|
||||
else
|
||||
editor.insertText(char)
|
||||
|
||||
onMouseDown: (event) ->
|
||||
{editor} = @props
|
||||
{detail, shiftKey, metaKey} = event
|
||||
screenPosition = @screenPositionForMouseEvent(event)
|
||||
|
||||
if shiftKey
|
||||
editor.selectToScreenPosition(screenPosition)
|
||||
else if metaKey
|
||||
editor.addCursorAtScreenPosition(screenPosition)
|
||||
else
|
||||
editor.setCursorScreenPosition(screenPosition)
|
||||
switch detail
|
||||
when 2 then editor.selectWord()
|
||||
when 3 then editor.selectLine()
|
||||
|
||||
@selectToMousePositionUntilMouseUp(event)
|
||||
|
||||
selectToMousePositionUntilMouseUp: (event) ->
|
||||
{editor} = @props
|
||||
dragging = false
|
||||
lastMousePosition = {}
|
||||
|
||||
animationLoop = =>
|
||||
requestAnimationFrame =>
|
||||
if dragging
|
||||
@selectToMousePosition(lastMousePosition)
|
||||
animationLoop()
|
||||
|
||||
onMouseMove = (event) ->
|
||||
lastMousePosition.clientX = event.clientX
|
||||
lastMousePosition.clientY = event.clientY
|
||||
|
||||
# Start the animation loop when the mouse moves prior to a mouseup event
|
||||
unless dragging
|
||||
dragging = true
|
||||
animationLoop()
|
||||
|
||||
# Stop dragging when cursor enters dev tools because we can't detect mouseup
|
||||
onMouseUp() if event.which is 0
|
||||
|
||||
onMouseUp = ->
|
||||
dragging = false
|
||||
window.removeEventListener('mousemove', onMouseMove)
|
||||
window.removeEventListener('mouseup', onMouseUp)
|
||||
editor.finalizeSelections()
|
||||
|
||||
window.addEventListener('mousemove', onMouseMove)
|
||||
window.addEventListener('mouseup', onMouseUp)
|
||||
|
||||
selectToMousePosition: (event) ->
|
||||
@props.editor.selectToScreenPosition(@screenPositionForMouseEvent(event))
|
||||
|
||||
screenPositionForMouseEvent: (event) ->
|
||||
pixelPosition = @pixelPositionForMouseEvent(event)
|
||||
@props.editor.screenPositionForPixelPosition(pixelPosition)
|
||||
|
||||
pixelPositionForMouseEvent: (event) ->
|
||||
{editor} = @props
|
||||
{clientX, clientY} = event
|
||||
|
||||
editorClientRect = @getDOMNode().getBoundingClientRect()
|
||||
top = clientY - editorClientRect.top + editor.getScrollTop()
|
||||
left = clientX - editorClientRect.left + editor.getScrollLeft()
|
||||
{top, left}
|
||||
|
||||
getHiddenInputPosition: ->
|
||||
{editor} = @props
|
||||
return {top: 0, left: 0} unless @isMounted() and editor.getCursor()?
|
||||
|
||||
{top, left, height, width} = editor.getCursor().getPixelRect()
|
||||
top = top - editor.getScrollTop()
|
||||
top = Math.max(0, Math.min(editor.getHeight() - height, top))
|
||||
left = left - editor.getScrollLeft()
|
||||
left = Math.max(0, Math.min(editor.getWidth() - width, left))
|
||||
|
||||
{top, left}
|
||||
|
||||
# Measure explicitly-styled height and width and relay them to the model. If
|
||||
# these values aren't explicitly styled, we assume the editor is unconstrained
|
||||
# and use the scrollHeight / scrollWidth as its height and width in
|
||||
# calculations.
|
||||
measureHeightAndWidth: ->
|
||||
return unless @isMounted()
|
||||
|
||||
node = @getDOMNode()
|
||||
computedStyle = getComputedStyle(node)
|
||||
{editor} = @props
|
||||
|
||||
unless computedStyle.height is '0px'
|
||||
clientHeight = node.clientHeight
|
||||
editor.setHeight(clientHeight) if clientHeight > 0
|
||||
|
||||
unless computedStyle.width is '0px'
|
||||
clientWidth = node.clientWidth
|
||||
editor.setWidth(clientWidth) if clientHeight > 0
|
||||
|
||||
focus: ->
|
||||
@refs.input.focus()
|
||||
@@ -28,7 +28,7 @@ LongLineLength = 1000
|
||||
# ## Iterating over the open editor views
|
||||
#
|
||||
# ```coffee
|
||||
# for editorView in atom.workspace.getEditorViews()
|
||||
# for editorView in atom.workspaceView.getEditorViews()
|
||||
# console.log(editorView.getEditor().getPath())
|
||||
# ```
|
||||
#
|
||||
@@ -50,7 +50,7 @@ class EditorView extends View
|
||||
showLineNumbers: true
|
||||
autoIndent: true
|
||||
normalizeIndentOnPaste: true
|
||||
nonWordCharacters: "./\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
|
||||
nonWordCharacters: "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
|
||||
preferredLineLength: 80
|
||||
tabLength: 2
|
||||
softWrap: false
|
||||
@@ -154,11 +154,13 @@ 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()
|
||||
'editor:move-to-beginning-of-next-paragraph': => @editor.moveCursorToBeginningOfNextParagraph()
|
||||
'editor:move-to-beginning-of-previous-paragraph': => @editor.moveCursorToBeginningOfPreviousParagraph()
|
||||
'editor:move-to-beginning-of-screen-line': => @editor.moveCursorToBeginningOfScreenLine()
|
||||
'editor:move-to-beginning-of-line': => @editor.moveCursorToBeginningOfLine()
|
||||
'editor:move-to-end-of-screen-line': => @editor.moveCursorToEndOfScreenLine()
|
||||
@@ -387,7 +389,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 +462,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())
|
||||
|
||||
@@ -542,7 +547,6 @@ class EditorView extends View
|
||||
@showBufferConflictAlert(@editor)
|
||||
|
||||
@subscribe @editor, "path-changed", =>
|
||||
@editor.reloadGrammar()
|
||||
@trigger 'editor:path-changed'
|
||||
|
||||
@subscribe @editor, "grammar-changed", =>
|
||||
@@ -1487,7 +1491,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
|
||||
|
||||
|
||||
+91
-26
@@ -109,8 +109,8 @@ TextMateScopeSelector = require('first-mate').ScopeSelector
|
||||
# - {::insertNewlineAbove}
|
||||
# - {::insertNewlineBelow}
|
||||
# - {::backspace}
|
||||
# - {::backspaceToBeginningOfWord}
|
||||
# - {::backspaceToBeginningOfLine}
|
||||
# - {::deleteToBeginningOfWord}
|
||||
# - {::deleteToBeginningOfLine}
|
||||
# - {::delete}
|
||||
# - {::deleteToEndOfWord}
|
||||
# - {::deleteLine}
|
||||
@@ -144,12 +144,13 @@ class Editor extends Model
|
||||
cursors: null
|
||||
selections: null
|
||||
suppressSelectionMerging: false
|
||||
updateBatchDepth: 0
|
||||
|
||||
@delegatesMethods 'suggestedIndentForBufferRow', 'autoIndentBufferRow', 'autoIndentBufferRows',
|
||||
'autoDecreaseIndentForBufferRow', 'toggleLineCommentForBufferRow', 'toggleLineCommentsForBufferRows',
|
||||
toProperty: 'languageMode'
|
||||
|
||||
@delegatesProperties '$lineHeight', '$defaultCharWidth', '$height', '$width',
|
||||
@delegatesProperties '$lineHeightInPixels', '$defaultCharWidth', '$height', '$width',
|
||||
'$scrollTop', '$scrollLeft', 'manageScrollPosition', toProperty: 'displayBuffer'
|
||||
|
||||
constructor: ({@softTabs, initialLine, initialColumn, tabLength, softWrap, @displayBuffer, buffer, registerEditor, suppressCursorCreation}) ->
|
||||
@@ -160,7 +161,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)
|
||||
@@ -211,6 +212,7 @@ class Editor extends Model
|
||||
@subscribe @displayBuffer, "changed", (e) => @emit 'screen-lines-changed', e
|
||||
@subscribe @displayBuffer, "markers-updated", => @mergeIntersectingSelections()
|
||||
@subscribe @displayBuffer, 'grammar-changed', => @handleGrammarChange()
|
||||
@subscribe @displayBuffer, 'tokenized', => @handleTokenization()
|
||||
@subscribe @displayBuffer, 'soft-wrap-changed', (args...) => @emit 'soft-wrap-changed', args...
|
||||
|
||||
getViewClass: ->
|
||||
@@ -259,12 +261,13 @@ class Editor extends Model
|
||||
getLongTitle: ->
|
||||
if sessionPath = @getPath()
|
||||
fileName = path.basename(sessionPath)
|
||||
directory = path.basename(path.dirname(sessionPath))
|
||||
directory = atom.project.relativize(path.dirname(sessionPath))
|
||||
directory = if directory.length > 0 then directory else path.basename(path.dirname(sessionPath))
|
||||
"#{fileName} - #{directory}"
|
||||
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
|
||||
@@ -275,7 +278,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
|
||||
@@ -317,6 +320,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
|
||||
@@ -563,8 +579,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:
|
||||
@@ -645,17 +661,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.
|
||||
@@ -725,13 +751,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
|
||||
|
||||
@@ -1007,7 +1044,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
|
||||
@@ -1235,6 +1272,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.
|
||||
#
|
||||
@@ -1269,7 +1309,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) ->
|
||||
@@ -1451,6 +1491,14 @@ class Editor extends Model
|
||||
moveCursorToNextWordBoundary: ->
|
||||
@moveCursors (cursor) -> cursor.moveToNextWordBoundary()
|
||||
|
||||
# Public: Move every cursor to the beginning of the next paragraph.
|
||||
moveCursorToBeginningOfNextParagraph: ->
|
||||
@moveCursors (cursor) -> cursor.moveToBeginningOfNextParagraph()
|
||||
|
||||
# Public: Move every cursor to the beginning of the previous paragraph.
|
||||
moveCursorToBeginningOfPreviousParagraph: ->
|
||||
@moveCursors (cursor) -> cursor.moveToBeginningOfPreviousParagraph()
|
||||
|
||||
scrollToCursorPosition: ->
|
||||
@getCursor().autoscroll()
|
||||
|
||||
@@ -1543,28 +1591,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()
|
||||
|
||||
@@ -1574,7 +1622,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
|
||||
@@ -1585,7 +1633,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
|
||||
@@ -1795,9 +1843,11 @@ class Editor extends Model
|
||||
abortTransaction: -> @buffer.abortTransaction()
|
||||
|
||||
batchUpdates: (fn) ->
|
||||
@emit 'batched-updates-started'
|
||||
@emit 'batched-updates-started' if @updateBatchDepth is 0
|
||||
@updateBatchDepth++
|
||||
result = fn()
|
||||
@emit 'batched-updates-ended'
|
||||
@updateBatchDepth--
|
||||
@emit 'batched-updates-ended' if @updateBatchDepth is 0
|
||||
result
|
||||
|
||||
inspect: ->
|
||||
@@ -1805,6 +1855,9 @@ class Editor extends Model
|
||||
|
||||
logScreenLines: (start, end) -> @displayBuffer.logLines(start, end)
|
||||
|
||||
handleTokenization: ->
|
||||
@softTabs = @usesSoftTabs() ? @softTabs
|
||||
|
||||
handleGrammarChange: ->
|
||||
@unfoldAll()
|
||||
@emit 'grammar-changed'
|
||||
@@ -1822,8 +1875,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)
|
||||
@@ -1838,6 +1891,8 @@ class Editor extends Model
|
||||
setHeight: (height) -> @displayBuffer.setHeight(height)
|
||||
getHeight: -> @displayBuffer.getHeight()
|
||||
|
||||
getClientHeight: -> @displayBuffer.getClientHeight()
|
||||
|
||||
setWidth: (width) -> @displayBuffer.setWidth(width)
|
||||
getWidth: -> @displayBuffer.getWidth()
|
||||
|
||||
@@ -1876,6 +1931,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")
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
{join, sep} = require 'path'
|
||||
{join} = require 'path'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
{Emitter, Subscriber} = require 'emissary'
|
||||
@@ -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.
|
||||
#
|
||||
@@ -246,7 +246,7 @@ class Git
|
||||
# Returns a {Number} representing the status. This value can be passed to
|
||||
# {::isStatusModified} or {::isStatusNew} to get more information.
|
||||
getDirectoryStatus: (directoryPath) ->
|
||||
directoryPath = "#{@relativize(directoryPath)}#{sep}"
|
||||
directoryPath = "#{@relativize(directoryPath)}/"
|
||||
directoryStatus = 0
|
||||
for path, status of @statuses
|
||||
directoryStatus |= status if path.indexOf(directoryPath) is 0
|
||||
|
||||
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