Comparar commits
1256 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| fae28d8c21 | |||
| f7e11a1fb5 | |||
| aab5deaae6 | |||
| 50f68c38ec | |||
| d778e13ffb | |||
| 9918a0b73f | |||
| 374fe99aaf | |||
| ea052fc667 | |||
| d11037f6d2 | |||
| 777c4a5e43 | |||
| a897a320a9 | |||
| a94e9df641 | |||
| 45d8df37fa | |||
| 83639c4cfa | |||
| 6c960183fd | |||
| 0730c7902d | |||
| 012c38cf11 | |||
| 0785fb3a48 | |||
| 563065aeb1 | |||
| 2b1f9531e5 | |||
| 665d1b9581 | |||
| d79d819048 | |||
| 6ae2d9737d | |||
| 54b67ea6b9 | |||
| 41dc113088 | |||
| 3216d767da | |||
| 94987c6769 | |||
| f423b54d5b | |||
| 86dc499a99 | |||
| ec6184322c | |||
| bde4b88f9f | |||
| e0f41f8cc5 | |||
| c49c48381a | |||
| 73d36e464d | |||
| 2e8fd21193 | |||
| 39d525590b | |||
| 4f6adc51b4 | |||
| 73c714b62d | |||
| caa16f2718 | |||
| 1b17441bfe | |||
| 9a97bac05d | |||
| 9252cd2a1b | |||
| 1c790a3c55 | |||
| 11f61c88d8 | |||
| 64d9eb24d2 | |||
| 73b3b9870f | |||
| 1c8781268e | |||
| 662c130caf | |||
| 40d9d6110c | |||
| a6f42d6989 | |||
| e9b4fb828e | |||
| 84c5a25ef3 | |||
| 4a54565f59 | |||
| 1b396b5ce7 | |||
| fe748dbcc8 | |||
| 1e759ee646 | |||
| 09952d18c0 | |||
| 337390df47 | |||
| d29a0114ef | |||
| b97db1914f | |||
| 29f480661c | |||
| d7638b4420 | |||
| ce881c87b9 | |||
| d9b00f16f7 | |||
| 2590bad75f | |||
| 6ff553e41c | |||
| ec3ca7e429 | |||
| 386767868e | |||
| e2bb4747fa | |||
| 885a4e2b2a | |||
| e31fab7fe4 | |||
| 0a2b173270 | |||
| c1a9c3b5fb | |||
| 1550511e54 | |||
| 44999b0b37 | |||
| 0f2931d0a2 | |||
| 064a384318 | |||
| 5708140f10 | |||
| aceeb10a0d | |||
| 0940668263 | |||
| bcac3cc000 | |||
| 5b39fc2e11 | |||
| cb0e0751d8 | |||
| a67d6362c2 | |||
| 5eb8875ad2 | |||
| e6e7106ac5 | |||
| 257d0d3a25 | |||
| 632ba0217a | |||
| bf1a2a532e | |||
| 72bb83ccd0 | |||
| def2001eb6 | |||
| 5bda9b8a8e | |||
| 0b0ad42610 | |||
| 7c4b453e64 | |||
| 740e0731fe | |||
| b6cf097abd | |||
| aa8d03b092 | |||
| aec9e75ecb | |||
| b05e20245f | |||
| 17d30cc526 | |||
| f631660b32 | |||
| 2b34b2b9ba | |||
| 5be6cfa678 | |||
| b5449dec6c | |||
| be0e36b663 | |||
| f92f130606 | |||
| d931299b9f | |||
| b1778aa7df | |||
| c5d7614d80 | |||
| 0d34df2238 | |||
| ee24b373c0 | |||
| 8e6aa1ee9a | |||
| 9e381db265 | |||
| 6d4f532a32 | |||
| 998aedf367 | |||
| 5f0a3061ac | |||
| c424a39538 | |||
| 150815edfd | |||
| 00ba7d92d0 | |||
| 3dc51365b3 | |||
| d18a45f37c | |||
| d8c1587534 | |||
| a4754b2bd5 | |||
| f3f6ec424f | |||
| 020326ffad | |||
| ac2985af36 | |||
| ca8609c925 | |||
| d2e4c61e0b | |||
| 399c4f9f95 | |||
| 3da7fe7300 | |||
| 40048da314 | |||
| 04c8549c38 | |||
| 4ef6eae02f | |||
| 52dcf6a721 | |||
| 9169e9e227 | |||
| 19e0854036 | |||
| cd41637df0 | |||
| 8973a6d777 | |||
| e6b9c67aad | |||
| eb28c15f69 | |||
| a42a4dd352 | |||
| cc53b6fbef | |||
| b31089cbb9 | |||
| 4d4ff84047 | |||
| 0875e00f9c | |||
| b9d45680c3 | |||
| 1fa2099eba | |||
| 4d74a69277 | |||
| 3aa67bff8b | |||
| 325482353e | |||
| cf4e08cdc0 | |||
| ced02faf0f | |||
| 8afca3ac86 | |||
| d13d2d3112 | |||
| c3d5f713ca | |||
| 042684212e | |||
| 81987efed7 | |||
| f2e123e295 | |||
| 8d193d542b | |||
| 8ca8ac5efc | |||
| f6c7dd0b2f | |||
| b380551e5e | |||
| 7dcb340643 | |||
| f0339936ee | |||
| 1ae10e59ae | |||
| e4bcb52573 | |||
| 292ff0de52 | |||
| 5a70276201 | |||
| 1bd4d51879 | |||
| a95a510770 | |||
| 7d1be155fa | |||
| 41de6bdb70 | |||
| 1d9bca12e0 | |||
| e9c296968d | |||
| 352eab47df | |||
| 002c4dcb80 | |||
| 03250f79c6 | |||
| 1142da1848 | |||
| e53ed10169 | |||
| 3fb54b657d | |||
| 4b3b0145a7 | |||
| ec558f9a9b | |||
| 051f1b4777 | |||
| 2e4e178091 | |||
| 1fa04cb161 | |||
| 69df046cb0 | |||
| d491007da8 | |||
| 71c65e65eb | |||
| 3fad9c616f | |||
| 45875188a4 | |||
| 933787f1a4 | |||
| 726b546004 | |||
| 5e83dee493 | |||
| 11ab6fd2ef | |||
| 0758bd0231 | |||
| 7f63460a8c | |||
| 627e43dccf | |||
| cb8b38b7f7 | |||
| 04b2eefec3 | |||
| 0694180715 | |||
| f51b116c14 | |||
| f001e05262 | |||
| 77e97a7552 | |||
| 80a1249d59 | |||
| a03ba290f9 | |||
| c9e498f36f | |||
| e6a98125af | |||
| 025d2be606 | |||
| 26e21e98db | |||
| e3f32ab30b | |||
| 5d8ac56018 | |||
| 1603fab984 | |||
| 07c7f1ea46 | |||
| 31b68c0937 | |||
| 3a02f4de0c | |||
| 45fe04f630 | |||
| b4a9409ec8 | |||
| 7432d39301 | |||
| c5349f53b2 | |||
| 8b4b4a6ea9 | |||
| 19177123a7 | |||
| dd032b739f | |||
| f0ebe71c4e | |||
| 8bba4d8add | |||
| 7be0f6dd3b | |||
| 571947ae93 | |||
| 338168e145 | |||
| 4190f2f1ca | |||
| e74f026750 | |||
| 525b5c9978 | |||
| 86106cbf4d | |||
| 519ebb1ca6 | |||
| da56c1def5 | |||
| a6f9b6d2de | |||
| d965e9f56c | |||
| 8f0e0ae4a6 | |||
| 57a71b3cb3 | |||
| 71c48ec07f | |||
| 4ac3be7ab5 | |||
| 5dd83b12c0 | |||
| 905f628b79 | |||
| ddad42432b | |||
| 52e999fc11 | |||
| 3114efbf9c | |||
| 8436e8f62e | |||
| ba1303a895 | |||
| 59c4c15afd | |||
| 2831b43042 | |||
| 3c8cb557ed | |||
| 6b8393dced | |||
| 3356b4ac47 | |||
| 3eb7c9d767 | |||
| bbb3ebc2b9 | |||
| ed93695d64 | |||
| 4db2ad53fd | |||
| 149a6825b4 | |||
| dd1581aaca | |||
| 99f025d5d6 | |||
| e0cf20cda9 | |||
| c305384334 | |||
| d879923cdb | |||
| d16526a1f7 | |||
| a774c2ff08 | |||
| 475ff140a7 | |||
| fa6bce3085 | |||
| b2dff15e51 | |||
| 1e3dc05b3d | |||
| 1a12a17b7a | |||
| 8757e1f145 | |||
| 12ca214d6a | |||
| 8e75831318 | |||
| 2f049b95a8 | |||
| 386d4d3eb5 | |||
| 6a14c0df81 | |||
| 083204f737 | |||
| 4d17f625f8 | |||
| 64ebc37204 | |||
| b4dcec9417 | |||
| ede03fd634 | |||
| 926493f05c | |||
| 4f82662263 | |||
| dfa3d8a1c7 | |||
| 677ec44b37 | |||
| 0359e933d1 | |||
| ff88c1c41f | |||
| 0acc632cf5 | |||
| 2c65a30d4a | |||
| 590b8ec221 | |||
| cff1a012af | |||
| 3ea057a145 | |||
| b30ea21ab9 | |||
| 44d42380b8 | |||
| f906afa8ec | |||
| 815a282146 | |||
| 9fe24acbe9 | |||
| f2f3745022 | |||
| d2ecc8c2b6 | |||
| 9166a563c2 | |||
| 7388468b5a | |||
| 21bc658230 | |||
| 72f85aeae4 | |||
| 26b3cfc3b2 | |||
| c91eb1fbbe | |||
| 75ed3aa03b | |||
| 00d783fb23 | |||
| b80e7fc137 | |||
| e1d2bbabfb | |||
| 9de5adf572 | |||
| 2a3eab945d | |||
| df8d77cc47 | |||
| 9d11ea8bcd | |||
| f19d2d7369 | |||
| 7f790137ed | |||
| c56dce0c80 | |||
| 1f105542a3 | |||
| ec499e2bde | |||
| 59202f9a56 | |||
| e437673fbc | |||
| ded222551a | |||
| fe2def8321 | |||
| e19368a291 | |||
| 3980082e3c | |||
| 9f29e423db | |||
| 23d9f6e41f | |||
| 649f0ac246 | |||
| b9dcb569b1 | |||
| 6538ee47aa | |||
| 043a50012e | |||
| 9ed0ded91b | |||
| ad5c0f93ed | |||
| 4265de2232 | |||
| b7ae12f86f | |||
| 20e7fea3ab | |||
| 910469d373 | |||
| ff9c9adeff | |||
| ceac8ba0e1 | |||
| 24f386df2d | |||
| 1006706cf6 | |||
| 0b0a3ae4c2 | |||
| 276de1e5ae | |||
| fcffcc83f9 | |||
| 4122b5a43f | |||
| 2430edbcb8 | |||
| e2269d5d51 | |||
| 2d04c50184 | |||
| 2dc8c94165 | |||
| c1022d15ba | |||
| b620be7780 | |||
| 70c89dfefa | |||
| ace61c330a | |||
| 0626df5cdd | |||
| b1679a03d6 | |||
| af95abe185 | |||
| a23561f9ce | |||
| a93048d3ae | |||
| 1814b187e9 | |||
| d7d7bd2db5 | |||
| f86ca9b59a | |||
| 58cccd76a4 | |||
| fd1477fc92 | |||
| eca9ba107c | |||
| 94126942eb | |||
| e6697ce3a7 | |||
| ccd86d91de | |||
| 2f8ec967f6 | |||
| 709ae6a1bc | |||
| 5e14d44d6a | |||
| 41dd4a386a | |||
| 9af4b14716 | |||
| aed9f18457 | |||
| edf2fbe0e5 | |||
| ca71bf224b | |||
| 012363a785 | |||
| 1a48903f96 | |||
| 44331d0ba6 | |||
| fe7c5b4bc1 | |||
| 1825af1309 | |||
| b5947f5a56 | |||
| 864d6ce8f2 | |||
| ac07cf3d9a | |||
| 6ba3698547 | |||
| d57e5f389d | |||
| 2023aeef97 | |||
| 1c6564f7a5 | |||
| d9d28b5236 | |||
| 8734eab8cb | |||
| 1ad5158f19 | |||
| 453e034a5f | |||
| 9841a3588f | |||
| dd0ae8a8ea | |||
| d8ddd52df8 | |||
| 83696bb9c7 | |||
| 243c4efe20 | |||
| 22c65f2407 | |||
| e553fefc25 | |||
| cf6fc22c87 | |||
| a6d8f588c3 | |||
| 2ef74de0f8 | |||
| 2ee6469b17 | |||
| 9977884a2c | |||
| c331723c55 | |||
| dd3ca1fc2f | |||
| f7f2da6ad2 | |||
| eba74d1420 | |||
| 91c9f6ffa5 | |||
| db319c9f9c | |||
| 134affcd8f | |||
| 9bfa7704e6 | |||
| 8acd84c7e2 | |||
| a53d223b6f | |||
| 673b78561a | |||
| 57c56fc46e | |||
| d81fb8cabf | |||
| 1dec9d0ad2 | |||
| bda75c1a8f | |||
| c0c2d797b1 | |||
| a342a18440 | |||
| 047425c564 | |||
| 8374911dab | |||
| 6ca3c90abe | |||
| 96cb266c27 | |||
| 21723b155e | |||
| 14c3feee17 | |||
| 0255f1e223 | |||
| 76a5912126 | |||
| 848dc12f28 | |||
| 7a41fabf60 | |||
| f4b82fe3a4 | |||
| aec32b65a0 | |||
| 01e0970406 | |||
| b08978a431 | |||
| e4b934d3fa | |||
| c350285044 | |||
| 4a7b43f609 | |||
| 0d66c68fe8 | |||
| b72028c68d | |||
| 332b918c01 | |||
| bc94dc63e6 | |||
| a1d540d288 | |||
| af4034ba39 | |||
| 89cbd6b834 | |||
| fe01ded75b | |||
| aab4f3b761 | |||
| adf5cfc78c | |||
| 66bb9dab93 | |||
| c8e4535e8b | |||
| 7199cda549 | |||
| 1eb9c3d0d5 | |||
| 27c03ae3f3 | |||
| a8ddc530a2 | |||
| 8e78583521 | |||
| 7317d52896 | |||
| 54dd5ea28e | |||
| 22fe04fb27 | |||
| adb174ef5f | |||
| d3a016bf69 | |||
| 46c5f09721 | |||
| 6401213510 | |||
| 5f92c6df6c | |||
| 75e3659809 | |||
| b458b86bbd | |||
| 645f950368 | |||
| 8dd183fc78 | |||
| 6ef6fb77f9 | |||
| 159adcb00c | |||
| 06ccb48efe | |||
| 95f2d02d18 | |||
| 63df8dfcdb | |||
| 12bf0f5381 | |||
| 637acf63ce | |||
| 9ff23e6bed | |||
| d96f15e8b5 | |||
| 5bf654f6cc | |||
| 16dba3e68e | |||
| 08716fd888 | |||
| 43e30ad466 | |||
| 16c86047eb | |||
| 0d36eec288 | |||
| 2e697f65e8 | |||
| edab3e5a27 | |||
| 97999f1080 | |||
| a268b3e963 | |||
| 8931bf8f3a | |||
| 918c31905f | |||
| c7ff4f9897 | |||
| 4f604ced3c | |||
| 8c3147f975 | |||
| a29c18f8c0 | |||
| 43f517abff | |||
| 8b901f81e0 | |||
| 4182ee4f12 | |||
| 601b314236 | |||
| 7d596355b9 | |||
| fe0cdbf349 | |||
| c2381ba61c | |||
| ccc5f8fa66 | |||
| e29ffbea8c | |||
| efbd508163 | |||
| 4179d9d268 | |||
| 3fc3d48def | |||
| b438b311f3 | |||
| 561e31c0c5 | |||
| 2a8a5268c6 | |||
| 28b085be1c | |||
| f5bc71e559 | |||
| 2188dd201d | |||
| b21eb6f934 | |||
| 9b0d8ec242 | |||
| 3c32a8e8fb | |||
| 3ab7836ab2 | |||
| ef8b7531b0 | |||
| b04f9f9488 | |||
| 452d86ac0c | |||
| 6fe1bae40d | |||
| e2170ea907 | |||
| 545bf4bd98 | |||
| 1fdc78a157 | |||
| 738bfd7253 | |||
| 8b0b997db6 | |||
| 263ab3b4a6 | |||
| 3afbcbe25f | |||
| 578ca8b197 | |||
| 5e674fe29e | |||
| 72fe586101 | |||
| 82d5577bdc | |||
| c703cf2050 | |||
| c19c8c5341 | |||
| 32b5592475 | |||
| 993f1ac2d6 | |||
| 21b40acdf1 | |||
| ddf7c04e66 | |||
| c127237cc6 | |||
| 9694d255f0 | |||
| 81b4803d56 | |||
| 893e9873a1 | |||
| 23e805fe9e | |||
| 43257de7cd | |||
| c7fded0d7f | |||
| 284d823ad5 | |||
| 164a121de9 | |||
| 50ab49aedb | |||
| a127d3c4eb | |||
| cac5c6e3a5 | |||
| 1ee783fdb9 | |||
| 47870a1214 | |||
| 7801d8562f | |||
| 104271861c | |||
| de25b8ea37 | |||
| ba9f353c4a | |||
| 7665cd1a6a | |||
| c39d8d9aa7 | |||
| 4b0d22917b | |||
| 720b2ad47d | |||
| d34327a667 | |||
| d2146f9b2e | |||
| 732d36af28 | |||
| edfc86f153 | |||
| 339e30d973 | |||
| 5e1e092650 | |||
| c69febd44d | |||
| a9d7564f3e | |||
| a0b733b53d | |||
| 8cb565ad2a | |||
| 32158711ce | |||
| e099f00739 | |||
| e707ab5441 | |||
| 3b7f1467e1 | |||
| 1c7eef89c3 | |||
| 4fc1f5b248 | |||
| 6eacfa7077 | |||
| 9aefafb831 | |||
| 5a3353ec28 | |||
| e87b8dc463 | |||
| 60daa483e6 | |||
| cd699d8b9b | |||
| d33a962848 | |||
| 0d2067e1a4 | |||
| 8647137952 | |||
| fbcfad28c3 | |||
| 5e1b2e2696 | |||
| a0346e95cf | |||
| 20ee7b432c | |||
| 4026e6ca5c | |||
| 378901e0d2 | |||
| 964abd3141 | |||
| 5ca7ad3bce | |||
| 4dcba4bb27 | |||
| 073ea84d69 | |||
| 2965d2e974 | |||
| a67f0d4d57 | |||
| 5309d5f24d | |||
| af3ca57094 | |||
| db375cd190 | |||
| d96b63d791 | |||
| e435b48750 | |||
| a1f8a21c7c | |||
| 69f9f10c6b | |||
| d3c6bd2f98 | |||
| 466868e639 | |||
| 2317c6835e | |||
| 4e99d003ee | |||
| 1a5e10c1d2 | |||
| 8efcb1abfa | |||
| f031a9706d | |||
| eb7f3ff5af | |||
| bb595ab08a | |||
| 101326a130 | |||
| ad60594c13 | |||
| ca6da5f9c1 | |||
| c397b3cc60 | |||
| 1074c6c34e | |||
| 6cb0f1ff78 | |||
| 371e31c786 | |||
| dce70b35b5 | |||
| 99a67ca1ab | |||
| 9dc1758d76 | |||
| 196942d126 | |||
| d95aa0aac1 | |||
| 27f0d11039 | |||
| a69e6136ea | |||
| 0fd8b6be8d | |||
| 8e51a7f6d6 | |||
| bdb4cd5247 | |||
| 3afe750a66 | |||
| c8f3e056e9 | |||
| 32fdf0b681 | |||
| 82c73c9911 | |||
| fe0184d067 | |||
| 0ef6757e65 | |||
| deb4365d65 | |||
| 750f4ee410 | |||
| 1382bd3b3b | |||
| d908c8b026 | |||
| 6f766acac8 | |||
| 7eba9d3a23 | |||
| cde5861cb8 | |||
| 765c15829d | |||
| 0471619269 | |||
| d8ba8f13d8 | |||
| c089429c14 | |||
| b22f850e03 | |||
| cf47ee5063 | |||
| 9d6a01d11c | |||
| bcf9dfd236 | |||
| c281eb9596 | |||
| 2acde6a727 | |||
| ee9b78afb6 | |||
| a379d47230 | |||
| 57c23e1b5f | |||
| f8d959ae16 | |||
| 831454bd30 | |||
| 13abb28486 | |||
| 717704c8ed | |||
| eab26fb3a6 | |||
| 17947d0f99 | |||
| 626e22e4ae | |||
| 5837b7cfda | |||
| 02a9d11bb5 | |||
| 0d89be26be | |||
| 1a487db29f | |||
| 1144e52fea | |||
| 2938a8e650 | |||
| cd97de76fc | |||
| 25c099f3a2 | |||
| eb39b8505e | |||
| 7a90cc46ad | |||
| c6c1cb233d | |||
| f9e37c9b47 | |||
| dfb6835449 | |||
| bf13b426c5 | |||
| 14175d80ef | |||
| c99e211144 | |||
| 1d04cbf584 | |||
| 92650e079f | |||
| f29ce127db | |||
| b29f1965f0 | |||
| 48a2a1934b | |||
| bb65a1a47e | |||
| f6a8b72fd4 | |||
| 196e908961 | |||
| 72744494b8 | |||
| c287be1725 | |||
| 28ec1f3e2d | |||
| d0bf769896 | |||
| 8fe60b5838 | |||
| 484a7c95ee | |||
| 44a3365787 | |||
| f3e88b56f3 | |||
| d618472f95 | |||
| a45ffb3aaa | |||
| 1d2b2eec4c | |||
| 5c1f750f94 | |||
| 85a6db253b | |||
| 72caf279a6 | |||
| 05ff058ca5 | |||
| 022ead9228 | |||
| 14fd9aac7e | |||
| 5975884a0e | |||
| e8ce559034 | |||
| 9add438ea4 | |||
| d65a6c3fc4 | |||
| a493359b58 | |||
| d6de973500 | |||
| 25cc37bc86 | |||
| 0e6bc275b7 | |||
| cec731b697 | |||
| 6942b82a70 | |||
| b6d5a1ddd8 | |||
| 8d4ae33134 | |||
| cbec03c158 | |||
| 1fe0a1ad7b | |||
| 8b9ede8414 | |||
| 194094043e | |||
| 9068f28e83 | |||
| ebb5d38e1a | |||
| aa1ed1dfcc | |||
| 0292c66f93 | |||
| 4ce68fe5d8 | |||
| 289e7d56c3 | |||
| cb7471945c | |||
| 8da9e8ddc1 | |||
| a68694e6e2 | |||
| 98b5a400e6 | |||
| 0bbd9630f5 | |||
| c6770aa83e | |||
| e26d97d5ac | |||
| b2177cbc09 | |||
| c8663541f0 | |||
| 9f32a24e7e | |||
| 914a87290b | |||
| 112b8bfa29 | |||
| 5d99acd8c5 | |||
| 066d8dc944 | |||
| 9af6e99682 | |||
| bc65137911 | |||
| 02dfe074e6 | |||
| 2b4c2f1758 | |||
| a7494cf649 | |||
| 3d494ed9fa | |||
| 47a14bb2e6 | |||
| 7a71f26345 | |||
| 870c4d4214 | |||
| ac5d10fae2 | |||
| 2963fe177f | |||
| 0947947a9d | |||
| 0112e8887f | |||
| 3a7ecccec7 | |||
| f9a9712f54 | |||
| 5e5ba63c59 | |||
| 2c58d1a2b7 | |||
| 5e2e5a4b58 | |||
| 344d237a42 | |||
| 04eef20c84 | |||
| 77dba8d19b | |||
| 704294a2d5 | |||
| 847a8165e0 | |||
| 18f2f5f821 | |||
| 7f2e0e2317 | |||
| 963513e840 | |||
| b1470fc1b5 | |||
| 43482ea78e | |||
| 167e6dc1bf | |||
| 088a627468 | |||
| 4f6c655294 | |||
| e2db58c6ee | |||
| 64aba6ec24 | |||
| 2910170eeb | |||
| 9bc24b8736 | |||
| 401ef87bb2 | |||
| 0c5ed1eee4 | |||
| 790c227924 | |||
| 822f13d6e3 | |||
| 70c14eb4f5 | |||
| 673d2330f0 | |||
| e187604942 | |||
| c2cd1cd13d | |||
| c720a6a029 | |||
| 601466782f | |||
| 92ef8f22e4 | |||
| 690ffab9c0 | |||
| 512f373ca6 | |||
| e8f4da54a6 | |||
| d56137e3c0 | |||
| 9629afb145 | |||
| 5ecbd024cf | |||
| b4974eee41 | |||
| 3ca3b23ef8 | |||
| 9b48df928c | |||
| 884d276458 | |||
| 441e179c48 | |||
| e1248561b6 | |||
| 7b5055f974 | |||
| 206db1c271 | |||
| 821debcb85 | |||
| b9395d2946 | |||
| 44e6e7f45d | |||
| 88ee021b4d | |||
| b5c8e3e1fe | |||
| 9962ce9859 | |||
| 882d766689 | |||
| 959401f5a7 | |||
| fa9aa3691b | |||
| a95fdce85f | |||
| e8edc83e39 | |||
| 3ff702581a | |||
| 88c9275bff | |||
| 826d536c09 | |||
| c99c2af6ae | |||
| ccc6eed3da | |||
| e1aec57ffe | |||
| c1fc09e510 | |||
| 19212f99ee | |||
| 2681dcc63c | |||
| 6f5d85edb9 | |||
| a59c01c6be | |||
| a78613b7e5 | |||
| 33c1353500 | |||
| 22a7c25104 | |||
| 902406c572 | |||
| b0077986b4 | |||
| 686ebf8759 | |||
| 66831ce8b9 | |||
| dd2c6d2f24 | |||
| 30a175230a | |||
| fe3e71cbd7 | |||
| 8d2e1b7e43 | |||
| c37b884007 | |||
| 033d85fd4f | |||
| 963aef70e8 | |||
| 1dce2c439d | |||
| b3ed9d468b | |||
| 878eca30e9 | |||
| 97aed1f680 | |||
| bac76784e0 | |||
| 8e970b64b8 | |||
| dfe9f5684e | |||
| f0f477d481 | |||
| 470ce7bd22 | |||
| f59080ec74 | |||
| f5ca836e49 | |||
| 319f9a22d8 | |||
| 458d3b3d3c | |||
| 11ec939924 | |||
| e14019e2dd | |||
| 180912db61 | |||
| aba1900d13 | |||
| 5fdb3196a3 | |||
| 8763a49dc6 | |||
| ffbd15eb98 | |||
| 871b7406cd | |||
| 66fa9d6a42 | |||
| 8c7649dd57 | |||
| 38c4fb3884 | |||
| 2d17ffc792 | |||
| 1d70e12594 | |||
| 8fb729d000 | |||
| b9fd05ba1e | |||
| 2ad9fb52ae | |||
| 91778cb566 | |||
| a76cda4564 | |||
| 7cb1ea038c | |||
| fa18a5cb33 | |||
| a012248316 | |||
| 53ccf7cf87 | |||
| fbda0028ca | |||
| 9e3648c22f | |||
| d8374eb251 | |||
| fd6e5e7a13 | |||
| 1da6bca2ae | |||
| 40630114e7 | |||
| 6736b6af3e | |||
| 9c7747efbf | |||
| 42b203d502 | |||
| d7d4a990a5 | |||
| 8597951f0c | |||
| 7d47527b17 | |||
| 197d185ea4 | |||
| 91d1c2914e | |||
| da2487ba84 | |||
| 171c3e018b | |||
| bda8397d3d | |||
| c9aa082e63 | |||
| 6c4eb7439a | |||
| 76922c2d46 | |||
| fd7c3f3980 | |||
| 6b750c82ca | |||
| 4d643242a1 | |||
| e974e61012 | |||
| 1ce4f3c552 | |||
| 3ac2cae355 | |||
| 2ffa989ba4 | |||
| 341454cd81 | |||
| 4507981f2a | |||
| 14f2444883 | |||
| 267b1bdce6 | |||
| 53451e0bcd | |||
| 4223ea25ee | |||
| bb517467eb | |||
| dd16aefbb6 | |||
| 471c323ca5 | |||
| bf021ab7f7 | |||
| 9198f3b809 | |||
| b7c227dbfc | |||
| 0dc031140c | |||
| e853bbfcb6 | |||
| cef7577826 | |||
| 8366887df6 | |||
| e7f63b7c62 | |||
| 81a04769fb | |||
| d4fcfbd034 | |||
| bab92c6d0a | |||
| 72523de046 | |||
| 82fbba4547 | |||
| 8b112cfd65 | |||
| ec83c1061b | |||
| 75c573fe61 | |||
| ed8800d182 | |||
| 0aabfddfcb | |||
| 1bf1785885 | |||
| 20df9f4666 | |||
| e40b7b1412 | |||
| 611559ecd6 | |||
| c74783ebbf | |||
| 9df69f801b | |||
| 105f74e15e | |||
| d7e56c447b | |||
| cecee6a430 | |||
| e3e83918e5 | |||
| 0438565c43 | |||
| cbfa87a3b4 | |||
| 3187013eac | |||
| e7b632eb18 | |||
| cdce91157f | |||
| a566bd469d | |||
| 389586bb41 | |||
| 51b39500fe | |||
| b5a6f0db0f | |||
| 259faa5b5a | |||
| c8477c2f01 | |||
| f6b00fc90f | |||
| b3246d63fb | |||
| 07eb2d014a | |||
| bf0015f6cc | |||
| ca3d1e869c | |||
| a57083a48b | |||
| 2ae46734db | |||
| 3918435c7f | |||
| 180e5d4164 | |||
| d8f5ef71cd | |||
| 2e2bab7778 | |||
| b10ef9454b | |||
| 31fc4fcde8 | |||
| f48514114d | |||
| fd929364d1 | |||
| 4f758f1fe2 | |||
| 35e1940984 | |||
| bdadf43341 | |||
| 92b829c89b | |||
| 3db9e16637 | |||
| 29d7735848 | |||
| 5b4b57f10f | |||
| ed4b50e3b4 | |||
| d526cfcba0 | |||
| 0cf2cdceea | |||
| 8239bb7e05 | |||
| 6d3e137db8 | |||
| fa34eea27a | |||
| 3e41b0967f | |||
| bd3cfda2bb | |||
| 386b8a9e46 | |||
| 099b5186ab | |||
| deab6027fb | |||
| 590582782b | |||
| abb0a3c792 | |||
| 43baee5abd | |||
| 75153388cc | |||
| b959fc0a6a | |||
| 77b5fd47ca | |||
| f3a9500e61 | |||
| dadb0d5427 | |||
| 9cdc15b161 | |||
| 7c27cbd9f5 | |||
| 010931391e | |||
| eaf6aba943 | |||
| d60d710325 | |||
| bb1bcc233a | |||
| f0813dbca8 | |||
| 9bded1cd19 | |||
| c6e96a6188 | |||
| 09ff90bfc4 | |||
| 2bf9036578 | |||
| 6a7e4c30fb | |||
| ed41cc3cad | |||
| 9aee5adeb6 | |||
| 70f3527123 | |||
| e67e8ff0f5 | |||
| 4448a7419c | |||
| e18a0f045a | |||
| e8e84cbd2f | |||
| e8c5027026 | |||
| da964a8f15 | |||
| 2c4aee1181 | |||
| 24d3f1daeb | |||
| beb2fb08ea | |||
| 7b86891642 | |||
| 87e325dce1 | |||
| ee87c757ed | |||
| a25812dca7 | |||
| 1f9a7599b7 | |||
| 07dee8838c | |||
| 8367b020ec | |||
| 9fce6a2f1c | |||
| 165a417a9d | |||
| 39fe0c418b | |||
| 75cee638bc | |||
| ae72d4ad56 | |||
| bfcb24f517 | |||
| 208ed09109 | |||
| 0b7f291e17 | |||
| a61b057aea | |||
| 4c817baf4c | |||
| a564cc66f6 | |||
| 14c58c4517 | |||
| ebe77065cc | |||
| e1d35ed90f | |||
| 5d23a8fceb | |||
| 30b0fed60f | |||
| 90f3726c5c | |||
| 5f10c48219 | |||
| 23957d7f66 | |||
| 58bf19cc83 | |||
| f439b04978 | |||
| f6d1647021 | |||
| 0ccaf9049f | |||
| 132194337e | |||
| f0198944b5 | |||
| f414c0955b | |||
| 4ffa5bb90e | |||
| 4dbca94d32 | |||
| 7111961929 | |||
| 5d46d7a881 | |||
| 8d1d64d9d3 | |||
| 60498616b7 | |||
| ed745d2072 | |||
| 1f69963982 | |||
| 787b6fb677 | |||
| 2d5b04579f | |||
| 9fbfeb970b | |||
| 63cac904ae | |||
| 04ad048e1f | |||
| b9b8c61c11 | |||
| 6a8697a357 | |||
| bb35655e67 | |||
| 865d94d758 | |||
| 965e146bf2 | |||
| d97b394000 | |||
| 29b85a1bb9 | |||
| dda65d82c3 | |||
| 56de32f433 | |||
| 3cb751723d | |||
| 344e6d15ce | |||
| 8e7de0dc89 | |||
| 3b388f16fd | |||
| c64b1a7628 | |||
| 5ce71f5ded | |||
| 02415ca400 | |||
| 69f357b538 | |||
| 780441bdb8 | |||
| e0ad22d0bd | |||
| 115203cbd2 | |||
| 0b5155f496 | |||
| 4f18baaed5 | |||
| 095320680f | |||
| 8a8df8d6cb | |||
| 6bb4eaee93 | |||
| 7710845cdc | |||
| 8471ffed29 | |||
| ce6d793528 | |||
| e5bf70544e | |||
| c591911455 | |||
| 44955e536b | |||
| 6f25945911 | |||
| e5bd341f05 | |||
| e9d4563084 | |||
| b67ce8f4f8 | |||
| 6a90bc1b17 | |||
| 6ba8855d51 | |||
| e6b55cd521 | |||
| 59f453bb06 | |||
| e275b6c414 | |||
| f3cdb6e0a2 | |||
| c4fc75215b | |||
| 68e7c209bd | |||
| 1737df6ff9 | |||
| 897bf2f8e0 | |||
| 1851c81e60 | |||
| 938c85e616 | |||
| 350602a740 | |||
| f1ed51a823 | |||
| 086f0a0684 | |||
| fbd8f7a1a7 | |||
| 3e359e3c4e | |||
| 425c076221 | |||
| 351c1f1ab8 | |||
| 1efde26230 | |||
| 527aa61fa1 | |||
| 18a712a1f7 | |||
| 6c8db8ee49 | |||
| 4bfcdf4d5b | |||
| c1f3499359 | |||
| 201717a9aa | |||
| 3e83364e38 | |||
| ba681023da | |||
| 2ae7775caf | |||
| b2ab9d3246 | |||
| 7cc61c5252 | |||
| b3b2be2430 | |||
| 1674f16a0e | |||
| 24c84fe09c | |||
| 2136fdaa60 | |||
| 347eb9ce83 | |||
| a3eb76d630 | |||
| 163994340d | |||
| 8ffa8fed12 | |||
| 2141d06f61 | |||
| e1a1a7efc9 | |||
| f3f94a2af0 | |||
| 3d8e304b1c | |||
| 2d0ba3e1f9 | |||
| be0ebab841 | |||
| 8c1d49c703 | |||
| 4af71391a6 | |||
| 90802e3725 | |||
| a9d1afdf65 | |||
| 2a51eb7614 | |||
| 96d38d7252 | |||
| 4705e382bf | |||
| 11f8eba2c3 | |||
| d638fe9ca0 | |||
| 3f69197a08 | |||
| 43bf47fa26 | |||
| 092ed37ed9 | |||
| 19da22d440 | |||
| 51ff3ec46a | |||
| 36b5518add | |||
| dbdb51db2f | |||
| 7db11ea3c6 | |||
| f6ce92e52d | |||
| 6c1999ab60 | |||
| 3dde91dd3d | |||
| 9deec49f81 | |||
| 7c3106d9d9 | |||
| 7138d5b888 | |||
| 43495f05ba | |||
| 518a929880 | |||
| dd00f0d4ea | |||
| 20d8458009 | |||
| 13f45c615a | |||
| 998d542825 | |||
| fccc47023c | |||
| a2f2efe8b3 | |||
| c6bdc59fef | |||
| 047e5e696f | |||
| 72191f3d3d | |||
| 5ced583a7c | |||
| c326988a7d | |||
| 5ff47f3aa1 | |||
| 308bb95ea2 | |||
| 22ec8c3a75 | |||
| 3490022180 | |||
| ec3e139632 | |||
| 63e3be8630 | |||
| e61d482384 | |||
| b108b5030a | |||
| 943fe29ca3 | |||
| 2bbf7fd0d7 | |||
| 42e9c612b2 | |||
| 33778cefdd | |||
| 39fb5dc134 | |||
| ffedaa1bc1 | |||
| 3402504779 | |||
| c8253be1ae | |||
| 9ede5f85e3 | |||
| 640fd0051b | |||
| 779c918179 | |||
| ee1f21439d | |||
| f25d8d83c5 | |||
| b4270b1e9e | |||
| 98701283e8 | |||
| 34f71a2623 | |||
| af06bf8984 | |||
| 796632c36c | |||
| 65fa5bf880 | |||
| 1ccc93c50b | |||
| f4d19810b8 | |||
| 1c74925637 | |||
| a4d2b4d21a | |||
| 88eb803d91 | |||
| b61654b52f | |||
| db1768a9c7 | |||
| 6156dac3de | |||
| d5a7f7396e | |||
| ca65ff6c55 | |||
| 49782d3335 | |||
| e45aeecb0a | |||
| d3edf33dc4 | |||
| c54c925793 | |||
| e3b89aca93 | |||
| 9183d4968f | |||
| 1aa96fc37a | |||
| d75d3b6d09 | |||
| f60856eca7 | |||
| 3a7049a1d9 | |||
| 29d1b42d04 | |||
| 80552675d0 | |||
| ff696355ee | |||
| 3a2c4ca74d | |||
| 777cc4b561 | |||
| c0fe3c01de | |||
| 97dbe0d3b2 | |||
| 250938869e | |||
| 6092de9af8 | |||
| 01855901e7 | |||
| 161542a4af | |||
| 6d2bd7756e | |||
| f0fece2cf0 | |||
| c36ee1dbe6 | |||
| 76ec5a98c4 | |||
| ec022b5c77 | |||
| 912636a599 | |||
| 011c3cc223 | |||
| 433b64d581 | |||
| 365d69a19c | |||
| 5d1e507988 | |||
| 776b267396 | |||
| 77e4b7f90f | |||
| 2501b98e8f | |||
| 32d301ef34 | |||
| f4861b7919 | |||
| 94e4fef108 | |||
| 7ae432fad5 | |||
| 18057e303b | |||
| 71064adae2 | |||
| 43d964c357 | |||
| 5f4115cb81 | |||
| 8cf2cdb2a9 | |||
| a460530770 | |||
| 162bab506d | |||
| 6c5669b53d | |||
| a7db8229d5 | |||
| b7848d760f | |||
| e6d8f3b9bf | |||
| 0e270f85ba | |||
| 9e3f4dc033 | |||
| c1a744b29c | |||
| a283161c1e |
@@ -9,4 +9,6 @@ debug.log
|
||||
/tags
|
||||
/atom-shell/
|
||||
docs/output
|
||||
docs/includes
|
||||
spec/fixtures/evil-files/
|
||||
/apm
|
||||
|
||||
+22
-2
@@ -10,11 +10,26 @@ to propose changes to this document in a pull request.
|
||||
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.
|
||||
|
||||
### 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 org](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).
|
||||
|
||||
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.
|
||||
|
||||
## Pull Requests
|
||||
* Include screenshots and animated GIFs whenever possible.
|
||||
* Follow the [JavaScript](https://github.com/styleguide/javascript) and
|
||||
[CSS](https://github.com/styleguide/css) styleguides
|
||||
* 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
|
||||
@@ -41,3 +56,8 @@ to propose changes to this document in a pull request.
|
||||
* :racehorse: when improving performance
|
||||
* :non-potable_water: when plugging memory leaks
|
||||
* :memo: when writing docs
|
||||
|
||||
## CoffeeScript Styleguide
|
||||
|
||||
* Set parameter defaults without spaces around the equal sign
|
||||
* `clear = (count=1) ->` instead of `clear = (count = 1) ->`
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# Atom — The hackable, ~~collaborative~~ editor of tomorrow!
|
||||
# Atom — The hackable editor
|
||||
|
||||

|
||||
|
||||
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'Atom',
|
||||
'type': 'none',
|
||||
'postbuilds': [
|
||||
{
|
||||
'postbuild_name': 'Create Atom, basically do everything',
|
||||
'action': ['script/constructicon/build'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -2,12 +2,39 @@ fs = require 'fs'
|
||||
path = require 'path'
|
||||
os = require 'os'
|
||||
|
||||
# Add support for obselete APIs of vm module so we can make some third-party
|
||||
# modules work under node v0.11.x.
|
||||
require 'vm-compatibility-layer'
|
||||
|
||||
fm = require 'json-front-matter'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
packageJson = require './package.json'
|
||||
packageJson = require '../package.json'
|
||||
|
||||
# OAuth token for atom-bot
|
||||
# TODO Remove once all repositories are public
|
||||
process.env.ATOM_ACCESS_TOKEN ?= '362295be4c5258d3f7b967bbabae662a455ca2a7'
|
||||
|
||||
# Shim harmony collections in case grunt was invoked without harmony
|
||||
# collections enabled
|
||||
_.extend(global, require('harmony-collections')) unless global.WeakMap?
|
||||
|
||||
module.exports = (grunt) ->
|
||||
grunt.loadNpmTasks('grunt-coffeelint')
|
||||
grunt.loadNpmTasks('grunt-lesslint')
|
||||
grunt.loadNpmTasks('grunt-cson')
|
||||
grunt.loadNpmTasks('grunt-contrib-csslint')
|
||||
grunt.loadNpmTasks('grunt-contrib-coffee')
|
||||
grunt.loadNpmTasks('grunt-contrib-less')
|
||||
grunt.loadNpmTasks('grunt-markdown')
|
||||
grunt.loadNpmTasks('grunt-shell')
|
||||
grunt.loadNpmTasks('grunt-download-atom-shell')
|
||||
grunt.loadNpmTasks('grunt-peg')
|
||||
grunt.loadTasks('tasks')
|
||||
|
||||
# This allows all subsequent paths to the relative to the root of the repo
|
||||
grunt.file.setBase(path.resolve('..'))
|
||||
|
||||
if not grunt.option('verbose')
|
||||
grunt.log.writeln = (args...) -> grunt.log
|
||||
grunt.log.write = (args...) -> grunt.log
|
||||
@@ -19,6 +46,7 @@ module.exports = (grunt) ->
|
||||
installRoot = process.env.ProgramFiles
|
||||
buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'atom-build')
|
||||
shellAppDir = path.join(buildDir, appName)
|
||||
contentsDir = shellAppDir
|
||||
appDir = path.join(shellAppDir, 'resources', 'app')
|
||||
atomShellDownloadDir = path.join(os.tmpdir(), 'atom-cached-atom-shells')
|
||||
else
|
||||
@@ -79,6 +107,13 @@ module.exports = (grunt) ->
|
||||
dest: appDir
|
||||
ext: '.json'
|
||||
|
||||
pegConfig =
|
||||
glob_to_multiple:
|
||||
expand: true
|
||||
src: ['src/**/*.pegjs']
|
||||
dest: appDir
|
||||
ext: '.js'
|
||||
|
||||
for child in fs.readdirSync('node_modules') when child isnt '.bin'
|
||||
directory = path.join('node_modules', child)
|
||||
{engines, theme} = grunt.file.readJSON(path.join(directory, 'package.json'))
|
||||
@@ -87,6 +122,7 @@ module.exports = (grunt) ->
|
||||
lessConfig.glob_to_multiple.src.push("#{directory}/**/*.less")
|
||||
prebuildLessConfig.src.push("#{directory}/**/*.less") unless theme
|
||||
csonConfig.glob_to_multiple.src.push("#{directory}/**/*.cson")
|
||||
pegConfig.glob_to_multiple.src.push("#{directory}/**/*.pegjs")
|
||||
|
||||
grunt.initConfig
|
||||
pkg: grunt.file.readJSON('package.json')
|
||||
@@ -101,6 +137,8 @@ module.exports = (grunt) ->
|
||||
|
||||
cson: csonConfig
|
||||
|
||||
peg: pegConfig
|
||||
|
||||
coffeelint:
|
||||
options:
|
||||
no_empty_param_list:
|
||||
@@ -113,8 +151,10 @@ module.exports = (grunt) ->
|
||||
'dot-atom/**/*.coffee'
|
||||
'exports/**/*.coffee'
|
||||
'src/**/*.coffee'
|
||||
'tasks/**/*.coffee'
|
||||
'Gruntfile.coffee'
|
||||
]
|
||||
build: [
|
||||
'build/tasks/**/*.coffee'
|
||||
'build/Gruntfile.coffee'
|
||||
]
|
||||
test: [
|
||||
'spec/*.coffee'
|
||||
@@ -184,21 +224,9 @@ module.exports = (grunt) ->
|
||||
stderr: false
|
||||
failOnError: false
|
||||
|
||||
grunt.loadNpmTasks('grunt-coffeelint')
|
||||
grunt.loadNpmTasks('grunt-lesslint')
|
||||
grunt.loadNpmTasks('grunt-cson')
|
||||
grunt.loadNpmTasks('grunt-contrib-csslint')
|
||||
grunt.loadNpmTasks('grunt-contrib-coffee')
|
||||
grunt.loadNpmTasks('grunt-contrib-less')
|
||||
grunt.loadNpmTasks('grunt-markdown')
|
||||
grunt.loadNpmTasks('grunt-download-atom-shell')
|
||||
grunt.loadNpmTasks('grunt-shell')
|
||||
grunt.loadTasks('tasks')
|
||||
|
||||
grunt.registerTask('compile', ['coffee', 'prebuild-less', 'cson'])
|
||||
grunt.registerTask('compile', ['coffee', 'prebuild-less', 'cson', 'peg'])
|
||||
grunt.registerTask('lint', ['coffeelint', 'csslint', 'lesslint'])
|
||||
grunt.registerTask('test', ['shell:kill-atom', 'run-specs'])
|
||||
grunt.registerTask('ci', ['download-atom-shell', 'build', 'set-development-version', 'lint', 'test'])
|
||||
grunt.registerTask('deploy', ['partial-clean', 'download-atom-shell', 'build', 'codesign'])
|
||||
grunt.registerTask('ci', ['output-disk-space', 'download-atom-shell', 'build', 'set-version', 'lint', 'test', 'codesign', 'publish-build'])
|
||||
grunt.registerTask('docs', ['markdown:guides', 'build-docs'])
|
||||
grunt.registerTask('default', ['download-atom-shell', 'build', 'set-development-version', 'install'])
|
||||
grunt.registerTask('default', ['download-atom-shell', 'build', 'set-version', 'install'])
|
||||
@@ -0,0 +1,10 @@
|
||||
# Atom Build
|
||||
|
||||
This folder contains the grunt configuration and tasks to build Atom.
|
||||
|
||||
It was moved from the root of the repository so that any native modules used
|
||||
would be compiled against node's v8 headers since anything stored in
|
||||
`node_modules` at the root of the repo is compiled against atom's v8 headers.
|
||||
|
||||
New build dependencies should be added to the `package.json` file located in
|
||||
this folder.
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "atom-build",
|
||||
"description": "Atom build",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "~0.2.9",
|
||||
"biscotto": "git://github.com/atom/biscotto.git#12188bfbe5f7303fa9f1aa3c4f8662d40ce3c3be",
|
||||
"first-mate": "1.x",
|
||||
"formidable": "~1.0.14",
|
||||
"fs-plus": "1.x",
|
||||
"github-releases": "~0.2.0",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-cli": "~0.1.9",
|
||||
"grunt-coffeelint": "git://github.com/atom/grunt-coffeelint.git",
|
||||
"grunt-contrib-csslint": "~0.1.2",
|
||||
"grunt-contrib-coffee": "~0.9.0",
|
||||
"grunt-contrib-less": "~0.8.0",
|
||||
"grunt-cson": "0.6.0",
|
||||
"grunt-download-atom-shell": "git+https://atom-bot:362295be4c5258d3f7b967bbabae662a455ca2a7@github.com/atom/grunt-download-atom-shell#v0.6.0",
|
||||
"grunt-lesslint": "0.13.0",
|
||||
"grunt-markdown": "~0.4.0",
|
||||
"grunt-peg": "~1.1.0",
|
||||
"grunt-shell": "~0.3.1",
|
||||
"harmony-collections": "~0.3.8",
|
||||
"js-yaml": "~2.1.0",
|
||||
"json-front-matter": "~0.1.3",
|
||||
"rcedit": "~0.1.2",
|
||||
"request": "~2.27.0",
|
||||
"rimraf": "~2.2.2",
|
||||
"runas": "~0.3.0",
|
||||
"underscore-plus": "1.x",
|
||||
"unzip": "~0.1.9",
|
||||
"vm-compatibility-layer": "~0.1.0",
|
||||
"walkdir": "0.0.7"
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ module.exports = (grunt) ->
|
||||
|
||||
cp 'atom.sh', path.join(appDir, 'atom.sh')
|
||||
cp 'package.json', path.join(appDir, 'package.json')
|
||||
cp 'apm', path.join(appDir, 'apm')
|
||||
|
||||
packageDirectories = []
|
||||
nonPackageDirectories = [
|
||||
@@ -38,19 +39,27 @@ module.exports = (grunt) ->
|
||||
else
|
||||
nonPackageDirectories.push(directory)
|
||||
|
||||
# Put any paths here that shouldn't end up in the built Atom.app
|
||||
# so that it doesn't becomes larger than it needs to be.
|
||||
ignoredPaths = [
|
||||
path.join('git-utils', 'deps')
|
||||
path.join('oniguruma', 'deps')
|
||||
path.join('less', 'dist')
|
||||
path.join('less', 'test')
|
||||
path.join('bootstrap', 'docs')
|
||||
path.join('spellchecker', 'vendor')
|
||||
path.join('xmldom', 'test')
|
||||
path.join('vendor', 'apm')
|
||||
path.join('resources', 'mac')
|
||||
path.join('resources', 'win')
|
||||
]
|
||||
ignoredPaths = ignoredPaths.map (ignoredPath) -> "(#{ignoredPath})"
|
||||
nodeModulesFilter = new RegExp(ignoredPaths.join('|'))
|
||||
packageFilter = new RegExp("(#{ignoredPaths.join('|')})|(.+\\.(cson|coffee)$)")
|
||||
for directory in nonPackageDirectories
|
||||
cp directory, path.join(appDir, directory), filter: nodeModulesFilter
|
||||
for directory in packageDirectories
|
||||
cp directory, path.join(appDir, directory), filter: /.+\.(cson|coffee)$/
|
||||
cp directory, path.join(appDir, directory), filter: packageFilter
|
||||
|
||||
cp 'spec', path.join(appDir, 'spec')
|
||||
cp 'src', path.join(appDir, 'src'), filter: /.+\.(cson|coffee)$/
|
||||
@@ -0,0 +1,25 @@
|
||||
module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'codesign', 'Codesign the app', ->
|
||||
done = @async()
|
||||
|
||||
if process.env.XCODE_KEYCHAIN
|
||||
unlockKeychain (error) ->
|
||||
if error?
|
||||
done(error)
|
||||
else
|
||||
signApp(done)
|
||||
else
|
||||
signApp(done)
|
||||
|
||||
unlockKeychain = (callback) ->
|
||||
cmd = 'security'
|
||||
{XCODE_KEYCHAIN_PASSWORD, XCODE_KEYCHAIN} = process.env
|
||||
args = ['unlock-keychain', '-p', XCODE_KEYCHAIN_PASSWORD, XCODE_KEYCHAIN]
|
||||
spawn {cmd, args}, (error) -> callback(error)
|
||||
|
||||
signApp = (callback) ->
|
||||
cmd = 'codesign'
|
||||
args = ['-f', '-v', '-s', 'Developer ID Application: GitHub', grunt.config.get('atom.shellAppDir')]
|
||||
spawn {cmd, args}, (error) -> callback(error)
|
||||
@@ -1,16 +1,51 @@
|
||||
path = require 'path'
|
||||
fs = require 'fs'
|
||||
|
||||
async = require 'async'
|
||||
fs = require 'fs-plus'
|
||||
request = require 'request'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{rm} = require('./task-helpers')(grunt)
|
||||
|
||||
cmd = path.join('node_modules', '.bin', 'coffee')
|
||||
commonArgs = [path.join('node_modules', '.bin', 'biscotto'), '--']
|
||||
commonArgs = [path.join('build', 'node_modules', '.bin', 'biscotto'), '--']
|
||||
opts =
|
||||
stdio: 'inherit'
|
||||
|
||||
grunt.registerTask 'build-docs', 'Builds the API docs in src/app', ->
|
||||
grunt.registerTask 'build-docs', 'Builds the API docs in src', ->
|
||||
done = @async()
|
||||
args = [commonArgs..., '--title', 'Atom API Documentation', '-o', 'docs/output/api', 'src/', '../telepath/src/range.coffee', '../telepath/src/point.coffee', '../telepath/src/string-marker.coffee']
|
||||
grunt.util.spawn({cmd, args, opts}, done)
|
||||
|
||||
downloadFileFromRepo = ({repo, file}, callback) ->
|
||||
uri = "https://raw2.github.com/atom/#{repo}/master/#{file}"
|
||||
request uri, (error, response, contents) ->
|
||||
return callback(error) if error?
|
||||
downloadPath = path.join('docs', 'includes', repo, file)
|
||||
fs.writeFile downloadPath, contents, (error) ->
|
||||
callback(error, downloadPath)
|
||||
|
||||
includes = [
|
||||
{repo: 'first-mate', file: 'src/grammar-registry.coffee'}
|
||||
{repo: 'space-pen', file: 'src/space-pen.coffee'}
|
||||
{repo: 'text-buffer', file: 'src/marker.coffee'}
|
||||
{repo: 'text-buffer', file: 'src/point.coffee'}
|
||||
{repo: 'text-buffer', file: 'src/range.coffee'}
|
||||
{repo: 'theorist', file: 'src/model.coffee'}
|
||||
]
|
||||
|
||||
async.map includes, downloadFileFromRepo, (error, includePaths) ->
|
||||
if error?
|
||||
done(error)
|
||||
else
|
||||
rm('docs/output/api')
|
||||
args = [
|
||||
commonArgs...
|
||||
'--title', 'Atom API Documentation'
|
||||
'-o', 'docs/output/api'
|
||||
'-r', 'docs/README.md'
|
||||
'src/'
|
||||
includePaths...
|
||||
]
|
||||
grunt.util.spawn({cmd, args, opts}, done)
|
||||
|
||||
grunt.registerTask 'lint-docs', 'Generate stats about the doc coverage', ->
|
||||
done = @async()
|
||||
@@ -0,0 +1,25 @@
|
||||
path = require 'path'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{cp, mkdir, rm, spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'install', 'Install the built application', ->
|
||||
installDir = grunt.config.get('atom.installDir')
|
||||
shellAppDir = grunt.config.get('atom.shellAppDir')
|
||||
if process.platform is 'win32'
|
||||
done = @async()
|
||||
|
||||
runas = require 'runas'
|
||||
copyFolder = path.resolve 'script', 'copy-folder.cmd'
|
||||
# cmd /c ""script" "source" "destination""
|
||||
arg = "/c \"\"#{copyFolder}\" \"#{shellAppDir}\" \"#{installDir}\"\""
|
||||
if runas('cmd', [arg], hide: true) isnt 0
|
||||
done("Failed to copy #{shellAppDir} to #{installDir}")
|
||||
|
||||
createShortcut = path.resolve 'script', 'create-shortcut.cmd'
|
||||
args = ['/c', createShortcut, path.join(installDir, 'atom.exe'), 'Atom']
|
||||
spawn {cmd: 'cmd', args}, done
|
||||
else
|
||||
rm installDir
|
||||
mkdir path.dirname(installDir)
|
||||
cp shellAppDir, installDir
|
||||
@@ -0,0 +1,25 @@
|
||||
module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'output-disk-space', 'Print diskspace available', ->
|
||||
return unless process.platform is 'darwin'
|
||||
|
||||
done = @async()
|
||||
|
||||
cmd = 'df'
|
||||
args = ['-Hl']
|
||||
spawn {cmd, args}, (error, result, code) ->
|
||||
return done(error) if error?
|
||||
|
||||
lines = result.stdout.split("\n")
|
||||
|
||||
for line in lines[1..]
|
||||
[filesystem, size, used, avail, capacity, extra] = line.split(/\s+/)
|
||||
capacity = parseInt(capacity)
|
||||
|
||||
if capacity > 90
|
||||
grunt.log.error("#{filesystem} is at #{capacity}% capacity!")
|
||||
else if capacity > 80
|
||||
grunt.log.ok("#{filesystem} is at #{capacity}% capacity.")
|
||||
|
||||
done()
|
||||
@@ -0,0 +1,172 @@
|
||||
child_process = require 'child_process'
|
||||
path = require 'path'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
GitHub = require 'github-releases'
|
||||
request = require 'request'
|
||||
|
||||
grunt = null
|
||||
maxReleases = 10
|
||||
assetName = 'atom-mac.zip'
|
||||
assetPath = "/tmp/atom-build/#{assetName}"
|
||||
commitSha = process.env.JANKY_SHA1
|
||||
token = process.env.ATOM_ACCESS_TOKEN
|
||||
defaultHeaders =
|
||||
Authorization: "token #{token}"
|
||||
'User-Agent': 'Atom'
|
||||
|
||||
module.exports = (gruntObject) ->
|
||||
grunt = gruntObject
|
||||
grunt.registerTask 'publish-build', 'Publish the built app', ->
|
||||
return unless process.platform is 'darwin'
|
||||
return if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH isnt 'master'
|
||||
|
||||
done = @async()
|
||||
|
||||
createBuildRelease (error, release) ->
|
||||
return done(error) if error?
|
||||
zipApp (error) ->
|
||||
return done(error) if error?
|
||||
uploadAsset release, (error) ->
|
||||
return done(error) if error?
|
||||
publishRelease release, (error) ->
|
||||
return done(error) if error?
|
||||
getAtomDraftRelease (error, release) ->
|
||||
return done(error) if error?
|
||||
deleteExistingAsset release, (error) ->
|
||||
return done(error) if error?
|
||||
uploadAsset(release, done)
|
||||
|
||||
logError = (message, error, details) ->
|
||||
grunt.log.error(message)
|
||||
grunt.log.error(error.message ? error) if error?
|
||||
grunt.log.error(details) if details
|
||||
|
||||
zipApp = (callback) ->
|
||||
fs.removeSync(assetPath)
|
||||
|
||||
options = {cwd: path.dirname(assetPath), maxBuffer: Infinity}
|
||||
child_process.exec "zip -r --symlinks #{assetName} Atom.app", options, (error, stdout, stderr) ->
|
||||
if error?
|
||||
logError('Zipping Atom.app failed', error, stderr)
|
||||
callback(error)
|
||||
|
||||
getRelease = (callback) ->
|
||||
options =
|
||||
uri: 'https://api.github.com/repos/atom/atom-master-builds/releases'
|
||||
method: 'GET'
|
||||
headers: defaultHeaders
|
||||
json: true
|
||||
request options, (error, response, releases=[]) ->
|
||||
if error? or response.statusCode isnt 200
|
||||
logError('Fetching releases failed', error, releases)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
if releases.length > maxReleases
|
||||
deleteRelease(release) for release in releases[maxReleases..]
|
||||
|
||||
for release in releases when release.name is commitSha
|
||||
callback(null, release)
|
||||
return
|
||||
callback()
|
||||
|
||||
getAtomDraftRelease = (callback) ->
|
||||
atomRepo = new GitHub({repo: 'atom/atom', token})
|
||||
atomRepo.getReleases (error, releases=[]) ->
|
||||
if error?
|
||||
logError('Fetching atom/atom releases failed', error, releases)
|
||||
callback(error)
|
||||
else
|
||||
for release in releases when release.draft
|
||||
callback(null, release)
|
||||
return
|
||||
callback(new Error('No draft release in atom/atom repo'))
|
||||
|
||||
deleteRelease = (release) ->
|
||||
options =
|
||||
uri: release.url
|
||||
method: 'DELETE'
|
||||
headers: defaultHeaders
|
||||
json: true
|
||||
request options, (error, response, body='') ->
|
||||
if error? or response.statusCode isnt 204
|
||||
logError('Deleting release failed', error, body)
|
||||
|
||||
deleteExistingAsset = (release, callback) ->
|
||||
for asset in release.assets when asset.name is assetName
|
||||
options =
|
||||
uri: asset.url
|
||||
method: 'DELETE'
|
||||
headers: defaultHeaders
|
||||
request options, (error, response, body='') ->
|
||||
if error? or response.statusCode isnt 204
|
||||
logError('Deleting existing release asset failed', error, body)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
callback()
|
||||
|
||||
return
|
||||
|
||||
callback()
|
||||
|
||||
createBuildRelease = (callback) ->
|
||||
getRelease (error, release) ->
|
||||
if error?
|
||||
callback(error)
|
||||
return
|
||||
|
||||
if release?
|
||||
deleteExistingAsset release, (error) ->
|
||||
callback(error, release)
|
||||
return
|
||||
|
||||
options =
|
||||
uri: 'https://api.github.com/repos/atom/atom-master-builds/releases'
|
||||
method: 'POST'
|
||||
headers: defaultHeaders
|
||||
json:
|
||||
tag_name: "v#{commitSha}"
|
||||
target_commitish: 'master'
|
||||
name: commitSha
|
||||
body: "Build of [atom@#{commitSha.substring(0, 7)}](https://github.com/atom/atom/commits/#{commitSha})"
|
||||
draft: true
|
||||
prerelease: true
|
||||
request options, (error, response, release={}) ->
|
||||
if error? or response.statusCode isnt 201
|
||||
logError('Creating release failed', error, release)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
callback(null, release)
|
||||
|
||||
uploadAsset = (release, callback) ->
|
||||
options =
|
||||
uri: release.upload_url.replace(/\{.*$/, "?name=#{assetName}")
|
||||
method: 'POST'
|
||||
headers: _.extend({
|
||||
'Content-Type': 'application/zip'
|
||||
'Content-Length': fs.getSizeSync(assetPath)
|
||||
}, defaultHeaders)
|
||||
|
||||
assetRequest = request options, (error, response, body='') ->
|
||||
if error? or response.statusCode >= 400
|
||||
logError('Upload release asset failed', error, body)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
callback(null, release)
|
||||
|
||||
fs.createReadStream(assetPath).pipe(assetRequest)
|
||||
|
||||
publishRelease = (release, callback) ->
|
||||
options =
|
||||
uri: release.url
|
||||
method: 'POST'
|
||||
headers: defaultHeaders
|
||||
json:
|
||||
draft: false
|
||||
request options, (error, response, body={}) ->
|
||||
if error? or response.statusCode isnt 200
|
||||
logError('Creating release failed', error, body)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
callback()
|
||||
@@ -6,7 +6,7 @@ module.exports = (grunt) ->
|
||||
|
||||
shellAppDir = grunt.config.get('atom.shellAppDir')
|
||||
shellExePath = path.join(shellAppDir, 'atom.exe')
|
||||
iconPath = path.resolve(__dirname, '..', 'resources', 'win', 'atom.ico')
|
||||
iconPath = path.resolve('resources', 'win', 'atom.ico')
|
||||
|
||||
rcedit = require('rcedit')
|
||||
rcedit(shellExePath, {'icon': iconPath}, done)
|
||||
@@ -0,0 +1,50 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
|
||||
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'))
|
||||
callback(null, version)
|
||||
else
|
||||
cmd = 'git'
|
||||
args = ['rev-parse', '--short', 'HEAD']
|
||||
spawn {cmd, args}, (error, {stdout}={}, code) ->
|
||||
callback(error, stdout?.trim?())
|
||||
|
||||
grunt.registerTask 'set-version', 'Set the version in the plist and package.json', ->
|
||||
done = @async()
|
||||
|
||||
getVersion (error, version) ->
|
||||
if error?
|
||||
done(error)
|
||||
return
|
||||
|
||||
appDir = grunt.config.get('atom.appDir')
|
||||
|
||||
# Replace version field of package.json.
|
||||
packageJsonPath = path.join(appDir, 'package.json')
|
||||
packageJson = require(packageJsonPath)
|
||||
packageJson.version = version
|
||||
packageJsonString = JSON.stringify(packageJson, null, 2)
|
||||
fs.writeFileSync(packageJsonPath, packageJsonString)
|
||||
|
||||
if process.platform is 'darwin'
|
||||
cmd = 'script/set-version'
|
||||
args = [grunt.config.get('atom.buildDir'), version]
|
||||
spawn {cmd, args}, (error, result, code) -> done(error)
|
||||
else if process.platform is 'win32'
|
||||
shellAppDir = grunt.config.get('atom.shellAppDir')
|
||||
shellExePath = path.join(shellAppDir, 'atom.exe')
|
||||
|
||||
strings =
|
||||
CompanyName: 'GitHub, Inc.'
|
||||
FileDescription: 'The hackable editor'
|
||||
LegalCopyright: 'Copyright (C) 2013 GitHub, Inc. All rights reserved'
|
||||
ProductName: 'Atom'
|
||||
ProductVersion: version
|
||||
|
||||
rcedit = require('rcedit')
|
||||
rcedit(shellExePath, {'version-string': strings}, done)
|
||||
@@ -0,0 +1,112 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
async = require 'async'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{isAtomPackage, spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
packageSpecQueue = null
|
||||
|
||||
runPackageSpecs = (callback) ->
|
||||
failedPackages = []
|
||||
rootDir = grunt.config.get('atom.shellAppDir')
|
||||
contentsDir = grunt.config.get('atom.contentsDir')
|
||||
resourcePath = process.cwd()
|
||||
if process.platform is 'darwin'
|
||||
appPath = path.join(contentsDir, 'MacOS', 'Atom')
|
||||
else if process.platform is 'win32'
|
||||
appPath = path.join(contentsDir, 'atom.exe')
|
||||
|
||||
packageSpecQueue = async.queue (packagePath, callback) ->
|
||||
if process.platform is 'darwin'
|
||||
options =
|
||||
cmd: appPath
|
||||
args: ['--test', "--resource-path=#{resourcePath}", "--spec-directory=#{path.join(packagePath, 'spec')}"]
|
||||
opts:
|
||||
cwd: packagePath
|
||||
env: _.extend({}, process.env, ATOM_PATH: rootDir)
|
||||
else if process.platform is 'win32'
|
||||
options =
|
||||
cmd: process.env.comspec
|
||||
args: ['/c', appPath, '--test', "--resource-path=#{resourcePath}", "--spec-directory=#{path.join(packagePath, 'spec')}", "--log-file=ci.log"]
|
||||
opts:
|
||||
cwd: packagePath
|
||||
env: _.extend({}, process.env, ATOM_PATH: rootDir)
|
||||
|
||||
grunt.verbose.writeln "Launching #{path.basename(packagePath)} specs."
|
||||
spawn options, (error, results, code) ->
|
||||
if process.platform is 'win32'
|
||||
process.stderr.write(fs.readFileSync(path.join(packagePath, 'ci.log')))
|
||||
fs.unlinkSync(path.join(packagePath, 'ci.log'))
|
||||
|
||||
failedPackages.push path.basename(packagePath) if error
|
||||
callback()
|
||||
|
||||
modulesDirectory = path.resolve('node_modules')
|
||||
for packageDirectory in fs.readdirSync(modulesDirectory)
|
||||
packagePath = path.join(modulesDirectory, packageDirectory)
|
||||
continue unless grunt.file.isDir(path.join(packagePath, 'spec'))
|
||||
continue unless isAtomPackage(packagePath)
|
||||
packageSpecQueue.push(packagePath)
|
||||
|
||||
# TODO: Restore concurrency on Windows
|
||||
packageSpecQueue.concurrency = 1 unless process.platform is 'win32'
|
||||
|
||||
packageSpecQueue.drain = -> callback(null, failedPackages)
|
||||
|
||||
runCoreSpecs = (callback) ->
|
||||
contentsDir = grunt.config.get('atom.contentsDir')
|
||||
if process.platform is 'darwin'
|
||||
appPath = path.join(contentsDir, 'MacOS', 'Atom')
|
||||
else if process.platform is 'win32'
|
||||
appPath = path.join(contentsDir, 'atom.exe')
|
||||
resourcePath = process.cwd()
|
||||
coreSpecsPath = path.resolve('spec')
|
||||
|
||||
if process.platform is 'darwin'
|
||||
options =
|
||||
cmd: appPath
|
||||
args: ['--test', "--resource-path=#{resourcePath}", "--spec-directory=#{coreSpecsPath}"]
|
||||
else if process.platform is 'win32'
|
||||
options =
|
||||
cmd: process.env.comspec
|
||||
args: ['/c', appPath, '--test', "--resource-path=#{resourcePath}", "--spec-directory=#{coreSpecsPath}", "--log-file=ci.log"]
|
||||
|
||||
spawn options, (error, results, code) ->
|
||||
if process.platform is 'win32'
|
||||
process.stderr.write(fs.readFileSync('ci.log'))
|
||||
fs.unlinkSync('ci.log')
|
||||
else
|
||||
# TODO: Restore concurrency on Windows
|
||||
packageSpecQueue.concurrency = 2
|
||||
|
||||
callback(null, error)
|
||||
|
||||
grunt.registerTask 'run-specs', 'Run the specs', ->
|
||||
done = @async()
|
||||
startTime = Date.now()
|
||||
|
||||
# TODO: This should really be parallel on both platforms, however our
|
||||
# fixtures step on each others toes currently.
|
||||
if process.platform is 'darwin'
|
||||
method = async.parallel
|
||||
else if process.platform is 'win32'
|
||||
method = async.series
|
||||
|
||||
method [runCoreSpecs, runPackageSpecs], (error, results) ->
|
||||
[coreSpecFailed, failedPackages] = results
|
||||
elapsedTime = Math.round((Date.now() - startTime) / 100) / 10
|
||||
grunt.verbose.writeln("Total spec time: #{elapsedTime}s")
|
||||
failures = failedPackages
|
||||
failures.push "atom core" if coreSpecFailed
|
||||
|
||||
grunt.log.error("[Error]".red + " #{failures.join(', ')} spec(s) failed") if failures.length > 0
|
||||
|
||||
# TODO: Mark the build as green on Windows until specs pass.
|
||||
if process.platform is 'darwin'
|
||||
done(!coreSpecFailed and failedPackages.length == 0)
|
||||
else if process.platform is 'win32'
|
||||
done(true)
|
||||
@@ -39,7 +39,7 @@ module.exports = (grunt) ->
|
||||
proc = childProcess.spawn(options.cmd, options.args, options.opts)
|
||||
proc.stdout.on 'data', (data) -> stdout.push(data.toString())
|
||||
proc.stderr.on 'data', (data) -> stderr.push(data.toString())
|
||||
proc.on 'exit', (exitCode, signal) ->
|
||||
proc.on 'close', (exitCode, signal) ->
|
||||
error = new Error(signal) if exitCode != 0
|
||||
results = {stderr: stderr.join(''), stdout: stdout.join(''), code: exitCode}
|
||||
grunt.log.error results.stderr if exitCode != 0
|
||||
@@ -0,0 +1,62 @@
|
||||
# Welcome to the Atom API Documentation
|
||||
|
||||

|
||||
|
||||
## FAQ
|
||||
|
||||
### Where do I start?
|
||||
|
||||
Check out [EditorView][EditorView] and [Editor][Editor] classes for a good
|
||||
overview of the main editor API.
|
||||
|
||||
### How do I access these classes?
|
||||
|
||||
Check out the [Atom][Atom] class docs to see what globals are available and
|
||||
what they provide.
|
||||
|
||||
You can also require many of these classes in your package via:
|
||||
|
||||
```coffee
|
||||
{EditorView} = require 'atom'
|
||||
```
|
||||
|
||||
The classes available from `require 'atom'` are:
|
||||
* [BufferedProcess][BufferedProcess]
|
||||
* [BufferedNodeProcess][BufferedNodeProcess]
|
||||
* [Directory][Directory]
|
||||
* [EditorView][EditorView]
|
||||
* [File][File]
|
||||
* [Git][Git]
|
||||
* [Point][Point]
|
||||
* [Range][Range]
|
||||
* [ScrollView][ScrollView]
|
||||
* [SelectListView][SelectListView]
|
||||
* [View][View]
|
||||
* [WorkspaceView][WorkspaceView]
|
||||
|
||||
### How do I create a package?
|
||||
|
||||
You probably want to read the [creating a package][creating-a-package]
|
||||
doc first and come back here when you are done.
|
||||
|
||||
### Where are the node docs?
|
||||
|
||||
Atom ships with node 0.11.10 and the comprehensive node API docs are available
|
||||
[here][node-docs].
|
||||
|
||||
[Atom]: ../classes/Atom.html
|
||||
[BufferedProcess]: ../classes/BufferedProcess.html
|
||||
[BufferedNodeProcess]: ../classes/BufferedNodeProcess.html
|
||||
[Directory]: ../classes/Directory.html
|
||||
[Editor]: ../classes/Editor.html
|
||||
[EditorView]: ../classes/EditorView.html
|
||||
[File]: ../classes/File.html
|
||||
[Git]: ../classes/Git.html
|
||||
[Point]: ../classes/Point.html
|
||||
[Range]: ../classes/Range.html
|
||||
[ScrollView]: ../classes/ScrollView.html
|
||||
[SelectListView]: ../classes/SelectListView.html
|
||||
[View]: ../classes/View.html
|
||||
[WorkspaceView]: ../classes/WorkspaceView.html
|
||||
[creating-a-package]: https://www.atom.io/docs/latest/creating-a-package
|
||||
[node-docs]: http://nodejs.org/docs/v0.11.10/api
|
||||
@@ -0,0 +1,76 @@
|
||||
## Configuration API
|
||||
|
||||
### Reading Config Settings
|
||||
|
||||
If you are writing a package that you want to make configurable, you'll need to
|
||||
read config settings via the `atom.config` global. You can read the current
|
||||
value of a namespaced config key with `atom.config.get`:
|
||||
|
||||
```coffeescript
|
||||
# read a value with `config.get`
|
||||
@showInvisibles() if atom.config.get "editor.showInvisibles"
|
||||
```
|
||||
|
||||
Or you can use the `::observeConfig` to track changes from any view object.
|
||||
|
||||
```coffeescript
|
||||
class MyView extends View
|
||||
initialize: ->
|
||||
@observeConfig 'editor.fontSize', () =>
|
||||
@adjustFontSize()
|
||||
```
|
||||
|
||||
The `::observeConfig` method will call the given callback immediately with the
|
||||
current value for the specified key path, and it will also call it in the future
|
||||
whenever the value of that key path changes.
|
||||
|
||||
Subscriptions made with `observeConfig` are automatically canceled when the
|
||||
view is removed. You can cancel config subscriptions manually via the
|
||||
`unobserveConfig` method.
|
||||
|
||||
```coffeescript
|
||||
view1.unobserveConfig() # unobserve all properties
|
||||
```
|
||||
|
||||
You can add the ability to observe config values to non-view classes by
|
||||
extending their prototype with the `ConfigObserver` mixin:
|
||||
|
||||
```coffeescript
|
||||
{ConfigObserver} = require 'atom'
|
||||
|
||||
class MyClass
|
||||
ConfigObserver.includeInto(this)
|
||||
|
||||
constructor: ->
|
||||
@observeConfig 'editor.showInvisibles', -> # ...
|
||||
|
||||
destroy: ->
|
||||
@unobserveConfig()
|
||||
```
|
||||
|
||||
### Writing Config Settings
|
||||
|
||||
The `atom.config` database is populated on startup from `~/.atom/config.cson`,
|
||||
but you can programmatically write to it with `atom.config.set`:
|
||||
|
||||
```coffeescript
|
||||
# basic key update
|
||||
atom.config.set("core.showInvisibles", true)
|
||||
```
|
||||
|
||||
You should never mutate the value of a config key, because that would circumvent
|
||||
the notification of observers. You can however use methods like `pushAtKeyPath`,
|
||||
`unshiftAtKeyPath`, and `removeAtKeyPath` to manipulate mutable config values.
|
||||
|
||||
```coffeescript
|
||||
atom.config.pushAtKeyPath("core.disabledPackages", "wrap-guide")
|
||||
atom.config.removeAtKeyPath("core.disabledPackages", "terminal")
|
||||
```
|
||||
|
||||
You can also use `setDefaults`, which will assign default values for keys that
|
||||
are always overridden by values assigned with `set`. Defaults are not written
|
||||
out to the the `config.json` file to prevent it from becoming cluttered.
|
||||
|
||||
```coffeescript
|
||||
atom.config.setDefaults("editor", fontSize: 18, showInvisibles: true)
|
||||
```
|
||||
@@ -0,0 +1,34 @@
|
||||
# Globals
|
||||
|
||||
Atom exposes several services through singleton objects accessible via the
|
||||
`atom` global:
|
||||
|
||||
* atom
|
||||
* workspace:
|
||||
Manipulate and query the state of the user interface for the current
|
||||
window. Open editors, manipulate panes.
|
||||
* workspaceView:
|
||||
Similar to workspace, but provides access to the root of all views in the
|
||||
current window.
|
||||
* project:
|
||||
Access the directory associated with the current window. Load editors,
|
||||
perform project-wide searches, register custom openers for special file
|
||||
types.
|
||||
* config:
|
||||
Read, write, and observe user configuration settings.
|
||||
* keymap:
|
||||
Add and query the currently active keybindings.
|
||||
* deserializers:
|
||||
Deserialize instances from their state objects and register deserializers.
|
||||
* packages:
|
||||
Activate, deactivate, and query user packages.
|
||||
* themes:
|
||||
Activate, deactivate, and query user themes.
|
||||
* contextMenu:
|
||||
Register context menus.
|
||||
* menu:
|
||||
Register application menus.
|
||||
* pasteboard:
|
||||
Read from and write to the system pasteboard.
|
||||
* syntax:
|
||||
Assign and query syntactically-scoped properties.
|
||||
@@ -0,0 +1,123 @@
|
||||
# Keymaps In-Depth
|
||||
|
||||
## Structure of a Keymap File
|
||||
|
||||
Keymap files are encoded as JSON or CSON files containing nested hashes. They
|
||||
work much like stylesheets, but instead of applying style properties to elements
|
||||
matching the selector, they specify the meaning of keystrokes on elements
|
||||
matching the selector. Here is an example of some bindings that apply when
|
||||
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'
|
||||
'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'
|
||||
|
||||
'.editor:not(.mini)'
|
||||
'cmd-alt-[': 'editor:fold-current-row'
|
||||
'cmd-alt-]': 'editor:unfold-current-row'
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
sense on mini-editors, so the selector restricts them to regular editors.
|
||||
|
||||
### Keystroke Patterns
|
||||
|
||||
Keystroke patterns express one or more keystrokes combined with optional
|
||||
modifier keys. For example: `ctrl-w v`, or `cmd-shift-up`. A keystroke is
|
||||
composed of the following symbols, separated by a `-`. A multi-keystroke pattern
|
||||
can be expressed as keystroke patterns separated by spaces.
|
||||
|
||||
|
||||
| Type | Examples
|
||||
| --------------------|----------------------------
|
||||
| Character literals | `a` `4` `$`
|
||||
| Modifier keys | `cmd` `ctrl` `alt` `shift`
|
||||
| Special keys | `enter` `escape` `backspace` `delete` `tab` `home` `end` `pageup` `pagedown` `left` `right` `up` `down`
|
||||
|
||||
### Commands
|
||||
|
||||
Commands are custom DOM events that are triggered when a keystroke matches a
|
||||
binding. This allows user interface code to listen for named commands without
|
||||
specifying the specific keybinding that triggers it. For example, the following
|
||||
code sets up {EditorView} to listen for commands to move the cursor to the first
|
||||
character of the current line:
|
||||
|
||||
```coffee
|
||||
class EditorView
|
||||
listenForEvents: ->
|
||||
@command 'editor:move-to-first-character-of-line', =>
|
||||
@editor.moveCursorToFirstCharacterOfLine()
|
||||
```
|
||||
|
||||
The `::command` method is basically an enhanced version of jQuery's `::on`
|
||||
method that listens for a custom DOM event and adds some metadata to the DOM,
|
||||
which is read by the command palette.
|
||||
|
||||
When you are looking to bind new keys, it is often useful to use the command
|
||||
palette (`ctrl-shift-p`) to discover what commands are being listened for in a
|
||||
given focus context. Commands are "humanized" following a simple algorithm, so a
|
||||
command like `editor:fold-current-row` would appear as "Editor: Fold Current
|
||||
Row".
|
||||
|
||||
### Specificity and Cascade Order
|
||||
|
||||
As is the case with CSS applying styles, when multiple bindings match for a
|
||||
single element, the conflict is resolved by choosing the most *specific*
|
||||
selector. If two matching selectors have the same specificity, the binding
|
||||
for the selector appearing later in the cascade takes precedence.
|
||||
|
||||
Currently, there's no way to specify selector ordering within a single keymap,
|
||||
because JSON objects do not preserve order. We eventually plan to introduce a
|
||||
custom CSS-like file format for keymaps that allows for ordering within a single
|
||||
file. For now, we've opted to handle cases where selector ordering is critical
|
||||
by breaking the keymap into two separate files, such as `snippets-1.cson` and
|
||||
`snippets-2.cson`.
|
||||
|
||||
## Overloading Key Bindings
|
||||
|
||||
Occasionally, it makes sense to layer multiple actions on top of the same key
|
||||
binding. An example of this is the snippets package. Snippets are inserted by
|
||||
typing a snippet prefix such as `for` and then pressing `tab`. Every time `tab`
|
||||
is pressed, we want to execute code attempting to expand a snippet if one exists
|
||||
for the text preceding the cursor. If a snippet *doesn't* exist, we want `tab`
|
||||
to actually insert whitespace.
|
||||
|
||||
To achieve this, the snippets package makes use of the `.abortKeyBinding()`
|
||||
method on the event object representing the `snippets:expand` command.
|
||||
|
||||
```coffee-script
|
||||
# pseudo-code
|
||||
editor.command 'snippets:expand', (e) =>
|
||||
if @cursorFollowsValidPrefix()
|
||||
@expandSnippet()
|
||||
else
|
||||
e.abortKeyBinding()
|
||||
```
|
||||
|
||||
When the event handler observes that the cursor does not follow a valid prefix,
|
||||
it calls `e.abortKeyBinding()`, telling the keymap system to continue searching
|
||||
for another matching binding.
|
||||
|
||||
## Step-by-Step: How Keydown Events are Mapped to Commands
|
||||
|
||||
* A keydown event occurs on a *focused* element.
|
||||
* Starting at the focused element, the keymap walks upward towards the root of
|
||||
the document, searching for the most specific CSS selector that matches the
|
||||
current DOM element and also contains a keystroke pattern matching the keydown
|
||||
event.
|
||||
* When a matching keystroke pattern is found, the search is terminated and the
|
||||
pattern's corresponding command is triggered on the current element.
|
||||
* If `.abortKeyBinding()` is called on the triggered event object, the search
|
||||
is resumed, triggering a binding on the next-most-specific CSS selector for
|
||||
the same element or continuing upward to parent elements.
|
||||
* If no bindings are found, the event is handled by Chromium normally.
|
||||
@@ -40,25 +40,18 @@ window, and contains every other view. If you open Atom's inspector with
|
||||
|
||||
#### Panes
|
||||
|
||||
The `WorkspaceView` contains a `#horizontal` and a `#vertical` axis surrounding
|
||||
`#panes`. Elements in the horizontal axis will tile across the window
|
||||
horizontally, appearing to have a vertical orientation. Items in the vertical
|
||||
axis will tile across the window vertically, appearing to have a horizontal
|
||||
orientation. You would typically attach tool panels to the root view's primary
|
||||
axes. Tool panels are elements which take up some screen real estate that isn't
|
||||
devoted to direct editing. In the example above, the `TreeView` is present in
|
||||
the `#horizontal` axis to the left of the `#panes`, and the `CommandPanel` is
|
||||
present in the `#vertical` axis below the `#panes`.
|
||||
|
||||
You can attach a tool panel to an axis using the `horizontal` or `vertical`
|
||||
outlets as follows:
|
||||
The `WorkspaceView` contains `prependToBottom/Top/Left/Right` and
|
||||
`appendToBottom/Top/Left/Right` methods, which are used to add Tool Panels. Tool
|
||||
panels are elements that take up screen real estate not devoted to text editing.
|
||||
In the example above, the `TreeView` is appended to the left, and the
|
||||
`CommandPanel` is appended to the top.
|
||||
|
||||
```coffeescript
|
||||
# place a view to the left of the panes (or use .append() to place it to the right)
|
||||
atom.workspaceView.horizontal.prepend(new MyView)
|
||||
# place a view to the left of the panes
|
||||
atom.workspaceView.appendToLeft(new MyView)
|
||||
|
||||
# place a view below the panes (or use .prepend() to place it above)
|
||||
atom.workspaceView.vertical.append(new MyOtherView)
|
||||
# place a view below the panes
|
||||
atom.workspaceView.appendToBottom(new MyOtherView)
|
||||
```
|
||||
|
||||
[spacepen]: http://github.com/nathansobo/space-pen
|
||||
@@ -44,13 +44,13 @@ key mappings your package needs to load. If not specified, mappings in the
|
||||
_keymaps_ directory are added alphabetically.
|
||||
- `menus`(**Optional**): an Array of Strings identifying the order of
|
||||
the menu mappings your package needs to load. If not specified, mappings
|
||||
in the _keymap_ directory are added alphabetically.
|
||||
in the _menus_ directory are added alphabetically.
|
||||
- `snippets` (**Optional**): an Array of Strings identifying the order of the
|
||||
snippets your package needs to load. If not specified, snippets in the
|
||||
_snippets_ directory are added alphabetically.
|
||||
- `activationEvents` (**Optional**): an Array of Strings identifying events that
|
||||
trigger your package's activation. You can delay the loading of your package
|
||||
until one of these events is trigged.
|
||||
until one of these events is triggered.
|
||||
|
||||
## Source Code
|
||||
|
||||
@@ -154,7 +154,7 @@ loaded in alphabetical order. An optional `keymaps` array in your _package.json_
|
||||
can specify which keymaps to load and in what order.
|
||||
|
||||
|
||||
Keybindings are executed by determining which element the keypress occured on. In
|
||||
Keybindings are executed by determining which element the keypress occurred on. In
|
||||
the example above, `changer:magic` command is executed when pressing `ctrl-V` on
|
||||
the `.tree-view-scroller` element.
|
||||
|
||||
@@ -172,7 +172,7 @@ specify which menus to load and in what order.
|
||||
It's recommended that you create an application menu item for common actions
|
||||
with your package that aren't tied to a specific element:
|
||||
|
||||
```coffee-script
|
||||
```coffeescript
|
||||
'menu': [
|
||||
{
|
||||
'label': 'Packages'
|
||||
@@ -202,7 +202,7 @@ by other packages in the order which they were loaded.
|
||||
It's recommended to specify a context menu item for commands that are linked to
|
||||
specific parts of the interface, like adding a file in the tree-view:
|
||||
|
||||
```coffee-script
|
||||
```coffeescript
|
||||
'context-menu':
|
||||
'.tree-view':
|
||||
'Add file': 'tree-view:add-file'
|
||||
@@ -311,7 +311,7 @@ protocol URLs to load resources in the package.
|
||||
|
||||
The URLs should be in the format of
|
||||
`atom://package-name/relative-path-to-package-of-resource`, for example, the
|
||||
`atom://image-view/images/transparent-background.gif` would be equivablent to
|
||||
`atom://image-view/images/transparent-background.gif` would be equivalent to
|
||||
`~/.atom/packages/image-view/images/transparent-background.gif`.
|
||||
|
||||
You can also use the `atom` protocol URLs in themes.
|
||||
@@ -381,7 +381,7 @@ Additional libraries can be found by browsing Atom's *node_modules* folder.
|
||||
[apm]: https://github.com/atom/apm
|
||||
[git-tag]: http://git-scm.com/book/en/Git-Basics-Tagging
|
||||
[wrap-guide]: https://github.com/atom/wrap-guide/
|
||||
[keymaps]: internals/keymaps.md
|
||||
[keymaps]: advanced/keymaps.md
|
||||
[theme-variables]: theme-variables.md
|
||||
[tm-tokens]: http://manual.macromates.com/en/language_grammars.html
|
||||
[spacepen]: https://github.com/nathansobo/space-pen
|
||||
|
||||
+22
-11
@@ -19,6 +19,16 @@ Themes` section on the `Packages` tab within the Settings panel.
|
||||
You can install non-bundled packages by going to the `Available Packages`
|
||||
section on the `Packages` tab within the Settings panel (`cmd-,`).
|
||||
|
||||
You can also install packages from the command line using the
|
||||
[apm](https://github.com/atom/apm) command:
|
||||
|
||||
`apm install <package_name>` to install the latest version.
|
||||
|
||||
`apm install <package_name>@<package_version>` to install a specific version.
|
||||
|
||||
For example `apm install emmet@0.1.5` installs the `0.1.5` release of the
|
||||
[Emmet](https://github.com/atom/emmet) package into `~/.atom/packages`.
|
||||
|
||||
## Customizing Key Bindings
|
||||
|
||||
Atom keymaps work similarly to stylesheets. Just as stylesheets use selectors
|
||||
@@ -56,7 +66,7 @@ directory, which contains CoffeeScript-style JSON:
|
||||
|
||||
```coffeescript
|
||||
core:
|
||||
hideGitIgnoredFiles: true
|
||||
excludeVcsIgnoredPaths: true
|
||||
editor:
|
||||
fontSize: 18
|
||||
```
|
||||
@@ -69,7 +79,6 @@ namespaces: `core` and `editor`.
|
||||
- `core`
|
||||
- `disabledPackages`: An array of package names to disable
|
||||
- `excludeVcsIgnoredPaths`: Don't search within files specified by _.gitignore_
|
||||
- `hideGitIgnoredFiles`: Whether files in the _.gitignore_ should be hidden
|
||||
- `ignoredNames`: File names to ignore across all of Atom
|
||||
- `projectHome`: The directory where projects are assumed to be located
|
||||
- `themes`: An array of theme names to load, in cascading order
|
||||
@@ -97,7 +106,7 @@ namespaces: `core` and `editor`.
|
||||
- `whitespace`
|
||||
- `ensureSingleTrailingNewline`: Whether to reduce multiple newlines to one at the end of files
|
||||
- `removeTrailingWhitespace`: Enable/disable striping of whitespace at the end of lines (defaults to `true`)
|
||||
- `wrapGuide`
|
||||
- `wrap-guide`
|
||||
- `columns`: Array of hashes with a `pattern` and `column` key to match the
|
||||
the path of the current editor to a column position.
|
||||
|
||||
@@ -111,22 +120,24 @@ make customizations. You have full access to Atom's API from code in this file.
|
||||
If customizations become extensive, consider [creating a
|
||||
package][create-a-package].
|
||||
|
||||
### user.less
|
||||
### styles.css
|
||||
|
||||
If you want to apply quick-and-dirty personal styling changes without creating
|
||||
an entire theme that you intend to distribute, you can add styles to
|
||||
_user.less_ in your _~/.atom_ directory.
|
||||
_styles.css_ in your _~/.atom_ directory.
|
||||
|
||||
For example, to change the color of the highlighted line number for the line
|
||||
that contains the cursor, you could add the following style to _user.less_:
|
||||
that contains the cursor, you could add the following style to _styles.css_:
|
||||
|
||||
```less
|
||||
@highlight-color: pink;
|
||||
|
||||
.editor .line-number.cursor-line {
|
||||
color: @highlight-color;
|
||||
```css
|
||||
.editor .cursor {
|
||||
border-color: pink;
|
||||
}
|
||||
```
|
||||
|
||||
You can also name the file _styles.less_ if you want to style Atom using
|
||||
[LESS][LESS].
|
||||
|
||||
[create-a-package]: creating-packages.md
|
||||
[create-theme]: creating-a-theme.md
|
||||
[LESS]: http://www.lesscss.org
|
||||
|
||||
+35
-32
@@ -19,13 +19,11 @@ bindings][key-bindings] section.
|
||||
|
||||
### Working With Files
|
||||
|
||||
Atom windows are scoped to the directory they're opened from. If you launch Atom
|
||||
from the command line everything will be relative to the current directory. This
|
||||
means that the tree view on the left will only show files contained within that
|
||||
directory.
|
||||
|
||||
This can be a useful way to organize multiple projects, as each project will be
|
||||
contained within its own window.
|
||||
Atom windows are scoped to a single directory on disk. If you launch Atom from
|
||||
the command line via the `atom` command and don't specify a path, Atom opens a
|
||||
window for the current working directory. The current window's directory will be
|
||||
visible as the root of the tree view at the left, and also serve as the context
|
||||
for all file-related operations.
|
||||
|
||||
#### Finding Files
|
||||
|
||||
@@ -34,20 +32,17 @@ begin typing the name of the file you're looking for. If you are looking for a
|
||||
file that is already open press `cmd-b` to bring up a searchable list of open
|
||||
files.
|
||||
|
||||
You can also use the tree view to navigate to a file. To open or move focus to
|
||||
the tree view, press `cmd-\`. You can then navigate to a file using the arrow
|
||||
keys and select it with `return`.
|
||||
You can also use the tree view to navigate to a file. To open and focus the
|
||||
the tree view, press `ctrl-0`. The tree view can be toggled open and closed with
|
||||
`cmd-\`.
|
||||
|
||||
#### Adding, Moving, Deleting Files
|
||||
|
||||
Currently, all file modification is performed via the tree view. To add a file,
|
||||
select a directory in the tree view and press `a`. Then type the name of the
|
||||
file. Any intermediate directories you type will be created automatically if
|
||||
needed.
|
||||
|
||||
To move or rename a file or directory, select it in the tree view and press `m`.
|
||||
|
||||
To delete a file, select it in the tree view and press `delete`.
|
||||
Currently, all file modification is performed via the tree view. Add, move, or
|
||||
delete a file by right-clicking in the tree view and selecting the desired
|
||||
operation from the context menu. You can also perform these operations from the
|
||||
keyboard by selecting a file or directory and using `a` to add, `m` to move, and
|
||||
`delete` to delete.
|
||||
|
||||
### Searching
|
||||
|
||||
@@ -58,35 +53,43 @@ To search within a buffer use `cmd-f`. To search the entire project use
|
||||
|
||||
#### Navigating By Symbols
|
||||
|
||||
If you want to jump to a method press `cmd-r`. It opens a list of all symbols
|
||||
in the current file.
|
||||
To jump to a symbol such as a method definition, press `cmd-r`. This opens a
|
||||
list of all symbols in the current file, which you can fuzzy filter similarly to
|
||||
`cmd-t`.
|
||||
|
||||
To search for symbols across your project use `cmd-shift-r`, but you'll need to
|
||||
make sure you have a ctags installed and a tags file generated for your project.
|
||||
Also, if you're editing CoffeeScript, it's a good idea to update your `~/.ctags`
|
||||
file to understand the language. Here is [a good example][ctags].
|
||||
To search for symbols across your project, use `cmd-shift-r`. First you'll need
|
||||
to make sure you have ctags installed and a tags file generated for your
|
||||
project. Also, if you're editing CoffeeScript, it's a good idea to update your
|
||||
`~/.ctags` file to understand the language. Here is [a good example][ctags].
|
||||
|
||||
### Split Panes
|
||||
|
||||
You can split any editor pane horizontally or vertically by using `cmd-k right` or
|
||||
`cmd-k down`. Once you have a split pane, you can move focus between them with
|
||||
`cmd-k cmd-right` or `cmd-k cmd-down`. To close a pane, close all tabs inside it.
|
||||
You can split any editor pane horizontally or vertically by using `cmd-k right`
|
||||
or `cmd-k down`. Once you have a split pane, you can move focus between them
|
||||
with `cmd-k cmd-right` or `cmd-k cmd-down`. To close a pane, close all its
|
||||
editors with `meta-w`, then press `meta-w` one more time to close the pane. You
|
||||
can configure panes to auto-close with empty in the preferences.
|
||||
|
||||
### Folding
|
||||
|
||||
You can fold everything with `alt-cmd-{` and unfold everything with
|
||||
`alt-cmd-}`. Or, you can fold / unfold by a single level with `alt-cmd-[` and
|
||||
`alt-cmd-]`.
|
||||
You can fold blocks of code by clicking the arrows that appear when you hover
|
||||
your mouse cursor over the gutter. You can also fold and unfold from the
|
||||
keyboard with `alt-cmd-[` and `alt-cmd-]`. To fold everything, use
|
||||
`alt-cmd-shift-{` and to unfold everything use `alt-cmd-shift-}`. You can also
|
||||
fold at a specific indentation level with `cmd-k cmd-N` where N is the
|
||||
indentation depth.
|
||||
|
||||
### Soft-Wrap
|
||||
|
||||
If you want to toggle soft wrap, trigger the command from the command palette.
|
||||
Press `cmd-shift-P` to open the palette, then type "wrap" to find the correct
|
||||
command.
|
||||
command. By default, lines will wrap based on the size of the editor. If you
|
||||
prefer to wrap at a specific line length, toggle "Wrap at preferred line length"
|
||||
in preferences.
|
||||
|
||||
## Configuration
|
||||
|
||||
Press `cmd-,` to display the a settings pane. This serves as the primary
|
||||
Press `cmd-,` to display the preferences pane. This serves as the primary
|
||||
interface for adjusting config settings, installing packages and changing
|
||||
themes.
|
||||
|
||||
|
||||
+4
-4
@@ -7,7 +7,7 @@
|
||||
|
||||
### Advanced Topics
|
||||
|
||||
* [Configuration](internals/configuration.md)
|
||||
* [Keymaps](internals/keymaps.md)
|
||||
* [Serialization](internals/serialization.md)
|
||||
* [View System](internals/view-system.md)
|
||||
* [Configuration](advanced/configuration.md)
|
||||
* [Keymaps](advanced/keymaps.md)
|
||||
* [Serialization](advanced/serialization.md)
|
||||
* [View System](advanced/view-system.md)
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
## Configuration API
|
||||
|
||||
### Reading Config Settings
|
||||
|
||||
If you are writing a package that you want to make configurable, you'll need to
|
||||
read config settings. You can read a value from `config` with `config.get`:
|
||||
|
||||
```coffeescript
|
||||
# read a value with `config.get`
|
||||
@showInvisibles() if config.get "edtior.showInvisibles"
|
||||
```
|
||||
|
||||
Or you can use `observeConfig` to track changes from a view object.
|
||||
|
||||
```coffeescript
|
||||
class MyView extends View
|
||||
initialize: ->
|
||||
@observeConfig 'editor.fontSize', () =>
|
||||
@adjustFontSize()
|
||||
```
|
||||
|
||||
The `observeConfig` method will call the given callback immediately with the
|
||||
current value for the specified key path, and it will also call it in the future
|
||||
whenever the value of that key path changes.
|
||||
|
||||
Subscriptions made with `observeConfig` are automatically cancelled when the
|
||||
view is removed. You can cancel config subscriptions manually via the
|
||||
`unobserveConfig` method.
|
||||
|
||||
```coffeescript
|
||||
view1.unobserveConfig() # unobserve all properties
|
||||
```
|
||||
|
||||
You can add the ability to observe config values to non-view classes by
|
||||
extending their prototype with the `ConfigObserver` mixin:
|
||||
|
||||
```coffeescript
|
||||
ConfigObserver = require 'config-observer'
|
||||
_.extend MyClass.prototype, ConfigObserver
|
||||
```
|
||||
|
||||
### Writing Config Settings
|
||||
|
||||
As discussed above, the config database is automatically populated from
|
||||
`config.cson` when Atom is started, but you can programmatically write to it in
|
||||
the following way:
|
||||
|
||||
```coffeescript
|
||||
# basic key update
|
||||
config.set("core.showInvisibles", true)
|
||||
|
||||
config.pushAtKeyPath("core.disabledPackages", "wrap-guide")
|
||||
```
|
||||
|
||||
You can also use `setDefaults`, which will assign default values for keys that
|
||||
are always overridden by values assigned with `set`. Defaults are not written out
|
||||
to the the `config.json` file to prevent it from becoming cluttered.
|
||||
|
||||
```coffeescript
|
||||
config.setDefaults("editor", fontSize: 18, showInvisibles: true)
|
||||
```
|
||||
@@ -1,69 +0,0 @@
|
||||
## Keymaps In-Depth
|
||||
|
||||
### Structure of a Keymap File
|
||||
|
||||
Keymap files are encoded as JSON or CSON files containing nested hashes. The
|
||||
top-level keys of a keymap are **CSS 3 selectors**, which specify a particular
|
||||
context in Atom's interface. Common selectors are `.editor`, which scopes
|
||||
bindings to just work when an editor is focused, and `body`, which scopes
|
||||
bindings globally.
|
||||
|
||||
Beneath the selectors are hashes mapping **keystroke patterns** to
|
||||
**semantic events**. A keystroke pattern looks like the following examples.
|
||||
Note that the last example describes multiple keystrokes in succession:
|
||||
|
||||
- `p`
|
||||
- `2`
|
||||
- `ctrl-p`
|
||||
- `ctrl-alt-cmd-p`
|
||||
- `tab`
|
||||
- `escape`
|
||||
- `enter`
|
||||
- `ctrl-w w`
|
||||
|
||||
A semantic event is the name of the custom event that will be triggered on the
|
||||
target of the keydown event when a key binding matches. You can use the command
|
||||
palette (bound to `cmd-shift-P`), to get a list of relevant events and their bindings
|
||||
in any focused context in Atom.
|
||||
|
||||
### Rules for Mapping A Keydown Event to A Semantic Event
|
||||
|
||||
A keymap's job is to translate a physical keystroke event (like `cmd-D`) into a
|
||||
semantic event (like `editor:duplicate-line`). Whenever a keydown event occurs
|
||||
on a focused element, it bubbles up the DOM as usual. As soon as an element on
|
||||
the bubble path matches a key binding for the keystroke, the binding's semantic
|
||||
event is triggered on the original target of the keydown event. Just as with
|
||||
CSS, if multiple selectors match an element, the most specific selector is
|
||||
favored. If two selectors have the same specificity, the selector that occurs
|
||||
latest in the cascade is favored.
|
||||
|
||||
Currently, there's no way to specify selector ordering within a single keymap,
|
||||
because JSON hashes do not preserve order. Rather than making the format more
|
||||
awkward in order to preserve order, we've opted to handle cases where order is
|
||||
critical by breaking the keymap into two separate files, such as
|
||||
`snippets-1.cson` and `snippets-2.cson`.
|
||||
|
||||
### Overloading Key Bindings
|
||||
|
||||
Occasionally, it makes sense to layer multiple actions on top of the same key
|
||||
binding. An example of this is the snippets package. You expand a snippet by
|
||||
pressing `tab` immediately following a snippet's prefix. But if the cursor is
|
||||
not following a valid snippet prefix, then we want tab to perform its normal
|
||||
action (probably inserting a tab character or the appropriate number of spaces).
|
||||
|
||||
To achieve this, the snippets package makes use of the `abortKeyBinding` method
|
||||
on the event object that's triggered by the binding for `tab`.
|
||||
|
||||
```coffee-script
|
||||
# pseudo-code
|
||||
editor.command 'snippets:expand', (e) =>
|
||||
if @cursorFollowsValidPrefix()
|
||||
@expandSnippet()
|
||||
else
|
||||
e.abortKeyBinding()
|
||||
```
|
||||
|
||||
When the event handler observes that the cursor does not follow a valid prefix,
|
||||
it calls `e.abortKeyBinding()`, which tells the keymap system to continue
|
||||
searching up the cascade for another matching binding. In this case, the default
|
||||
implementation of `tab` ends up getting triggered.
|
||||
@@ -1,70 +0,0 @@
|
||||
**Polish the user experience**
|
||||
|
||||
First and foremost, Atom is a **product**. Atom needs to feel familiar and
|
||||
inviting. This includes a solid introductory experience and parity with the most
|
||||
important features of Sublime Text.
|
||||
|
||||
* First launch UI and flow (actions below should be easily discoverable)
|
||||
* Create a new file
|
||||
* Open a project and edit an existing file
|
||||
* Install a package
|
||||
* Change settings (adjust theme, change key bindings, set config options)
|
||||
* How to use command P
|
||||
* Use collaboration internally
|
||||
* How and where to edit keyBinding should be obvious to new users
|
||||
* Finish find and replace in buffer/project
|
||||
* Atom should start < 300ms
|
||||
* Match Sublime's multiple selection functionality (#523)
|
||||
* Fix softwrap bugs
|
||||
* Menus & Context menus
|
||||
* Track usage/engagement of our users (make this opted in?)
|
||||
* Windows support
|
||||
* Reliably and securely auto-update and list what's new
|
||||
* Secure access to the keychain (don't give every package access to the keychain)
|
||||
* Secure access to GitHub (each package can ask to have it's own oauth token)
|
||||
* Don't crash when opening/editing large (> 10Mb) files
|
||||
* Send js and native crash reports to a remote server
|
||||
|
||||
**Lay solid groundwork for a package and theme ecosystem**
|
||||
|
||||
Extensibility is one of Atom's key value propositions, so a smooth experience
|
||||
for creating and maintaining packages is just as important as the user
|
||||
experience. The package development, dependency and publishing workflow needs to
|
||||
be solid. We also want to have a mechanism for clearly communicating with
|
||||
package authors about breaking API changes.
|
||||
|
||||
* Finish APM backend (integrate with GitHub Releases)
|
||||
* Streamline Dev workflow
|
||||
* `apm create` - create package scaffolding
|
||||
* `apm test` - so users can run focused package tests
|
||||
* `apm publish` - should integrate release best practices (ie npm version)
|
||||
* Determine which classes and methods should be included in the public API
|
||||
* Users can find/install/update/fork existing packages and themes
|
||||
|
||||
**Tighten up the view layer**
|
||||
Our current approach to the view layer need some improvement. We want to
|
||||
actively promote the use of the M-V-VM design pattern, provide some declarative
|
||||
event binding mechanisms in the view layer, and improve the performance of the
|
||||
typical package specs. We don't want the current approach to be used as an
|
||||
example in a bunch of new packages, so it's important to improve it now.
|
||||
|
||||
* Add marker view API
|
||||
|
||||
**Get atom.io online with some exciting articles and documentation**
|
||||
We'd love to send our private alpha candidates to a nice site with information
|
||||
about what Atom is, the philosophies and technologies behind it, and guidance
|
||||
for how to get started.
|
||||
|
||||
* Design and create www.atom.io
|
||||
* Guides
|
||||
* Theme & Package creation guide
|
||||
* Full API per release tag
|
||||
* Changelog per release
|
||||
* Explanation of features
|
||||
* Explain Semver and general plans for the future (reassure developers we care about them)
|
||||
* General Values/Goals
|
||||
* Make docs accessible from Atom
|
||||
* Community/contribution guidelines
|
||||
* Is all communication to be done through issues?
|
||||
* When should you publish a plugin?
|
||||
* Do we need to vet plugins from a security perspective?
|
||||
@@ -1,16 +0,0 @@
|
||||
## Proposed Timeline
|
||||
|
||||
1. **October 30st** - Internal launch - persuade as many githubbers to switch as
|
||||
possible.
|
||||
|
||||
1. Triage bugs and identify what needs to be fixed before private alpha. Maybe
|
||||
talk to @chrissiebrodigan about doing a UX study.
|
||||
|
||||
1. **November 22st** - Private alpha launch
|
||||
|
||||
1. Trickle out invites as people ask/we need more testers.
|
||||
|
||||
1. If our usage metrics/engagement metrics decrease, stop, identify the issue
|
||||
and fix it before continuing.
|
||||
|
||||
1. Launch
|
||||
@@ -0,0 +1,39 @@
|
||||
# Setting up Travis CI
|
||||
|
||||
Packages under the [atom org][atom-org] should use [Travis CI][travis-ci] for
|
||||
builds.
|
||||
|
||||
Currently we have a [Travis Pro][travis-pro] account since the repositories
|
||||
are private. This process will be simpler and have fewer steps once the
|
||||
package repos are made public.
|
||||
|
||||
Each package builds using the [build-package][build-package] script, any
|
||||
changes to the build should be made in that script and will affect all
|
||||
package builds immediately.
|
||||
|
||||
Each package builds against the latest successful build of atom@master. The
|
||||
master builds are stored in the [atom-master-builds][atom-master-builds] repo
|
||||
as releases named by the SHA-1 built.
|
||||
|
||||
## Configuring a package
|
||||
|
||||
* Run `cd ~/github/my-package` to navigate to the package repo locally
|
||||
* Run `apm test` to verify that the package currently builds via [apm][apm]
|
||||
* Add the package repo to the [Travis CI team][travis-ci-team]
|
||||
* Run `gem install travis` to install the [travis gem][travis-gem]
|
||||
* Run `travis login --pro` and log in using the [atom-build][atom-build] user
|
||||
and the password from the *Shared-Developers* folder in LastPass
|
||||
* Run `apm ci` to add a `.travis.yml` file to the repo and to configure Travis
|
||||
* Log into [Travis][travis-ci] as the `atom-build` user and you should now see
|
||||
the package listed and building
|
||||
|
||||
|
||||
[apm]: https://github.com/atom/apm
|
||||
[build-package]: https://github.com/atom/apm/blob/master/script/build-package
|
||||
[atom-build]: https://github.com/atom-build
|
||||
[atom-master-builds]: https://github.com/atom/atom-master-builds/releases
|
||||
[atom-org]: https://github.com/atom
|
||||
[travis-ci]: https://magnum.travis-ci.com
|
||||
[travis-ci-team]: https://github.com/organizations/atom/teams/596636
|
||||
[travis-gem]: https://rubygems.org/gems/travis
|
||||
[travis-pro]: http://about.travis-ci.org/docs/user/travis-pro
|
||||
+117
-306
@@ -1,334 +1,145 @@
|
||||
# Creating Your First Package
|
||||
# Create Your First Package
|
||||
|
||||
Let's take a look at creating your first package.
|
||||
This tutorial will guide you though creating a simple command that replaces the
|
||||
selected text with [ascii art](http://en.wikipedia.org/wiki/ASCII_art). When you
|
||||
run our new command with the word "cool" selected, it will be replaced with:
|
||||
|
||||
To get started, hit `cmd-shift-P`, and start typing "Generate Package" to generate
|
||||
a package. Once you select the "Generate Package" command, it'll ask you for a
|
||||
name for your new package. Let's call ours _changer_.
|
||||
|
||||
Atom will pop open a new window, showing the _changer_ package with a default set of
|
||||
folders and files created for us. Hit `cmd-shift-P` and start typing "Changer." You'll
|
||||
see a new `Changer:Toggle` command which, if selected, pops up a greeting. So far,
|
||||
so good!
|
||||
|
||||
In order to demonstrate the capabilities of Atom and its API, our Changer plugin
|
||||
is going to do two things:
|
||||
|
||||
1. It'll show only modified files in the file tree
|
||||
2. It'll append a new pane to the editor with some information about the modified
|
||||
files
|
||||
|
||||
Let's get started!
|
||||
|
||||
## Changing Keybindings and Commands
|
||||
|
||||
Since Changer is primarily concerned with the file tree, let's write a
|
||||
key binding that works only when the tree is focused. Instead of using the
|
||||
default `toggle`, our keybinding executes a new command called `magic`.
|
||||
|
||||
_keymaps/changer.cson_ should change to look like this:
|
||||
|
||||
```coffeescript
|
||||
'.tree-view':
|
||||
'ctrl-V': 'changer:magic'
|
||||
```
|
||||
/\_ \
|
||||
___ ___ ___\//\ \
|
||||
/'___\ / __`\ / __`\\ \ \
|
||||
/\ \__//\ \L\ \/\ \L\ \\_\ \_
|
||||
\ \____\ \____/\ \____//\____\
|
||||
\/____/\/___/ \/___/ \/____/
|
||||
```
|
||||
|
||||
Notice that the keybinding is called `ctrl-V` — that's actually `ctrl-shift-v`.
|
||||
You can use capital letters to denote using `shift` for your binding.
|
||||
The final package can be viewed at
|
||||
[https://github.com/atom/ascii-art](https://github.com/atom/ascii-art).
|
||||
|
||||
`.tree-view` represents the parent container for the tree view.
|
||||
Keybindings only work within the context of where they're entered. In this case,
|
||||
hitting `ctrl-V` anywhere other than tree won't do anything. Obviously, you can
|
||||
bind to any part of the editor using element, id, or class names. For example,
|
||||
you can map to `body` if you want to scope to anywhere in Atom, or just `.editor`
|
||||
for the editor portion.
|
||||
To begin, press `cmd-shift-P` to bring up the [Command
|
||||
Palette](https://github.com/atom/command-palette). Type "generate package" and
|
||||
select the "Package Generator: Generate Package" command. Now we need to name
|
||||
the package. Let's call it _ascii-art_.
|
||||
|
||||
To bind keybindings to a command, we'll need to do a bit of association in our
|
||||
CoffeeScript code using the `atom.workspaceView.command` method. This method takes a command
|
||||
name and executes a callback function. Open up _lib/changer-view.coffee_, and
|
||||
change `atom.workspaceView.command "changer:toggle"` to look like this:
|
||||
Atom will open a new window with the contents of our new _ascii-art_ package
|
||||
displayed in the Tree View. Because this window is opened **after** the package
|
||||
is created, the ASCII Art package will be loaded and available in our new
|
||||
window. To verify this, toggle the Command Palette (`cmd-shift-P`) and type
|
||||
"ASCII Art" you'll see a new `ASCII Art: Toggle` command. When triggered, this
|
||||
command displays a default message.
|
||||
|
||||
Now let's edit the package files to make our ascii art package do something
|
||||
interesting. Since this package doesn't need any UI, we can remove all
|
||||
view-related code. Start by opening up _lib/ascii-art.coffee_. Remove all view
|
||||
code, so the file looks like this:
|
||||
|
||||
```coffeescript
|
||||
atom.workspaceView.command "changer:magic", => @magic()
|
||||
module.exports =
|
||||
activate: ->
|
||||
```
|
||||
|
||||
It's common practice to namespace your commands with your package name, separated
|
||||
with a colon (`:`). Make sure to rename your `toggle` method to `magic` as well.
|
||||
## Create a Command
|
||||
|
||||
Every time you reload the Atom editor, changes to your package code will be reevaluated,
|
||||
just as if you were writing a script for the browser. Reload the editor, click on
|
||||
the tree, hit your keybinding, and...nothing happens! What the heck?!
|
||||
Now let's add a command. We recommend that you namespace your commands with the
|
||||
package name followed by a `:`, so we'll call our command `ascii-art:convert`.
|
||||
Register the command in _lib/ascii-art.coffee_:
|
||||
|
||||
Open up the _package.json_ file, and find the property called `activationEvents`.
|
||||
Basically, this key tells Atom to not load a package until it hears a certain event.
|
||||
Change the event to `changer:magic` and reload the editor:
|
||||
```coffeescript
|
||||
module.exports =
|
||||
activate: ->
|
||||
atom.workspaceView.command "ascii-art:convert", => @convert()
|
||||
|
||||
convert: ->
|
||||
# This assumes the active pane item is an editor
|
||||
editor = atom.workspace.activePaneItem
|
||||
editor.insertText('Hello, World!')
|
||||
```
|
||||
|
||||
The `atom.workspaceView.command` method takes a command name and a callback. The
|
||||
callback executes when the command is triggered. In this case, when the command
|
||||
is triggered the callback will call the `convert` method and insert 'Hello,
|
||||
World!'.
|
||||
|
||||
## Reload the Package
|
||||
|
||||
Before we can trigger `ascii-art:convert`, we need to load the latest code for
|
||||
our package by reloading the window. Run the command `window:reload` from the
|
||||
command palette or by pressing `ctrl-alt-cmd-l`.
|
||||
|
||||
## Trigger the Command
|
||||
|
||||
Now open the command panel and search for the `ascii-art:convert` command. But
|
||||
its not there! To fix this open _package.json_ and find the property called
|
||||
`activationEvents`. Activation Events speed up load time by allowing an Atom to
|
||||
delay a package's activation until it's needed. So add the `ascii-art:convert`
|
||||
to the activationEvents array:
|
||||
|
||||
```json
|
||||
"activationEvents": ["changer:magic"]
|
||||
"activationEvents": ["ascii-art:convert"],
|
||||
```
|
||||
|
||||
Hitting the key binding on the tree now works!
|
||||
First, run reload the window by running the command `window:reload`. Now when
|
||||
you run the `ascii-art:convert` command it will output 'Hello, World!'
|
||||
|
||||
## Working with Styles
|
||||
## Add A Key Binding
|
||||
|
||||
The next step is to hide elements in the tree that aren't modified. To do that,
|
||||
we'll first try and get a list of files that have not changed.
|
||||
|
||||
All packages are able to use jQuery in their code. In fact, there's [a list of
|
||||
the bundled libraries Atom provides by default][bundled-libs].
|
||||
|
||||
We bring in jQuery by requiring the `atom` package and binding it to the `$` variable:
|
||||
Now let's add a key binding to trigger the `ascii-art:convert` command. Open
|
||||
_keymaps/ascii-art.cson_ and add a key binding linking `ctrl-alt-a` to the
|
||||
`ascii-art:convert` command. When finished, the file will look like this:
|
||||
|
||||
```coffeescript
|
||||
{$, View} = require 'atom'
|
||||
'.editor':
|
||||
'cmd-alt-a': 'ascii-art:convert'
|
||||
```
|
||||
|
||||
Now, we can define the `magic` method to query the tree to get us a list of every
|
||||
file that _wasn't_ modified:
|
||||
Notice `.editor` on the first line. Just like CSS, keymap selectors *scope* key
|
||||
bindings so they only apply to specific elements. In this case, our binding is
|
||||
only active for elements matching the `.editor` selector. If the Tree View has
|
||||
focus, pressing `cmd-alt-a` won't trigger the `ascii-art:convert` command. But
|
||||
if the editor has focus, the `ascii-art:convert` method *will* be triggered.
|
||||
More information on key bindings can be found in the
|
||||
[keymaps](advanced/keymaps.html) documentation.
|
||||
|
||||
Now reload the window and verify that the key binding works! You can also verify
|
||||
that it **doesn't** work when the Tree View is focused.
|
||||
|
||||
## Add the ASCII Art
|
||||
|
||||
Now we need to convert the selected text to ascii art. To do this we will use
|
||||
the [figlet](https://npmjs.org/package/figlet) [node](http://nodejs.org/) module
|
||||
from [npm](https://npmjs.org/). Open _package.json_ and add the latest version of
|
||||
figlet to the dependencies:
|
||||
|
||||
```json
|
||||
"dependencies": {
|
||||
"figlet": "1.0.8"
|
||||
}
|
||||
```
|
||||
|
||||
After saving the file run the command 'update-package-dependencies:update' from
|
||||
the Command Palette. This will install the packages node module dependencies,
|
||||
only figlet in this case. You will need to run
|
||||
'update-package-dependencies:update' whenever you update the dependencies field
|
||||
in your _package.json_ file.
|
||||
|
||||
Now require the figlet node module in _lib/ascii-art.coffee_ and instead of
|
||||
inserting 'Hello, World!' convert the selected text to ascii art!
|
||||
|
||||
```coffeescript
|
||||
magic: ->
|
||||
$('ol.entries li').each (i, el) ->
|
||||
if !$(el).hasClass("status-modified")
|
||||
console.log el
|
||||
convert: ->
|
||||
# This assumes the active pane item is an editor
|
||||
editor = atom.workspace.activePaneItem
|
||||
selection = editor.getSelection()
|
||||
|
||||
figlet = require 'figlet'
|
||||
figlet selection.getText(), {font: "Larry 3D 2"}, (error, asciiArt) ->
|
||||
if error
|
||||
console.error(error)
|
||||
else
|
||||
selection.insertText("\n#{asciiArt}\n")
|
||||
```
|
||||
|
||||
You can access the dev console by hitting `alt-cmd-i`. Here, you'll see all the
|
||||
statements from `console` calls. When we execute the `changer:magic` command, the
|
||||
browser console lists items that are not being modified (_i.e._, those without the
|
||||
`status-modified` class). Let's add a class to each of these elements called `hide-me`:
|
||||
|
||||
```coffeescript
|
||||
magic: ->
|
||||
$('ol.entries li').each (i, el) ->
|
||||
if !$(el).hasClass("status-modified")
|
||||
$(el).addClass("hide-me")
|
||||
```
|
||||
|
||||
With our newly added class, we can manipulate the visibility of the elements
|
||||
with a simple stylesheet. Open up _changer.css_ in the _stylesheets_ directory,
|
||||
and add a single entry:
|
||||
|
||||
```css
|
||||
ol.entries .hide-me {
|
||||
display: none;
|
||||
}
|
||||
```
|
||||
|
||||
Refresh Atom, and run the `changer` command. You'll see all the non-changed
|
||||
files disappear from the tree. Success!
|
||||
|
||||
![Changer_File_View]
|
||||
|
||||
There are a number of ways you can get the list back; let's just naively iterate
|
||||
over the same elements and remove the class:
|
||||
|
||||
```coffeescript
|
||||
magic: ->
|
||||
$('ol.entries li').each (i, el) ->
|
||||
if !$(el).hasClass("status-modified")
|
||||
if !$(el).hasClass("hide-me")
|
||||
$(el).addClass("hide-me")
|
||||
else
|
||||
$(el).removeClass("hide-me")
|
||||
```
|
||||
|
||||
## Creating a New Panel
|
||||
|
||||
The next goal of this package is to append a panel to the Atom editor that lists
|
||||
some information about the modified files.
|
||||
|
||||
To do that, we're going to first open up [the style guide][styleguide]. The Style
|
||||
Guide lists every type of UI element that can be created by an Atom package. Aside
|
||||
from helping you avoid writing fresh code from scratch, it ensures that packages
|
||||
have the same look and feel no matter how they're built.
|
||||
|
||||
Every package that extends from the `View` class can provide an optional class
|
||||
method called `content`. The `content` method constructs the DOM that your
|
||||
package uses as its UI. The principals of `content` are built entirely on
|
||||
[SpacePen][space-pen], which we'll touch upon only briefly here.
|
||||
|
||||
Our display will simply be an unordered list of the file names, and their
|
||||
modified times. We'll append this list to a panel on the bottom of the editor. A
|
||||
basic `panel` element inside a `tool-panel` will work well for us. Let's start by carving out a
|
||||
`div` to hold the filenames:
|
||||
|
||||
```coffeescript
|
||||
@content: ->
|
||||
@div class: "changer tool-panel panel-bottom", =>
|
||||
@div class: "panel", =>
|
||||
@div class: "panel-heading", "Modified Files"
|
||||
@div class: "panel-body padded", outlet: 'modifiedFilesContainer', =>
|
||||
@ul class: 'modified-files-list', outlet: 'modifiedFilesList', =>
|
||||
@li 'Modified File Test'
|
||||
@li 'Modified File Test'
|
||||
```
|
||||
|
||||
You can add any HTML attribute you like. `outlet` names the variable your
|
||||
package can use to manipulate the element directly. The fat arrow (`=>`)
|
||||
indicates that the next DOM set are nested children.
|
||||
|
||||
Once again, you can style `li` elements using your stylesheets. Let's test that
|
||||
out by adding these lines to the _changer.css_ file:
|
||||
|
||||
```css
|
||||
ul.modified-files-list {
|
||||
color: white;
|
||||
}
|
||||
```
|
||||
|
||||
We'll add one more line to the end of the `magic` method to make this pane appear:
|
||||
|
||||
```coffeescript
|
||||
atom.workspaceView.vertical.append(this)
|
||||
```
|
||||
|
||||
If you refresh Atom and hit the key command, you'll see a box appear right underneath
|
||||
the editor:
|
||||
|
||||
![Changer_Panel_Append]
|
||||
|
||||
As you might have guessed, `atom.workspaceView.vertical.append` tells Atom to append `this`
|
||||
item (_i.e._, whatever is defined by`@content`) _vertically_ to the editor. If
|
||||
we had called `atom.workspaceView.horizontal.append`, the pane would be attached to the
|
||||
right-hand side of the editor.
|
||||
|
||||
Before we populate this panel for real, let's apply some logic to toggle the pane
|
||||
off and on, just like we did with the tree view. Replace the `atom.workspaceView.vertical.append`
|
||||
call with this code:
|
||||
|
||||
```coffeescript
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
atom.workspaceView.vertical.children().last().remove()
|
||||
else
|
||||
atom.workspaceView.vertical.append(this)
|
||||
```
|
||||
|
||||
There are about a hundred different ways to toggle a pane on and off, and this
|
||||
might not be the most efficient one. If you know your package needs to be
|
||||
toggled on and off more freely, it might be better to draw the interface during the
|
||||
initialization, then immediately call `hide()` on the element to remove it from
|
||||
the view. You can then swap between `show()` and `hide()`, and instead of
|
||||
forcing Atom to add and remove the element as we're doing here, it'll just set a
|
||||
CSS property to control your package's visibility.
|
||||
|
||||
Refresh Atom, hit the key combo, and watch your test list appear and disappear.
|
||||
|
||||
## Calling Node.js Code
|
||||
|
||||
Since Atom is built on top of [Node.js][node], you can call any of its libraries,
|
||||
including other modules that your package requires.
|
||||
|
||||
We'll iterate through our resulting tree, and construct the path to our modified
|
||||
file based on its depth in the tree. We'll use Node to handle path joining for
|
||||
directories.
|
||||
|
||||
Add the following Node module to the top of your file:
|
||||
|
||||
```coffeescript
|
||||
path = require 'path'
|
||||
```
|
||||
|
||||
Then, add these lines to your `magic` method, _before_ your pane drawing code:
|
||||
|
||||
```coffeescript
|
||||
modifiedFiles = []
|
||||
# for each single entry...
|
||||
$('ol.entries li.file.status-modified span.name').each (i, el) ->
|
||||
filePath = []
|
||||
# ...grab its name...
|
||||
filePath.unshift($(el).text())
|
||||
|
||||
# ... then find its parent directories, and grab their names
|
||||
parents = $(el).parents('.directory.status-modified')
|
||||
parents.each (i, el) ->
|
||||
filePath.unshift($(el).find('div.header span.name').eq(0).text())
|
||||
|
||||
modifiedFilePath = path.join(atom.project.rootDirectory.path, filePath.join(path.sep))
|
||||
modifiedFiles.push modifiedFilePath
|
||||
```
|
||||
|
||||
`modifiedFiles` is an array containing a list of our modified files. We're also
|
||||
using the node.js [`path` library][path] to get the proper directory separator
|
||||
for our system.
|
||||
|
||||
Remove the two `@li` elements we added in `@content`, so that we can
|
||||
populate our `modifiedFilesList` with real information. We'll do that by
|
||||
iterating over `modifiedFiles`, accessing a file's last modified time, and
|
||||
appending it to `modifiedFilesList`:
|
||||
|
||||
```coffeescript
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
atom.workspaceView.vertical.children().last().remove()
|
||||
else
|
||||
for file in modifiedFiles
|
||||
stat = fs.lstatSync(file)
|
||||
mtime = stat.mtime
|
||||
@modifiedFilesList.append("<li>#{file} - Modified at #{mtime}")
|
||||
atom.workspaceView.vertical.append(this)
|
||||
```
|
||||
|
||||
When you toggle the modified files list, your pane is now populated with the
|
||||
filenames and modified times of files in your project:
|
||||
|
||||
![Changer_Panel_Timestamps]
|
||||
|
||||
You might notice that subsequent calls to this command reduplicate information.
|
||||
We could provide an elegant way of rechecking files already in the list, but for
|
||||
this demonstration, we'll just clear the `modifiedFilesList` each time it's closed:
|
||||
|
||||
```coffeescript
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
@modifiedFilesList.empty() # added this to clear the list on close
|
||||
atom.workspaceView.vertical.children().last().remove()
|
||||
else
|
||||
for file in modifiedFiles
|
||||
stat = fs.lstatSync(file)
|
||||
mtime = stat.mtime
|
||||
@modifiedFilesList.append("<li>#{file} - Modified at #{mtime}")
|
||||
atom.workspaceView.vertical.append(this)
|
||||
```
|
||||
|
||||
## Coloring UI Elements
|
||||
|
||||
For packages that create new UI elements, adhering to the style guide is just one
|
||||
part to keeping visual consistency. Packages dealing with color, fonts, padding,
|
||||
margins, and other visual cues should rely on [Theme Variables][theme-vars], instead
|
||||
of developing individual styles. Theme variables are variables defined by Atom
|
||||
for use in packages and themes. They're only available in [`LESS`](http://lesscss.org/)
|
||||
stylesheets.
|
||||
|
||||
For our package, let's remove the style defined by `ul.modified-files-list` in
|
||||
_changer.css_. Create a new file under the _stylesheets_ directory called _text-colors.less_.
|
||||
Here, we'll import the _ui-variables.less_ file, and define some Atom-specific
|
||||
styles:
|
||||
|
||||
```less
|
||||
@import "ui-variables";
|
||||
|
||||
ul.modified-files-list {
|
||||
color: @text-color;
|
||||
background-color: @background-color-info;
|
||||
}
|
||||
```
|
||||
|
||||
Using theme variables ensures that packages look great alongside any theme.
|
||||
|
||||
## Further reading
|
||||
|
||||
For more information on the mechanics of packages, check out
|
||||
[Creating a Package][creating-a-package].
|
||||
|
||||
[bundled-libs]: creating-a-package.html#included-libraries
|
||||
[styleguide]: https://github.com/atom/styleguide
|
||||
[space-pen]: https://github.com/atom/space-pen
|
||||
[node]: http://nodejs.org/
|
||||
[path]: http://nodejs.org/docs/latest/api/path.html
|
||||
[changer_file_view]: https://f.cloud.github.com/assets/69169/1441187/d7a7cb46-41a7-11e3-8128-d93f70a5d5c1.png
|
||||
[changer_panel_append]: https://f.cloud.github.com/assets/69169/1441189/db0c74da-41a7-11e3-8286-b82dd9190c34.png
|
||||
[changer_panel_timestamps]: https://f.cloud.github.com/assets/69169/1441190/dcc8eeb6-41a7-11e3-830f-1f1b33072fcd.png
|
||||
[theme-vars]: theme-variables.html
|
||||
[creating-a-package]: creating-a-package.html
|
||||
For more information on the mechanics of packages, check out [Creating a
|
||||
Package](creating-a-package.html)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# Your snippets
|
||||
#
|
||||
# Atom snippets allow you to enter a simple prefix in the editor and hit tab to
|
||||
# expand the prefix into a larger code block with templated values.
|
||||
#
|
||||
# You can create a new snippet in this file by typing `snip` and then hitting
|
||||
# tab.
|
||||
#
|
||||
# An example CoffeeScript snippet to expand log to console.log:
|
||||
#
|
||||
# '.source.coffee':
|
||||
# 'Console log':
|
||||
# 'prefix': 'log'
|
||||
# 'body': 'console.log $1'
|
||||
#
|
||||
@@ -1,44 +0,0 @@
|
||||
".source.coffee":
|
||||
"Describe block":
|
||||
prefix: "de"
|
||||
body: """
|
||||
describe "${1:description}", ->
|
||||
${2:body}
|
||||
"""
|
||||
"It block":
|
||||
prefix: "i"
|
||||
body: """
|
||||
it "$1", ->
|
||||
$2
|
||||
"""
|
||||
"Before each":
|
||||
prefix: "be"
|
||||
body: """
|
||||
beforeEach ->
|
||||
$1
|
||||
"""
|
||||
"After each":
|
||||
prefix: "af"
|
||||
body: """
|
||||
afterEach ->
|
||||
$1
|
||||
"""
|
||||
"Expectation":
|
||||
prefix: "ex"
|
||||
body: "expect($1).to$2"
|
||||
"Console log":
|
||||
prefix: "log"
|
||||
body: "console.log $1"
|
||||
"Range array":
|
||||
prefix: "ra"
|
||||
body: "[[$1, $2], [$3, $4]]"
|
||||
"Point array":
|
||||
prefix: "pt"
|
||||
body: "[$1, $2]"
|
||||
|
||||
"Key-value pair":
|
||||
prefix: ":"
|
||||
body: '${1:"${2:key}"}: ${3:value}'
|
||||
"Create Jasmine spy":
|
||||
prefix: "spy"
|
||||
body: 'jasmine.createSpy("${1:description}")$2'
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Your Stylesheet
|
||||
*
|
||||
* This stylesheet is loaded when Atom starts up and is reloaded automatically
|
||||
* when it is changed.
|
||||
*/
|
||||
|
||||
.tree-view {
|
||||
|
||||
}
|
||||
|
||||
.editor {
|
||||
|
||||
}
|
||||
|
||||
.editor .cursor {
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/* User styles */
|
||||
.tree-view {
|
||||
|
||||
}
|
||||
|
||||
.editor {
|
||||
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
{Document, Point, Range} = require 'telepath'
|
||||
{Point, Range} = require 'text-buffer'
|
||||
|
||||
module.exports =
|
||||
_: require 'underscore-plus'
|
||||
BufferedNodeProcess: require '../src/buffered-node-process'
|
||||
BufferedProcess: require '../src/buffered-process'
|
||||
ConfigObserver: require '../src/config-observer'
|
||||
Directory: require '../src/directory'
|
||||
Document: Document
|
||||
File: require '../src/file'
|
||||
fs: require 'fs-plus'
|
||||
Git: require '../src/git'
|
||||
@@ -21,8 +21,8 @@ unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
|
||||
module.exports.$$ = $$
|
||||
module.exports.$$$ = $$$
|
||||
module.exports.EditorView = require '../src/editor-view'
|
||||
module.exports.WorkspaceView = require '../src/workspace-view'
|
||||
module.exports.SelectList = require '../src/select-list'
|
||||
module.exports.ScrollView = require '../src/scroll-view'
|
||||
module.exports.SelectListView = require '../src/select-list-view'
|
||||
module.exports.Task = require '../src/task'
|
||||
module.exports.View = View
|
||||
module.exports.WorkspaceView = require '../src/workspace-view'
|
||||
|
||||
+29
-8
@@ -1,11 +1,11 @@
|
||||
'.editor':
|
||||
# Platform Bindings
|
||||
'alt-left': 'editor:move-to-previous-word-boundary'
|
||||
'alt-right': 'editor:move-to-next-word-boundary'
|
||||
'alt-shift-left': 'editor:select-to-previous-word-boundary'
|
||||
'alt-shift-right': 'editor:select-to-next-word-boundary'
|
||||
'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-line'
|
||||
'end': 'editor:move-to-end-of-screen-line'
|
||||
'shift-home': 'editor:select-to-first-character-of-line'
|
||||
'shift-end': 'editor:select-to-end-of-line'
|
||||
|
||||
@@ -24,9 +24,6 @@
|
||||
'ctrl-shift-up': 'editor:add-selection-above'
|
||||
'ctrl-shift-down': 'editor:add-selection-below'
|
||||
|
||||
'.tool-panel':
|
||||
'escape': 'core:close'
|
||||
|
||||
'.tool-panel.panel-left, .tool-panel.panel-right':
|
||||
'escape': 'tool-panel:unfocus'
|
||||
|
||||
@@ -41,6 +38,22 @@
|
||||
'backspace': 'native!'
|
||||
'shift-backspace': 'native!'
|
||||
'delete': 'native!'
|
||||
'up': 'native!'
|
||||
'down': 'native!'
|
||||
'shift-up': 'native!'
|
||||
'shift-down': 'native!'
|
||||
'alt-up': 'native!'
|
||||
'alt-down': 'native!'
|
||||
'alt-shift-up': 'native!'
|
||||
'alt-shift-down': 'native!'
|
||||
'cmd-up': 'native!'
|
||||
'cmd-down': 'native!'
|
||||
'cmd-shift-up': 'native!'
|
||||
'cmd-shift-down': 'native!'
|
||||
'ctrl-up': 'native!'
|
||||
'ctrl-down': 'native!'
|
||||
'ctrl-shift-up': 'native!'
|
||||
'ctrl-shift-down': 'native!'
|
||||
'left': 'native!'
|
||||
'right': 'native!'
|
||||
'shift-left': 'native!'
|
||||
@@ -49,6 +62,14 @@
|
||||
'alt-right': 'native!'
|
||||
'alt-shift-left': 'native!'
|
||||
'alt-shift-right': 'native!'
|
||||
'cmd-left': 'native!'
|
||||
'cmd-right': 'native!'
|
||||
'cmd-shift-left': 'native!'
|
||||
'cmd-shift-right': 'native!'
|
||||
'ctrl-left': 'native!'
|
||||
'ctrl-right': 'native!'
|
||||
'ctrl-shift-left': 'native!'
|
||||
'ctrl-shift-right': 'native!'
|
||||
'ctrl-b': 'native!'
|
||||
'ctrl-f': 'native!'
|
||||
'ctrl-F': 'native!'
|
||||
|
||||
+10
-8
@@ -1,8 +1,8 @@
|
||||
'.platform-darwin':
|
||||
'body':
|
||||
# Apple specific
|
||||
'cmd-q': 'application:quit'
|
||||
'cmd-h': 'application:hide'
|
||||
'cmd-H': 'application:hide-other-applications'
|
||||
'cmd-alt-h': 'application:hide-other-applications'
|
||||
'cmd-m': 'application:minimize'
|
||||
'alt-cmd-ctrl-m': 'application:zoom'
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
'down': 'core:move-down'
|
||||
'left': 'core:move-left'
|
||||
'right': 'core:move-right'
|
||||
'ctrl-alt-cmd-r': 'window:reload'
|
||||
'ctrl-alt-cmd-l': 'window:reload'
|
||||
'alt-cmd-i': 'window:toggle-dev-tools'
|
||||
'cmd-alt-ctrl-p': 'window:run-package-specs'
|
||||
|
||||
@@ -87,19 +87,19 @@
|
||||
'cmd-8': 'pane:show-item-8'
|
||||
'cmd-9': 'pane:show-item-9'
|
||||
|
||||
'.platform-darwin .editor':
|
||||
'.editor':
|
||||
# Apple Specific
|
||||
'cmd-backspace': 'editor:backspace-to-beginning-of-line'
|
||||
'cmd-delete': 'editor:backspace-to-beginning-of-line'
|
||||
'ctrl-A': 'editor:select-to-first-character-of-line'
|
||||
'ctrl-E': 'editor:select-to-end-of-line'
|
||||
'cmd-left': 'editor:move-to-first-character-of-line'
|
||||
'cmd-right': 'editor:move-to-end-of-line'
|
||||
'cmd-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-delete': 'editor:delete-to-end-of-word'
|
||||
'ctrl-a': 'editor:move-to-first-character-of-line'
|
||||
'ctrl-a': 'editor:move-to-beginning-of-line'
|
||||
'ctrl-e': 'editor:move-to-end-of-line'
|
||||
'ctrl-k': 'editor:cut-to-end-of-line'
|
||||
|
||||
@@ -111,8 +111,9 @@
|
||||
'cmd-alt-p': 'editor:log-cursor-scope'
|
||||
'cmd-k cmd-u': 'editor:upper-case'
|
||||
'cmd-k cmd-l': 'editor:lower-case'
|
||||
'cmd-l': 'editor:select-line'
|
||||
|
||||
'body.platform-darwin .editor:not(.mini)':
|
||||
'.workspace .editor:not(.mini)':
|
||||
# Atom specific
|
||||
'alt-cmd-z': 'editor:checkout-head-revision'
|
||||
'cmd-<': 'editor:scroll-to-cursor'
|
||||
@@ -129,6 +130,7 @@
|
||||
'cmd-/': 'editor:toggle-line-comments'
|
||||
'cmd-j': 'editor:join-line'
|
||||
'cmd-D': 'editor:duplicate-line'
|
||||
'cmd-L': 'editor:split-selections-into-lines'
|
||||
|
||||
'cmd-alt-[': 'editor:fold-current-row'
|
||||
'cmd-alt-]': 'editor:unfold-current-row'
|
||||
@@ -146,7 +148,7 @@
|
||||
'cmd-k cmd-9': 'editor:fold-at-indent-level-9'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
'body.platform-darwin .native-key-bindings':
|
||||
'body .native-key-bindings':
|
||||
'cmd-z': 'native!'
|
||||
'cmd-Z': 'native!'
|
||||
'cmd-x': 'native!'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'.platform-win32':
|
||||
'body':
|
||||
# Atom Specific
|
||||
'enter': 'core:confirm'
|
||||
'escape': 'core:cancel'
|
||||
@@ -50,7 +50,7 @@
|
||||
'ctrl-k ctrl-left': 'window:focus-previous-pane'
|
||||
'ctrl-k ctrl-right': 'window:focus-next-pane'
|
||||
|
||||
'.platform-win32 .editor':
|
||||
'.workspace .editor':
|
||||
# Windows specific
|
||||
'ctrl-delete': 'editor:backspace-to-beginning-of-word'
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
'ctrl-k ctrl-u': 'editor:upper-case'
|
||||
'ctrl-k ctrl-l': 'editor:lower-case'
|
||||
|
||||
'.platform-win32 .editor:not(.mini)':
|
||||
'.workspace .editor:not(.mini)':
|
||||
# Atom specific
|
||||
'alt-ctrl-z': 'editor:checkout-head-revision'
|
||||
'ctrl-<': 'editor:scroll-to-cursor'
|
||||
@@ -94,7 +94,7 @@
|
||||
'ctrl-k ctrl-9': 'editor:fold-at-indent-level-9'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
'.platform-win32 input:not(.hidden-input), .platform-win32 .native-key-bindings':
|
||||
'body .native-key-bindings':
|
||||
'ctrl-z': 'native!'
|
||||
'ctrl-Z': 'native!'
|
||||
'ctrl-x': 'native!'
|
||||
|
||||
+7
-17
@@ -7,6 +7,12 @@
|
||||
{ label: "Install update", command: 'application:install-update', visible: false }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Preferences...', command: 'application:show-settings' }
|
||||
{ label: 'Open Your Config', command: 'application:open-your-config' }
|
||||
{ label: 'Open Your Keymap', command: 'application:open-your-keymap' }
|
||||
{ label: 'Open Your Snippets', command: 'application:open-your-snippets' }
|
||||
{ label: 'Open Your Stylesheet', command: 'application:open-your-stylesheet' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Install Shell Commands', command: 'window:install-shell-commands' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Hide Atom', command: 'application:hide' }
|
||||
{ label: 'Hide Others', command: 'application:hide-other-applications' }
|
||||
@@ -99,6 +105,7 @@
|
||||
submenu: [
|
||||
{ label: 'Add Selection Above', command: 'editor:add-selection-above' }
|
||||
{ label: 'Add Selection Below', command: 'editor:add-selection-below' }
|
||||
{ label: 'Split into Lines', command: 'editor:split-selections-into-lines'}
|
||||
{ type: 'separator' }
|
||||
{ label: 'Select to Top', command: 'core:select-to-top' }
|
||||
{ label: 'Select to Bottom', command: 'core:select-to-bottom' }
|
||||
@@ -113,23 +120,6 @@
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: 'Movement'
|
||||
submenu: [
|
||||
{ label: 'Move to Top', command: 'core:move-to-top' }
|
||||
{ label: 'Move to Bottom', command: 'core:move-to-bottom' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Move to Beginning of Line', command: 'editor:move-to-beginning-of-line' }
|
||||
{ label: 'Move to First Character of Line', command: 'editor:move-to-first-character-of-line' }
|
||||
{ label: 'Move to End of Line', command: 'editor:move-to-end-of-line' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Move to Beginning of Word', command: 'editor:move-to-beginning-of-word' }
|
||||
{ label: 'Move to End of Word', command: 'editor:move-to-end-of-word' }
|
||||
{ label: 'Move to Next Word', command: 'editor:move-to-next-word-boundary' }
|
||||
{ label: 'Move to Previous Word', command: 'editor:move-to-previous-word-boundary' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: 'Find'
|
||||
submenu: []
|
||||
|
||||
+1
-17
@@ -106,6 +106,7 @@
|
||||
submenu: [
|
||||
{ label: 'Add Selection &Above', command: 'editor:add-selection-above' }
|
||||
{ label: 'Add Selection &Below', command: 'editor:add-selection-below' }
|
||||
{ label: 'S&plit into Lines', command: 'editor:split-selections-into-lines'}
|
||||
{ type: 'separator' }
|
||||
{ label: 'Select to &Top', command: 'core:select-to-top' }
|
||||
{ label: 'Select to Botto&m', command: 'core:select-to-bottom' }
|
||||
@@ -120,23 +121,6 @@
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Movement'
|
||||
submenu: [
|
||||
{ label: 'Move to &Top', command: 'core:move-to-top' }
|
||||
{ label: 'Move to &Bottom', command: 'core:move-to-bottom' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Move to Beginning of &Line', command: 'editor:move-to-beginning-of-line' }
|
||||
{ label: 'Move to &First Character of Line', command: 'editor:move-to-first-character-of-line' }
|
||||
{ label: 'Move to &End of Line', command: 'editor:move-to-end-of-line' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Move to Beginning of &Word', command: 'editor:move-to-beginning-of-word' }
|
||||
{ label: 'Move to End of Wor&d', command: 'editor:move-to-end-of-word' }
|
||||
{ label: 'Move to &Next Word', command: 'editor:move-to-next-word-boundary' }
|
||||
{ label: 'Move to &Previous Word', command: 'editor:move-to-previous-word-boundary' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: 'F&ind'
|
||||
submenu: []
|
||||
|
||||
+90
-103
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"version": "0.41.0",
|
||||
"productName": "Atom",
|
||||
"version": "0.49.0",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -15,119 +16,106 @@
|
||||
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
|
||||
}
|
||||
],
|
||||
"atomShellVersion": "0.7.4",
|
||||
"atomShellVersion": "0.8.7",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"bootstrap": "git://github.com/benogle/bootstrap.git",
|
||||
"bootstrap": "git://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
|
||||
"clear-cut": "0.2.0",
|
||||
"coffee-script": "1.6.3",
|
||||
"coffeestack": "0.6.0",
|
||||
"emissary": "0.19.0",
|
||||
"first-mate": "0.5.0",
|
||||
"fs-plus": "0.10.0",
|
||||
"fuzzaldrin": "0.1.0",
|
||||
"git-utils": "0.29.0",
|
||||
"guid": "0.0.10",
|
||||
"jasmine-focused": "~0.15.0",
|
||||
"mkdirp": "0.3.5",
|
||||
"less-cache": "0.10.0",
|
||||
"nslog": "0.1.0",
|
||||
"oniguruma": "0.24.0",
|
||||
"optimist": "0.4.0",
|
||||
"pathwatcher": "0.10.0",
|
||||
"pegjs": "0.7.0",
|
||||
"q": "0.9.7",
|
||||
"scandal": "0.8.0",
|
||||
"season": "0.14.0",
|
||||
"semver": "1.1.4",
|
||||
"space-pen": "2.0.1",
|
||||
"telepath": "0.45.1",
|
||||
"temp": "0.5.0",
|
||||
"underscore-plus": "0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"biscotto": "0.0.17",
|
||||
"formidable": "~1.0.14",
|
||||
"coffee-script": "1.7.0",
|
||||
"coffeestack": "0.7.0",
|
||||
"delegato": "1.x",
|
||||
"emissary": "1.x",
|
||||
"first-mate": ">=1.1 <2.0",
|
||||
"fs-plus": "1.x",
|
||||
"fstream": "0.1.24",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-cli": "~0.1.9",
|
||||
"grunt-coffeelint": "git://github.com/atom/grunt-coffeelint.git",
|
||||
"grunt-lesslint": "0.13.0",
|
||||
"grunt-cson": "0.5.0",
|
||||
"grunt-contrib-csslint": "~0.1.2",
|
||||
"grunt-contrib-coffee": "~0.7.0",
|
||||
"grunt-contrib-less": "~0.8.0",
|
||||
"walkdir": "0.0.7",
|
||||
"js-yaml": "~2.1.0",
|
||||
"grunt-markdown": "~0.4.0",
|
||||
"json-front-matter": "~0.1.3",
|
||||
"grunt-shell": "~0.3.1",
|
||||
"jasmine-node": "git://github.com/kevinsawicki/jasmine-node.git#short-stacks",
|
||||
"jasmine-tagged": "0.2.0",
|
||||
"request": "~2.27.0",
|
||||
"unzip": "~0.1.9",
|
||||
"rcedit": "~0.1.2",
|
||||
"rimraf": "~2.2.2"
|
||||
"fuzzaldrin": "1.x",
|
||||
"git-utils": "0.34.0",
|
||||
"guid": "0.0.10",
|
||||
"jasmine-tagged": "1.x",
|
||||
"mkdirp": "0.3.5",
|
||||
"keytar": "0.15.1",
|
||||
"less-cache": "0.11.0",
|
||||
"mixto": "1.x",
|
||||
"nslog": "0.4.0",
|
||||
"oniguruma": "1.x",
|
||||
"optimist": "0.4.0",
|
||||
"pathwatcher": "0.14.2",
|
||||
"pegjs": "0.8.0",
|
||||
"property-accessors": "1.x",
|
||||
"q": "0.9.7",
|
||||
"scandal": "0.13.0",
|
||||
"season": "1.x",
|
||||
"semver": "1.1.4",
|
||||
"serializable": "1.x",
|
||||
"space-pen": "3.1.1",
|
||||
"temp": "0.5.0",
|
||||
"text-buffer": "0.16.0",
|
||||
"theorist": "1.x",
|
||||
"underscore-plus": "1.x",
|
||||
"vm-compatibility-layer": "0.1.0"
|
||||
},
|
||||
"packageDependencies": {
|
||||
"atom-dark-syntax": "0.6.0",
|
||||
"atom-dark-ui": "0.11.0",
|
||||
"atom-light-syntax": "0.6.0",
|
||||
"atom-light-ui": "0.13.0",
|
||||
"base16-tomorrow-dark-theme": "0.6.0",
|
||||
"solarized-dark-syntax": "0.4.0",
|
||||
"archive-view": "0.16.0",
|
||||
"autocomplete": "0.18.0",
|
||||
"autoflow": "0.11.0",
|
||||
"autosave": "0.9.0",
|
||||
"bookmarks": "0.15.0",
|
||||
"bracket-matcher": "0.15.0",
|
||||
"command-logger": "0.8.0",
|
||||
"command-palette": "0.11.0",
|
||||
"dev-live-reload": "0.18.0",
|
||||
"editor-stats": "0.8.0",
|
||||
"exception-reporting": "0.8.0",
|
||||
"feedback": "0.12.0",
|
||||
"find-and-replace": "0.55.0",
|
||||
"fuzzy-finder": "0.27.0",
|
||||
"gists": "0.12.0",
|
||||
"git-diff": "0.19.0",
|
||||
"github-sign-in": "0.14.0",
|
||||
"go-to-line": "0.12.0",
|
||||
"grammar-selector": "0.13.0",
|
||||
"image-view": "0.10.0",
|
||||
"keybinding-resolver": "0.6.0",
|
||||
"link": "0.11.0",
|
||||
"markdown-preview": "0.22.0",
|
||||
"metrics": "0.12.0",
|
||||
"package-generator": "0.23.0",
|
||||
"release-notes": "0.14.0",
|
||||
"settings-view": "0.49.0",
|
||||
"snippets": "0.17.0",
|
||||
"spell-check": "0.17.0",
|
||||
"status-bar": "0.23.0",
|
||||
"styleguide": "0.17.0",
|
||||
"symbols-view": "0.27.0",
|
||||
"tabs": "0.15.0",
|
||||
"terminal": "0.23.0",
|
||||
"timecop": "0.11.0",
|
||||
"to-the-hubs": "0.15.0",
|
||||
"tree-view": "0.42.0",
|
||||
"atom-dark-syntax": "0.12.0",
|
||||
"atom-dark-ui": "0.21.0",
|
||||
"atom-light-syntax": "0.12.0",
|
||||
"atom-light-ui": "0.20.0",
|
||||
"base16-tomorrow-dark-theme": "0.10.0",
|
||||
"solarized-dark-syntax": "0.8.0",
|
||||
"solarized-light-syntax": "0.4.0",
|
||||
"archive-view": "0.21.0",
|
||||
"autocomplete": "0.21.0",
|
||||
"autoflow": "0.12.0",
|
||||
"autosave": "0.10.0",
|
||||
"background-tips": "0.5.0",
|
||||
"bookmarks": "0.18.0",
|
||||
"bracket-matcher": "0.19.0",
|
||||
"command-logger": "0.10.0",
|
||||
"command-palette": "0.15.0",
|
||||
"dev-live-reload": "0.23.0",
|
||||
"editor-stats": "0.12.0",
|
||||
"exception-reporting": "0.13.0",
|
||||
"feedback": "0.22.0",
|
||||
"find-and-replace": "0.81.0",
|
||||
"fuzzy-finder": "0.32.0",
|
||||
"gists": "0.15.0",
|
||||
"git-diff": "0.23.0",
|
||||
"github-sign-in": "0.18.0",
|
||||
"go-to-line": "0.16.0",
|
||||
"grammar-selector": "0.18.0",
|
||||
"image-view": "0.17.0",
|
||||
"keybinding-resolver": "0.9.0",
|
||||
"link": "0.15.0",
|
||||
"markdown-preview": "0.25.1",
|
||||
"metrics": "0.24.0",
|
||||
"package-generator": "0.25.0",
|
||||
"release-notes": "0.17.0",
|
||||
"settings-view": "0.63.0",
|
||||
"snippets": "0.24.0",
|
||||
"spell-check": "0.21.0",
|
||||
"status-bar": "0.32.0",
|
||||
"styleguide": "0.22.0",
|
||||
"symbols-view": "0.30.0",
|
||||
"tabs": "0.18.0",
|
||||
"terminal": "0.27.0",
|
||||
"timecop": "0.13.0",
|
||||
"to-the-hubs": "0.18.0",
|
||||
"tree-view": "0.65.0",
|
||||
"update-package-dependencies": "0.2.0",
|
||||
"visual-bell": "0.6.0",
|
||||
"welcome": "0.3.0",
|
||||
"welcome": "0.4.0",
|
||||
"whitespace": "0.10.0",
|
||||
"wrap-guide": "0.8.0",
|
||||
"wrap-guide": "0.12.0",
|
||||
"language-c": "0.2.0",
|
||||
"language-clojure": "0.1.0",
|
||||
"language-coffee-script": "0.3.0",
|
||||
"language-coffee-script": "0.6.0",
|
||||
"language-css": "0.2.0",
|
||||
"language-gfm": "0.9.0",
|
||||
"language-gfm": "0.12.0",
|
||||
"language-git": "0.3.0",
|
||||
"language-go": "0.2.0",
|
||||
"language-html": "0.2.0",
|
||||
"language-hyperlink": "0.3.0",
|
||||
"language-java": "0.2.0",
|
||||
"language-javascript": "0.3.0",
|
||||
"language-javascript": "0.5.0",
|
||||
"language-json": "0.2.0",
|
||||
"language-less": "0.1.0",
|
||||
"language-make": "0.1.0",
|
||||
@@ -135,12 +123,12 @@
|
||||
"language-objective-c": "0.2.0",
|
||||
"language-pegjs": "0.1.0",
|
||||
"language-perl": "0.2.0",
|
||||
"language-php": "0.2.0",
|
||||
"language-php": "0.3.0",
|
||||
"language-property-list": "0.2.0",
|
||||
"language-puppet": "0.2.0",
|
||||
"language-python": "0.2.0",
|
||||
"language-ruby": "0.5.0",
|
||||
"language-ruby-on-rails": "0.3.0",
|
||||
"language-ruby": "0.8.0",
|
||||
"language-ruby-on-rails": "0.4.0",
|
||||
"language-sass": "0.3.0",
|
||||
"language-shellscript": "0.2.0",
|
||||
"language-source": "0.2.0",
|
||||
@@ -149,8 +137,7 @@
|
||||
"language-todo": "0.2.0",
|
||||
"language-toml": "0.7.0",
|
||||
"language-xml": "0.2.0",
|
||||
"language-yaml": "0.1.0",
|
||||
"grunt-download-atom-shell": "0.1.1"
|
||||
"language-yaml": "0.1.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
Arquivo executável → Arquivo normal
BIN
Arquivo binário não exibido.
|
Antes Largura: | Altura: | Tamanho: 52 KiB Depois Largura: | Altura: | Tamanho: 48 KiB |
Arquivo binário não exibido.
Arquivo binário não exibido.
+18
-10
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
var safeExec = require('./utils/child-process-wrapper.js').safeExec;
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
// OAuth token for atom-bot
|
||||
@@ -22,21 +23,28 @@ function executeCommands(commands, done, index) {
|
||||
done(null);
|
||||
}
|
||||
|
||||
// Join multiple commands into one line.
|
||||
function joinCommands() {
|
||||
var commandSeparator = process.platform == 'win32' ? '&' : ';';
|
||||
return Array.prototype.slice.call(arguments, 0).join(commandSeparator);
|
||||
}
|
||||
var apmVendorPath = path.resolve(__dirname, '..', 'vendor', 'apm');
|
||||
var apmInstallPath = path.resolve(__dirname, '..', 'apm');
|
||||
if (!fs.existsSync(apmInstallPath))
|
||||
fs.mkdirSync(apmInstallPath);
|
||||
if (!fs.existsSync(path.join(apmInstallPath, 'node_modules')))
|
||||
fs.mkdirSync(path.join(apmInstallPath, 'node_modules'));
|
||||
|
||||
var apmFlags = process.env.JANKY_SHA1 || process.argv.indexOf('--no-color') !== -1 ? '--no-color' : '';
|
||||
var packagesToDedupe = ['fs-plus', 'humanize-plus', 'nan', 'oniguruma', 'roaster', 'season'];
|
||||
var echoNewLine = process.platform == 'win32' ? 'echo.' : 'echo';
|
||||
var commands = [
|
||||
'git submodule --quiet sync',
|
||||
'git submodule --quiet update --recursive --init',
|
||||
{command: joinCommands('cd vendor/apm', 'npm install --silent .'), options: {ignoreStdout: true}},
|
||||
{command: 'npm install --silent vendor/apm', options: {ignoreStdout: true}},
|
||||
{command: 'npm install --quiet', options: {cwd: path.resolve(__dirname, '..', 'build'), ignoreStdout: true}},
|
||||
{command: 'npm install --quiet', options: {cwd: apmVendorPath, ignoreStdout: true}},
|
||||
{command: 'npm install --quiet ' + apmVendorPath, options: {cwd: apmInstallPath, ignoreStdout: true}},
|
||||
{command: 'npm install --quiet ' + apmVendorPath, options: {ignoreStdout: true}},
|
||||
{command: 'node ../../apm/node_modules/atom-package-manager/bin/apm rebuild', options: {cwd: path.resolve('node_modules', 'atom-package-manager'), ignoreStdout: true}},
|
||||
echoNewLine,
|
||||
'node node_modules/atom-package-manager/bin/apm clean',
|
||||
'node node_modules/atom-package-manager/bin/apm install --silent',
|
||||
'node apm/node_modules/atom-package-manager/bin/apm clean ' + apmFlags,
|
||||
'node apm/node_modules/atom-package-manager/bin/apm install --quiet ' + apmFlags,
|
||||
'node apm/node_modules/atom-package-manager/bin/apm dedupe --quiet ' + apmFlags + ' ' + packagesToDedupe.join(' '),
|
||||
];
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
||||
+6
-5
@@ -1,12 +1,13 @@
|
||||
#!/usr/bin/env node
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
var cp = require('./utils/child-process-wrapper.js');
|
||||
var path = require('path');
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
||||
cp.safeExec('node script/bootstrap', function() {
|
||||
// node node_modules/grunt-cli/bin/grunt "$@"
|
||||
var gruntPath = path.join('node_modules', 'grunt-cli', 'bin', 'grunt');
|
||||
var args = [gruntPath].concat(process.argv.slice(2));
|
||||
cp.safeSpawn(process.execPath, args, process.exit);
|
||||
// build/node_modules/.bin/grunt "$@"
|
||||
var gruntPath = path.join('build', 'node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
|
||||
var args = ['--gruntfile', path.resolve('build', 'Gruntfile.coffee')];
|
||||
args = args.concat(process.argv.slice(2));
|
||||
cp.safeSpawn(gruntPath, args, process.exit);
|
||||
});
|
||||
|
||||
+16
-11
@@ -1,20 +1,18 @@
|
||||
#!/usr/bin/env node
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
var cp = require('./utils/child-process-wrapper.js');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
||||
if (process.platform != 'darwin')
|
||||
throw new Error('cibuild can not run on ' + process.platform + ' yet!');
|
||||
if (process.platform == 'linux')
|
||||
throw new Error('cibuild can not run on linux yet!');
|
||||
|
||||
var homeDir = process.platform == 'win32' ? process.env.USERPROFILE : process.env.HOME;
|
||||
|
||||
function readEnvironmentVariables() {
|
||||
var credentialsPath = '/var/lib/jenkins/config/atomcredentials';
|
||||
function loadEnvironmentVariables(filePath) {
|
||||
try {
|
||||
var credentials = fs.readFileSync(credentialsPath, 'utf8');
|
||||
var lines = credentials.trim().split('\n');
|
||||
var lines = fs.readFileSync(filePath, 'utf8').trim().split('\n');
|
||||
for (i in lines) {
|
||||
var parts = lines[i].split('=');
|
||||
var key = parts[0].trim();
|
||||
@@ -24,16 +22,23 @@ function readEnvironmentVariables() {
|
||||
} catch(error) { }
|
||||
}
|
||||
|
||||
function readEnvironmentVariables() {
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/atomcredentials')
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/xcodekeychain')
|
||||
}
|
||||
|
||||
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');
|
||||
async.series([
|
||||
require('rimraf').bind(global, path.join(homeDir, '.atom')),
|
||||
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, 'node node_modules/grunt-cli/bin/grunt ci --stack --no-color'),
|
||||
], function(error) {
|
||||
cp.safeExec.bind(global, gruntPath + ' ci --gruntfile build/Gruntfile.coffee --stack --no-color'),
|
||||
]
|
||||
async.series(tasks, function(error) {
|
||||
process.exit(error ? 1 : 0);
|
||||
});
|
||||
})();
|
||||
|
||||
Arquivo executável
+37
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
var cp = require('./utils/child-process-wrapper.js');
|
||||
var path = require('path');
|
||||
var os = require('os');
|
||||
|
||||
var removeCommand = process.platform === 'win32' ? 'del /F /Q /S ' : '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';
|
||||
|
||||
// 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';
|
||||
|
||||
var commands = [
|
||||
killatom,
|
||||
[__dirname, '..', 'node_modules'],
|
||||
[__dirname, '..', 'build', 'node_modules'],
|
||||
[__dirname, '..', 'apm', 'node_modules'],
|
||||
[__dirname, '..', 'vendor', 'apm', 'node_modules'],
|
||||
[__dirname, '..', 'atom-shell'],
|
||||
[home, '.atom', '.node-gyp'],
|
||||
[home, '.atom', 'storage'],
|
||||
[tmpdir, 'atom-build'],
|
||||
[tmpdir, 'atom-cached-atom-shells'],
|
||||
[tmpdir, 'atom-compile-cache'],
|
||||
];
|
||||
var run = function() {
|
||||
var next = commands.shift();
|
||||
if (!next)
|
||||
process.exit(0);
|
||||
if (Array.isArray(next))
|
||||
next = removeCommand + path.resolve.apply(path.resolve, next);
|
||||
cp.safeExec(next, run);
|
||||
};
|
||||
run();
|
||||
@@ -0,0 +1,5 @@
|
||||
@IF EXIST "%~dp0\node.exe" (
|
||||
"%~dp0\node.exe" "%~dp0\clean" %*
|
||||
) ELSE (
|
||||
node "%~dp0\clean" %*
|
||||
)
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
|
||||
# This entire file is a hack so that constructicon can build Atom via
|
||||
# xcode
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
rm -fr node_modules
|
||||
./script/bootstrap
|
||||
./node_modules/.bin/grunt --no-color --build-dir="$BUILT_PRODUCTS_DIR" deploy
|
||||
|
||||
echo "TARGET_BUILD_DIR=$BUILT_PRODUCTS_DIR"
|
||||
echo "FULL_PRODUCT_NAME=Atom.app"
|
||||
echo "PRODUCT_NAME=Atom"
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
cd "$(dirname "$0")/../.."
|
||||
export PATH="atom-shell/Atom.app/Contents/Resources/:${PATH}"
|
||||
|
||||
rm -rf atom.xcodeproj
|
||||
gyp --depth=. atom.gyp
|
||||
@@ -0,0 +1,18 @@
|
||||
@echo off
|
||||
|
||||
set USAGE=Usage: %0 source destination
|
||||
|
||||
if [%1] == [] (
|
||||
echo %USAGE%
|
||||
exit 1
|
||||
)
|
||||
if [%2] == [] (
|
||||
echo %USAGE%
|
||||
exit 2
|
||||
)
|
||||
|
||||
:: rm -rf %2
|
||||
if exist %2 rmdir %2 /s /q
|
||||
|
||||
:: cp -rf %1 %2
|
||||
xcopy %1 %2 /e /h /c /i /y /r
|
||||
@@ -0,0 +1,23 @@
|
||||
@echo off
|
||||
|
||||
set USAGE=Usage: %0 source name-on-desktop
|
||||
|
||||
if [%1] == [] (
|
||||
echo %USAGE%
|
||||
exit 1
|
||||
)
|
||||
if [%2] == [] (
|
||||
echo %USAGE%
|
||||
exit 2
|
||||
)
|
||||
|
||||
set SCRIPT="%TEMP%\%RANDOM%-%RANDOM%-%RANDOM%-%RANDOM%.vbs"
|
||||
|
||||
echo Set oWS = WScript.CreateObject("WScript.Shell") >> %SCRIPT%
|
||||
echo sLinkFile = "%USERPROFILE%\Desktop\%2.lnk" >> %SCRIPT%
|
||||
echo Set oLink = oWS.CreateShortcut(sLinkFile) >> %SCRIPT%
|
||||
echo oLink.TargetPath = %1 >> %SCRIPT%
|
||||
echo oLink.Save >> %SCRIPT%
|
||||
|
||||
cscript /nologo %SCRIPT%
|
||||
del %SCRIPT%
|
||||
Arquivo executável
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
var cp = require('./utils/child-process-wrapper.js');
|
||||
var path = require('path');
|
||||
|
||||
// node build/node_modules/.bin/grunt "$@"
|
||||
var gruntPath = path.resolve(__dirname, '..', 'build', 'node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
|
||||
var args = ['--gruntfile', path.resolve('build', 'Gruntfile.coffee')];
|
||||
args = args.concat(process.argv.slice(2));
|
||||
cp.safeSpawn(gruntPath, args, process.exit);
|
||||
Arquivo executável
+10
@@ -0,0 +1,10 @@
|
||||
#!/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)
|
||||
@@ -3,11 +3,11 @@
|
||||
set -e
|
||||
|
||||
BUILT_PRODUCTS_DIR=$1
|
||||
VERSION=$2
|
||||
PLIST_PATH="$BUILT_PRODUCTS_DIR/Atom.app/Contents/Info.plist"
|
||||
HELPER_PLIST_PATH="$BUILT_PRODUCTS_DIR/Atom.app/Contents/Frameworks/Atom Helper.app/Contents/Info.plist"
|
||||
|
||||
# Update version
|
||||
VERSION=$(git rev-parse --short HEAD | tr -d "\n")
|
||||
/usr/libexec/PlistBuddy -c "Set CFBundleShortVersionString $VERSION" "$PLIST_PATH"
|
||||
/usr/libexec/PlistBuddy -c "Set CFBundleVersion $VERSION" "$PLIST_PATH"
|
||||
/usr/libexec/PlistBuddy -c "Set CFBundleShortVersionString $VERSION" "$HELPER_PLIST_PATH"
|
||||
|
||||
+3
-2
@@ -1,9 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
var safeExec = require('./utils/child-process-wrapper.js').safeExec;
|
||||
var path = require('path');
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
||||
safeExec('node script/bootstrap', function() {
|
||||
safeExec('node node_modules/grunt-cli/bin/grunt ci --stack --no-color', process.exit);
|
||||
var gruntPath = path.join('node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
|
||||
safeExec(gruntPath + ' ci --stack --no-color', process.exit);
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ fs.createReadStream(fontSrc).pipe(fs.createWriteStream(fontDest))
|
||||
|
||||
# Update Octicon UTF codes
|
||||
glyphsSrc = path.join(pathToOcticons, 'data', 'glyphs.yml')
|
||||
octiconUtfDest = path.join atomDir, 'static', 'octicon-utf-codes.less'
|
||||
octiconUtfDest = path.join atomDir, 'static', 'variables', 'octicon-utf-codes.less'
|
||||
output = []
|
||||
for {css, code} in YAML.load(fs.readFileSync(glyphsSrc).toString())
|
||||
output.push "@#{css}: \"\\#{code}\";"
|
||||
|
||||
@@ -438,3 +438,11 @@ describe "the `atom` global", ->
|
||||
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
|
||||
|
||||
describe ".isReleasedVersion()", ->
|
||||
it "returns false if the version is a SHA and true otherwise", ->
|
||||
version = '0.1.0'
|
||||
spyOn(atom.constructor, 'getVersion').andCallFake -> version
|
||||
expect(atom.isReleasedVersion()).toBe true
|
||||
version = '36b5518'
|
||||
expect(atom.isReleasedVersion()).toBe false
|
||||
|
||||
@@ -4,33 +4,31 @@ temp = require 'temp'
|
||||
installer = require '../src/command-installer'
|
||||
|
||||
describe "install(commandPath, callback)", ->
|
||||
directory = path.join(temp.dir, 'install-atom-command', 'atom')
|
||||
commandPath = path.join(directory, 'source')
|
||||
destinationPath = path.join(directory, 'bin', 'source')
|
||||
commandFilePath = temp.openSync("atom-command").path
|
||||
commandName = path.basename(commandFilePath)
|
||||
installationPath = temp.mkdirSync("atom-bin")
|
||||
installationFilePath = path.join(installationPath, commandName)
|
||||
|
||||
beforeEach ->
|
||||
spyOn(installer, 'findInstallDirectory').andCallFake (callback) ->
|
||||
callback(directory)
|
||||
|
||||
fs.removeSync(directory) if fs.existsSync(directory)
|
||||
spyOn(installer, 'getInstallDirectory').andReturn installationPath
|
||||
|
||||
describe "on #darwin", ->
|
||||
it "symlinks the command and makes it executable", ->
|
||||
fs.writeFileSync(commandPath, 'test')
|
||||
expect(fs.isFileSync(commandPath)).toBeTruthy()
|
||||
expect(fs.isExecutableSync(commandPath)).toBeFalsy()
|
||||
expect(fs.isFileSync(destinationPath)).toBeFalsy()
|
||||
expect(fs.isFileSync(commandFilePath)).toBeTruthy()
|
||||
expect(fs.isExecutableSync(commandFilePath)).toBeFalsy()
|
||||
expect(fs.isFileSync(installationFilePath)).toBeFalsy()
|
||||
|
||||
installDone = false
|
||||
installError = null
|
||||
installer.install commandPath, (error) ->
|
||||
installer.install commandFilePath, (error) ->
|
||||
installDone = true
|
||||
installError = error
|
||||
|
||||
waitsFor -> installDone
|
||||
waitsFor ->
|
||||
installDone
|
||||
|
||||
runs ->
|
||||
expect(installError).toBeNull()
|
||||
expect(fs.isFileSync(destinationPath)).toBeTruthy()
|
||||
expect(fs.realpathSync(destinationPath)).toBe fs.realpathSync(commandPath)
|
||||
expect(fs.isExecutableSync(destinationPath)).toBeTruthy()
|
||||
expect(fs.isFileSync(installationFilePath)).toBeTruthy()
|
||||
expect(fs.realpathSync(installationFilePath)).toBe fs.realpathSync(commandFilePath)
|
||||
expect(fs.isExecutableSync(installationFilePath)).toBeTruthy()
|
||||
|
||||
+10
-10
@@ -108,10 +108,10 @@ describe "Config", ->
|
||||
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
|
||||
describe ".save()", ->
|
||||
nodeFs = require 'fs'
|
||||
CSON = require 'season'
|
||||
|
||||
beforeEach ->
|
||||
spyOn(nodeFs, 'writeFileSync')
|
||||
spyOn(CSON, 'writeFileSync')
|
||||
jasmine.unspy atom.config, 'save'
|
||||
|
||||
describe "when ~/.atom/config.json exists", ->
|
||||
@@ -122,12 +122,12 @@ describe "Config", ->
|
||||
atom.config.set("x.y.z", 3)
|
||||
atom.config.setDefaults("a.b", e: 4, f: 5)
|
||||
|
||||
nodeFs.writeFileSync.reset()
|
||||
CSON.writeFileSync.reset()
|
||||
atom.config.save()
|
||||
|
||||
expect(nodeFs.writeFileSync.argsForCall[0][0]).toBe(path.join(atom.config.configDirPath, "atom.config.json"))
|
||||
writtenConfig = JSON.parse(nodeFs.writeFileSync.argsForCall[0][1])
|
||||
expect(writtenConfig).toEqual atom.config.settings
|
||||
expect(CSON.writeFileSync.argsForCall[0][0]).toBe(path.join(atom.config.configDirPath, "atom.config.json"))
|
||||
writtenConfig = CSON.writeFileSync.argsForCall[0][1]
|
||||
expect(writtenConfig).toBe atom.config.settings
|
||||
|
||||
describe "when ~/.atom/config.json doesn't exist", ->
|
||||
it "writes any non-default properties to ~/.atom/config.cson", ->
|
||||
@@ -137,12 +137,12 @@ describe "Config", ->
|
||||
atom.config.set("x.y.z", 3)
|
||||
atom.config.setDefaults("a.b", e: 4, f: 5)
|
||||
|
||||
nodeFs.writeFileSync.reset()
|
||||
CSON.writeFileSync.reset()
|
||||
atom.config.save()
|
||||
|
||||
expect(nodeFs.writeFileSync.argsForCall[0][0]).toBe(path.join(atom.config.configDirPath, "atom.config.cson"))
|
||||
expect(CSON.writeFileSync.argsForCall[0][0]).toBe(path.join(atom.config.configDirPath, "atom.config.cson"))
|
||||
CoffeeScript = require 'coffee-script'
|
||||
writtenConfig = CoffeeScript.eval(nodeFs.writeFileSync.argsForCall[0][1], bare: true)
|
||||
writtenConfig = CSON.writeFileSync.argsForCall[0][1]
|
||||
expect(writtenConfig).toEqual atom.config.settings
|
||||
|
||||
describe ".setDefaults(keyPath, defaults)", ->
|
||||
@@ -218,7 +218,7 @@ describe "Config", ->
|
||||
runs ->
|
||||
expect(fs.existsSync(atom.config.configDirPath)).toBeTruthy()
|
||||
expect(fs.existsSync(path.join(atom.config.configDirPath, 'packages'))).toBeTruthy()
|
||||
expect(fs.existsSync(path.join(atom.config.configDirPath, 'snippets'))).toBeTruthy()
|
||||
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'snippets.cson'))).toBeTruthy()
|
||||
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'config.cson'))).toBeTruthy()
|
||||
|
||||
describe ".loadUserConfig()", ->
|
||||
|
||||
@@ -68,7 +68,7 @@ describe "Directory", ->
|
||||
|
||||
describe "on #darwin or #linux", ->
|
||||
it "includes symlink information about entries", ->
|
||||
entries = directory.getEntries()
|
||||
entries = directory.getEntriesSync()
|
||||
for entry in entries
|
||||
name = entry.getBaseName()
|
||||
if name is 'symlink-to-dir' or name is 'symlink-to-file'
|
||||
@@ -76,6 +76,20 @@ describe "Directory", ->
|
||||
else
|
||||
expect(entry.symlink).toBeFalsy()
|
||||
|
||||
callback = jasmine.createSpy('getEntries')
|
||||
directory.getEntries(callback)
|
||||
|
||||
waitsFor -> callback.callCount is 1
|
||||
|
||||
runs ->
|
||||
entries = callback.mostRecentCall.args[1]
|
||||
for entry in entries
|
||||
name = entry.getBaseName()
|
||||
if name is 'symlink-to-dir' or name is 'symlink-to-file'
|
||||
expect(entry.symlink).toBeTruthy()
|
||||
else
|
||||
expect(entry.symlink).toBeFalsy()
|
||||
|
||||
describe ".relativize(path)", ->
|
||||
describe "on #darwin or #linux", ->
|
||||
it "returns a relative path based on the directory's path", ->
|
||||
|
||||
@@ -15,19 +15,6 @@ describe "DisplayBuffer", ->
|
||||
displayBuffer.destroy()
|
||||
buffer.release()
|
||||
|
||||
describe "@deserialize(state)", ->
|
||||
it "constructs a display buffer with the same buffer, folds, editorWidthInChars, and tabLength", ->
|
||||
displayBuffer.setTabLength(4)
|
||||
displayBuffer.setEditorWidthInChars(64)
|
||||
displayBuffer.createFold(2, 4)
|
||||
displayBuffer2 = atom.deserializers.deserialize(displayBuffer.serialize())
|
||||
expect(displayBuffer2.id).toBe displayBuffer.id
|
||||
expect(displayBuffer2.buffer).toBe displayBuffer.buffer
|
||||
expect(displayBuffer2.tokenizedBuffer.buffer).toBe displayBuffer.tokenizedBuffer.buffer
|
||||
expect(displayBuffer2.isFoldedAtBufferRow(2)).toBeTruthy()
|
||||
expect(displayBuffer2.getSoftWrapColumn()).toBe displayBuffer.getSoftWrapColumn()
|
||||
expect(displayBuffer2.getTabLength()).toBe displayBuffer.getTabLength()
|
||||
|
||||
describe ".copy()", ->
|
||||
it "creates a new DisplayBuffer with the same initial state", ->
|
||||
marker1 = displayBuffer.markBufferRange([[1, 2], [3, 4]], id: 1)
|
||||
@@ -103,6 +90,14 @@ describe "DisplayBuffer", ->
|
||||
expect(displayBuffer.lineForRow(3).text).toBe ' var pivot = items.shift(), current, left = [], '
|
||||
expect(displayBuffer.lineForRow(4).text).toBe 'right = [];'
|
||||
|
||||
describe "when there are hard tabs", ->
|
||||
beforeEach ->
|
||||
buffer.setText(buffer.getText().replace(new RegExp(' ', 'g'), '\t'))
|
||||
|
||||
it "correctly tokenizes the hard tabs", ->
|
||||
expect(displayBuffer.lineForRow(3).tokens[0].isHardTab).toBeTruthy()
|
||||
expect(displayBuffer.lineForRow(3).tokens[1].isHardTab).toBeTruthy()
|
||||
|
||||
describe "when the buffer changes", ->
|
||||
describe "when buffer lines are updated", ->
|
||||
describe "when whitespace is added after the max line length", ->
|
||||
|
||||
+137
-14
@@ -21,20 +21,21 @@ describe "Editor", ->
|
||||
buffer = editor.buffer
|
||||
lineLengths = buffer.getLines().map (line) -> line.length
|
||||
|
||||
describe "@deserialize(state)", ->
|
||||
describe "when the editor is deserialized", ->
|
||||
it "restores selections and folds based on markers in the buffer", ->
|
||||
editor.setSelectedBufferRange([[1, 2], [3, 4]])
|
||||
editor.addSelectionForBufferRange([[5, 6], [7, 5]], isReversed: true)
|
||||
editor.foldBufferRow(4)
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
|
||||
editor2 = atom.deserializers.deserialize(editor.serialize())
|
||||
editor2 = editor.testSerialization()
|
||||
|
||||
expect(editor2.id).toBe editor.id
|
||||
expect(editor2.getBuffer().getPath()).toBe editor.getBuffer().getPath()
|
||||
expect(editor2.getSelectedBufferRanges()).toEqual [[[1, 2], [3, 4]], [[5, 6], [7, 5]]]
|
||||
expect(editor2.getSelection(1).isReversed()).toBeTruthy()
|
||||
expect(editor2.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
editor2.destroy()
|
||||
|
||||
describe ".copy()", ->
|
||||
it "returns a different edit session with the same initial state", ->
|
||||
@@ -101,6 +102,16 @@ describe "Editor", ->
|
||||
lastCursor = editor.addCursorAtScreenPosition([2, 0])
|
||||
expect(editor.getCursor()).toBe lastCursor
|
||||
|
||||
describe "when the cursor moves", ->
|
||||
it "clears a goal column established by vertical movement", ->
|
||||
editor.setText('b')
|
||||
editor.setCursorBufferPosition([0,0])
|
||||
editor.insertNewline()
|
||||
editor.moveCursorUp()
|
||||
editor.insertText('a')
|
||||
editor.moveCursorDown()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [1, 1]
|
||||
|
||||
describe ".setCursorScreenPosition(screenPosition)", ->
|
||||
it "clears a goal column established by vertical movement", ->
|
||||
# set a goal column by moving down
|
||||
@@ -347,13 +358,13 @@ describe "Editor", ->
|
||||
expect(editor.getCursors().length).toBe 1
|
||||
expect(editor.getCursorBufferPosition()).toEqual [12,2]
|
||||
|
||||
describe ".moveCursorToBeginningOfLine()", ->
|
||||
describe ".moveCursorToBeginningOfScreenLine()", ->
|
||||
describe "when soft wrap is on", ->
|
||||
it "moves cursor to the beginning of the screen line", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setEditorWidthInChars(10)
|
||||
editor.setCursorScreenPosition([1, 2])
|
||||
editor.moveCursorToBeginningOfLine()
|
||||
editor.moveCursorToBeginningOfScreenLine()
|
||||
cursor = editor.getCursor()
|
||||
expect(cursor.getScreenPosition()).toEqual [1, 0]
|
||||
|
||||
@@ -361,19 +372,19 @@ describe "Editor", ->
|
||||
it "moves cursor to the beginning of then line", ->
|
||||
editor.setCursorScreenPosition [0,5]
|
||||
editor.addCursorAtScreenPosition [1,7]
|
||||
editor.moveCursorToBeginningOfLine()
|
||||
editor.moveCursorToBeginningOfScreenLine()
|
||||
expect(editor.getCursors().length).toBe 2
|
||||
[cursor1, cursor2] = editor.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,0]
|
||||
|
||||
describe ".moveCursorToEndOfLine()", ->
|
||||
describe ".moveCursorToEndOfScreenLine()", ->
|
||||
describe "when soft wrap is on", ->
|
||||
it "moves cursor to the beginning of the screen line", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setEditorWidthInChars(10)
|
||||
editor.setCursorScreenPosition([1, 2])
|
||||
editor.moveCursorToEndOfLine()
|
||||
editor.moveCursorToEndOfScreenLine()
|
||||
cursor = editor.getCursor()
|
||||
expect(cursor.getScreenPosition()).toEqual [1, 9]
|
||||
|
||||
@@ -381,12 +392,30 @@ describe "Editor", ->
|
||||
it "moves cursor to the end of line", ->
|
||||
editor.setCursorScreenPosition [0,0]
|
||||
editor.addCursorAtScreenPosition [1,0]
|
||||
editor.moveCursorToEndOfLine()
|
||||
editor.moveCursorToEndOfScreenLine()
|
||||
expect(editor.getCursors().length).toBe 2
|
||||
[cursor1, cursor2] = editor.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,29]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,30]
|
||||
|
||||
describe ".moveCursorToBeginningOfLine()", ->
|
||||
it "moves cursor to the beginning of the buffer line", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setEditorWidthInChars(10)
|
||||
editor.setCursorScreenPosition([1, 2])
|
||||
editor.moveCursorToBeginningOfLine()
|
||||
cursor = editor.getCursor()
|
||||
expect(cursor.getScreenPosition()).toEqual [0, 0]
|
||||
|
||||
describe ".moveCursorToEndOfLine()", ->
|
||||
it "moves cursor to the end of the buffer line", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setEditorWidthInChars(10)
|
||||
editor.setCursorScreenPosition([0, 2])
|
||||
editor.moveCursorToEndOfLine()
|
||||
cursor = editor.getCursor()
|
||||
expect(cursor.getScreenPosition()).toEqual [3, 4]
|
||||
|
||||
describe ".moveCursorToFirstCharacterOfLine()", ->
|
||||
describe "when soft wrap is on", ->
|
||||
it "moves to the first character of the current screen line or the beginning of the screen line if it's already on the first character", ->
|
||||
@@ -418,6 +447,13 @@ describe "Editor", ->
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,0]
|
||||
|
||||
it "moves to the beginning of the line if it only contains whitespace ", ->
|
||||
editor.setText("first\n \nthird")
|
||||
editor.setCursorScreenPosition [1,2]
|
||||
editor.moveCursorToFirstCharacterOfLine()
|
||||
cursor = editor.getCursor()
|
||||
expect(cursor.getBufferPosition()).toEqual [1,0]
|
||||
|
||||
describe ".moveCursorToBeginningOfWord()", ->
|
||||
it "moves the cursor to the beginning of the word", ->
|
||||
editor.setCursorBufferPosition [0, 8]
|
||||
@@ -783,6 +819,11 @@ describe "Editor", ->
|
||||
editor.selectLine()
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[12,0], [12,2]]
|
||||
|
||||
editor.setCursorBufferPosition([0, 2])
|
||||
editor.selectLine()
|
||||
editor.selectLine()
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[0,0], [2,0]]
|
||||
|
||||
describe ".selectToBeginningOfWord()", ->
|
||||
it "selects text from cusor position to beginning of word", ->
|
||||
editor.setCursorScreenPosition [0,13]
|
||||
@@ -1145,6 +1186,27 @@ describe "Editor", ->
|
||||
[[10, 0], [10, 0]]
|
||||
]
|
||||
|
||||
describe ".splitSelectionsIntoLines()", ->
|
||||
it "splits all multi-line selections into one selection per line", ->
|
||||
editor.setSelectedBufferRange([[0, 3], [2, 4]])
|
||||
editor.splitSelectionsIntoLines()
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [
|
||||
[[0, 3], [0, 29]]
|
||||
[[1, 0], [1, 30]]
|
||||
[[2, 0], [2, 4]]
|
||||
]
|
||||
|
||||
editor.setSelectedBufferRange([[0, 3], [1, 10]])
|
||||
editor.splitSelectionsIntoLines()
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [
|
||||
[[0, 3], [0, 29]]
|
||||
[[1, 0], [1, 10]]
|
||||
]
|
||||
|
||||
editor.setSelectedBufferRange([[0, 0], [0, 3]])
|
||||
editor.splitSelectionsIntoLines()
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 3]]]
|
||||
|
||||
describe ".consolidateSelections()", ->
|
||||
it "destroys all selections but the most recent, returning true if any selections were destroyed", ->
|
||||
editor.setSelectedBufferRange([[3, 16], [3, 21]])
|
||||
@@ -2389,17 +2451,16 @@ describe "Editor", ->
|
||||
|
||||
describe "when a better-matched grammar is added to syntax", ->
|
||||
it "switches to the better-matched grammar and re-tokenizes the buffer", ->
|
||||
editor.destroy()
|
||||
jsGrammar = atom.syntax.selectGrammar('a.js')
|
||||
atom.syntax.removeGrammar(jsGrammar)
|
||||
|
||||
editor = atom.project.openSync('sample.js', autoIndent: false)
|
||||
expect(editor.getGrammar()).toBe atom.syntax.nullGrammar
|
||||
expect(editor.lineForScreenRow(0).tokens.length).toBe 1
|
||||
editor2 = atom.project.openSync('sample.js', autoIndent: false)
|
||||
expect(editor2.getGrammar()).toBe atom.syntax.nullGrammar
|
||||
expect(editor2.lineForScreenRow(0).tokens.length).toBe 1
|
||||
|
||||
atom.syntax.addGrammar(jsGrammar)
|
||||
expect(editor.getGrammar()).toBe jsGrammar
|
||||
expect(editor.lineForScreenRow(0).tokens.length).toBeGreaterThan 1
|
||||
expect(editor2.getGrammar()).toBe jsGrammar
|
||||
expect(editor2.lineForScreenRow(0).tokens.length).toBeGreaterThan 1
|
||||
|
||||
describe "auto-indent", ->
|
||||
copyText = (text, {startColumn}={}) ->
|
||||
@@ -2611,6 +2672,7 @@ describe "Editor", ->
|
||||
|
||||
describe ".shouldPromptToSave()", ->
|
||||
it "returns false when an edit session's buffer is in use by more than one session", ->
|
||||
jasmine.unspy(editor, 'shouldPromptToSave')
|
||||
expect(editor.shouldPromptToSave()).toBeFalsy()
|
||||
buffer.setText('changed')
|
||||
expect(editor.shouldPromptToSave()).toBeTruthy()
|
||||
@@ -2659,3 +2721,64 @@ describe "Editor", ->
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 2]
|
||||
editor.moveCursorLeft()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 0]
|
||||
|
||||
describe ".setIndentationForBufferRow", ->
|
||||
describe "when the editor uses soft tabs but the row has hard tabs", ->
|
||||
it "only replaces whitespace charachters", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setText(" 1\n 2")
|
||||
editor.setCursorBufferPosition([0, 0])
|
||||
editor.setIndentationForBufferRow(0, 2)
|
||||
expect(editor.getText()).toBe(" 1\n 2")
|
||||
|
||||
describe "when the editor's grammar has an injection selector", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-text', sync: true)
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
|
||||
it "includes the grammar's patterns when the selector matches the current scope in other grammars", ->
|
||||
atom.packages.activatePackage('language-hyperlink', sync: true)
|
||||
grammar = atom.syntax.selectGrammar("text.js")
|
||||
{tokens} = grammar.tokenizeLine("var i; // http://github.com")
|
||||
|
||||
expect(tokens[0].value).toBe "var"
|
||||
expect(tokens[0].scopes).toEqual ["source.js", "storage.modifier.js"]
|
||||
|
||||
expect(tokens[6].value).toBe "http://github.com"
|
||||
expect(tokens[6].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
|
||||
describe "when the grammar is added", ->
|
||||
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor.setText("// http://github.com")
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " http://github.com"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
atom.packages.activatePackage('language-hyperlink', sync: true)
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "http://github.com"
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
|
||||
describe "when the grammar is updated", ->
|
||||
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor.setText("// SELECT * FROM OCTOCATS")
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
atom.packages.activatePackage('package-with-injection-selector', sync: true)
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
atom.packages.activatePackage('language-sql', sync: true)
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "SELECT"
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "keyword.other.DML.sql"]
|
||||
|
||||
+450
-399
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -63,7 +63,7 @@ describe 'File', ->
|
||||
afterEach ->
|
||||
if fs.existsSync(newPath)
|
||||
fs.removeSync(newPath)
|
||||
waitsFor "remove event", (done) -> file.on 'removed', done
|
||||
waitsFor "remove event", 30000, (done) -> file.on 'removed', done
|
||||
|
||||
it "it updates its path", ->
|
||||
jasmine.unspy(window, "setTimeout")
|
||||
@@ -73,7 +73,7 @@ describe 'File', ->
|
||||
|
||||
fs.moveSync(filePath, newPath)
|
||||
|
||||
waitsFor "move event", ->
|
||||
waitsFor "move event", 30000, ->
|
||||
moveHandler.callCount > 0
|
||||
|
||||
runs ->
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
'name': 'test'
|
||||
'scopeName': 'source.test'
|
||||
'injectionSelector': 'comment'
|
||||
'patterns': [{'include': 'source.sql'}]
|
||||
+1
-1
@@ -15,6 +15,6 @@ var quicksort = function () {
|
||||
}
|
||||
return sort(left).concat(pivot).concat(sort(right));
|
||||
};
|
||||
|
||||
// this is a single-line comment
|
||||
return sort(Array.apply(this, arguments));
|
||||
};
|
||||
@@ -187,6 +187,7 @@ describe "Git", ->
|
||||
newPath = atom.project.resolve('git/working-dir/untracked.txt')
|
||||
cleanPath = atom.project.resolve('git/working-dir/other.txt')
|
||||
fs.writeFileSync(newPath, '')
|
||||
newPath = fs.absolute newPath # specs could be running under symbol path.
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(modifiedPath, originalModifiedPathText)
|
||||
@@ -257,8 +258,7 @@ describe "Git", ->
|
||||
|
||||
it "subscribes to all the serialized buffers in the project", ->
|
||||
atom.project.openSync('sample.js')
|
||||
#TODO Replace with atom.replicate().project when Atom is a telepath model
|
||||
project2 = atom.replicate().get('project')
|
||||
project2 = atom.project.testSerialization()
|
||||
buffer = project2.getBuffers()[0]
|
||||
|
||||
waitsFor ->
|
||||
|
||||
@@ -1,23 +1,31 @@
|
||||
module.exports.runSpecSuite = (specSuite, logErrors=true) ->
|
||||
fs = require 'fs'
|
||||
|
||||
module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) ->
|
||||
{$, $$} = require 'atom'
|
||||
window[key] = value for key, value of require '../vendor/jasmine'
|
||||
|
||||
require 'jasmine-focused'
|
||||
require 'jasmine-tagged'
|
||||
{TerminalReporter} = require 'jasmine-tagged'
|
||||
|
||||
TimeReporter = require './time-reporter'
|
||||
timeReporter = new TimeReporter()
|
||||
|
||||
logStream = fs.openSync(logFile, 'w') if logFile?
|
||||
log = (str) ->
|
||||
if logStream?
|
||||
fs.writeSync(logStream, str)
|
||||
else
|
||||
process.stderr.write(str)
|
||||
|
||||
if atom.getLoadSettings().exitWhenDone
|
||||
{jasmineNode} = require 'jasmine-node/lib/jasmine-node/reporter'
|
||||
reporter = new jasmineNode.TerminalReporter
|
||||
print: (args...) ->
|
||||
process.stderr.write(args...)
|
||||
reporter = new TerminalReporter
|
||||
print: (str) ->
|
||||
log(str)
|
||||
onComplete: (runner) ->
|
||||
process.stdout.write('\n')
|
||||
timeReporter.logLongestSuites 10, (line) -> process.stdout.write("#{line}\n")
|
||||
process.stdout.write('\n')
|
||||
timeReporter.logLongestSpecs 10, (line) -> process.stdout.write("#{line}\n")
|
||||
log('\n')
|
||||
timeReporter.logLongestSuites 10, (line) -> log("#{line}\n")
|
||||
log('\n')
|
||||
timeReporter.logLongestSpecs 10, (line) -> log("#{line}\n")
|
||||
fs.closeSync(logStream) if logStream?
|
||||
atom.exit(runner.results().failedCount > 0 ? 1 : 0)
|
||||
else
|
||||
AtomReporter = require './atom-reporter'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
|
||||
temp = require 'temp'
|
||||
Keymap = require '../src/keymap'
|
||||
{$, $$, WorkspaceView} = require 'atom'
|
||||
|
||||
@@ -7,9 +8,11 @@ describe "Keymap", ->
|
||||
fragment = null
|
||||
keymap = null
|
||||
resourcePath = atom.getLoadSettings().resourcePath
|
||||
configDirPath = null
|
||||
|
||||
beforeEach ->
|
||||
keymap = new Keymap({configDirPath: atom.getConfigDirPath(), resourcePath})
|
||||
configDirPath = temp.mkdirSync('atom')
|
||||
keymap = new Keymap({configDirPath, resourcePath})
|
||||
fragment = $ """
|
||||
<div class="command-mode">
|
||||
<div class="child-node">
|
||||
@@ -18,6 +21,9 @@ describe "Keymap", ->
|
||||
</div>
|
||||
"""
|
||||
|
||||
afterEach ->
|
||||
keymap.destroy()
|
||||
|
||||
describe ".handleKeyEvent(event)", ->
|
||||
deleteCharHandler = null
|
||||
insertCharHandler = null
|
||||
@@ -347,3 +353,49 @@ describe "Keymap", ->
|
||||
el = $$ -> @div class: 'brown'
|
||||
bindings = keymap.keyBindingsForCommandMatchingElement('cultivate', el)
|
||||
expect(bindings).toHaveLength 0
|
||||
|
||||
describe "loading platform specific keybindings", ->
|
||||
customKeymap = null
|
||||
|
||||
beforeEach ->
|
||||
resourcePath = temp.mkdirSync('atom')
|
||||
customKeymap = new Keymap({configDirPath, resourcePath})
|
||||
|
||||
afterEach ->
|
||||
customKeymap.destroy()
|
||||
|
||||
it "doesn't load keybindings from other platforms", ->
|
||||
win32FilePath = path.join(resourcePath, "keymaps", "win32.cson")
|
||||
darwinFilePath = path.join(resourcePath, "keymaps", "darwin.cson")
|
||||
fs.writeFileSync(win32FilePath, '"body": "ctrl-l": "core:win32-move-left"')
|
||||
fs.writeFileSync(darwinFilePath, '"body": "ctrl-l": "core:darwin-move-left"')
|
||||
|
||||
customKeymap.loadBundledKeymaps()
|
||||
keyBindings = customKeymap.keyBindingsForKeystroke('ctrl-l')
|
||||
expect(keyBindings).toHaveLength 1
|
||||
expect(keyBindings[0].command).toBe "core:#{process.platform}-move-left"
|
||||
|
||||
describe "when the user keymap file is changed", ->
|
||||
it "is reloaded", ->
|
||||
keymapFilePath = path.join(configDirPath, "keymap.cson")
|
||||
fs.writeFileSync(keymapFilePath, '"body": "ctrl-l": "core:move-left"')
|
||||
keymap.loadUserKeymap()
|
||||
|
||||
spyOn(keymap, 'loadUserKeymap').andCallThrough()
|
||||
fs.writeFileSync(keymapFilePath, "'body': 'ctrl-l': 'core:move-right'")
|
||||
|
||||
waitsFor ->
|
||||
keymap.loadUserKeymap.callCount > 0
|
||||
|
||||
runs ->
|
||||
keyBinding = keymap.keyBindingsForKeystroke('ctrl-l')[0]
|
||||
expect(keyBinding.command).toBe 'core:move-right'
|
||||
keymap.loadUserKeymap.reset()
|
||||
fs.removeSync(keymapFilePath)
|
||||
|
||||
waitsFor ->
|
||||
keymap.loadUserKeymap.callCount > 0
|
||||
|
||||
runs ->
|
||||
keyBinding = keymap.keyBindingsForKeystroke('ctrl-l')[0]
|
||||
expect(keyBinding).toBeUndefined()
|
||||
|
||||
@@ -44,20 +44,12 @@ describe "LanguageMode", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 0)
|
||||
expect(buffer.lineForRow(0)).toBe " // var i;"
|
||||
|
||||
describe "fold suggestion", ->
|
||||
describe ".doesBufferRowStartFold(bufferRow)", ->
|
||||
it "returns true only when the buffer row starts a foldable region", ->
|
||||
expect(languageMode.doesBufferRowStartFold(0)).toBeTruthy()
|
||||
expect(languageMode.doesBufferRowStartFold(1)).toBeTruthy()
|
||||
expect(languageMode.doesBufferRowStartFold(2)).toBeFalsy()
|
||||
expect(languageMode.doesBufferRowStartFold(3)).toBeFalsy()
|
||||
|
||||
describe ".rowRangeForCodeFoldAtBufferRow(bufferRow)", ->
|
||||
it "returns the start/end rows of the foldable region starting at the given row", ->
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(0)).toEqual [0, 12]
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(1)).toEqual [1, 9]
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(2)).toBeNull()
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(4)).toEqual [4, 7]
|
||||
describe ".rowRangeForCodeFoldAtBufferRow(bufferRow)", ->
|
||||
it "returns the start/end rows of the foldable region starting at the given row", ->
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(0)).toEqual [0, 12]
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(1)).toEqual [1, 9]
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(2)).toBeNull()
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(4)).toEqual [4, 7]
|
||||
|
||||
describe "suggestedIndentForBufferRow", ->
|
||||
it "returns the suggested indentation based on auto-indent/outdent rules", ->
|
||||
@@ -106,7 +98,6 @@ describe "LanguageMode", ->
|
||||
range = languageMode.rowRangeForParagraphAtBufferRow(15)
|
||||
expect(range).toEqual [[15,0], [15,26]]
|
||||
|
||||
|
||||
describe "coffeescript", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-coffee-script', sync: true)
|
||||
@@ -139,13 +130,13 @@ describe "LanguageMode", ->
|
||||
expect(buffer.lineForRow(7)).toBe " # "
|
||||
|
||||
describe "fold suggestion", ->
|
||||
describe ".doesBufferRowStartFold(bufferRow)", ->
|
||||
describe ".isFoldableAtBufferRow(bufferRow)", ->
|
||||
it "returns true only when the buffer row starts a foldable region", ->
|
||||
expect(languageMode.doesBufferRowStartFold(0)).toBeTruthy()
|
||||
expect(languageMode.doesBufferRowStartFold(1)).toBeTruthy()
|
||||
expect(languageMode.doesBufferRowStartFold(2)).toBeFalsy()
|
||||
expect(languageMode.doesBufferRowStartFold(3)).toBeFalsy()
|
||||
expect(languageMode.doesBufferRowStartFold(19)).toBeTruthy()
|
||||
expect(languageMode.isFoldableAtBufferRow(0)).toBeTruthy()
|
||||
expect(languageMode.isFoldableAtBufferRow(1)).toBeTruthy()
|
||||
expect(languageMode.isFoldableAtBufferRow(2)).toBeFalsy()
|
||||
expect(languageMode.isFoldableAtBufferRow(3)).toBeFalsy()
|
||||
expect(languageMode.isFoldableAtBufferRow(19)).toBeTruthy()
|
||||
|
||||
describe ".rowRangeForCodeFoldAtBufferRow(bufferRow)", ->
|
||||
it "returns the start/end rows of the foldable region starting at the given row", ->
|
||||
@@ -297,6 +288,14 @@ describe "LanguageMode", ->
|
||||
languageMode.unfoldBufferRow(1)
|
||||
expect(editor.lineForScreenRow(1).fold).toBeUndefined()
|
||||
|
||||
describe ".isFoldableAtBufferRow(bufferRow)", ->
|
||||
it "returns true if the line starts a foldable row range", ->
|
||||
expect(languageMode.isFoldableAtBufferRow(0)).toBe true
|
||||
expect(languageMode.isFoldableAtBufferRow(1)).toBe true
|
||||
expect(languageMode.isFoldableAtBufferRow(2)).toBe false
|
||||
expect(languageMode.isFoldableAtBufferRow(3)).toBe false
|
||||
expect(languageMode.isFoldableAtBufferRow(4)).toBe true
|
||||
|
||||
describe "folding with comments", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
@@ -350,6 +349,17 @@ describe "LanguageMode", ->
|
||||
fold2 = editor.lineForScreenRow(5).fold
|
||||
expect(fold2).toBeFalsy()
|
||||
|
||||
describe ".isFoldableAtBufferRow(bufferRow)", ->
|
||||
it "returns true if the line starts a multi-line comment", ->
|
||||
expect(languageMode.isFoldableAtBufferRow(1)).toBe true
|
||||
expect(languageMode.isFoldableAtBufferRow(6)).toBe true
|
||||
expect(languageMode.isFoldableAtBufferRow(17)).toBe false
|
||||
|
||||
it "does not return true for a line in the middle of a comment that's followed by an indented line", ->
|
||||
expect(languageMode.isFoldableAtBufferRow(7)).toBe false
|
||||
editor.buffer.insert([8, 0], ' ')
|
||||
expect(languageMode.isFoldableAtBufferRow(7)).toBe false
|
||||
|
||||
describe "css", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-source', sync: true)
|
||||
|
||||
@@ -1,341 +1,67 @@
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
PaneContainer = require '../src/pane-container'
|
||||
Pane = require '../src/pane'
|
||||
{_, $, View, $$} = require 'atom'
|
||||
|
||||
describe "PaneContainer", ->
|
||||
[TestView, container, pane1, pane2, pane3] = []
|
||||
|
||||
beforeEach ->
|
||||
class TestView extends View
|
||||
atom.deserializers.add(this)
|
||||
@deserialize: ({name}) -> new TestView(name)
|
||||
@content: -> @div tabindex: -1
|
||||
initialize: (@name) -> @text(@name)
|
||||
serialize: -> { deserializer: 'TestView', @name }
|
||||
getUri: -> path.join(temp.dir, @name)
|
||||
save: -> @saved = true
|
||||
isEqual: (other) -> @name is other.name
|
||||
|
||||
container = new PaneContainer
|
||||
pane1 = new Pane(new TestView('1'))
|
||||
container.setRoot(pane1)
|
||||
pane2 = pane1.splitRight(new TestView('2'))
|
||||
pane3 = pane2.splitDown(new TestView('3'))
|
||||
|
||||
afterEach ->
|
||||
atom.deserializers.remove(TestView)
|
||||
|
||||
describe ".focusNextPane()", ->
|
||||
it "focuses the pane following the focused pane or the first pane if no pane has focus", ->
|
||||
container.attachToDom()
|
||||
container.focusNextPane()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPane()
|
||||
expect(pane2.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPane()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPane()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".focusPreviousPane()", ->
|
||||
it "focuses the pane preceding the focused pane or the last pane if no pane has focus", ->
|
||||
container.attachToDom()
|
||||
container.focusPreviousPane()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPane()
|
||||
expect(pane2.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPane()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPane()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".getActivePane()", ->
|
||||
it "returns the most-recently focused pane", ->
|
||||
focusStealer = $$ -> @div tabindex: -1, "focus stealer"
|
||||
focusStealer.attachToDom()
|
||||
container.attachToDom()
|
||||
|
||||
pane2.focus()
|
||||
expect(container.getFocusedPane()).toBe pane2
|
||||
expect(container.getActivePane()).toBe pane2
|
||||
|
||||
focusStealer.focus()
|
||||
expect(container.getFocusedPane()).toBeUndefined()
|
||||
expect(container.getActivePane()).toBe pane2
|
||||
|
||||
pane3.focus()
|
||||
expect(container.getFocusedPane()).toBe pane3
|
||||
expect(container.getActivePane()).toBe pane3
|
||||
|
||||
# returns the first pane if none have been set to active
|
||||
container.find('.pane.active').removeClass('active')
|
||||
expect(container.getActivePane()).toBe pane1
|
||||
|
||||
describe ".eachPane(callback)", ->
|
||||
it "runs the callback with all current and future panes until the subscription is cancelled", ->
|
||||
panes = []
|
||||
subscription = container.eachPane (pane) -> panes.push(pane)
|
||||
expect(panes).toEqual [pane1, pane2, pane3]
|
||||
|
||||
panes = []
|
||||
pane4 = pane3.splitRight(pane3.copyActiveItem())
|
||||
expect(panes).toEqual [pane4]
|
||||
|
||||
panes = []
|
||||
subscription.off()
|
||||
pane4.splitDown()
|
||||
expect(panes).toEqual []
|
||||
|
||||
describe ".reopenItem()", ->
|
||||
describe "when there is an active pane", ->
|
||||
it "reconstructs and shows the last-closed pane item", ->
|
||||
expect(container.getActivePane()).toBe pane3
|
||||
item3 = pane3.activeItem
|
||||
item4 = new TestView('4')
|
||||
pane3.showItem(item4)
|
||||
|
||||
pane3.destroyItem(item3)
|
||||
pane3.destroyItem(item4)
|
||||
expect(container.getActivePane()).toBe pane1
|
||||
|
||||
expect(container.reopenItem()).toBeTruthy()
|
||||
expect(pane1.activeItem).toEqual item4
|
||||
|
||||
expect(container.reopenItem()).toBeTruthy()
|
||||
expect(pane1.activeItem).toEqual item3
|
||||
|
||||
expect(container.reopenItem()).toBeFalsy()
|
||||
expect(pane1.activeItem).toEqual item3
|
||||
|
||||
describe "when the last-closed pane item is an edit session", ->
|
||||
it "reopens the edit session (regression)", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
pane3.showItem(editor)
|
||||
pane3.destroyItem(editor)
|
||||
expect(container.reopenItem()).toBeTruthy()
|
||||
expect(pane3.activeItem.getPath()).toBe editor.getPath()
|
||||
expect(container.reopenItem()).toBeFalsy()
|
||||
|
||||
describe "when there is no active pane", ->
|
||||
it "attaches a new pane with the reconstructed last pane item and focuses it", ->
|
||||
container.attachToDom()
|
||||
pane1.remove()
|
||||
pane2.remove()
|
||||
item3 = pane3.activeItem
|
||||
pane3.destroyItem(item3)
|
||||
expect(container.getActivePane()).toBeUndefined()
|
||||
|
||||
container.reopenItem()
|
||||
|
||||
expect(container.getActivePane().activeItem).toEqual item3
|
||||
expect(container.getActivePane().activeView).toMatchSelector ':focus'
|
||||
|
||||
it "does not reopen an item that is already open", ->
|
||||
item3 = pane3.activeItem
|
||||
item4 = new TestView('4')
|
||||
pane3.showItem(item4)
|
||||
pane3.destroyItem(item3)
|
||||
pane3.destroyItem(item4)
|
||||
|
||||
expect(container.getActivePane()).toBe pane1
|
||||
pane1.showItem(new TestView('4'))
|
||||
|
||||
expect(container.reopenItem()).toBeTruthy()
|
||||
expect(_.pluck(pane1.getItems(), 'name')).toEqual ['1', '4', '3']
|
||||
expect(pane1.activeItem).toEqual item3
|
||||
|
||||
expect(container.reopenItem()).toBeFalsy()
|
||||
expect(pane1.activeItem).toEqual item3
|
||||
|
||||
pane1.destroyItem(item3)
|
||||
container.setRoot(new Pane(item3))
|
||||
expect(container.reopenItem()).toBeFalsy()
|
||||
expect(container.getActivePane().getItems().length).toBe 1
|
||||
expect(container.getActivePaneItem()).toEqual item3
|
||||
|
||||
describe ".saveAll()", ->
|
||||
it "saves all open pane items", ->
|
||||
pane1.showItem(new TestView('4'))
|
||||
|
||||
container.saveAll()
|
||||
|
||||
for pane in container.getPanes()
|
||||
for item in pane.getItems()
|
||||
expect(item.saved).toBeTruthy()
|
||||
|
||||
describe ".confirmClose()", ->
|
||||
it "returns true after modified files are saved", ->
|
||||
pane1.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
pane2.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
spyOn(atom, "confirm").andReturn(0)
|
||||
|
||||
saved = container.confirmClose()
|
||||
|
||||
runs ->
|
||||
expect(saved).toBeTruthy()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "returns false if the user cancels saving", ->
|
||||
pane1.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
pane2.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
spyOn(atom, "confirm").andReturn(1)
|
||||
|
||||
saved = container.confirmClose()
|
||||
|
||||
runs ->
|
||||
expect(saved).toBeFalsy()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
describe "serialization", ->
|
||||
it "can be serialized and deserialized, and correctly adjusts dimensions of deserialized panes after attach", ->
|
||||
newContainer = atom.deserializers.deserialize(container.serialize())
|
||||
expect(newContainer.find('.row > :contains(1)')).toExist()
|
||||
expect(newContainer.find('.row > .column > :contains(2)')).toExist()
|
||||
expect(newContainer.find('.row > .column > :contains(3)')).toExist()
|
||||
[containerA, pane1A, pane2A, pane3A] = []
|
||||
|
||||
newContainer.height(200).width(300).attachToDom()
|
||||
expect(newContainer.find('.row > :contains(1)').width()).toBe 150
|
||||
expect(newContainer.find('.row > .column > :contains(2)').height()).toBe 100
|
||||
|
||||
xit "removes empty panes on deserialization", ->
|
||||
# only deserialize pane 1's view successfully
|
||||
TestView.deserialize = ({name}) -> new TestView(name) if name is '1'
|
||||
newContainer = atom.deserializers.deserialize(container.serialize())
|
||||
expect(newContainer.find('.row, .column')).not.toExist()
|
||||
expect(newContainer.find('> :contains(1)')).toExist()
|
||||
|
||||
describe "pane-container:active-pane-item-changed", ->
|
||||
[pane1, item1a, item1b, item2a, item2b, item3a, container, activeItemChangedHandler] = []
|
||||
beforeEach ->
|
||||
item1a = new TestView('1a')
|
||||
item1b = new TestView('1b')
|
||||
item2a = new TestView('2a')
|
||||
item2b = new TestView('2b')
|
||||
item3a = new TestView('3a')
|
||||
# This is a dummy item to prevent panes from being empty on deserialization
|
||||
class Item
|
||||
atom.deserializers.add(this)
|
||||
@deserialize: -> new this
|
||||
serialize: -> deserializer: 'Item'
|
||||
|
||||
pane1A = new Pane(items: [new Item])
|
||||
containerA = new PaneContainer(root: pane1A)
|
||||
pane2A = pane1A.splitRight(items: [new Item])
|
||||
pane3A = pane2A.splitDown(items: [new Item])
|
||||
|
||||
it "preserves the focused pane across serialization", ->
|
||||
expect(pane3A.focused).toBe true
|
||||
|
||||
containerB = containerA.testSerialization()
|
||||
[pane1B, pane2B, pane3B] = containerB.getPanes()
|
||||
expect(pane3B.focused).toBe true
|
||||
|
||||
it "preserves the active pane across serialization, independent of focus", ->
|
||||
pane3A.activate()
|
||||
expect(containerA.activePane).toBe pane3A
|
||||
|
||||
containerB = containerA.testSerialization()
|
||||
[pane1B, pane2B, pane3B] = containerB.getPanes()
|
||||
expect(containerB.activePane).toBe pane3B
|
||||
|
||||
describe "::activePane", ->
|
||||
[container, pane1, pane2] = []
|
||||
|
||||
beforeEach ->
|
||||
container = new PaneContainer
|
||||
container.attachToDom()
|
||||
pane1 = new Pane(item1a)
|
||||
container.setRoot(pane1)
|
||||
pane1 = container.root
|
||||
|
||||
activeItemChangedHandler = jasmine.createSpy("activeItemChangedHandler")
|
||||
container.on 'pane-container:active-pane-item-changed', activeItemChangedHandler
|
||||
it "references the first pane if no pane has been made active", ->
|
||||
expect(container.activePane).toBe pane1
|
||||
expect(pane1.active).toBe true
|
||||
|
||||
describe "when there are no panes", ->
|
||||
it "is triggered when a new pane containing a pane item is added", ->
|
||||
container.setRoot()
|
||||
expect(container.getPanes().length).toBe 0
|
||||
activeItemChangedHandler.reset()
|
||||
it "references the most pane on which ::activate was most recently called", ->
|
||||
pane2 = pane1.splitRight()
|
||||
pane2.activate()
|
||||
expect(container.activePane).toBe pane2
|
||||
expect(pane1.active).toBe false
|
||||
expect(pane2.active).toBe true
|
||||
pane1.activate()
|
||||
expect(container.activePane).toBe pane1
|
||||
expect(pane1.active).toBe true
|
||||
expect(pane2.active).toBe false
|
||||
|
||||
pane = new Pane(item1a)
|
||||
container.setRoot(pane)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
it "is reassigned to the next pane if the current active pane is destroyed", ->
|
||||
pane2 = pane1.splitRight()
|
||||
pane2.activate()
|
||||
pane2.destroy()
|
||||
expect(container.activePane).toBe pane1
|
||||
expect(pane1.active).toBe true
|
||||
|
||||
describe "when there is one pane", ->
|
||||
it "is triggered when a new pane item is added", ->
|
||||
pane1.showItem(item1b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1b
|
||||
|
||||
it "is not triggered when the active pane item is shown again", ->
|
||||
pane1.showItem(item1a)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when switching to an existing pane item", ->
|
||||
pane1.showItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.showItem(item1a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is triggered when the active pane item is removed", ->
|
||||
pane1.showItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.removeItem(item1b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is not triggered when an inactive pane item is removed", ->
|
||||
pane1.showItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.removeItem(item1a)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when all pane items are removed", ->
|
||||
pane1.removeItem(item1a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toBe undefined
|
||||
|
||||
it "is triggered when the pane is removed", ->
|
||||
pane1.remove()
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toBe undefined
|
||||
|
||||
describe "when there are two panes", ->
|
||||
[pane2] = []
|
||||
|
||||
beforeEach ->
|
||||
pane2 = pane1.splitLeft(item2a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
it "is triggered when a new pane item is added to the active pane", ->
|
||||
pane2.showItem(item2b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item2b
|
||||
|
||||
it "is not triggered when a new pane item is added to an inactive pane", ->
|
||||
pane1.showItem(item1b)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane item removed from the active pane", ->
|
||||
pane2.showItem(item2b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane2.removeItem(item2b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item2a
|
||||
|
||||
it "is not triggered when the active pane item removed from an inactive pane", ->
|
||||
pane1.showItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.removeItem(item1b)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane is removed", ->
|
||||
pane2.remove()
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is not triggered when an inactive pane is removed", ->
|
||||
pane1.remove()
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane is changed", ->
|
||||
pane1.makeActive()
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
describe "when there are multiple panes", ->
|
||||
beforeEach ->
|
||||
pane2 = pane1.splitRight(item2a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
it "is triggered when a new pane is added", ->
|
||||
pane2.splitDown(item3a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item3a
|
||||
|
||||
it "is not triggered when the non active pane is removed", ->
|
||||
pane3 = pane2.splitDown(item3a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.remove()
|
||||
pane2.remove()
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
it "does not allow the root pane to be destroyed", ->
|
||||
pane1.destroy()
|
||||
expect(container.root).toBe pane1
|
||||
expect(pane1.isDestroyed()).toBe false
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
PaneContainerView = require '../src/pane-container-view'
|
||||
PaneView = require '../src/pane-view'
|
||||
{_, $, View, $$} = require 'atom'
|
||||
|
||||
describe "PaneContainerView", ->
|
||||
[TestView, container, pane1, pane2, pane3] = []
|
||||
|
||||
beforeEach ->
|
||||
class TestView extends View
|
||||
atom.deserializers.add(this)
|
||||
@deserialize: ({name}) -> new TestView(name)
|
||||
@content: -> @div tabindex: -1
|
||||
initialize: (@name) -> @text(@name)
|
||||
serialize: -> { deserializer: 'TestView', @name }
|
||||
getUri: -> path.join(temp.dir, @name)
|
||||
save: -> @saved = true
|
||||
isEqual: (other) -> @name is other?.name
|
||||
|
||||
container = new PaneContainerView
|
||||
pane1 = container.getRoot()
|
||||
pane1.activateItem(new TestView('1'))
|
||||
pane2 = pane1.splitRight(new TestView('2'))
|
||||
pane3 = pane2.splitDown(new TestView('3'))
|
||||
|
||||
afterEach ->
|
||||
atom.deserializers.remove(TestView)
|
||||
|
||||
describe ".focusNextPane()", ->
|
||||
it "focuses the pane following the focused pane or the first pane if no pane has focus", ->
|
||||
container.attachToDom()
|
||||
container.focusNextPane()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPane()
|
||||
expect(pane2.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPane()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPane()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".focusPreviousPane()", ->
|
||||
it "focuses the pane preceding the focused pane or the last pane if no pane has focus", ->
|
||||
container.attachToDom()
|
||||
container.getPanes()[0].focus() # activate first pane
|
||||
|
||||
container.focusPreviousPane()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPane()
|
||||
expect(pane2.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPane()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPane()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".getActivePane()", ->
|
||||
it "returns the most-recently focused pane", ->
|
||||
focusStealer = $$ -> @div tabindex: -1, "focus stealer"
|
||||
focusStealer.attachToDom()
|
||||
container.attachToDom()
|
||||
|
||||
pane2.focus()
|
||||
expect(container.getFocusedPane()).toBe pane2
|
||||
expect(container.getActivePane()).toBe pane2
|
||||
|
||||
focusStealer.focus()
|
||||
expect(container.getFocusedPane()).toBeUndefined()
|
||||
expect(container.getActivePane()).toBe pane2
|
||||
|
||||
pane3.focus()
|
||||
expect(container.getFocusedPane()).toBe pane3
|
||||
expect(container.getActivePane()).toBe pane3
|
||||
|
||||
describe ".eachPane(callback)", ->
|
||||
it "runs the callback with all current and future panes until the subscription is cancelled", ->
|
||||
panes = []
|
||||
subscription = container.eachPane (pane) -> panes.push(pane)
|
||||
expect(panes).toEqual [pane1, pane2, pane3]
|
||||
|
||||
panes = []
|
||||
pane4 = pane3.splitRight(pane3.copyActiveItem())
|
||||
expect(panes).toEqual [pane4]
|
||||
|
||||
panes = []
|
||||
subscription.off()
|
||||
pane4.splitDown()
|
||||
expect(panes).toEqual []
|
||||
|
||||
describe ".saveAll()", ->
|
||||
it "saves all open pane items", ->
|
||||
pane1.activateItem(new TestView('4'))
|
||||
|
||||
container.saveAll()
|
||||
|
||||
for pane in container.getPanes()
|
||||
for item in pane.getItems()
|
||||
expect(item.saved).toBeTruthy()
|
||||
|
||||
describe ".confirmClose()", ->
|
||||
it "returns true after modified files are saved", ->
|
||||
pane1.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
pane2.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
spyOn(atom, "confirm").andReturn(0)
|
||||
|
||||
saved = container.confirmClose()
|
||||
|
||||
runs ->
|
||||
expect(saved).toBeTruthy()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "returns false if the user cancels saving", ->
|
||||
pane1.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
pane2.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
spyOn(atom, "confirm").andReturn(1)
|
||||
|
||||
saved = container.confirmClose()
|
||||
|
||||
runs ->
|
||||
expect(saved).toBeFalsy()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
describe "serialization", ->
|
||||
it "can be serialized and deserialized, and correctly adjusts dimensions of deserialized panes after attach", ->
|
||||
newContainer = new PaneContainerView(container.model.testSerialization())
|
||||
expect(newContainer.find('.pane-row > :contains(1)')).toExist()
|
||||
expect(newContainer.find('.pane-row > .pane-column > :contains(2)')).toExist()
|
||||
expect(newContainer.find('.pane-row > .pane-column > :contains(3)')).toExist()
|
||||
|
||||
newContainer.height(200).width(300).attachToDom()
|
||||
expect(newContainer.find('.pane-row > :contains(1)').width()).toBe 150
|
||||
expect(newContainer.find('.pane-row > .pane-column > :contains(2)').height()).toBe 100
|
||||
|
||||
describe "if there are empty panes after deserialization", ->
|
||||
beforeEach ->
|
||||
# only deserialize pane 1's view successfully
|
||||
TestView.deserialize = ({name}) -> new TestView(name) if name is '1'
|
||||
|
||||
describe "if the 'core.destroyEmptyPanes' config option is false (the default)", ->
|
||||
it "leaves the empty panes intact", ->
|
||||
newContainer = new PaneContainerView(container.model.testSerialization())
|
||||
expect(newContainer.find('.pane-row > :contains(1)')).toExist()
|
||||
expect(newContainer.find('.pane-row > .pane-column > .pane').length).toBe 2
|
||||
|
||||
describe "if the 'core.destroyEmptyPanes' config option is true", ->
|
||||
it "removes empty panes on deserialization", ->
|
||||
atom.config.set('core.destroyEmptyPanes', true)
|
||||
newContainer = new PaneContainerView(container.model.testSerialization())
|
||||
expect(newContainer.find('.pane-row, .pane-column')).not.toExist()
|
||||
expect(newContainer.find('> :contains(1)')).toExist()
|
||||
|
||||
describe "pane-container:active-pane-item-changed", ->
|
||||
[pane1, item1a, item1b, item2a, item2b, item3a, container, activeItemChangedHandler] = []
|
||||
beforeEach ->
|
||||
item1a = new TestView('1a')
|
||||
item1b = new TestView('1b')
|
||||
item2a = new TestView('2a')
|
||||
item2b = new TestView('2b')
|
||||
item3a = new TestView('3a')
|
||||
|
||||
container = new PaneContainerView
|
||||
pane1 = container.getRoot()
|
||||
pane1.activateItem(item1a)
|
||||
container.attachToDom()
|
||||
|
||||
activeItemChangedHandler = jasmine.createSpy("activeItemChangedHandler")
|
||||
container.on 'pane-container:active-pane-item-changed', activeItemChangedHandler
|
||||
|
||||
describe "when there is one pane", ->
|
||||
it "is triggered when a new pane item is added", ->
|
||||
pane1.activateItem(item1b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1b
|
||||
|
||||
it "is not triggered when the active pane item is shown again", ->
|
||||
pane1.activateItem(item1a)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when switching to an existing pane item", ->
|
||||
pane1.activateItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.activateItem(item1a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is triggered when the active pane item is destroyed", ->
|
||||
pane1.activateItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.destroyItem(item1b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is not triggered when an inactive pane item is destroyed", ->
|
||||
pane1.activateItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.destroyItem(item1a)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when all pane items are destroyed", ->
|
||||
pane1.destroyItem(item1a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toBe undefined
|
||||
|
||||
describe "when there are two panes", ->
|
||||
[pane2] = []
|
||||
|
||||
beforeEach ->
|
||||
pane2 = pane1.splitLeft(item2a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
it "is triggered when a new pane item is added to the active pane", ->
|
||||
pane2.activateItem(item2b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item2b
|
||||
|
||||
it "is not triggered when a new pane item is added to an inactive pane", ->
|
||||
pane1.activateItem(item1b)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane's active item is destroyed", ->
|
||||
pane2.activateItem(item2b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane2.destroyItem(item2b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item2a
|
||||
|
||||
it "is not triggered when an inactive pane's active item is destroyed", ->
|
||||
pane1.activateItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.destroyItem(item1b)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane is destroyed", ->
|
||||
pane2.remove()
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is not triggered when an inactive pane is destroyed", ->
|
||||
pane1.remove()
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane is changed", ->
|
||||
pane1.activate()
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
describe "when there are multiple panes", ->
|
||||
beforeEach ->
|
||||
pane2 = pane1.splitRight(item2a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
it "is triggered when a new pane is added", ->
|
||||
pane2.splitDown(item3a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item3a
|
||||
|
||||
it "is not triggered when an inactive pane is destroyed", ->
|
||||
pane3 = pane2.splitDown(item3a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.remove()
|
||||
pane2.remove()
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
+398
-602
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -0,0 +1,276 @@
|
||||
PaneContainerView = require '../src/pane-container-view'
|
||||
PaneView = require '../src/pane-view'
|
||||
{fs, $, View} = require 'atom'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
|
||||
describe "PaneView", ->
|
||||
[container, view1, view2, editor1, editor2, pane, paneModel] = []
|
||||
|
||||
class TestView extends View
|
||||
@deserialize: ({id, text}) -> new TestView({id, text})
|
||||
@content: ({id, text}) -> @div class: 'test-view', id: id, tabindex: -1, text
|
||||
initialize: ({@id, @text}) ->
|
||||
serialize: -> { deserializer: 'TestView', @id, @text }
|
||||
getUri: -> @id
|
||||
isEqual: (other) -> other? and @id == other.id and @text == other.text
|
||||
|
||||
beforeEach ->
|
||||
atom.deserializers.add(TestView)
|
||||
container = new PaneContainerView
|
||||
view1 = new TestView(id: 'view-1', text: 'View 1')
|
||||
view2 = new TestView(id: 'view-2', text: 'View 2')
|
||||
editor1 = atom.project.openSync('sample.js')
|
||||
editor2 = atom.project.openSync('sample.txt')
|
||||
pane = container.getRoot()
|
||||
paneModel = pane.model
|
||||
paneModel.addItems([view1, editor1, view2, editor2])
|
||||
|
||||
afterEach ->
|
||||
atom.deserializers.remove(TestView)
|
||||
|
||||
describe "when the active pane item changes", ->
|
||||
it "hides all item views except the active one", ->
|
||||
expect(pane.activeItem).toBe view1
|
||||
expect(view1.css('display')).not.toBe 'none'
|
||||
|
||||
pane.activateItem(view2)
|
||||
expect(view1.css('display')).toBe 'none'
|
||||
expect(view2.css('display')).not.toBe 'none'
|
||||
|
||||
it "triggers 'pane:active-item-changed'", ->
|
||||
itemChangedHandler = jasmine.createSpy("itemChangedHandler")
|
||||
container.on 'pane:active-item-changed', itemChangedHandler
|
||||
|
||||
expect(pane.activeItem).toBe view1
|
||||
paneModel.activateItem(view2)
|
||||
paneModel.activateItem(view2)
|
||||
|
||||
expect(itemChangedHandler.callCount).toBe 1
|
||||
expect(itemChangedHandler.argsForCall[0][1]).toBe view2
|
||||
itemChangedHandler.reset()
|
||||
|
||||
paneModel.activateItem(editor1)
|
||||
expect(itemChangedHandler).toHaveBeenCalled()
|
||||
expect(itemChangedHandler.argsForCall[0][1]).toBe editor1
|
||||
itemChangedHandler.reset()
|
||||
|
||||
it "transfers focus to the new active view if the previous view was focused", ->
|
||||
container.attachToDom()
|
||||
pane.focus()
|
||||
expect(pane.activeView).not.toBe view2
|
||||
expect(pane.activeView).toMatchSelector ':focus'
|
||||
paneModel.activateItem(view2)
|
||||
expect(view2).toMatchSelector ':focus'
|
||||
|
||||
describe "when the new activeItem is a model", ->
|
||||
it "shows the item's view or creates and shows a new view for the item if none exists", ->
|
||||
initialViewCount = pane.itemViews.find('.test-view').length
|
||||
|
||||
model1 =
|
||||
id: 'test-model-1'
|
||||
text: 'Test Model 1'
|
||||
serialize: -> {@id, @text}
|
||||
getViewClass: -> TestView
|
||||
|
||||
model2 =
|
||||
id: 'test-model-2'
|
||||
text: 'Test Model 2'
|
||||
serialize: -> {@id, @text}
|
||||
getViewClass: -> TestView
|
||||
|
||||
paneModel.activateItem(model1)
|
||||
paneModel.activateItem(model2)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2
|
||||
|
||||
paneModel.activatePreviousItem()
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2
|
||||
|
||||
paneModel.destroyItem(model2)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 1
|
||||
|
||||
paneModel.destroyItem(model1)
|
||||
expect(pane.itemViews.find('.test-view').length).toBe initialViewCount
|
||||
|
||||
describe "when the new activeItem is a view", ->
|
||||
it "appends it to the itemViews div if it hasn't already been appended and shows it", ->
|
||||
expect(pane.itemViews.find('#view-2')).not.toExist()
|
||||
paneModel.activateItem(view2)
|
||||
expect(pane.itemViews.find('#view-2')).toExist()
|
||||
paneModel.activateItem(view1)
|
||||
paneModel.activateItem(view2)
|
||||
expect(pane.itemViews.find('#view-2').length).toBe 1
|
||||
|
||||
describe "when an item is destroyed", ->
|
||||
it "triggers the 'pane:item-removed' event with the item and its former index", ->
|
||||
itemRemovedHandler = jasmine.createSpy("itemRemovedHandler")
|
||||
pane.on 'pane:item-removed', itemRemovedHandler
|
||||
paneModel.destroyItem(editor1)
|
||||
expect(itemRemovedHandler).toHaveBeenCalled()
|
||||
expect(itemRemovedHandler.argsForCall[0][1..2]).toEqual [editor1, 1]
|
||||
|
||||
describe "when the destroyed item is a view", ->
|
||||
it "removes the item from the 'item-views' div", ->
|
||||
expect(view1.parent()).toMatchSelector pane.itemViews
|
||||
paneModel.destroyItem(view1)
|
||||
expect(view1.parent()).not.toMatchSelector pane.itemViews
|
||||
|
||||
describe "when the destroyed item is a model", ->
|
||||
it "removes the associated view", ->
|
||||
paneModel.activateItem(editor1)
|
||||
expect(pane.itemViews.find('.editor').length).toBe 1
|
||||
pane.destroyItem(editor1)
|
||||
expect(pane.itemViews.find('.editor').length).toBe 0
|
||||
|
||||
describe "when an item is moved within the same pane", ->
|
||||
it "emits a 'pane:item-moved' event with the item and the new index", ->
|
||||
pane.on 'pane:item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler")
|
||||
paneModel.moveItem(view1, 2)
|
||||
expect(itemMovedHandler).toHaveBeenCalled()
|
||||
expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [view1, 2]
|
||||
|
||||
describe "when an item is moved to another pane", ->
|
||||
it "detaches the item's view rather than removing it", ->
|
||||
paneModel2 = paneModel.splitRight()
|
||||
view1.data('preservative', 1234)
|
||||
paneModel.moveItemToPane(view1, paneModel2, 1)
|
||||
expect(view1.data('preservative')).toBe 1234
|
||||
paneModel2.activateItemAtIndex(1)
|
||||
expect(view1.data('preservative')).toBe 1234
|
||||
|
||||
describe "when the title of the active item changes", ->
|
||||
it "emits pane:active-item-title-changed", ->
|
||||
activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler")
|
||||
pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler
|
||||
|
||||
expect(pane.activeItem).toBe view1
|
||||
|
||||
view2.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
view1.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
activeItemTitleChangedHandler.reset()
|
||||
|
||||
pane.activateItem(view2)
|
||||
view2.trigger 'title-changed'
|
||||
expect(activeItemTitleChangedHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when an unmodifed buffer's path is deleted", ->
|
||||
it "removes the pane item", ->
|
||||
filePath = temp.openSync('atom').path
|
||||
editor = atom.project.openSync(filePath)
|
||||
pane.activateItem(editor)
|
||||
expect(pane.items).toHaveLength(5)
|
||||
|
||||
fs.removeSync(filePath)
|
||||
waitsFor ->
|
||||
pane.items.length == 4
|
||||
|
||||
describe "when a pane is destroyed", ->
|
||||
[pane2, pane2Model] = []
|
||||
|
||||
beforeEach ->
|
||||
pane2Model = paneModel.splitRight() # Can't destroy the last pane, so we add another
|
||||
pane2 = pane2Model._view
|
||||
|
||||
it "triggers a 'pane:removed' event with the pane", ->
|
||||
removedHandler = jasmine.createSpy("removedHandler")
|
||||
container.on 'pane:removed', removedHandler
|
||||
paneModel.destroy()
|
||||
expect(removedHandler).toHaveBeenCalled()
|
||||
expect(removedHandler.argsForCall[0][1]).toBe pane
|
||||
|
||||
describe "if the destroyed pane has focus", ->
|
||||
[paneToLeft, paneToRight] = []
|
||||
|
||||
it "focuses the next pane", ->
|
||||
container.attachToDom()
|
||||
expect(pane.hasFocus()).toBe false
|
||||
expect(pane2.hasFocus()).toBe true
|
||||
pane2Model.destroy()
|
||||
expect(pane.hasFocus()).toBe true
|
||||
|
||||
describe "::getNextPane()", ->
|
||||
it "returns the next pane if one exists, wrapping around from the last pane to the first", ->
|
||||
pane.activateItem(editor1)
|
||||
expect(pane.getNextPane()).toBeUndefined
|
||||
pane2 = pane.splitRight(pane.copyActiveItem())
|
||||
expect(pane.getNextPane()).toBe pane2
|
||||
expect(pane2.getNextPane()).toBe pane
|
||||
|
||||
describe "when the pane's active status changes", ->
|
||||
[pane2, pane2Model] = []
|
||||
|
||||
beforeEach ->
|
||||
pane2Model = paneModel.splitRight(items: [pane.copyActiveItem()])
|
||||
pane2 = pane2Model._view
|
||||
expect(pane2Model.isActive()).toBe true
|
||||
|
||||
it "adds or removes the .active class as appropriate", ->
|
||||
expect(pane).not.toHaveClass('active')
|
||||
paneModel.activate()
|
||||
expect(pane).toHaveClass('active')
|
||||
pane2Model.activate()
|
||||
expect(pane).not.toHaveClass('active')
|
||||
|
||||
it "triggers 'pane:became-active' or 'pane:became-inactive' according to the current status", ->
|
||||
pane.on 'pane:became-active', becameActiveHandler = jasmine.createSpy("becameActiveHandler")
|
||||
pane.on 'pane:became-inactive', becameInactiveHandler = jasmine.createSpy("becameInactiveHandler")
|
||||
paneModel.activate()
|
||||
|
||||
expect(becameActiveHandler.callCount).toBe 1
|
||||
expect(becameInactiveHandler.callCount).toBe 0
|
||||
|
||||
pane2Model.activate()
|
||||
expect(becameActiveHandler.callCount).toBe 1
|
||||
expect(becameInactiveHandler.callCount).toBe 1
|
||||
|
||||
describe "when the pane is focused", ->
|
||||
beforeEach ->
|
||||
container.attachToDom()
|
||||
|
||||
it "transfers focus to the active view", ->
|
||||
focusHandler = jasmine.createSpy("focusHandler")
|
||||
pane.activeItem.on 'focus', focusHandler
|
||||
pane.focus()
|
||||
expect(focusHandler).toHaveBeenCalled()
|
||||
|
||||
it "makes the pane active", ->
|
||||
paneModel.splitRight(items: [pane.copyActiveItem()])
|
||||
expect(paneModel.isActive()).toBe false
|
||||
pane.focus()
|
||||
expect(paneModel.isActive()).toBe true
|
||||
|
||||
describe "when a pane is split", ->
|
||||
it "builds the appropriate pane-row and pane-column views", ->
|
||||
pane1 = pane
|
||||
pane1Model = pane.model
|
||||
pane.activateItem(editor1)
|
||||
|
||||
pane2Model = pane1Model.splitRight(items: [pane1Model.copyActiveItem()])
|
||||
pane3Model = pane2Model.splitDown(items: [pane2Model.copyActiveItem()])
|
||||
pane2 = pane2Model._view
|
||||
pane3 = pane3Model._view
|
||||
|
||||
expect(container.find('> .pane-row > .pane').toArray()).toEqual [pane1[0]]
|
||||
expect(container.find('> .pane-row > .pane-column > .pane').toArray()).toEqual [pane2[0], pane3[0]]
|
||||
|
||||
pane1Model.destroy()
|
||||
expect(container.find('> .pane-column > .pane').toArray()).toEqual [pane2[0], pane3[0]]
|
||||
|
||||
describe "serialization", ->
|
||||
it "focuses the pane after attach only if had focus when serialized", ->
|
||||
container.attachToDom()
|
||||
pane.focus()
|
||||
|
||||
container2 = new PaneContainerView(container.model.testSerialization())
|
||||
pane2 = container2.getRoot()
|
||||
container2.attachToDom()
|
||||
expect(pane2).toMatchSelector(':has(:focus)')
|
||||
|
||||
$(document.activeElement).blur()
|
||||
container3 = new PaneContainerView(container.model.testSerialization())
|
||||
pane3 = container3.getRoot()
|
||||
container3.attachToDom()
|
||||
expect(pane3).not.toMatchSelector(':has(:focus)')
|
||||
@@ -16,21 +16,17 @@ describe "Project", ->
|
||||
afterEach ->
|
||||
deserializedProject?.destroy()
|
||||
|
||||
it "destroys unretained buffers and does not include them in the serialized state", ->
|
||||
it "does not include unretained buffers in the serialized state", ->
|
||||
atom.project.bufferForPathSync('a')
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
|
||||
atom.project.getState().serializeForPersistence()
|
||||
deserializedProject = atom.replicate().get('project')
|
||||
|
||||
deserializedProject = atom.project.testSerialization()
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
expect(atom.project.getBuffers().length).toBe 0
|
||||
|
||||
it "listens for destroyed events on deserialized buffers and removes them when they are destroyed", ->
|
||||
atom.project.openSync('a')
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
atom.project.getState().serializeForPersistence()
|
||||
deserializedProject = atom.replicate().get('project')
|
||||
deserializedProject = atom.project.testSerialization()
|
||||
|
||||
expect(deserializedProject.getBuffers().length).toBe 1
|
||||
deserializedProject.getBuffers()[0].destroy()
|
||||
@@ -59,22 +55,6 @@ describe "Project", ->
|
||||
editor.saveAs(tempFile)
|
||||
expect(atom.project.getPath()).toBe path.dirname(tempFile)
|
||||
|
||||
describe "when an edit session is deserialized", ->
|
||||
it "emits an 'editor-created' event and stores the edit session", ->
|
||||
handler = jasmine.createSpy('editorCreatedHandler')
|
||||
atom.project.on 'editor-created', handler
|
||||
|
||||
editor1 = atom.project.openSync("a")
|
||||
expect(handler.callCount).toBe 1
|
||||
expect(atom.project.getEditors().length).toBe 1
|
||||
expect(atom.project.getEditors()[0]).toBe editor1
|
||||
|
||||
editor2 = atom.deserializers.deserialize(editor1.serialize())
|
||||
expect(handler.callCount).toBe 2
|
||||
expect(atom.project.getEditors().length).toBe 2
|
||||
expect(atom.project.getEditors()[0]).toBe editor1
|
||||
expect(atom.project.getEditors()[1]).toBe editor2
|
||||
|
||||
describe "when an edit session is copied", ->
|
||||
it "emits an 'editor-created' event and stores the edit session", ->
|
||||
handler = jasmine.createSpy('editorCreatedHandler')
|
||||
@@ -419,6 +399,7 @@ describe "Project", ->
|
||||
range: [[2, 6], [2, 11]]
|
||||
|
||||
it "works on evil filenames", ->
|
||||
platform.generateEvilFiles()
|
||||
atom.project.setPath(path.join(__dirname, 'fixtures', 'evil-files'))
|
||||
paths = []
|
||||
matches = []
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
SelectList = require '../src/select-list'
|
||||
SelectListView = require '../src/select-list-view'
|
||||
{$, $$} = require 'atom'
|
||||
|
||||
describe "SelectList", ->
|
||||
describe "SelectListView", ->
|
||||
[selectList, array, list, miniEditor] = []
|
||||
|
||||
beforeEach ->
|
||||
@@ -10,7 +10,7 @@ describe "SelectList", ->
|
||||
["D", "Delta"], ["E", "Echo"], ["F", "Foxtrot"]
|
||||
]
|
||||
|
||||
selectList = new SelectList
|
||||
selectList = new SelectListView
|
||||
selectList.maxItems = 4
|
||||
selectList.filterKey = 1
|
||||
selectList.itemForElement = (element) ->
|
||||
@@ -33,7 +33,7 @@ describe "SelectList", ->
|
||||
selectList.attachToDom()
|
||||
|
||||
it "filters the elements in the list based on the scoreElement function and selects the first item", ->
|
||||
miniEditor.insertText('la')
|
||||
miniEditor.getEditor().insertText('la')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 2
|
||||
@@ -43,13 +43,13 @@ describe "SelectList", ->
|
||||
expect(selectList.error).not.toBeVisible()
|
||||
|
||||
it "displays an error if there are no matches, removes error when there are matches", ->
|
||||
miniEditor.insertText('nothing will match this')
|
||||
miniEditor.getEditor().insertText('nothing will match this')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 0
|
||||
expect(selectList.error).not.toBeHidden()
|
||||
|
||||
miniEditor.setText('la')
|
||||
miniEditor.getEditor().setText('la')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 2
|
||||
@@ -58,7 +58,7 @@ describe "SelectList", ->
|
||||
it "displays no elements until the array has been set on the list", ->
|
||||
selectList.array = null
|
||||
selectList.list.empty()
|
||||
miniEditor.insertText('la')
|
||||
miniEditor.getEditor().insertText('la')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 0
|
||||
@@ -124,7 +124,7 @@ describe "SelectList", ->
|
||||
selectList.attachToDom()
|
||||
|
||||
it "does not trigger the confirmed hook", ->
|
||||
miniEditor.insertText("i will never match anything")
|
||||
miniEditor.getEditor().insertText("i will never match anything")
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li')).not.toExist()
|
||||
@@ -132,7 +132,7 @@ describe "SelectList", ->
|
||||
expect(selectList.confirmed).not.toHaveBeenCalled()
|
||||
|
||||
it "does trigger the cancelled hook", ->
|
||||
miniEditor.insertText("i will never match anything")
|
||||
miniEditor.getEditor().insertText("i will never match anything")
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li')).not.toExist()
|
||||
@@ -53,15 +53,31 @@ describe "SpacePen extensions", ->
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
|
||||
describe "tooltips", ->
|
||||
describe "replaceModifiers", ->
|
||||
replaceModifiers = $.fn.setTooltip.replaceModifiers
|
||||
describe "humanizeKeystrokes", ->
|
||||
humanizeKeystrokes = $.fn.setTooltip.humanizeKeystrokes
|
||||
|
||||
it "replaces single keystroke", ->
|
||||
expect(replaceModifiers('cmd-O')).toEqual '⌘⇧O'
|
||||
expect(replaceModifiers('cmd-shift-up')).toEqual '⌘⇧↑'
|
||||
expect(replaceModifiers('cmd-option-down')).toEqual '⌘⌥↓'
|
||||
expect(replaceModifiers('cmd-option-left')).toEqual '⌘⌥←'
|
||||
expect(replaceModifiers('cmd-option-right')).toEqual '⌘⌥→'
|
||||
expect(humanizeKeystrokes('cmd-O')).toEqual '⌘⇧O'
|
||||
expect(humanizeKeystrokes('cmd-shift-up')).toEqual '⌘⇧↑'
|
||||
expect(humanizeKeystrokes('cmd-option-down')).toEqual '⌘⌥↓'
|
||||
expect(humanizeKeystrokes('cmd-option-left')).toEqual '⌘⌥←'
|
||||
expect(humanizeKeystrokes('cmd-option-right')).toEqual '⌘⌥→'
|
||||
|
||||
it "replaces multiple keystroke", ->
|
||||
expect(replaceModifiers('cmd-o ctrl-2')).toEqual '⌘O ⌃2'
|
||||
expect(humanizeKeystrokes('cmd-o ctrl-2')).toEqual '⌘O ⌃2'
|
||||
|
||||
describe "when the window is resized", ->
|
||||
it "hides the tooltips", ->
|
||||
class TooltipView extends View
|
||||
@content: ->
|
||||
@div()
|
||||
|
||||
view = new TooltipView()
|
||||
view.attachToDom()
|
||||
view.setTooltip('this is a tip')
|
||||
|
||||
view.tooltip('show')
|
||||
expect($(document.body).find('.tooltip')).toBeVisible()
|
||||
|
||||
$(window).trigger('resize')
|
||||
expect($(document.body).find('.tooltip')).not.toExist()
|
||||
|
||||
@@ -4,12 +4,12 @@ require('crash-reporter').start(productName: 'Atom', companyName: 'GitHub')
|
||||
try
|
||||
require '../src/window'
|
||||
Atom = require '../src/atom'
|
||||
window.atom = new Atom()
|
||||
window.atom = Atom.loadOrCreate('spec')
|
||||
window.atom.show() unless atom.getLoadSettings().exitWhenDone
|
||||
{runSpecSuite} = require './jasmine-helper'
|
||||
|
||||
document.title = "Spec Suite"
|
||||
runSpecSuite './spec-suite'
|
||||
runSpecSuite './spec-suite', atom.getLoadSettings().logFile
|
||||
catch error
|
||||
if atom?.getLoadSettings().exitWhenDone
|
||||
console.error(error.stack ? error)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
path = require 'path'
|
||||
fs = require 'fs-plus'
|
||||
|
||||
{_} = require 'atom'
|
||||
|
||||
## Platform specific helpers
|
||||
module.exports =
|
||||
# Public: Returns true if being run from within Windows
|
||||
@@ -18,20 +16,20 @@ module.exports =
|
||||
fs.removeSync(evilFilesPath) if fs.existsSync(evilFilesPath)
|
||||
fs.mkdirSync(evilFilesPath)
|
||||
|
||||
if (@isWindows())
|
||||
if @isWindows()
|
||||
filenames = [
|
||||
"a_file_with_utf8.txt",
|
||||
"file with spaces.txt",
|
||||
"a_file_with_utf8.txt"
|
||||
"file with spaces.txt"
|
||||
"utfa\u0306.md"
|
||||
]
|
||||
else
|
||||
filenames = [
|
||||
"a_file_with_utf8.txt",
|
||||
"file with spaces.txt",
|
||||
"goddam\nnewlines",
|
||||
"quote\".txt",
|
||||
"a_file_with_utf8.txt"
|
||||
"file with spaces.txt"
|
||||
"goddam\nnewlines"
|
||||
"quote\".txt"
|
||||
"utfa\u0306.md"
|
||||
]
|
||||
|
||||
for filename in filenames
|
||||
fd = fs.writeFileSync(path.join(evilFilesPath, filename), 'evil file!', flag: 'w')
|
||||
fs.writeFileSync(path.join(evilFilesPath, filename), 'evil file!', flag: 'w')
|
||||
|
||||
+18
-14
@@ -1,22 +1,20 @@
|
||||
require '../src/window'
|
||||
atom.setUpEnvironment('spec')
|
||||
atom.restoreDimensions()
|
||||
atom.initialize()
|
||||
atom.restoreWindowDimensions()
|
||||
|
||||
require '../vendor/jasmine-jquery'
|
||||
path = require 'path'
|
||||
{_, $, File, WorkspaceView, fs} = require 'atom'
|
||||
Keymap = require '../src/keymap'
|
||||
Config = require '../src/config'
|
||||
{Point} = require 'telepath'
|
||||
{Point} = require 'text-buffer'
|
||||
Project = require '../src/project'
|
||||
Editor = require '../src/editor'
|
||||
EditorView = require '../src/editor-view'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
pathwatcher = require 'pathwatcher'
|
||||
platform = require './spec-helper-platform'
|
||||
clipboard = require 'clipboard'
|
||||
|
||||
platform.generateEvilFiles()
|
||||
|
||||
atom.themes.loadBaseStylesheets()
|
||||
atom.themes.requireStylesheet '../static/jasmine'
|
||||
|
||||
@@ -27,9 +25,8 @@ keyBindingsToRestore = atom.keymap.getKeyBindings()
|
||||
|
||||
$(window).on 'core:close', -> window.close()
|
||||
$(window).on 'unload', ->
|
||||
atom.windowMode = 'spec'
|
||||
atom.getWindowState().set('dimensions', atom.getDimensions())
|
||||
atom.saveWindowState()
|
||||
atom.storeWindowDimensions()
|
||||
atom.saveSync()
|
||||
$('html,body').css('overflow', 'auto')
|
||||
|
||||
jasmine.getEnv().addEqualityTester(_.isEqual) # Use underscore's definition of equality for toEqual assertions
|
||||
@@ -50,12 +47,15 @@ if specDirectory
|
||||
beforeEach ->
|
||||
$.fx.off = true
|
||||
projectPath = specProjectPath ? path.join(@specDirectory, 'fixtures')
|
||||
atom.project = atom.getWindowState().set('project', new Project(path: projectPath))
|
||||
atom.project = new Project(path: projectPath)
|
||||
atom.keymap.keyBindings = _.clone(keyBindingsToRestore)
|
||||
|
||||
window.resetTimeouts()
|
||||
atom.packages.packageStates = {}
|
||||
spyOn(atom, 'saveWindowState')
|
||||
|
||||
serializedWindowState = null
|
||||
|
||||
spyOn(atom, 'saveSync')
|
||||
atom.syntax.clearGrammarOverrides()
|
||||
atom.syntax.clearProperties()
|
||||
|
||||
@@ -89,6 +89,7 @@ beforeEach ->
|
||||
spyOn(window, "setTimeout").andCallFake window.fakeSetTimeout
|
||||
spyOn(window, "clearTimeout").andCallFake window.fakeClearTimeout
|
||||
spyOn(File.prototype, "detectResurrectionAfterDelay").andCallFake -> @detectResurrection()
|
||||
spyOn(Editor.prototype, "shouldPromptToSave").andReturn false
|
||||
|
||||
# make tokenization synchronous
|
||||
TokenizedBuffer.prototype.chunkSize = Infinity
|
||||
@@ -106,13 +107,16 @@ afterEach ->
|
||||
|
||||
atom.workspaceView?.remove?()
|
||||
atom.workspaceView = null
|
||||
delete atom.state.workspace
|
||||
|
||||
atom.project?.destroy?()
|
||||
atom.project = null
|
||||
|
||||
delete atom.state.packageStates
|
||||
|
||||
$('#jasmine-content').empty() unless window.debugContent
|
||||
delete atom.windowState
|
||||
jasmine.unspy(atom, 'saveWindowState')
|
||||
|
||||
jasmine.unspy(atom, 'saveSync')
|
||||
ensureNoPathSubscriptions()
|
||||
atom.syntax.off()
|
||||
waits(0) # yield to ui thread to make screen update more frequently
|
||||
@@ -131,7 +135,7 @@ jasmine.StringPrettyPrinter.prototype.emitObject = (obj) ->
|
||||
emitObject.call(this, obj)
|
||||
|
||||
jasmine.unspy = (object, methodName) ->
|
||||
throw new Error("Not a spy") unless object[methodName].originalValue?
|
||||
throw new Error("Not a spy") unless object[methodName].hasOwnProperty('originalValue')
|
||||
object[methodName] = object[methodName].originalValue
|
||||
|
||||
addCustomMatchers = (spec) ->
|
||||
|
||||
+12
-10
@@ -1,7 +1,6 @@
|
||||
{fs} = require 'atom'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
TextMateGrammar = require '../src/text-mate-grammar'
|
||||
|
||||
describe "the `syntax` global", ->
|
||||
beforeEach ->
|
||||
@@ -62,20 +61,23 @@ describe "the `syntax` global", ->
|
||||
|
||||
describe "when multiple grammars have matching fileTypes", ->
|
||||
it "selects the grammar with the longest fileType match", ->
|
||||
grammar1 = new TextMateGrammar
|
||||
grammarPath1 = temp.path(suffix: '.json')
|
||||
fs.writeFileSync grammarPath1, JSON.stringify(
|
||||
name: 'test1'
|
||||
scopeName: 'source1'
|
||||
fileTypes: ['test', 'more.test']
|
||||
fileTypes: ['test']
|
||||
)
|
||||
grammar1 = atom.syntax.loadGrammarSync(grammarPath1)
|
||||
expect(atom.syntax.selectGrammar('more.test', '')).toBe grammar1
|
||||
|
||||
grammar2 = new TextMateGrammar
|
||||
grammarPath2 = temp.path(suffix: '.json')
|
||||
fs.writeFileSync grammarPath2, JSON.stringify(
|
||||
name: 'test2'
|
||||
scopeName: 'source2'
|
||||
fileTypes: ['test']
|
||||
|
||||
atom.syntax.addGrammar(grammar1)
|
||||
atom.syntax.addGrammar(grammar2)
|
||||
|
||||
expect(atom.syntax.selectGrammar('more.test', '')).toBe grammar1
|
||||
fileTypes: ['test', 'more.test']
|
||||
)
|
||||
grammar2 = atom.syntax.loadGrammarSync(grammarPath2)
|
||||
expect(atom.syntax.selectGrammar('more.test', '')).toBe grammar2
|
||||
|
||||
describe "when there is no file path", ->
|
||||
it "does not throw an exception (regression)", ->
|
||||
|
||||
+109
-46
@@ -1,7 +1,6 @@
|
||||
{_, fs} = require 'atom'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
{Site} = require 'telepath'
|
||||
TextBuffer = require '../src/text-buffer'
|
||||
|
||||
describe 'TextBuffer', ->
|
||||
@@ -34,11 +33,11 @@ describe 'TextBuffer', ->
|
||||
expect(buffer.getText()).toBe fs.readFileSync(filePath, 'utf8')
|
||||
|
||||
describe "when no file exists for the path", ->
|
||||
it "is modified and is initially empty", ->
|
||||
it "is not modified and is initially empty", ->
|
||||
filePath = "does-not-exist.txt"
|
||||
expect(fs.existsSync(filePath)).toBeFalsy()
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
expect(buffer.isModified()).toBeTruthy()
|
||||
expect(buffer.isModified()).not.toBeTruthy()
|
||||
expect(buffer.getText()).toBe ''
|
||||
|
||||
describe "when no path is given", ->
|
||||
@@ -113,10 +112,17 @@ describe 'TextBuffer', ->
|
||||
|
||||
runs ->
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
expect(event.oldRange).toEqual [[0, 0], [0, 5]]
|
||||
expect(event.oldRange).toEqual [[0, 0], [0, 0]]
|
||||
expect(event.newRange).toEqual [[0, 0], [0, 6]]
|
||||
expect(event.oldText).toBe "first"
|
||||
expect(event.oldText).toBe ""
|
||||
expect(event.newText).toBe "second"
|
||||
|
||||
[event] = changeHandler.argsForCall[1]
|
||||
expect(event.oldRange).toEqual [[0, 6], [0, 11]]
|
||||
expect(event.newRange).toEqual [[0, 6], [0, 6]]
|
||||
expect(event.oldText).toBe "first"
|
||||
expect(event.newText).toBe ""
|
||||
|
||||
expect(buffer.isModified()).toBeFalsy()
|
||||
|
||||
describe "when the buffer's memory contents differ from the *previous* disk contents", ->
|
||||
@@ -160,20 +166,38 @@ describe 'TextBuffer', ->
|
||||
filePath = bufferToDelete.getPath() # symlinks may have been converted
|
||||
|
||||
expect(bufferToDelete.getPath()).toBe filePath
|
||||
expect(bufferToDelete.isModified()).toBeFalsy()
|
||||
|
||||
removeHandler = jasmine.createSpy('removeHandler')
|
||||
bufferToDelete.file.on 'removed', removeHandler
|
||||
fs.removeSync(filePath)
|
||||
waitsFor "file to be removed", ->
|
||||
removeHandler.callCount > 0
|
||||
|
||||
afterEach ->
|
||||
bufferToDelete.destroy()
|
||||
|
||||
it "retains its path and reports the buffer as modified", ->
|
||||
expect(bufferToDelete.getPath()).toBe filePath
|
||||
expect(bufferToDelete.isModified()).toBeTruthy()
|
||||
describe "when the file is modified", ->
|
||||
beforeEach ->
|
||||
bufferToDelete.setText("I WAS MODIFIED")
|
||||
expect(bufferToDelete.isModified()).toBeTruthy()
|
||||
|
||||
removeHandler = jasmine.createSpy('removeHandler')
|
||||
bufferToDelete.file.on 'removed', removeHandler
|
||||
fs.removeSync(filePath)
|
||||
waitsFor "file to be removed", ->
|
||||
removeHandler.callCount > 0
|
||||
|
||||
it "retains its path and reports the buffer as modified", ->
|
||||
expect(bufferToDelete.getPath()).toBe filePath
|
||||
expect(bufferToDelete.isModified()).toBeTruthy()
|
||||
|
||||
describe "when the file is not modified", ->
|
||||
beforeEach ->
|
||||
expect(bufferToDelete.isModified()).toBeFalsy()
|
||||
|
||||
removeHandler = jasmine.createSpy('removeHandler')
|
||||
bufferToDelete.file.on 'removed', removeHandler
|
||||
fs.removeSync(filePath)
|
||||
waitsFor "file to be removed", ->
|
||||
removeHandler.callCount > 0
|
||||
|
||||
it "retains its path and reports the buffer as not modified", ->
|
||||
expect(bufferToDelete.getPath()).toBe filePath
|
||||
expect(bufferToDelete.isModified()).toBeFalsy()
|
||||
|
||||
it "resumes watching of the file when it is re-saved", ->
|
||||
bufferToDelete.save()
|
||||
@@ -210,19 +234,6 @@ describe 'TextBuffer', ->
|
||||
advanceClock(buffer.stoppedChangingDelay)
|
||||
expect(modifiedHandler).toHaveBeenCalledWith(false)
|
||||
|
||||
it "reports the modified status changing to true after the underlying file is deleted", ->
|
||||
buffer.release()
|
||||
filePath = path.join(temp.dir, 'atom-tmp-file')
|
||||
fs.writeFileSync(filePath, 'delete me')
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
modifiedHandler = jasmine.createSpy("modifiedHandler")
|
||||
buffer.on 'modified-status-changed', modifiedHandler
|
||||
|
||||
fs.removeSync(filePath)
|
||||
|
||||
waitsFor "modified status to change", -> modifiedHandler.callCount
|
||||
runs -> expect(buffer.isModified()).toBe true
|
||||
|
||||
it "reports the modified status changing to false after a modified buffer is saved", ->
|
||||
filePath = path.join(temp.dir, 'atom-tmp-file')
|
||||
fs.writeFileSync(filePath, '')
|
||||
@@ -454,6 +465,68 @@ describe 'TextBuffer', ->
|
||||
expect(event.oldRange).toEqual expectedPreRange
|
||||
expect(event.newRange).toEqual [[0, 0], [1, 14]]
|
||||
|
||||
describe ".setTextViaDiff(text)", ->
|
||||
it "can change the entire contents of the buffer when there are no newlines", ->
|
||||
buffer.setText('BUFFER CHANGE')
|
||||
newText = 'DISK CHANGE'
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
describe "with standard newlines", ->
|
||||
it "can change the entire contents of the buffer with no newline at the end", ->
|
||||
newText = "I know you are.\nBut what am I?"
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
it "can change the entire contents of the buffer with a newline at the end", ->
|
||||
newText = "I know you are.\nBut what am I?\n"
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
it "can change a few lines at the beginning in the buffer", ->
|
||||
newText = buffer.getText().replace(/function/g, 'omgwow')
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
it "can change a few lines in the middle of the buffer", ->
|
||||
newText = buffer.getText().replace(/shift/g, 'omgwow')
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
it "can adds a newline at the end", ->
|
||||
newText = buffer.getText() + '\n'
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
describe "with windows newlines", ->
|
||||
beforeEach ->
|
||||
buffer.setText(buffer.getText().replace(/\n/g, '\r\n'))
|
||||
|
||||
it "adds a newline at the end", ->
|
||||
newText = buffer.getText() + '\r\n'
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
it "changes the entire contents of the buffer with smaller content with no newline at the end", ->
|
||||
newText = "I know you are.\r\nBut what am I?"
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
it "changes the entire contents of the buffer with smaller content with newline at the end", ->
|
||||
newText = "I know you are.\r\nBut what am I?\r\n"
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
it "changes a few lines at the beginning in the buffer", ->
|
||||
newText = buffer.getText().replace(/function/g, 'omgwow')
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
it "changes a few lines in the middle of the buffer", ->
|
||||
newText = buffer.getText().replace(/shift/g, 'omgwow')
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
describe ".save()", ->
|
||||
saveBuffer = null
|
||||
|
||||
@@ -897,25 +970,22 @@ describe 'TextBuffer', ->
|
||||
expect(buffer.getText()).toBe "\ninitialtexthello\n1\n2\n"
|
||||
|
||||
describe "serialization", ->
|
||||
[buffer2, project2] = []
|
||||
buffer2 = null
|
||||
|
||||
beforeEach ->
|
||||
buffer.destroy()
|
||||
|
||||
filePath = temp.openSync('atom').path
|
||||
fs.writeFileSync(filePath, "words")
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
buffer = atom.project.bufferForPathSync(filePath).retain()
|
||||
|
||||
afterEach ->
|
||||
buffer2?.release()
|
||||
project2?.destroy()
|
||||
buffer2?.destroy()
|
||||
|
||||
describe "when the serialized buffer had no unsaved changes", ->
|
||||
it "loads the current contents of the file at the serialized path", ->
|
||||
expect(buffer.isModified()).toBeFalsy()
|
||||
|
||||
project2 = atom.replicate().get('project')
|
||||
buffer2 = project2.getBuffers()[0]
|
||||
buffer2 = buffer.testSerialization()
|
||||
|
||||
waitsForPromise ->
|
||||
buffer2.load()
|
||||
@@ -931,8 +1001,7 @@ describe 'TextBuffer', ->
|
||||
buffer.setText("BUFFER CHANGE")
|
||||
fs.writeFileSync(filePath, "DISK CHANGE")
|
||||
|
||||
project2 = atom.replicate().get('project')
|
||||
buffer2 = project2.getBuffers()[0]
|
||||
buffer2 = buffer.testSerialization()
|
||||
|
||||
waitsFor ->
|
||||
buffer2.cachedDiskContents
|
||||
@@ -948,9 +1017,7 @@ describe 'TextBuffer', ->
|
||||
buffer.setText("abc")
|
||||
buffer.retain()
|
||||
|
||||
buffer.getState().serializeForPersistence()
|
||||
project2 = atom.replicate().get('project')
|
||||
buffer2 = project2.getBuffers()[0]
|
||||
buffer2 = buffer.testSerialization()
|
||||
|
||||
waitsForPromise ->
|
||||
buffer2.load()
|
||||
@@ -964,15 +1031,11 @@ describe 'TextBuffer', ->
|
||||
|
||||
describe "when the serialized buffer was unsaved and had no path", ->
|
||||
it "restores the previous unsaved state of the buffer", ->
|
||||
buffer.release()
|
||||
buffer.destroy()
|
||||
|
||||
buffer = atom.project.bufferForPathSync()
|
||||
buffer.setText("abc")
|
||||
|
||||
state = buffer.getState().clone()
|
||||
expect(state.get('path')).toBeUndefined()
|
||||
expect(state.getObject('text')).toBe 'abc'
|
||||
|
||||
buffer2 = atom.project.addBuffer(new TextBuffer(state))
|
||||
buffer2 = buffer.testSerialization()
|
||||
expect(buffer2.getPath()).toBeUndefined()
|
||||
expect(buffer2.getText()).toBe("abc")
|
||||
|
||||
@@ -1,704 +0,0 @@
|
||||
TextMateGrammar = require '../src/text-mate-grammar'
|
||||
TextMatePackage = require '../src/text-mate-package'
|
||||
{_, fs} = require 'atom'
|
||||
|
||||
describe "TextMateGrammar", ->
|
||||
grammar = null
|
||||
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-text', sync: true)
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
atom.packages.activatePackage('language-coffee-script', sync: true)
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
atom.packages.activatePackage('language-html', sync: true)
|
||||
atom.packages.activatePackage('language-php', sync: true)
|
||||
atom.packages.activatePackage('language-python', sync: true)
|
||||
grammar = atom.syntax.selectGrammar("hello.coffee")
|
||||
|
||||
describe "@loadSync(path)", ->
|
||||
it "loads grammars from plists", ->
|
||||
grammar = TextMateGrammar.loadSync(require.resolve('./fixtures/sample.plist'))
|
||||
expect(grammar.scopeName).toBe "text.plain"
|
||||
{tokens} = grammar.tokenizeLine("this text is so plain. i love it.")
|
||||
expect(tokens[0]).toEqual value: "this text is so plain. i love it.", scopes: ["text.plain", "meta.paragraph.text"]
|
||||
|
||||
it "loads grammars from cson files", ->
|
||||
grammar = TextMateGrammar.loadSync(require.resolve('./fixtures/packages/package-with-grammars/grammars/alot.cson'))
|
||||
expect(grammar.scopeName).toBe "source.alot"
|
||||
{tokens} = grammar.tokenizeLine("this is alot of code")
|
||||
expect(tokens[1]).toEqual value: "alot", scopes: ["source.alot", "keyword.alot"]
|
||||
|
||||
describe ".tokenizeLine(line, ruleStack)", ->
|
||||
describe "when the entire line matches a single pattern with no capture groups", ->
|
||||
it "returns a single token with the correct scope", ->
|
||||
{tokens} = grammar.tokenizeLine("return")
|
||||
|
||||
expect(tokens.length).toBe 1
|
||||
[token] = tokens
|
||||
expect(token.scopes).toEqual ['source.coffee', 'keyword.control.coffee']
|
||||
|
||||
describe "when the entire line matches a single pattern with capture groups", ->
|
||||
it "returns a single token with the correct scope", ->
|
||||
{tokens} = grammar.tokenizeLine("new foo.bar.Baz")
|
||||
|
||||
expect(tokens.length).toBe 3
|
||||
[newOperator, whitespace, className] = tokens
|
||||
expect(newOperator).toEqual value: 'new', scopes: ['source.coffee', 'meta.class.instance.constructor', 'keyword.operator.new.coffee']
|
||||
expect(whitespace).toEqual value: ' ', scopes: ['source.coffee', 'meta.class.instance.constructor']
|
||||
expect(className).toEqual value: 'foo.bar.Baz', scopes: ['source.coffee', 'meta.class.instance.constructor', 'entity.name.type.instance.coffee']
|
||||
|
||||
describe "when the line doesn't match any patterns", ->
|
||||
it "returns the entire line as a single simple token with the grammar's scope", ->
|
||||
textGrammar = atom.syntax.selectGrammar('foo.txt')
|
||||
{tokens} = textGrammar.tokenizeLine("abc def")
|
||||
expect(tokens.length).toBe 1
|
||||
|
||||
describe "when the line matches multiple patterns", ->
|
||||
it "returns multiple tokens, filling in regions that don't match patterns with tokens in the grammar's global scope", ->
|
||||
{tokens} = grammar.tokenizeLine(" return new foo.bar.Baz ")
|
||||
|
||||
expect(tokens.length).toBe 7
|
||||
|
||||
expect(tokens[0]).toEqual value: ' ', scopes: ['source.coffee']
|
||||
expect(tokens[1]).toEqual value: 'return', scopes: ['source.coffee', 'keyword.control.coffee']
|
||||
expect(tokens[2]).toEqual value: ' ', scopes: ['source.coffee']
|
||||
expect(tokens[3]).toEqual value: 'new', scopes: ['source.coffee', 'meta.class.instance.constructor', 'keyword.operator.new.coffee']
|
||||
expect(tokens[4]).toEqual value: ' ', scopes: ['source.coffee', 'meta.class.instance.constructor']
|
||||
expect(tokens[5]).toEqual value: 'foo.bar.Baz', scopes: ['source.coffee', 'meta.class.instance.constructor', 'entity.name.type.instance.coffee']
|
||||
expect(tokens[6]).toEqual value: ' ', scopes: ['source.coffee']
|
||||
|
||||
describe "when the line matches a pattern with optional capture groups", ->
|
||||
it "only returns tokens for capture groups that matched", ->
|
||||
{tokens} = grammar.tokenizeLine("class Quicksort")
|
||||
expect(tokens.length).toBe 3
|
||||
expect(tokens[0].value).toBe "class"
|
||||
expect(tokens[1].value).toBe " "
|
||||
expect(tokens[2].value).toBe "Quicksort"
|
||||
|
||||
describe "when the line matches a rule with nested capture groups and lookahead capture groups beyond the scope of the overall match", ->
|
||||
it "creates distinct tokens for nested captures and does not return tokens beyond the scope of the overall capture", ->
|
||||
{tokens} = grammar.tokenizeLine(" destroy: ->")
|
||||
expect(tokens.length).toBe 6
|
||||
expect(tokens[0]).toEqual(value: ' ', scopes: ["source.coffee"])
|
||||
expect(tokens[1]).toEqual(value: 'destro', scopes: ["source.coffee", "meta.function.coffee", "entity.name.function.coffee"])
|
||||
# this dangling 'y' with a duplicated scope looks wrong, but textmate yields the same behavior. probably a quirk in the coffee grammar.
|
||||
expect(tokens[2]).toEqual(value: 'y', scopes: ["source.coffee", "meta.function.coffee", "entity.name.function.coffee", "entity.name.function.coffee"])
|
||||
expect(tokens[3]).toEqual(value: ':', scopes: ["source.coffee", "keyword.operator.coffee"])
|
||||
expect(tokens[4]).toEqual(value: ' ', scopes: ["source.coffee"])
|
||||
expect(tokens[5]).toEqual(value: '->', scopes: ["source.coffee", "storage.type.function.coffee"])
|
||||
|
||||
describe "when the line matches a pattern that includes a rule", ->
|
||||
it "returns tokens based on the included rule", ->
|
||||
{tokens} = grammar.tokenizeLine("7777777")
|
||||
expect(tokens.length).toBe 1
|
||||
expect(tokens[0]).toEqual value: '7777777', scopes: ['source.coffee', 'constant.numeric.coffee']
|
||||
|
||||
describe "when the line is an interpolated string", ->
|
||||
it "returns the correct tokens", ->
|
||||
{tokens} = grammar.tokenizeLine('"the value is #{@x} my friend"')
|
||||
|
||||
expect(tokens[0]).toEqual value: '"', scopes: ["source.coffee","string.quoted.double.coffee","punctuation.definition.string.begin.coffee"]
|
||||
expect(tokens[1]).toEqual value: "the value is ", scopes: ["source.coffee","string.quoted.double.coffee"]
|
||||
expect(tokens[2]).toEqual value: '#{', scopes: ["source.coffee","string.quoted.double.coffee","source.coffee.embedded.source","punctuation.section.embedded.coffee"]
|
||||
expect(tokens[3]).toEqual value: "@x", scopes: ["source.coffee","string.quoted.double.coffee","source.coffee.embedded.source","variable.other.readwrite.instance.coffee"]
|
||||
expect(tokens[4]).toEqual value: "}", scopes: ["source.coffee","string.quoted.double.coffee","source.coffee.embedded.source","punctuation.section.embedded.coffee"]
|
||||
expect(tokens[5]).toEqual value: " my friend", scopes: ["source.coffee","string.quoted.double.coffee"]
|
||||
expect(tokens[6]).toEqual value: '"', scopes: ["source.coffee","string.quoted.double.coffee","punctuation.definition.string.end.coffee"]
|
||||
|
||||
describe "when the line has an interpolated string inside an interpolated string", ->
|
||||
it "returns the correct tokens", ->
|
||||
{tokens} = grammar.tokenizeLine('"#{"#{@x}"}"')
|
||||
|
||||
expect(tokens[0]).toEqual value: '"', scopes: ["source.coffee","string.quoted.double.coffee","punctuation.definition.string.begin.coffee"]
|
||||
expect(tokens[1]).toEqual value: '#{', scopes: ["source.coffee","string.quoted.double.coffee","source.coffee.embedded.source","punctuation.section.embedded.coffee"]
|
||||
expect(tokens[2]).toEqual value: '"', scopes: ["source.coffee","string.quoted.double.coffee","source.coffee.embedded.source","string.quoted.double.coffee","punctuation.definition.string.begin.coffee"]
|
||||
expect(tokens[3]).toEqual value: '#{', scopes: ["source.coffee","string.quoted.double.coffee","source.coffee.embedded.source","string.quoted.double.coffee","source.coffee.embedded.source","punctuation.section.embedded.coffee"]
|
||||
expect(tokens[4]).toEqual value: '@x', scopes: ["source.coffee","string.quoted.double.coffee","source.coffee.embedded.source","string.quoted.double.coffee","source.coffee.embedded.source","variable.other.readwrite.instance.coffee"]
|
||||
expect(tokens[5]).toEqual value: '}', scopes: ["source.coffee","string.quoted.double.coffee","source.coffee.embedded.source","string.quoted.double.coffee","source.coffee.embedded.source","punctuation.section.embedded.coffee"]
|
||||
expect(tokens[6]).toEqual value: '"', scopes: ["source.coffee","string.quoted.double.coffee","source.coffee.embedded.source","string.quoted.double.coffee","punctuation.definition.string.end.coffee"]
|
||||
expect(tokens[7]).toEqual value: '}', scopes: ["source.coffee","string.quoted.double.coffee","source.coffee.embedded.source","punctuation.section.embedded.coffee"]
|
||||
expect(tokens[8]).toEqual value: '"', scopes: ["source.coffee","string.quoted.double.coffee","punctuation.definition.string.end.coffee"]
|
||||
|
||||
describe "when the line is empty", ->
|
||||
it "returns a single token which has the global scope", ->
|
||||
{tokens} = grammar.tokenizeLine('')
|
||||
expect(tokens[0]).toEqual value: '', scopes: ["source.coffee"]
|
||||
|
||||
describe "when the line matches no patterns", ->
|
||||
it "does not infinitely loop", ->
|
||||
grammar = atom.syntax.selectGrammar("sample.txt")
|
||||
{tokens} = grammar.tokenizeLine('hoo')
|
||||
expect(tokens.length).toBe 1
|
||||
expect(tokens[0]).toEqual value: 'hoo', scopes: ["text.plain", "meta.paragraph.text"]
|
||||
|
||||
describe "when the line matches a pattern with a 'contentName'", ->
|
||||
it "creates tokens using the content of contentName as the token name", ->
|
||||
grammar = atom.syntax.selectGrammar("sample.txt")
|
||||
{tokens} = grammar.tokenizeLine('ok, cool')
|
||||
expect(tokens[0]).toEqual value: 'ok, cool', scopes: ["text.plain", "meta.paragraph.text"]
|
||||
|
||||
describe "when the line matches a pattern with no `name` or `contentName`", ->
|
||||
it "creates tokens without adding a new scope", ->
|
||||
grammar = atom.syntax.selectGrammar('foo.rb')
|
||||
{tokens} = grammar.tokenizeLine('%w|oh \\look|')
|
||||
expect(tokens.length).toBe 5
|
||||
expect(tokens[0]).toEqual value: '%w|', scopes: ["source.ruby", "string.quoted.other.literal.lower.ruby", "punctuation.definition.string.begin.ruby"]
|
||||
expect(tokens[1]).toEqual value: 'oh ', scopes: ["source.ruby", "string.quoted.other.literal.lower.ruby"]
|
||||
expect(tokens[2]).toEqual value: '\\l', scopes: ["source.ruby", "string.quoted.other.literal.lower.ruby"]
|
||||
expect(tokens[3]).toEqual value: 'ook', scopes: ["source.ruby", "string.quoted.other.literal.lower.ruby"]
|
||||
|
||||
describe "when the line matches a begin/end pattern", ->
|
||||
it "returns tokens based on the beginCaptures, endCaptures and the child scope", ->
|
||||
{tokens} = grammar.tokenizeLine("'''single-quoted heredoc'''")
|
||||
|
||||
expect(tokens.length).toBe 3
|
||||
|
||||
expect(tokens[0]).toEqual value: "'''", scopes: ['source.coffee', 'string.quoted.heredoc.coffee', 'punctuation.definition.string.begin.coffee']
|
||||
expect(tokens[1]).toEqual value: "single-quoted heredoc", scopes: ['source.coffee', 'string.quoted.heredoc.coffee']
|
||||
expect(tokens[2]).toEqual value: "'''", scopes: ['source.coffee', 'string.quoted.heredoc.coffee', 'punctuation.definition.string.end.coffee']
|
||||
|
||||
describe "when the pattern spans multiple lines", ->
|
||||
it "uses the ruleStack returned by the first line to parse the second line", ->
|
||||
{tokens: firstTokens, ruleStack} = grammar.tokenizeLine("'''single-quoted")
|
||||
{tokens: secondTokens, ruleStack} = grammar.tokenizeLine("heredoc'''", ruleStack)
|
||||
|
||||
expect(firstTokens.length).toBe 2
|
||||
expect(secondTokens.length).toBe 2
|
||||
|
||||
expect(firstTokens[0]).toEqual value: "'''", scopes: ['source.coffee', 'string.quoted.heredoc.coffee', 'punctuation.definition.string.begin.coffee']
|
||||
expect(firstTokens[1]).toEqual value: "single-quoted", scopes: ['source.coffee', 'string.quoted.heredoc.coffee']
|
||||
|
||||
expect(secondTokens[0]).toEqual value: "heredoc", scopes: ['source.coffee', 'string.quoted.heredoc.coffee']
|
||||
expect(secondTokens[1]).toEqual value: "'''", scopes: ['source.coffee', 'string.quoted.heredoc.coffee', 'punctuation.definition.string.end.coffee']
|
||||
|
||||
describe "when the pattern contains sub-patterns", ->
|
||||
it "returns tokens within the begin/end scope based on the sub-patterns", ->
|
||||
{tokens} = grammar.tokenizeLine('"""heredoc with character escape \\t"""')
|
||||
|
||||
expect(tokens.length).toBe 4
|
||||
|
||||
expect(tokens[0]).toEqual value: '"""', scopes: ['source.coffee', 'string.quoted.double.heredoc.coffee', 'punctuation.definition.string.begin.coffee']
|
||||
expect(tokens[1]).toEqual value: "heredoc with character escape ", scopes: ['source.coffee', 'string.quoted.double.heredoc.coffee']
|
||||
expect(tokens[2]).toEqual value: "\\t", scopes: ['source.coffee', 'string.quoted.double.heredoc.coffee', 'constant.character.escape.coffee']
|
||||
expect(tokens[3]).toEqual value: '"""', scopes: ['source.coffee', 'string.quoted.double.heredoc.coffee', 'punctuation.definition.string.end.coffee']
|
||||
|
||||
describe "when the end pattern contains a back reference", ->
|
||||
it "constructs the end rule based on its back-references to captures in the begin rule", ->
|
||||
grammar = atom.syntax.selectGrammar('foo.rb')
|
||||
{tokens} = grammar.tokenizeLine('%w|oh|,')
|
||||
expect(tokens.length).toBe 4
|
||||
expect(tokens[0]).toEqual value: '%w|', scopes: ["source.ruby", "string.quoted.other.literal.lower.ruby", "punctuation.definition.string.begin.ruby"]
|
||||
expect(tokens[1]).toEqual value: 'oh', scopes: ["source.ruby", "string.quoted.other.literal.lower.ruby"]
|
||||
expect(tokens[2]).toEqual value: '|', scopes: ["source.ruby", "string.quoted.other.literal.lower.ruby", "punctuation.definition.string.end.ruby"]
|
||||
expect(tokens[3]).toEqual value: ',', scopes: ["source.ruby", "punctuation.separator.object.ruby"]
|
||||
|
||||
it "allows the rule containing that end pattern to be pushed to the stack multiple times", ->
|
||||
grammar = atom.syntax.selectGrammar('foo.rb')
|
||||
{tokens} = grammar.tokenizeLine('%Q+matz had some #{%Q-crazy ideas-} for ruby syntax+ # damn.')
|
||||
expect(tokens[0]).toEqual value: '%Q+', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","punctuation.definition.string.begin.ruby"]
|
||||
expect(tokens[1]).toEqual value: 'matz had some ', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby"]
|
||||
expect(tokens[2]).toEqual value: '#{', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","meta.embedded.line.ruby","punctuation.section.embedded.begin.ruby"]
|
||||
expect(tokens[3]).toEqual value: '%Q-', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","meta.embedded.line.ruby","string.quoted.other.literal.upper.ruby","punctuation.definition.string.begin.ruby"]
|
||||
expect(tokens[4]).toEqual value: 'crazy ideas', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","meta.embedded.line.ruby","string.quoted.other.literal.upper.ruby"]
|
||||
expect(tokens[5]).toEqual value: '-', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","meta.embedded.line.ruby","string.quoted.other.literal.upper.ruby","punctuation.definition.string.end.ruby"]
|
||||
expect(tokens[6]).toEqual value: '}', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","meta.embedded.line.ruby","punctuation.section.embedded.end.ruby", "source.ruby"]
|
||||
expect(tokens[7]).toEqual value: ' for ruby syntax', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby"]
|
||||
expect(tokens[8]).toEqual value: '+', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","punctuation.definition.string.end.ruby"]
|
||||
expect(tokens[9]).toEqual value: ' ', scopes: ["source.ruby"]
|
||||
expect(tokens[10]).toEqual value: '#', scopes: ["source.ruby","comment.line.number-sign.ruby","punctuation.definition.comment.ruby"]
|
||||
expect(tokens[11]).toEqual value: ' damn.', scopes: ["source.ruby","comment.line.number-sign.ruby"]
|
||||
|
||||
describe "when the pattern includes rules from another grammar", ->
|
||||
describe "when a grammar matching the desired scope is available", ->
|
||||
it "parses tokens inside the begin/end patterns based on the included grammar's rules", ->
|
||||
atom.packages.activatePackage('language-html', sync: true)
|
||||
atom.packages.activatePackage('language-ruby-on-rails', sync: true)
|
||||
|
||||
grammar = atom.syntax.grammarForScopeName('text.html.ruby')
|
||||
{tokens} = grammar.tokenizeLine("<div class='name'><%= User.find(2).full_name %></div>")
|
||||
|
||||
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"]
|
||||
expect(tokens[1]).toEqual value: 'div', scopes: ["text.html.ruby","meta.tag.block.any.html","entity.name.tag.block.any.html"]
|
||||
expect(tokens[2]).toEqual value: ' ', scopes: ["text.html.ruby","meta.tag.block.any.html"]
|
||||
expect(tokens[3]).toEqual value: 'class', scopes: ["text.html.ruby","meta.tag.block.any.html", "entity.other.attribute-name.html"]
|
||||
expect(tokens[4]).toEqual value: '=', scopes: ["text.html.ruby","meta.tag.block.any.html"]
|
||||
expect(tokens[5]).toEqual value: '\'', scopes: ["text.html.ruby","meta.tag.block.any.html","string.quoted.single.html","punctuation.definition.string.begin.html"]
|
||||
expect(tokens[6]).toEqual value: 'name', scopes: ["text.html.ruby","meta.tag.block.any.html","string.quoted.single.html"]
|
||||
expect(tokens[7]).toEqual value: '\'', scopes: ["text.html.ruby","meta.tag.block.any.html","string.quoted.single.html","punctuation.definition.string.end.html"]
|
||||
expect(tokens[8]).toEqual value: '>', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.end.html"]
|
||||
expect(tokens[9]).toEqual value: '<%=', scopes: ["text.html.ruby","source.ruby.rails.embedded.html","punctuation.section.embedded.ruby"]
|
||||
expect(tokens[10]).toEqual value: ' ', scopes: ["text.html.ruby","source.ruby.rails.embedded.html"]
|
||||
expect(tokens[11]).toEqual value: 'User', scopes: ["text.html.ruby","source.ruby.rails.embedded.html","support.class.ruby"]
|
||||
expect(tokens[12]).toEqual value: '.', scopes: ["text.html.ruby","source.ruby.rails.embedded.html","punctuation.separator.method.ruby"]
|
||||
expect(tokens[13]).toEqual value: 'find', scopes: ["text.html.ruby","source.ruby.rails.embedded.html"]
|
||||
expect(tokens[14]).toEqual value: '(', scopes: ["text.html.ruby","source.ruby.rails.embedded.html","punctuation.section.function.ruby"]
|
||||
expect(tokens[15]).toEqual value: '2', scopes: ["text.html.ruby","source.ruby.rails.embedded.html","constant.numeric.ruby"]
|
||||
expect(tokens[16]).toEqual value: ')', scopes: ["text.html.ruby","source.ruby.rails.embedded.html","punctuation.section.function.ruby"]
|
||||
expect(tokens[17]).toEqual value: '.', scopes: ["text.html.ruby","source.ruby.rails.embedded.html","punctuation.separator.method.ruby"]
|
||||
expect(tokens[18]).toEqual value: 'full_name ', scopes: ["text.html.ruby","source.ruby.rails.embedded.html"]
|
||||
expect(tokens[19]).toEqual value: '%>', scopes: ["text.html.ruby","source.ruby.rails.embedded.html","punctuation.section.embedded.ruby"]
|
||||
expect(tokens[20]).toEqual value: '</', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"]
|
||||
expect(tokens[21]).toEqual value: 'div', scopes: ["text.html.ruby","meta.tag.block.any.html","entity.name.tag.block.any.html"]
|
||||
expect(tokens[22]).toEqual value: '>', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.end.html"]
|
||||
|
||||
it "updates the grammar if the included grammar is updated later", ->
|
||||
atom.packages.activatePackage('language-html', sync: true)
|
||||
atom.packages.activatePackage('language-ruby-on-rails', sync: true)
|
||||
|
||||
grammar = atom.syntax.selectGrammar('foo.html.erb')
|
||||
grammarUpdatedHandler = jasmine.createSpy("grammarUpdatedHandler")
|
||||
grammar.on 'grammar-updated', grammarUpdatedHandler
|
||||
|
||||
{tokens} = grammar.tokenizeLine("<div class='name'><% <<-SQL select * from users;")
|
||||
expect(tokens[12].value).toBe " select * from users;"
|
||||
|
||||
atom.packages.activatePackage('language-sql', sync: true)
|
||||
expect(grammarUpdatedHandler).toHaveBeenCalled()
|
||||
{tokens} = grammar.tokenizeLine("<div class='name'><% <<-SQL select * from users;")
|
||||
expect(tokens[12].value).toBe " "
|
||||
expect(tokens[13].value).toBe "select"
|
||||
|
||||
describe "when a grammar matching the desired scope is unavailable", ->
|
||||
it "updates the grammar if a matching grammar is added later", ->
|
||||
atom.packages.deactivatePackage('language-html')
|
||||
atom.packages.activatePackage('language-ruby-on-rails', sync: true)
|
||||
|
||||
grammar = atom.syntax.grammarForScopeName('text.html.ruby')
|
||||
{tokens} = grammar.tokenizeLine("<div class='name'><%= User.find(2).full_name %></div>")
|
||||
expect(tokens[0]).toEqual value: "<div class='name'>", scopes: ["text.html.ruby"]
|
||||
expect(tokens[1]).toEqual value: '<%=', scopes: ["text.html.ruby","source.ruby.rails.embedded.html","punctuation.section.embedded.ruby"]
|
||||
expect(tokens[2]).toEqual value: ' ', scopes: ["text.html.ruby","source.ruby.rails.embedded.html"]
|
||||
expect(tokens[3]).toEqual value: 'User', scopes: ["text.html.ruby","source.ruby.rails.embedded.html","support.class.ruby"]
|
||||
|
||||
atom.packages.activatePackage('language-html', sync: true)
|
||||
{tokens} = grammar.tokenizeLine("<div class='name'><%= User.find(2).full_name %></div>")
|
||||
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"]
|
||||
expect(tokens[1]).toEqual value: 'div', scopes: ["text.html.ruby","meta.tag.block.any.html","entity.name.tag.block.any.html"]
|
||||
expect(tokens[2]).toEqual value: ' ', scopes: ["text.html.ruby","meta.tag.block.any.html"]
|
||||
expect(tokens[3]).toEqual value: 'class', scopes: ["text.html.ruby","meta.tag.block.any.html", "entity.other.attribute-name.html"]
|
||||
expect(tokens[4]).toEqual value: '=', scopes: ["text.html.ruby","meta.tag.block.any.html"]
|
||||
expect(tokens[5]).toEqual value: '\'', scopes: ["text.html.ruby","meta.tag.block.any.html","string.quoted.single.html","punctuation.definition.string.begin.html"]
|
||||
expect(tokens[6]).toEqual value: 'name', scopes: ["text.html.ruby","meta.tag.block.any.html","string.quoted.single.html"]
|
||||
expect(tokens[7]).toEqual value: '\'', scopes: ["text.html.ruby","meta.tag.block.any.html","string.quoted.single.html","punctuation.definition.string.end.html"]
|
||||
expect(tokens[8]).toEqual value: '>', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.end.html"]
|
||||
expect(tokens[9]).toEqual value: '<%=', scopes: ["text.html.ruby","source.ruby.rails.embedded.html","punctuation.section.embedded.ruby"]
|
||||
expect(tokens[10]).toEqual value: ' ', scopes: ["text.html.ruby","source.ruby.rails.embedded.html"]
|
||||
|
||||
it "can parse a grammar with newline characters in its regular expressions (regression)", ->
|
||||
grammar = new TextMateGrammar
|
||||
name: "test"
|
||||
scopeName: "source.imaginaryLanguage"
|
||||
repository: {}
|
||||
patterns: [
|
||||
{
|
||||
name: "comment-body"
|
||||
begin: "//"
|
||||
end: "\\n"
|
||||
beginCaptures:
|
||||
"0": { name: "comment-start" }
|
||||
}
|
||||
]
|
||||
|
||||
{tokens, ruleStack} = grammar.tokenizeLine("// a singleLineComment")
|
||||
expect(ruleStack.length).toBe 1
|
||||
expect(ruleStack[0].scopeName).toBe "source.imaginaryLanguage"
|
||||
|
||||
expect(tokens.length).toBe 2
|
||||
expect(tokens[0].value).toBe "//"
|
||||
expect(tokens[1].value).toBe " a singleLineComment"
|
||||
|
||||
it "does not loop infinitely (regression)", ->
|
||||
grammar = atom.syntax.selectGrammar("hello.js")
|
||||
{tokens, ruleStack} = grammar.tokenizeLine("// line comment")
|
||||
{tokens, ruleStack} = grammar.tokenizeLine(" // second line comment with a single leading space", ruleStack)
|
||||
|
||||
describe "when inside a C block", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-c', sync: true)
|
||||
|
||||
it "correctly parses a method. (regression)", ->
|
||||
grammar = atom.syntax.selectGrammar("hello.c")
|
||||
{tokens, ruleStack} = grammar.tokenizeLine("if(1){m()}")
|
||||
expect(tokens[5]).toEqual value: "m", scopes: ["source.c", "meta.block.c", "meta.function-call.c", "support.function.any-method.c"]
|
||||
|
||||
it "correctly parses nested blocks. (regression)", ->
|
||||
grammar = atom.syntax.selectGrammar("hello.c")
|
||||
{tokens, ruleStack} = grammar.tokenizeLine("if(1){if(1){m()}}")
|
||||
expect(tokens[5]).toEqual value: "if", scopes: ["source.c", "meta.block.c", "keyword.control.c"]
|
||||
expect(tokens[10]).toEqual value: "m", scopes: ["source.c", "meta.block.c", "meta.block.c", "meta.function-call.c", "support.function.any-method.c"]
|
||||
|
||||
describe "when the grammar can infinitely loop over a line", ->
|
||||
it "aborts tokenization", ->
|
||||
spyOn(console, 'error')
|
||||
atom.packages.activatePackage("package-with-infinite-loop-grammar")
|
||||
grammar = atom.syntax.selectGrammar("something.package-with-infinite-loop-grammar")
|
||||
{tokens} = grammar.tokenizeLine("abc")
|
||||
expect(tokens[0].value).toBe "a"
|
||||
expect(tokens[1].value).toBe "bc"
|
||||
expect(console.error).toHaveBeenCalled()
|
||||
|
||||
describe "when a grammar has a pattern that has back references in the match value", ->
|
||||
it "does not special handle the back references and instead allows oniguruma to resolve them", ->
|
||||
atom.packages.activatePackage('language-sass', sync: true)
|
||||
grammar = atom.syntax.selectGrammar("style.scss")
|
||||
{tokens} = grammar.tokenizeLine("@mixin x() { -moz-selector: whatever; }")
|
||||
expect(tokens[9]).toEqual value: "-moz-selector", scopes: ["source.css.scss", "meta.property-list.scss", "meta.property-name.scss"]
|
||||
|
||||
describe "when a line has more tokens than `maxTokensPerLine`", ->
|
||||
it "creates a final token with the remaining text and resets the ruleStack to match the begining of the line", ->
|
||||
grammar = atom.syntax.selectGrammar("hello.js")
|
||||
spyOn(grammar, 'getMaxTokensPerLine').andCallFake -> 5
|
||||
originalRuleStack = [grammar.initialRule, grammar.initialRule, grammar.initialRule]
|
||||
{tokens, ruleStack} = grammar.tokenizeLine("one(two(three(four(five(_param_)))))", originalRuleStack)
|
||||
expect(tokens.length).toBe 5
|
||||
expect(tokens[4].value).toBe "three(four(five(_param_)))))"
|
||||
expect(ruleStack).toEqual originalRuleStack
|
||||
|
||||
describe "when a grammar has a capture with patterns", ->
|
||||
it "matches the patterns and includes the scope specified as the pattern's match name", ->
|
||||
grammar = atom.syntax.selectGrammar("hello.php")
|
||||
{tokens} = grammar.tokenizeLine("<?php public final function meth() {} ?>")
|
||||
|
||||
expect(tokens[2].value).toBe "public"
|
||||
expect(tokens[2].scopes).toEqual ["text.html.php", "meta.embedded.line.php", "source.php", "meta.function.php", "storage.modifier.php"]
|
||||
|
||||
expect(tokens[3].value).toBe " "
|
||||
expect(tokens[3].scopes).toEqual ["text.html.php", "meta.embedded.line.php", "source.php", "meta.function.php"]
|
||||
|
||||
expect(tokens[4].value).toBe "final"
|
||||
expect(tokens[4].scopes).toEqual ["text.html.php", "meta.embedded.line.php", "source.php", "meta.function.php", "storage.modifier.php"]
|
||||
|
||||
expect(tokens[5].value).toBe " "
|
||||
expect(tokens[5].scopes).toEqual ["text.html.php", "meta.embedded.line.php", "source.php", "meta.function.php"]
|
||||
|
||||
expect(tokens[6].value).toBe "function"
|
||||
expect(tokens[6].scopes).toEqual ["text.html.php", "meta.embedded.line.php", "source.php", "meta.function.php", "storage.type.function.php"]
|
||||
|
||||
it "ignores child captures of a capture with patterns", ->
|
||||
grammar = new TextMateGrammar
|
||||
name: "test"
|
||||
scopeName: "source"
|
||||
repository: {}
|
||||
patterns: [
|
||||
{
|
||||
name: "text"
|
||||
match: "(a(b))"
|
||||
captures:
|
||||
"1":
|
||||
patterns: [
|
||||
{
|
||||
match: "ab"
|
||||
name: "a"
|
||||
}
|
||||
]
|
||||
"2":
|
||||
name: "b"
|
||||
}
|
||||
]
|
||||
{tokens} = grammar.tokenizeLine("ab")
|
||||
|
||||
expect(tokens[0].value).toBe "ab"
|
||||
expect(tokens[0].scopes).toEqual ["source", "text", "a"]
|
||||
|
||||
describe "when the grammar has injections", ->
|
||||
it "correctly includes the injected patterns when tokenizing", ->
|
||||
grammar = atom.syntax.selectGrammar("hello.php")
|
||||
{tokens} = grammar.tokenizeLine("<div><?php function hello() {} ?></div>")
|
||||
|
||||
expect(tokens[3].value).toBe "<?php"
|
||||
expect(tokens[3].scopes).toEqual ["text.html.php", "meta.embedded.line.php", "punctuation.section.embedded.begin.php"]
|
||||
|
||||
expect(tokens[5].value).toBe "function"
|
||||
expect(tokens[5].scopes).toEqual ["text.html.php", "meta.embedded.line.php", "source.php", "meta.function.php", "storage.type.function.php"]
|
||||
|
||||
expect(tokens[7].value).toBe "hello"
|
||||
expect(tokens[7].scopes).toEqual ["text.html.php", "meta.embedded.line.php", "source.php", "meta.function.php", "entity.name.function.php"]
|
||||
|
||||
expect(tokens[14].value).toBe "?"
|
||||
expect(tokens[14].scopes).toEqual ["text.html.php", "meta.embedded.line.php", "source.php", "punctuation.section.embedded.end.php", "source.php"]
|
||||
|
||||
expect(tokens[15].value).toBe ">"
|
||||
expect(tokens[15].scopes).toEqual ["text.html.php", "meta.embedded.line.php", "punctuation.section.embedded.end.php"]
|
||||
|
||||
expect(tokens[16].value).toBe "</"
|
||||
expect(tokens[16].scopes).toEqual ["text.html.php", "meta.tag.block.any.html", "punctuation.definition.tag.begin.html"]
|
||||
|
||||
expect(tokens[17].value).toBe "div"
|
||||
expect(tokens[17].scopes).toEqual ["text.html.php", "meta.tag.block.any.html", "entity.name.tag.block.any.html"]
|
||||
|
||||
describe "when the grammar's pattern name has a group number in it", ->
|
||||
it "replaces the group number with the matched captured text", ->
|
||||
atom.packages.activatePackage('language-hyperlink', sync: true)
|
||||
grammar = atom.syntax.grammarForScopeName("text.hyperlink")
|
||||
{tokens} = grammar.tokenizeLine("https://github.com")
|
||||
expect(tokens[0].scopes).toEqual ["text.hyperlink", "markup.underline.link.https.hyperlink"]
|
||||
|
||||
describe "when the grammar has an injection selector", ->
|
||||
it "includes the grammar's patterns when the selector matches the current scope in other grammars", ->
|
||||
atom.packages.activatePackage('language-hyperlink', sync: true)
|
||||
grammar = atom.syntax.selectGrammar("text.js")
|
||||
{tokens} = grammar.tokenizeLine("var i; // http://github.com")
|
||||
|
||||
expect(tokens[0].value).toBe "var"
|
||||
expect(tokens[0].scopes).toEqual ["source.js", "storage.modifier.js"]
|
||||
|
||||
expect(tokens[6].value).toBe "http://github.com"
|
||||
expect(tokens[6].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
|
||||
describe "when the grammar is added", ->
|
||||
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor.setText("// http://github.com")
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " http://github.com"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
atom.packages.activatePackage('language-hyperlink', sync: true)
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "http://github.com"
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
|
||||
describe "when the grammar is updated", ->
|
||||
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor.setText("// SELECT * FROM OCTOCATS")
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
atom.syntax.addGrammar(new TextMateGrammar(
|
||||
name: "test"
|
||||
scopeName: "source.test"
|
||||
repository: {}
|
||||
injectionSelector: "comment"
|
||||
patterns: [ { include: "source.sql" } ]
|
||||
))
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
atom.packages.activatePackage('language-sql', sync: true)
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "SELECT"
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "keyword.other.DML.sql"]
|
||||
|
||||
describe "when the position doesn't advance and rule includes $self and matches itself", ->
|
||||
it "tokenizes the entire line using the rule", ->
|
||||
grammar = new TextMateGrammar
|
||||
name: "test"
|
||||
scopeName: "source"
|
||||
repository: {}
|
||||
patterns: [
|
||||
{
|
||||
name: "text"
|
||||
begin: "(?=forever)"
|
||||
end: "whatevs"
|
||||
patterns: [
|
||||
include: "$self"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
{tokens} = grammar.tokenizeLine("forever and ever")
|
||||
|
||||
expect(tokens.length).toBe 1
|
||||
expect(tokens[0].value).toBe "forever and ever"
|
||||
expect(tokens[0].scopes).toEqual ["source", "text"]
|
||||
|
||||
describe "${capture:/command} style pattern names", ->
|
||||
lines = null
|
||||
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-todo', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('main.rb')
|
||||
lines = grammar.tokenizeLines "# TODO be nicer"
|
||||
|
||||
it "replaces the number with the capture group and translates the text", ->
|
||||
tokens = lines[0]
|
||||
expect(tokens[2].value).toEqual "TODO"
|
||||
expect(tokens[2].scopes).toEqual ["source.ruby", "comment.line.number-sign.ruby", "storage.type.class.todo"]
|
||||
|
||||
describe "language-specific integration tests", ->
|
||||
lines = null
|
||||
|
||||
describe "Git commit messages", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-git', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('COMMIT_EDITMSG')
|
||||
lines = grammar.tokenizeLines """
|
||||
longggggggggggggggggggggggggggggggggggggggggggggggg
|
||||
# Please enter the commit message for your changes. Lines starting
|
||||
"""
|
||||
|
||||
it "correctly parses a long line", ->
|
||||
tokens = lines[0]
|
||||
expect(tokens[0].value).toBe "longggggggggggggggggggggggggggggggggggggggggggggggg"
|
||||
expect(tokens[0].scopes).toEqual ["text.git-commit", "meta.scope.message.git-commit", "invalid.deprecated.line-too-long.git-commit"]
|
||||
|
||||
it "correctly parses the number sign of the first comment line", ->
|
||||
tokens = lines[1]
|
||||
expect(tokens[0].value).toBe "#"
|
||||
expect(tokens[0].scopes).toEqual ["text.git-commit", "meta.scope.metadata.git-commit", "comment.line.number-sign.git-commit", "punctuation.definition.comment.git-commit"]
|
||||
|
||||
describe "C++", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-c', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('includes.cc')
|
||||
lines = grammar.tokenizeLines """
|
||||
#include "a.h"
|
||||
#include "b.h"
|
||||
"""
|
||||
|
||||
it "correctly parses the first include line", ->
|
||||
tokens = lines[0]
|
||||
expect(tokens[0].value).toBe "#"
|
||||
expect(tokens[0].scopes).toEqual ["source.c++", "meta.preprocessor.c.include"]
|
||||
expect(tokens[1].value).toBe 'include'
|
||||
expect(tokens[1].scopes).toEqual ["source.c++", "meta.preprocessor.c.include", "keyword.control.import.include.c"]
|
||||
|
||||
it "correctly parses the second include line", ->
|
||||
tokens = lines[1]
|
||||
expect(tokens[0].value).toBe "#"
|
||||
expect(tokens[0].scopes).toEqual ["source.c++", "meta.preprocessor.c.include"]
|
||||
expect(tokens[1].value).toBe 'include'
|
||||
expect(tokens[1].scopes).toEqual ["source.c++", "meta.preprocessor.c.include", "keyword.control.import.include.c"]
|
||||
|
||||
describe "Ruby", ->
|
||||
beforeEach ->
|
||||
grammar = atom.syntax.selectGrammar('hello.rb')
|
||||
lines = grammar.tokenizeLines """
|
||||
a = {
|
||||
"b" => "c",
|
||||
}
|
||||
"""
|
||||
|
||||
it "doesn't loop infinitely (regression)", ->
|
||||
expect(_.pluck(lines[0], 'value').join('')).toBe 'a = {'
|
||||
expect(_.pluck(lines[1], 'value').join('')).toBe ' "b" => "c",'
|
||||
expect(_.pluck(lines[2], 'value').join('')).toBe '}'
|
||||
expect(_.pluck(lines[3], 'value').join('')).toBe ''
|
||||
|
||||
describe "Objective-C", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-c', sync: true)
|
||||
atom.packages.activatePackage('language-objective-c', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('function.mm')
|
||||
lines = grammar.tokenizeLines """
|
||||
void test() {
|
||||
NSString *a = @"a\\nb";
|
||||
}
|
||||
"""
|
||||
|
||||
it "correctly parses variable type when it is a built-in Cocoa class", ->
|
||||
tokens = lines[1]
|
||||
expect(tokens[0].value).toBe "NSString"
|
||||
expect(tokens[0].scopes).toEqual ["source.objc++", "meta.function.c", "meta.block.c", "support.class.cocoa"]
|
||||
|
||||
it "correctly parses the semicolon at the end of the line", ->
|
||||
tokens = lines[1]
|
||||
lastToken = _.last(tokens)
|
||||
expect(lastToken.value).toBe ";"
|
||||
expect(lastToken.scopes).toEqual ["source.objc++", "meta.function.c", "meta.block.c"]
|
||||
|
||||
it "correctly parses the string characters before the escaped character", ->
|
||||
tokens = lines[1]
|
||||
expect(tokens[2].value).toBe '@"'
|
||||
expect(tokens[2].scopes).toEqual ["source.objc++", "meta.function.c", "meta.block.c", "string.quoted.double.objc", "punctuation.definition.string.begin.objc"]
|
||||
|
||||
describe "Java", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-java', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('Function.java')
|
||||
|
||||
it "correctly parses single line comments", ->
|
||||
lines = grammar.tokenizeLines """
|
||||
public void test() {
|
||||
//comment
|
||||
}
|
||||
"""
|
||||
|
||||
tokens = lines[1]
|
||||
expect(tokens[0].scopes).toEqual ["source.java", "comment.line.double-slash.java", "punctuation.definition.comment.java"]
|
||||
expect(tokens[0].value).toEqual '//'
|
||||
expect(tokens[1].scopes).toEqual ["source.java", "comment.line.double-slash.java"]
|
||||
expect(tokens[1].value).toEqual 'comment'
|
||||
|
||||
it "correctly parses nested method calls", ->
|
||||
tokens = grammar.tokenizeLines('a(b(new Object[0]));')[0]
|
||||
lastToken = _.last(tokens)
|
||||
expect(lastToken.scopes).toEqual ['source.java', 'punctuation.terminator.java']
|
||||
expect(lastToken.value).toEqual ';'
|
||||
|
||||
describe "HTML (Ruby - ERB)", ->
|
||||
it "correctly parses strings inside tags", ->
|
||||
grammar = atom.syntax.selectGrammar('page.erb')
|
||||
lines = grammar.tokenizeLines '<% page_title "My Page" %>'
|
||||
tokens = lines[0]
|
||||
|
||||
expect(tokens[2].value).toEqual '"'
|
||||
expect(tokens[2].scopes).toEqual ["text.html.erb", "meta.embedded.line.erb", "string.quoted.double.ruby", "punctuation.definition.string.begin.ruby"]
|
||||
expect(tokens[3].value).toEqual 'My Page'
|
||||
expect(tokens[3].scopes).toEqual ["text.html.erb", "meta.embedded.line.erb", "string.quoted.double.ruby"]
|
||||
expect(tokens[4].value).toEqual '"'
|
||||
expect(tokens[4].scopes).toEqual ["text.html.erb", "meta.embedded.line.erb", "string.quoted.double.ruby", "punctuation.definition.string.end.ruby"]
|
||||
|
||||
it "does not loop infinitely on <%>", ->
|
||||
atom.packages.activatePackage('language-html', sync: true)
|
||||
atom.packages.activatePackage('language-ruby-on-rails', sync: true)
|
||||
|
||||
grammar = atom.syntax.selectGrammar('foo.html.erb')
|
||||
[tokens] = grammar.tokenizeLines '<%>'
|
||||
expect(tokens.length).toBe 1
|
||||
expect(tokens[0].value).toEqual '<%>'
|
||||
expect(tokens[0].scopes).toEqual ["text.html.erb"]
|
||||
|
||||
describe "Unicode support", ->
|
||||
describe "Surrogate pair characters", ->
|
||||
beforeEach ->
|
||||
grammar = atom.syntax.selectGrammar('main.js')
|
||||
lines = grammar.tokenizeLines "'\uD835\uDF97'"
|
||||
|
||||
it "correctly parses JavaScript strings containing surrogate pair characters", ->
|
||||
tokens = lines[0]
|
||||
expect(tokens.length).toBe 3
|
||||
expect(tokens[0].value).toBe "'"
|
||||
expect(tokens[1].value).toBe "\uD835\uDF97"
|
||||
expect(tokens[2].value).toBe "'"
|
||||
|
||||
describe "when the line contains unicode characters", ->
|
||||
it "correctly parses tokens starting after them", ->
|
||||
atom.packages.activatePackage('language-json', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('package.json')
|
||||
{tokens} = grammar.tokenizeLine '{"\u2026": 1}'
|
||||
|
||||
expect(tokens.length).toBe 8
|
||||
expect(tokens[6].value).toBe '1'
|
||||
expect(tokens[6].scopes).toEqual ["source.json", "meta.structure.dictionary.json", "meta.structure.dictionary.value.json", "constant.numeric.json"]
|
||||
|
||||
describe "python", ->
|
||||
it "parses import blocks correctly", ->
|
||||
grammar = atom.syntax.selectGrammar("file.py")
|
||||
lines = grammar.tokenizeLines "import a\nimport b"
|
||||
|
||||
line1 = lines[0]
|
||||
expect(line1.length).toBe 3
|
||||
expect(line1[0].value).toEqual "import"
|
||||
expect(line1[0].scopes).toEqual ["source.python", "keyword.control.import.python"]
|
||||
expect(line1[1].value).toEqual " "
|
||||
expect(line1[1].scopes).toEqual ["source.python"]
|
||||
expect(line1[2].value).toEqual "a"
|
||||
expect(line1[2].scopes).toEqual ["source.python"]
|
||||
|
||||
line2 = lines[1]
|
||||
expect(line2.length).toBe 3
|
||||
expect(line2[0].value).toEqual "import"
|
||||
expect(line2[0].scopes).toEqual ["source.python", "keyword.control.import.python"]
|
||||
expect(line2[1].value).toEqual " "
|
||||
expect(line2[1].scopes).toEqual ["source.python"]
|
||||
expect(line2[2].value).toEqual "b"
|
||||
expect(line2[2].scopes).toEqual ["source.python"]
|
||||
@@ -1,5 +1,7 @@
|
||||
path = require 'path'
|
||||
|
||||
{$, $$, fs, WorkspaceView} = require 'atom'
|
||||
temp = require 'temp'
|
||||
|
||||
ThemeManager = require '../src/theme-manager'
|
||||
AtomPackage = require '../src/atom-package'
|
||||
@@ -32,14 +34,14 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "getImportPaths()", ->
|
||||
it "returns the theme directories before the themes are loaded", ->
|
||||
atom.config.set('core.themes', ['atom-dark-syntax', 'atom-dark-ui', 'atom-light-ui'])
|
||||
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui', 'atom-light-ui'])
|
||||
|
||||
paths = themeManager.getImportPaths()
|
||||
|
||||
# syntax theme is not a dir at this time, so only two.
|
||||
expect(paths.length).toBe 2
|
||||
expect(paths[0]).toContain 'atom-dark-ui'
|
||||
expect(paths[1]).toContain 'atom-light-ui'
|
||||
expect(paths[0]).toContain 'atom-light-ui'
|
||||
expect(paths[1]).toContain 'atom-dark-ui'
|
||||
|
||||
it "ignores themes that cannot be resolved to a directory", ->
|
||||
atom.config.set('core.themes', ['definitely-not-a-theme'])
|
||||
@@ -67,8 +69,8 @@ describe "ThemeManager", ->
|
||||
atom.config.set('core.themes', [])
|
||||
expect($('style.theme').length).toBe 0
|
||||
|
||||
# atom-dark-ui has an directory path, the syntax ones dont.
|
||||
atom.config.set('core.themes', ['atom-light-syntax', 'atom-dark-ui', 'atom-dark-syntax'])
|
||||
# atom-dark-ui has an directory path, the syntax one doesn't
|
||||
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui'])
|
||||
importPaths = themeManager.getImportPaths()
|
||||
expect(importPaths.length).toBe 1
|
||||
expect(importPaths[0]).toContain 'atom-dark-ui'
|
||||
@@ -155,3 +157,29 @@ describe "ThemeManager", ->
|
||||
expect($(".editor").css("padding-top")).toBe "150px"
|
||||
expect($(".editor").css("padding-right")).toBe "150px"
|
||||
expect($(".editor").css("padding-bottom")).toBe "150px"
|
||||
|
||||
describe "when the user stylesheet changes", ->
|
||||
it "reloads it", ->
|
||||
userStylesheetPath = path.join(temp.mkdirSync("atom"), 'styles.css')
|
||||
fs.writeFileSync(userStylesheetPath, 'body {border-style: dotted !important;}')
|
||||
|
||||
spyOn(themeManager, 'getUserStylesheetPath').andReturn userStylesheetPath
|
||||
themeManager.activateThemes()
|
||||
|
||||
expect($(document.body).css('border-style')).toBe 'dotted'
|
||||
spyOn(themeManager, 'loadUserStylesheet').andCallThrough()
|
||||
|
||||
fs.writeFileSync(userStylesheetPath, 'body {border-style: dashed}')
|
||||
|
||||
waitsFor ->
|
||||
themeManager.loadUserStylesheet.callCount is 1
|
||||
|
||||
runs ->
|
||||
expect($(document.body).css('border-style')).toBe 'dashed'
|
||||
fs.removeSync(userStylesheetPath)
|
||||
|
||||
waitsFor ->
|
||||
themeManager.loadUserStylesheet.callCount is 2
|
||||
|
||||
runs ->
|
||||
expect($(document.body).css('border-style')).toBe 'none'
|
||||
|
||||
@@ -18,14 +18,6 @@ describe "TokenizedBuffer", ->
|
||||
advanceClock() while tokenizedBuffer.firstInvalidRow()?
|
||||
changeHandler?.reset()
|
||||
|
||||
describe "@deserialize(state)", ->
|
||||
it "constructs a tokenized buffer with the same buffer and tabLength setting", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer1 = new TokenizedBuffer(buffer: buffer, tabLength: 4)
|
||||
tokenizedBuffer2 = atom.deserializers.deserialize(tokenizedBuffer1.serialize())
|
||||
expect(tokenizedBuffer2.buffer).toBe tokenizedBuffer1.buffer
|
||||
expect(tokenizedBuffer2.getTabLength()).toBe tokenizedBuffer1.getTabLength()
|
||||
|
||||
describe "when the buffer is destroyed", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
@@ -434,3 +426,13 @@ describe "TokenizedBuffer", ->
|
||||
describe "when the selector matches a run of multiple tokens at the position", ->
|
||||
it "returns the range covered by all contigous tokens (within a single line)", ->
|
||||
expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.function', [1, 18])).toEqual [[1, 6], [1, 28]]
|
||||
|
||||
describe "when the editor.tabLength config value changes", ->
|
||||
it "updates the tab length of the tokenized lines", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
buffer.setText('\ttest')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenForPosition([0,0]).value).toBe ' '
|
||||
atom.config.set('editor.tabLength', 6)
|
||||
expect(tokenizedBuffer.tokenForPosition([0,0]).value).toBe ' '
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{$, $$, fs} = require 'atom'
|
||||
path = require 'path'
|
||||
Editor = require '../src/editor'
|
||||
WindowEventHandler = require '../src/window-event-handler'
|
||||
|
||||
describe "Window", ->
|
||||
@@ -7,8 +8,11 @@ describe "Window", ->
|
||||
|
||||
beforeEach ->
|
||||
spyOn(atom, 'hide')
|
||||
atom.getLoadSettings() # Causes atom.loadSettings to be initialized
|
||||
atom.loadSettings.initialPath = atom.project.getPath()
|
||||
initialPath = atom.project.getPath()
|
||||
spyOn(atom, 'getLoadSettings').andCallFake ->
|
||||
loadSettings = atom.getLoadSettings.originalValue.call(atom)
|
||||
loadSettings.initialPath = initialPath
|
||||
loadSettings
|
||||
atom.project.destroy()
|
||||
windowEventHandler = new WindowEventHandler()
|
||||
atom.deserializeEditorWindow()
|
||||
@@ -55,6 +59,7 @@ describe "Window", ->
|
||||
[beforeUnloadEvent] = []
|
||||
|
||||
beforeEach ->
|
||||
jasmine.unspy(Editor.prototype, "shouldPromptToSave")
|
||||
beforeUnloadEvent = $.Event(new Event('beforeunload'))
|
||||
|
||||
describe "when pane items are are modified", ->
|
||||
@@ -83,14 +88,14 @@ describe "Window", ->
|
||||
|
||||
describe ".unloadEditorWindow()", ->
|
||||
it "saves the serialized state of the window so it can be deserialized after reload", ->
|
||||
workspaceViewState = atom.workspaceView.serialize()
|
||||
workspaceState = atom.workspace.serialize()
|
||||
syntaxState = atom.syntax.serialize()
|
||||
|
||||
atom.unloadEditorWindow()
|
||||
|
||||
expect(atom.getWindowState().getObject('workspaceView')).toEqual workspaceViewState.toObject()
|
||||
expect(atom.getWindowState().getObject('syntax')).toEqual syntaxState
|
||||
expect(atom.saveWindowState).toHaveBeenCalled()
|
||||
expect(atom.state.workspace).toEqual workspaceState
|
||||
expect(atom.state.syntax).toEqual syntaxState
|
||||
expect(atom.saveSync).toHaveBeenCalled()
|
||||
|
||||
it "unsubscribes from all buffers", ->
|
||||
atom.workspaceView.openSync('sample.js')
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
Workspace = require '../src/workspace'
|
||||
|
||||
describe "Workspace", ->
|
||||
workspace = null
|
||||
|
||||
beforeEach ->
|
||||
atom.project.setPath(atom.project.resolve('dir'))
|
||||
workspace = new Workspace
|
||||
|
||||
describe "::open(uri)", ->
|
||||
beforeEach ->
|
||||
spyOn(workspace.activePane, 'activate')
|
||||
|
||||
describe "when called without a uri", ->
|
||||
it "adds and activates an empty editor on the active pane", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
workspace.open().then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(workspace.activePane.items).toEqual [editor]
|
||||
expect(workspace.activePaneItem).toBe editor
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a uri", ->
|
||||
describe "when the active pane already has an editor for the given uri", ->
|
||||
it "activates the existing editor on the active pane", ->
|
||||
editor1 = workspace.openSync('a')
|
||||
editor2 = workspace.openSync('b')
|
||||
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
workspace.open('a').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor).toBe editor1
|
||||
expect(workspace.activePaneItem).toBe editor
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
|
||||
describe "when the active pane does not have an editor for the given uri", ->
|
||||
it "adds and activates a new editor for the given path on the active pane", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
workspace.open('a').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor.getUri()).toBe 'a'
|
||||
expect(workspace.activePaneItem).toBe editor
|
||||
expect(workspace.activePane.items).toEqual [editor]
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
|
||||
describe "::openSync(uri, options)", ->
|
||||
[activePane, initialItemCount] = []
|
||||
|
||||
beforeEach ->
|
||||
activePane = workspace.activePane
|
||||
spyOn(activePane, 'activate')
|
||||
initialItemCount = activePane.items.length
|
||||
|
||||
describe "when called without a uri", ->
|
||||
it "adds and activates an empty editor on the active pane", ->
|
||||
editor = workspace.openSync()
|
||||
expect(activePane.items.length).toBe initialItemCount + 1
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(activePane.activate).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a uri", ->
|
||||
describe "when the active pane already has an editor for the given uri", ->
|
||||
it "activates the existing editor on the active pane", ->
|
||||
editor1 = workspace.openSync('a')
|
||||
editor2 = workspace.openSync('b')
|
||||
expect(activePane.activeItem).toBe editor2
|
||||
expect(activePane.items.length).toBe 2
|
||||
|
||||
editor = workspace.openSync(editor1.getPath())
|
||||
expect(editor).toBe editor1
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(activePane.activate).toHaveBeenCalled()
|
||||
expect(activePane.items.length).toBe 2
|
||||
|
||||
describe "when the active pane does not have an editor for the given uri", ->
|
||||
it "adds and activates a new editor for the given path on the active pane", ->
|
||||
editor = workspace.openSync('a')
|
||||
expect(activePane.items.length).toBe 1
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(activePane.activate).toHaveBeenCalled()
|
||||
|
||||
describe "when the 'activatePane' option is false", ->
|
||||
it "does not activate the active pane", ->
|
||||
workspace.openSync('b', activatePane: false)
|
||||
expect(activePane.activate).not.toHaveBeenCalled()
|
||||
|
||||
describe "::openSingletonSync(uri, options)", ->
|
||||
describe "when an editor for the given uri is already open on the active pane", ->
|
||||
it "activates the existing editor", ->
|
||||
editor1 = workspace.openSync('a')
|
||||
editor2 = workspace.openSync('b')
|
||||
expect(workspace.activePaneItem).toBe editor2
|
||||
workspace.openSingletonSync('a')
|
||||
expect(workspace.activePaneItem).toBe editor1
|
||||
|
||||
describe "when an editor for the given uri is already open on an inactive pane", ->
|
||||
it "activates the existing editor on the inactive pane, then activates that pane", ->
|
||||
editor1 = workspace.openSync('a')
|
||||
pane1 = workspace.activePane
|
||||
pane2 = workspace.activePane.splitRight()
|
||||
editor2 = workspace.openSync('b')
|
||||
expect(workspace.activePaneItem).toBe editor2
|
||||
workspace.openSingletonSync('a')
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(workspace.activePaneItem).toBe editor1
|
||||
|
||||
describe "when no editor for the given uri is open in any pane", ->
|
||||
it "opens an editor for the given uri in the active pane", ->
|
||||
editor1 = workspace.openSingletonSync('a')
|
||||
expect(workspace.activePaneItem).toBe editor1
|
||||
|
||||
describe "when the 'split' option is 'left'", ->
|
||||
it "opens the editor in the leftmost pane of the current pane axis", ->
|
||||
pane1 = workspace.activePane
|
||||
pane2 = pane1.splitRight()
|
||||
expect(workspace.activePane).toBe pane2
|
||||
editor1 = workspace.openSingletonSync('a', split: 'left')
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(pane1.items).toEqual [editor1]
|
||||
expect(pane2.items).toEqual []
|
||||
|
||||
describe "when the 'split' option is 'right'", ->
|
||||
describe "when the active pane is in a horizontal pane axis", ->
|
||||
it "activates the editor on the rightmost pane of the current pane axis", ->
|
||||
pane1 = workspace.activePane
|
||||
pane2 = pane1.splitRight()
|
||||
pane1.activate()
|
||||
editor1 = workspace.openSingletonSync('a', split: 'right')
|
||||
expect(workspace.activePane).toBe pane2
|
||||
expect(pane2.items).toEqual [editor1]
|
||||
expect(pane1.items).toEqual []
|
||||
|
||||
describe "when the active pane is not in a horizontal pane axis", ->
|
||||
it "splits the current pane to the right, then activates the editor on the right pane", ->
|
||||
pane1 = workspace.activePane
|
||||
editor1 = workspace.openSingletonSync('a', split: 'right')
|
||||
pane2 = workspace.activePane
|
||||
expect(workspace.paneContainer.root.children).toEqual [pane1, pane2]
|
||||
expect(pane2.items).toEqual [editor1]
|
||||
expect(pane1.items).toEqual []
|
||||
|
||||
describe "::reopenItemSync()", ->
|
||||
it "opens the uri associated with the last closed pane that isn't currently open", ->
|
||||
pane = workspace.activePane
|
||||
workspace.openSync('a')
|
||||
workspace.openSync('b')
|
||||
workspace.openSync('file1')
|
||||
workspace.openSync()
|
||||
|
||||
# does not reopen items with no uri
|
||||
expect(workspace.activePaneItem.getUri()).toBeUndefined()
|
||||
pane.destroyActiveItem()
|
||||
workspace.reopenItemSync()
|
||||
expect(workspace.activePaneItem.getUri()).not.toBeUndefined()
|
||||
|
||||
# destroy all items
|
||||
expect(workspace.activePaneItem.getUri()).toBe 'file1'
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.activePaneItem.getUri()).toBe 'b'
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.activePaneItem.getUri()).toBe 'a'
|
||||
pane.destroyActiveItem()
|
||||
|
||||
# reopens items with uris
|
||||
expect(workspace.activePaneItem).toBeUndefined()
|
||||
workspace.reopenItemSync()
|
||||
expect(workspace.activePaneItem.getUri()).toBe 'a'
|
||||
|
||||
# does not reopen items that are already open
|
||||
workspace.openSync('b')
|
||||
expect(workspace.activePaneItem.getUri()).toBe 'b'
|
||||
workspace.reopenItemSync()
|
||||
expect(workspace.activePaneItem.getUri()).toBe 'file1'
|
||||
|
||||
describe "::increase/decreaseFontSize()", ->
|
||||
it "increases/decreases the font size without going below 1", ->
|
||||
atom.config.set('editor.fontSize', 1)
|
||||
workspace.increaseFontSize()
|
||||
expect(atom.config.get('editor.fontSize')).toBe 2
|
||||
workspace.increaseFontSize()
|
||||
expect(atom.config.get('editor.fontSize')).toBe 3
|
||||
workspace.decreaseFontSize()
|
||||
expect(atom.config.get('editor.fontSize')).toBe 2
|
||||
workspace.decreaseFontSize()
|
||||
expect(atom.config.get('editor.fontSize')).toBe 1
|
||||
workspace.decreaseFontSize()
|
||||
expect(atom.config.get('editor.fontSize')).toBe 1
|
||||
@@ -2,7 +2,8 @@
|
||||
Q = require 'q'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
Pane = require '../src/pane'
|
||||
PaneView = require '../src/pane-view'
|
||||
Workspace = require '../src/workspace'
|
||||
|
||||
describe "WorkspaceView", ->
|
||||
pathToOpen = null
|
||||
@@ -10,7 +11,8 @@ describe "WorkspaceView", ->
|
||||
beforeEach ->
|
||||
atom.project.setPath(atom.project.resolve('dir'))
|
||||
pathToOpen = atom.project.resolve('a')
|
||||
atom.workspaceView = new WorkspaceView
|
||||
atom.workspace = new Workspace
|
||||
atom.workspaceView = new WorkspaceView(atom.workspace)
|
||||
atom.workspaceView.enableKeymap()
|
||||
atom.workspaceView.openSync(pathToOpen)
|
||||
atom.workspaceView.focus()
|
||||
@@ -18,29 +20,28 @@ describe "WorkspaceView", ->
|
||||
describe "@deserialize()", ->
|
||||
viewState = null
|
||||
|
||||
refreshWorkspaceViewAndProject = ->
|
||||
workspaceViewState = atom.workspaceView.serialize()
|
||||
atom.project.getState().serializeForPersistence()
|
||||
project2 = atom.replicate().get('project')
|
||||
simulateReload = ->
|
||||
workspaceState = atom.workspace.serialize()
|
||||
projectState = atom.project.serialize()
|
||||
atom.workspaceView.remove()
|
||||
atom.project.destroy()
|
||||
atom.project = project2
|
||||
atom.workspaceView = atom.deserializers.deserialize(workspaceViewState)
|
||||
atom.project = atom.deserializers.deserialize(projectState)
|
||||
atom.workspace = Workspace.deserialize(workspaceState)
|
||||
atom.workspaceView = new WorkspaceView(atom.workspace)
|
||||
atom.workspaceView.attachToDom()
|
||||
|
||||
describe "when the serialized WorkspaceView has an unsaved buffer", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
atom.workspaceView.attachToDom()
|
||||
atom.workspaceView.openSync()
|
||||
editor1 = atom.workspaceView.getActiveView()
|
||||
buffer = editor1.getBuffer()
|
||||
editor1.splitRight()
|
||||
expect(atom.workspaceView.getActiveView()).toBe atom.workspaceView.getEditorViews()[2]
|
||||
editorView1 = atom.workspaceView.getActiveView()
|
||||
buffer = editorView1.getEditor().getBuffer()
|
||||
editorView1.splitRight()
|
||||
expect(atom.workspaceView.getActivePane()).toBe atom.workspaceView.getPanes()[1]
|
||||
|
||||
refreshWorkspaceViewAndProject()
|
||||
simulateReload()
|
||||
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 2
|
||||
expect(atom.workspaceView.getActiveView()).toBe atom.workspaceView.getEditorViews()[1]
|
||||
expect(atom.workspaceView.getActivePane()).toBe atom.workspaceView.getPanes()[1]
|
||||
expect(atom.workspaceView.title).toBe "untitled - #{atom.project.getPath()}"
|
||||
|
||||
describe "when there are open editors", ->
|
||||
@@ -50,86 +51,59 @@ describe "WorkspaceView", ->
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitRight()
|
||||
pane4 = pane2.splitDown()
|
||||
pane2.showItem(atom.project.openSync('b'))
|
||||
pane3.showItem(atom.project.openSync('../sample.js'))
|
||||
pane2.activateItem(atom.project.openSync('b'))
|
||||
pane3.activateItem(atom.project.openSync('../sample.js'))
|
||||
pane3.activeItem.setCursorScreenPosition([2, 4])
|
||||
pane4.showItem(atom.project.openSync('../sample.txt'))
|
||||
pane4.activateItem(atom.project.openSync('../sample.txt'))
|
||||
pane4.activeItem.setCursorScreenPosition([0, 2])
|
||||
pane2.focus()
|
||||
|
||||
refreshWorkspaceViewAndProject()
|
||||
simulateReload()
|
||||
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 4
|
||||
editor1 = atom.workspaceView.panes.find('.row > .pane .editor:eq(0)').view()
|
||||
editor3 = atom.workspaceView.panes.find('.row > .pane .editor:eq(1)').view()
|
||||
editor2 = atom.workspaceView.panes.find('.row > .column > .pane .editor:eq(0)').view()
|
||||
editor4 = atom.workspaceView.panes.find('.row > .column > .pane .editor:eq(1)').view()
|
||||
editorView1 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(0)').view()
|
||||
editorView3 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(1)').view()
|
||||
editorView2 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(0)').view()
|
||||
editorView4 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(1)').view()
|
||||
|
||||
expect(editor1.getPath()).toBe atom.project.resolve('a')
|
||||
expect(editor2.getPath()).toBe atom.project.resolve('b')
|
||||
expect(editor3.getPath()).toBe atom.project.resolve('../sample.js')
|
||||
expect(editor3.getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(editor4.getPath()).toBe atom.project.resolve('../sample.txt')
|
||||
expect(editor4.getCursorScreenPosition()).toEqual [0, 2]
|
||||
expect(editorView1.getEditor().getPath()).toBe atom.project.resolve('a')
|
||||
expect(editorView2.getEditor().getPath()).toBe atom.project.resolve('b')
|
||||
expect(editorView3.getEditor().getPath()).toBe atom.project.resolve('../sample.js')
|
||||
expect(editorView3.getEditor().getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(editorView4.getEditor().getPath()).toBe atom.project.resolve('../sample.txt')
|
||||
expect(editorView4.getEditor().getCursorScreenPosition()).toEqual [0, 2]
|
||||
|
||||
# ensure adjust pane dimensions is called
|
||||
expect(editor1.width()).toBeGreaterThan 0
|
||||
expect(editor2.width()).toBeGreaterThan 0
|
||||
expect(editor3.width()).toBeGreaterThan 0
|
||||
expect(editor4.width()).toBeGreaterThan 0
|
||||
expect(editorView1.width()).toBeGreaterThan 0
|
||||
expect(editorView2.width()).toBeGreaterThan 0
|
||||
expect(editorView3.width()).toBeGreaterThan 0
|
||||
expect(editorView4.width()).toBeGreaterThan 0
|
||||
|
||||
# ensure correct editor is focused again
|
||||
expect(editor2.isFocused).toBeTruthy()
|
||||
expect(editor1.isFocused).toBeFalsy()
|
||||
expect(editor3.isFocused).toBeFalsy()
|
||||
expect(editor4.isFocused).toBeFalsy()
|
||||
# ensure correct editorView is focused again
|
||||
expect(editorView2.isFocused).toBeTruthy()
|
||||
expect(editorView1.isFocused).toBeFalsy()
|
||||
expect(editorView3.isFocused).toBeFalsy()
|
||||
expect(editorView4.isFocused).toBeFalsy()
|
||||
|
||||
expect(atom.workspaceView.title).toBe "#{path.basename(editor2.getPath())} - #{atom.project.getPath()}"
|
||||
expect(atom.workspaceView.title).toBe "#{path.basename(editorView2.getEditor().getPath())} - #{atom.project.getPath()}"
|
||||
|
||||
describe "where there are no open editors", ->
|
||||
it "constructs the view with no open editors", ->
|
||||
atom.workspaceView.getActivePane().remove()
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 0
|
||||
refreshWorkspaceViewAndProject()
|
||||
simulateReload()
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 0
|
||||
|
||||
describe "focus", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView.attachToDom()
|
||||
|
||||
describe "when there is an active view", ->
|
||||
it "hands off focus to the active view", ->
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
editorView.isFocused = false
|
||||
atom.workspaceView.focus()
|
||||
expect(editorView.isFocused).toBeTruthy()
|
||||
|
||||
describe "when there is no active view", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView.getActivePane().remove()
|
||||
expect(atom.workspaceView.getActiveView()).toBeUndefined()
|
||||
atom.workspaceView.attachToDom()
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
describe "when are visible focusable elements (with a -1 tabindex)", ->
|
||||
it "passes focus to the first focusable element", ->
|
||||
focusable1 = $$ -> @div "One", id: 'one', tabindex: -1
|
||||
focusable2 = $$ -> @div "Two", id: 'two', tabindex: -1
|
||||
atom.workspaceView.horizontal.append(focusable1, focusable2)
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
atom.workspaceView.focus()
|
||||
expect(document.activeElement).toBe focusable1[0]
|
||||
|
||||
describe "when there are no visible focusable elements", ->
|
||||
it "surrenders focus to the body", ->
|
||||
focusable = $$ -> @div "One", id: 'one', tabindex: -1
|
||||
atom.workspaceView.horizontal.append(focusable)
|
||||
focusable.hide()
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
atom.workspaceView.focus()
|
||||
expect(document.activeElement).toBe document.body
|
||||
it "hands off focus to the active pane", ->
|
||||
activePane = atom.workspaceView.getActivePane()
|
||||
$('body').focus()
|
||||
expect(activePane.hasFocus()).toBe false
|
||||
atom.workspaceView.focus()
|
||||
expect(activePane.hasFocus()).toBe true
|
||||
|
||||
describe "keymap wiring", ->
|
||||
commandHandler = null
|
||||
@@ -189,303 +163,36 @@ describe "WorkspaceView", ->
|
||||
|
||||
describe "when the root view is deserialized", ->
|
||||
it "updates the title to contain the project's path", ->
|
||||
workspaceView2 = atom.deserializers.deserialize(atom.workspaceView.serialize())
|
||||
workspaceView2 = new WorkspaceView(atom.workspace.testSerialization())
|
||||
item = atom.workspaceView.getActivePaneItem()
|
||||
expect(workspaceView2.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
workspaceView2.remove()
|
||||
|
||||
describe "font size adjustment", ->
|
||||
it "increases/decreases font size when increase/decrease-font-size events are triggered", ->
|
||||
fontSizeBefore = atom.config.get('editor.fontSize')
|
||||
atom.workspaceView.trigger 'window:increase-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 1
|
||||
atom.workspaceView.trigger 'window:increase-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 2
|
||||
atom.workspaceView.trigger 'window:decrease-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 1
|
||||
atom.workspaceView.trigger 'window:decrease-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore
|
||||
|
||||
it "does not allow the font size to be less than 1", ->
|
||||
atom.config.set("editor.fontSize", 1)
|
||||
atom.workspaceView.trigger 'window:decrease-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe 1
|
||||
|
||||
describe ".openSync(filePath, options)", ->
|
||||
describe "when there is no active pane", ->
|
||||
beforeEach ->
|
||||
spyOn(Pane.prototype, 'focus')
|
||||
atom.workspaceView.getActivePane().remove()
|
||||
expect(atom.workspaceView.getActivePane()).toBeUndefined()
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "creates a empty edit session as an item on a new pane, and focuses the pane", ->
|
||||
editor = atom.workspaceView.openSync()
|
||||
expect(atom.workspaceView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(atom.workspaceView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
it "can create multiple empty edit sessions as an item on a new pane", ->
|
||||
editor = atom.workspaceView.openSync()
|
||||
editor2 = atom.workspaceView.openSync()
|
||||
expect(atom.workspaceView.getActivePane().getItems().length).toBe 2
|
||||
expect(editor).not.toBe editor2
|
||||
|
||||
describe "when called with a path", ->
|
||||
it "creates an edit session for the given path as an item on a new pane, and focuses the pane", ->
|
||||
editor = atom.workspaceView.openSync('b')
|
||||
expect(atom.workspaceView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
expect(atom.workspaceView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the changeFocus option is false", ->
|
||||
it "does not focus the new pane", ->
|
||||
editor = atom.workspaceView.openSync('b', changeFocus: false)
|
||||
expect(atom.workspaceView.getActivePane().focus).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the split option is 'right'", ->
|
||||
it "creates a new pane and opens the file in said pane", ->
|
||||
editor = atom.workspaceView.openSync('b', split: 'right')
|
||||
expect(atom.workspaceView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
|
||||
describe "when there is an active pane", ->
|
||||
[activePane, initialItemCount] = []
|
||||
beforeEach ->
|
||||
activePane = atom.workspaceView.getActivePane()
|
||||
spyOn(activePane, 'focus')
|
||||
initialItemCount = activePane.getItems().length
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "opens an edit session with an empty buffer as an item in the active pane and focuses it", ->
|
||||
editor = atom.workspaceView.openSync()
|
||||
expect(activePane.getItems().length).toBe initialItemCount + 1
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a path", ->
|
||||
describe "when the active pane already has an edit session item for the path being opened", ->
|
||||
it "shows the existing edit session in the pane", ->
|
||||
previousEditor = activePane.activeItem
|
||||
|
||||
editor = atom.workspaceView.openSync('b')
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor).not.toBe previousEditor
|
||||
|
||||
editor = atom.workspaceView.openSync(previousEditor.getPath())
|
||||
expect(editor).toBe previousEditor
|
||||
expect(activePane.activeItem).toBe editor
|
||||
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the active pane does not have an edit session item for the path being opened", ->
|
||||
it "creates a new edit session for the given path in the active editor", ->
|
||||
editor = atom.workspaceView.openSync('b')
|
||||
expect(activePane.items.length).toBe 2
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the changeFocus option is false", ->
|
||||
it "does not focus the active pane", ->
|
||||
editor = atom.workspaceView.openSync('b', changeFocus: false)
|
||||
expect(activePane.focus).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the split option is 'right'", ->
|
||||
it "creates a new pane and opens the file in said pane", ->
|
||||
pane1 = atom.workspaceView.getActivePane()
|
||||
|
||||
editor = atom.workspaceView.openSync('b', split: 'right')
|
||||
pane2 = atom.workspaceView.getActivePane()
|
||||
expect(pane2[0]).not.toBe pane1[0]
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
|
||||
expect(atom.workspaceView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
editor = atom.workspaceView.openSync('file1', split: 'right')
|
||||
pane3 = atom.workspaceView.getActivePane()
|
||||
expect(pane3[0]).toBe pane2[0]
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/file1')
|
||||
|
||||
expect(atom.workspaceView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
describe ".openSingletonSync(filePath, options)", ->
|
||||
describe "when there is an active pane", ->
|
||||
[pane1] = []
|
||||
beforeEach ->
|
||||
spyOn(Pane.prototype, 'focus').andCallFake -> @makeActive()
|
||||
pane1 = atom.workspaceView.getActivePane()
|
||||
|
||||
it "creates a new pane and reuses the file when already open", ->
|
||||
atom.workspaceView.openSingletonSync('b', split: 'right')
|
||||
pane2 = atom.workspaceView.getActivePane()
|
||||
expect(pane2[0]).not.toBe pane1[0]
|
||||
expect(pane1.itemForUri('b')).toBeFalsy()
|
||||
expect(pane2.itemForUri('b')).not.toBeFalsy()
|
||||
expect(atom.workspaceView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
pane1.focus()
|
||||
expect(atom.workspaceView.getActivePane()[0]).toBe pane1[0]
|
||||
|
||||
atom.workspaceView.openSingletonSync('b', split: 'right')
|
||||
pane3 = atom.workspaceView.getActivePane()
|
||||
expect(pane3[0]).toBe pane2[0]
|
||||
expect(pane1.itemForUri('b')).toBeFalsy()
|
||||
expect(pane2.itemForUri('b')).not.toBeFalsy()
|
||||
expect(atom.workspaceView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
it "handles split: left by opening to the left pane when necessary", ->
|
||||
atom.workspaceView.openSingletonSync('b', split: 'right')
|
||||
pane2 = atom.workspaceView.getActivePane()
|
||||
expect(pane2[0]).not.toBe pane1[0]
|
||||
|
||||
atom.workspaceView.openSingletonSync('file1', split: 'left')
|
||||
|
||||
activePane = atom.workspaceView.getActivePane()
|
||||
expect(activePane[0]).toBe pane1[0]
|
||||
|
||||
expect(pane1.itemForUri('file1')).toBeTruthy()
|
||||
expect(pane2.itemForUri('file1')).toBeFalsy()
|
||||
expect(atom.workspaceView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
pane2.focus()
|
||||
expect(atom.workspaceView.getActivePane()[0]).toBe pane2[0]
|
||||
|
||||
atom.workspaceView.openSingletonSync('file1', split: 'left')
|
||||
activePane = atom.workspaceView.getActivePane()
|
||||
expect(activePane[0]).toBe pane1[0]
|
||||
expect(atom.workspaceView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
it "reuses the file when already open", ->
|
||||
atom.workspaceView.openSync('b')
|
||||
atom.workspaceView.openSingletonSync('b', split: 'right')
|
||||
expect(atom.workspaceView.panes.find('.pane').toArray()).toEqual [pane1[0]]
|
||||
|
||||
describe ".open(filePath)", ->
|
||||
beforeEach ->
|
||||
spyOn(Pane.prototype, 'focus')
|
||||
|
||||
describe "when there is no active pane", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView.getActivePane().remove()
|
||||
expect(atom.workspaceView.getActivePane()).toBeUndefined()
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "creates a empty edit session as an item on a new pane, and focuses the pane", ->
|
||||
editor = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open().then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(atom.workspaceView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(atom.workspaceView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
it "can create multiple empty edit sessions as items on a pane", ->
|
||||
editor1 = null
|
||||
editor2 = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open()
|
||||
.then (o) ->
|
||||
editor1 = o
|
||||
atom.workspaceView.open()
|
||||
.then (o) ->
|
||||
editor2 = o
|
||||
|
||||
runs ->
|
||||
expect(atom.workspaceView.getActivePane().getItems().length).toBe 2
|
||||
expect(editor1).not.toBe editor2
|
||||
|
||||
describe "when called with a path", ->
|
||||
it "creates an edit session for the given path as an item on a new pane, and focuses the pane", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open('b').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(atom.workspaceView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
expect(atom.workspaceView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
describe "when there is an active pane", ->
|
||||
[activePane] = []
|
||||
|
||||
beforeEach ->
|
||||
activePane = atom.workspaceView.getActivePane()
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "opens an edit session with an empty buffer as an item in the active pane and focuses it", ->
|
||||
editor = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open().then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(activePane.getItems().length).toBe 2
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a path", ->
|
||||
describe "when the active pane already has an item for the given path", ->
|
||||
it "shows the existing edit session in the pane", ->
|
||||
previousEditor = activePane.activeItem
|
||||
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open('b').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor).not.toBe previousEditor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open(previousEditor.getPath()).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor).toBe previousEditor
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the active pane does not have an existing item for the given path", ->
|
||||
it "creates a new edit session for the given path in the active pane", ->
|
||||
editor = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open('b').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(activePane.getItems().length).toBe 2
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "window:toggle-invisibles event", ->
|
||||
it "shows/hides invisibles in all open and future editors", ->
|
||||
atom.workspaceView.height(200)
|
||||
atom.workspaceView.attachToDom()
|
||||
rightEditor = atom.workspaceView.getActiveView()
|
||||
rightEditor.setText(" \t ")
|
||||
leftEditor = rightEditor.splitLeft()
|
||||
expect(rightEditor.find(".line:first").text()).toBe " "
|
||||
expect(leftEditor.find(".line:first").text()).toBe " "
|
||||
rightEditorView = atom.workspaceView.getActiveView()
|
||||
rightEditorView.getEditor().setText(" \t ")
|
||||
leftEditorView = rightEditorView.splitLeft()
|
||||
expect(rightEditorView.find(".line:first").text()).toBe " "
|
||||
expect(leftEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
withInvisiblesShowing = "#{rightEditor.invisibles.space}#{rightEditor.invisibles.tab} #{rightEditor.invisibles.space}#{rightEditor.invisibles.eol}"
|
||||
withInvisiblesShowing = "#{rightEditorView.invisibles.space}#{rightEditorView.invisibles.tab} #{rightEditorView.invisibles.space}#{rightEditorView.invisibles.eol}"
|
||||
|
||||
atom.workspaceView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
expect(leftEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
expect(rightEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
expect(leftEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
lowerLeftEditor = leftEditor.splitDown()
|
||||
expect(lowerLeftEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
lowerLeftEditorView = leftEditorView.splitDown()
|
||||
expect(lowerLeftEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
atom.workspaceView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditor.find(".line:first").text()).toBe " "
|
||||
expect(leftEditor.find(".line:first").text()).toBe " "
|
||||
expect(rightEditorView.find(".line:first").text()).toBe " "
|
||||
expect(leftEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
lowerRightEditor = rightEditor.splitDown()
|
||||
expect(lowerRightEditor.find(".line:first").text()).toBe " "
|
||||
lowerRightEditorView = rightEditorView.splitDown()
|
||||
expect(lowerRightEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
describe ".eachEditorView(callback)", ->
|
||||
beforeEach ->
|
||||
@@ -526,3 +233,11 @@ describe "WorkspaceView", ->
|
||||
subscription.off()
|
||||
atom.workspaceView.getActiveView().splitRight()
|
||||
expect(count).toBe 2
|
||||
|
||||
describe "core:close", ->
|
||||
it "closes the active pane item until all that remains is a single empty pane", ->
|
||||
atom.config.set('core.destroyEmptyPanes', true)
|
||||
atom.project.openSync('../sample.txt')
|
||||
expect(atom.workspaceView.getActivePane().getItems()).toHaveLength 1
|
||||
atom.workspaceView.trigger('core:close')
|
||||
expect(atom.workspaceView.getActivePane().getItems()).toHaveLength 0
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
TextMateGrammar = require './text-mate-grammar'
|
||||
Package = require './package'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
@@ -53,12 +52,6 @@ class AtomPackage extends Package
|
||||
console.warn "Failed to load package named '#{@name}'", e.stack ? e
|
||||
this
|
||||
|
||||
enable: ->
|
||||
atom.config.removeAtKeyPath('core.disabledPackages', @metadata.name)
|
||||
|
||||
disable: ->
|
||||
atom.config.pushAtKeyPath('core.disabledPackages', @metadata.name)
|
||||
|
||||
reset: ->
|
||||
@stylesheets = []
|
||||
@keymaps = []
|
||||
@@ -105,7 +98,7 @@ class AtomPackage extends Package
|
||||
atom.keymap.add(keymapPath, map) for [keymapPath, map] in @keymaps
|
||||
atom.contextMenu.add(menuPath, map['context-menu']) for [menuPath, map] in @menus
|
||||
atom.menu.add(map.menu) for [menuPath, map] in @menus when map.menu
|
||||
atom.syntax.addGrammar(grammar) for grammar in @grammars
|
||||
grammar.activate() for grammar in @grammars
|
||||
for [scopedPropertiesPath, selector, properties] in @scopedProperties
|
||||
atom.syntax.addProperties(scopedPropertiesPath, selector, properties)
|
||||
|
||||
@@ -152,7 +145,7 @@ class AtomPackage extends Package
|
||||
@grammars = []
|
||||
grammarsDirPath = path.join(@path, 'grammars')
|
||||
for grammarPath in fs.listSync(grammarsDirPath, ['.json', '.cson'])
|
||||
@grammars.push(TextMateGrammar.loadSync(grammarPath))
|
||||
@grammars.push(atom.syntax.readGrammarSync(grammarPath))
|
||||
|
||||
loadScopedProperties: ->
|
||||
@scopedProperties = []
|
||||
@@ -180,7 +173,7 @@ class AtomPackage extends Package
|
||||
@configActivated = false
|
||||
|
||||
deactivateResources: ->
|
||||
atom.syntax.removeGrammar(grammar) for grammar in @grammars
|
||||
grammar.deactivate() for grammar in @grammars
|
||||
atom.syntax.removeProperties(scopedPropertiesPath) for [scopedPropertiesPath] in @scopedProperties
|
||||
atom.keymap.remove(keymapPath) for [keymapPath] in @keymaps
|
||||
atom.themes.removeStylesheet(stylesheetPath) for [stylesheetPath] in @stylesheets
|
||||
|
||||
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