Comparar commits
817 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| d18a45f37c | |||
| d8c1587534 | |||
| a4754b2bd5 | |||
| f3f6ec424f | |||
| ac2985af36 | |||
| 52dcf6a721 | |||
| cd41637df0 | |||
| a42a4dd352 | |||
| cc53b6fbef | |||
| b31089cbb9 | |||
| 4d4ff84047 | |||
| 0875e00f9c | |||
| b9d45680c3 | |||
| 1fa2099eba | |||
| 8afca3ac86 | |||
| d13d2d3112 | |||
| c3d5f713ca | |||
| 042684212e | |||
| 81987efed7 | |||
| f2e123e295 | |||
| 8d193d542b | |||
| 8ca8ac5efc | |||
| f6c7dd0b2f | |||
| b380551e5e | |||
| 7dcb340643 | |||
| f0339936ee | |||
| 1ae10e59ae | |||
| e4bcb52573 | |||
| 292ff0de52 | |||
| a95a510770 | |||
| 7d1be155fa | |||
| 41de6bdb70 | |||
| 1d9bca12e0 | |||
| e9c296968d | |||
| 352eab47df | |||
| 002c4dcb80 | |||
| 03250f79c6 | |||
| 1142da1848 | |||
| e53ed10169 | |||
| 3fb54b657d | |||
| 4b3b0145a7 | |||
| ec558f9a9b | |||
| 051f1b4777 | |||
| 2e4e178091 | |||
| 1fa04cb161 | |||
| 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 | |||
| bf0015f6cc | |||
| ca3d1e869c | |||
| a57083a48b | |||
| 2ae46734db | |||
| 3918435c7f | |||
| d8f5ef71cd | |||
| 2e2bab7778 | |||
| fd929364d1 | |||
| 92b829c89b | |||
| 3db9e16637 | |||
| fa34eea27a | |||
| bd3cfda2bb | |||
| ed41cc3cad | |||
| e67e8ff0f5 | |||
| e18a0f045a | |||
| da964a8f15 | |||
| 2c4aee1181 | |||
| 24d3f1daeb | |||
| beb2fb08ea | |||
| 165a417a9d | |||
| 39fe0c418b | |||
| bfcb24f517 | |||
| 208ed09109 | |||
| 0b7f291e17 | |||
| a61b057aea | |||
| 4c817baf4c | |||
| a564cc66f6 | |||
| 14c58c4517 | |||
| ebe77065cc | |||
| 30b0fed60f | |||
| 5f10c48219 | |||
| 23957d7f66 |
@@ -9,4 +9,6 @@ debug.log
|
||||
/tags
|
||||
/atom-shell/
|
||||
docs/output
|
||||
docs/includes
|
||||
spec/fixtures/evil-files/
|
||||
/apm
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# Atom — The hackable, ~~collaborative~~ editor
|
||||
# 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,16 +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
|
||||
@@ -23,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
|
||||
@@ -83,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'))
|
||||
@@ -91,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')
|
||||
@@ -105,6 +137,8 @@ module.exports = (grunt) ->
|
||||
|
||||
cson: csonConfig
|
||||
|
||||
peg: pegConfig
|
||||
|
||||
coffeelint:
|
||||
options:
|
||||
no_empty_param_list:
|
||||
@@ -117,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'
|
||||
@@ -188,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,37 @@
|
||||
{
|
||||
"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": "~0.13.0",
|
||||
"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.7.0",
|
||||
"grunt-contrib-less": "~0.8.0",
|
||||
"grunt-cson": "0.5.0",
|
||||
"grunt-download-atom-shell": "git+https://atom-bot:362295be4c5258d3f7b967bbabae662a455ca2a7@github.com/atom/grunt-download-atom-shell#v0.5.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",
|
||||
"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 @@
|
||||
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()
|
||||
Arquivo executável → Arquivo normal
+69
-34
@@ -1,7 +1,3 @@
|
||||
#!/usr/bin/env coffee
|
||||
|
||||
return if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH isnt 'master'
|
||||
|
||||
child_process = require 'child_process'
|
||||
path = require 'path'
|
||||
|
||||
@@ -10,6 +6,7 @@ fs = require 'fs-plus'
|
||||
GitHub = require 'github-releases'
|
||||
request = require 'request'
|
||||
|
||||
grunt = null
|
||||
maxReleases = 10
|
||||
assetName = 'atom-mac.zip'
|
||||
assetPath = "/tmp/atom-build/#{assetName}"
|
||||
@@ -19,16 +16,41 @@ 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?
|
||||
console.error('Zipping Atom.app failed', error, stderr)
|
||||
process.exit(1)
|
||||
else
|
||||
callback()
|
||||
logError('Zipping Atom.app failed', error, stderr)
|
||||
callback(error)
|
||||
|
||||
getRelease = (callback) ->
|
||||
options =
|
||||
@@ -38,17 +60,29 @@ getRelease = (callback) ->
|
||||
json: true
|
||||
request options, (error, response, releases=[]) ->
|
||||
if error? or response.statusCode isnt 200
|
||||
console.error('Fetching releases failed', error, releases)
|
||||
process.exit(1)
|
||||
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(release)
|
||||
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
|
||||
@@ -57,7 +91,7 @@ deleteRelease = (release) ->
|
||||
json: true
|
||||
request options, (error, response, body='') ->
|
||||
if error? or response.statusCode isnt 204
|
||||
console.error('Deleting release failed', error, body)
|
||||
logError('Deleting release failed', error, body)
|
||||
|
||||
deleteExistingAsset = (release, callback) ->
|
||||
for asset in release.assets when asset.name is assetName
|
||||
@@ -67,8 +101,8 @@ deleteExistingAsset = (release, callback) ->
|
||||
headers: defaultHeaders
|
||||
request options, (error, response, body='') ->
|
||||
if error? or response.statusCode isnt 204
|
||||
console.error('Deleting existing release asset failed', error, body)
|
||||
process.exit(1)
|
||||
logError('Deleting existing release asset failed', error, body)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
callback()
|
||||
|
||||
@@ -76,11 +110,15 @@ deleteExistingAsset = (release, callback) ->
|
||||
|
||||
callback()
|
||||
|
||||
createRelease = (callback) ->
|
||||
getRelease (release) ->
|
||||
createBuildRelease = (callback) ->
|
||||
getRelease (error, release) ->
|
||||
if error?
|
||||
callback(error)
|
||||
return
|
||||
|
||||
if release?
|
||||
deleteExistingAsset release, ->
|
||||
callback(release)
|
||||
deleteExistingAsset release, (error) ->
|
||||
callback(error, release)
|
||||
return
|
||||
|
||||
options =
|
||||
@@ -91,19 +129,19 @@ createRelease = (callback) ->
|
||||
tag_name: "v#{commitSha}"
|
||||
target_commitish: 'master'
|
||||
name: commitSha
|
||||
body: "Build of [atom@#{commitSha.substring(0, 7)}](https://github.com/atom/atom/commit/#{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
|
||||
console.error('Creating release failed', error, release)
|
||||
process.exit(1)
|
||||
logError('Creating release failed', error, release)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
callback(release)
|
||||
callback(null, release)
|
||||
|
||||
uploadAsset = (release, callback) ->
|
||||
options =
|
||||
uri: "https://uploads.github.com/repos/atom/atom-master-builds/releases/#{release.id}/assets?name=#{assetName}"
|
||||
uri: release.upload_url.replace(/\{.*$/, "?name=#{assetName}")
|
||||
method: 'POST'
|
||||
headers: _.extend({
|
||||
'Content-Type': 'application/zip'
|
||||
@@ -112,14 +150,14 @@ uploadAsset = (release, callback) ->
|
||||
|
||||
assetRequest = request options, (error, response, body='') ->
|
||||
if error? or response.statusCode >= 400
|
||||
console.error('Upload release asset failed', error, body)
|
||||
process.exit(1)
|
||||
logError('Upload release asset failed', error, body)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
callback(release)
|
||||
callback(null, release)
|
||||
|
||||
fs.createReadStream(assetPath).pipe(assetRequest)
|
||||
|
||||
publishRelease = (release) ->
|
||||
publishRelease = (release, callback) ->
|
||||
options =
|
||||
uri: release.url
|
||||
method: 'POST'
|
||||
@@ -128,10 +166,7 @@ publishRelease = (release) ->
|
||||
draft: false
|
||||
request options, (error, response, body={}) ->
|
||||
if error? or response.statusCode isnt 200
|
||||
console.error('Creating release failed', error, body)
|
||||
process.exit(1)
|
||||
|
||||
createRelease (release) ->
|
||||
zipApp ->
|
||||
uploadAsset release, ->
|
||||
publishRelease release
|
||||
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)
|
||||
@@ -4,15 +4,24 @@ path = require 'path'
|
||||
module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'set-development-version', 'Sets version to current SHA-1', ->
|
||||
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()
|
||||
|
||||
cmd = 'git'
|
||||
args = ['rev-parse', '--short', 'HEAD']
|
||||
spawn {cmd, args}, (error, result, code) ->
|
||||
return done(error) if error?
|
||||
getVersion (error, version) ->
|
||||
if error?
|
||||
done(error)
|
||||
return
|
||||
|
||||
version = result.stdout.trim()
|
||||
appDir = grunt.config.get('atom.appDir')
|
||||
|
||||
# Replace version field of package.json.
|
||||
@@ -32,7 +41,7 @@ module.exports = (grunt) ->
|
||||
|
||||
strings =
|
||||
CompanyName: 'GitHub, Inc.'
|
||||
FileDescription: 'The hackable, collaborative editor of tomorrow!'
|
||||
FileDescription: 'The hackable editor'
|
||||
LegalCopyright: 'Copyright (C) 2013 GitHub, Inc. All rights reserved'
|
||||
ProductName: 'Atom'
|
||||
ProductVersion: version
|
||||
@@ -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,37 @@
|
||||
# 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'
|
||||
```
|
||||
|
||||
### 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
|
||||
[Editor]: ../classes/Editor.html
|
||||
[EditorView]: ../classes/EditorView.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)
|
||||
```
|
||||
@@ -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'
|
||||
@@ -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
|
||||
|
||||
+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 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 '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,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
|
||||
@@ -177,32 +177,33 @@ ul.modified-files-list {
|
||||
}
|
||||
```
|
||||
|
||||
We'll add one more line to the end of the `magic` method to make this pane appear:
|
||||
We'll add one more line to the end of the `magic` method to make this pane
|
||||
appear:
|
||||
|
||||
```coffeescript
|
||||
atom.workspaceView.vertical.append(this)
|
||||
atom.workspaceView.prependToBottom(this)
|
||||
```
|
||||
|
||||
If you refresh Atom and hit the key command, you'll see a box appear right underneath
|
||||
the editor:
|
||||
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.
|
||||
As you might have guessed, `atom.workspaceView.prependToBottom` tells Atom to
|
||||
prepend `this` item (_i.e._, whatever is defined by`@content`). If we had called
|
||||
`atom.workspaceView.appendToBottom`, the pane would be attached below the status
|
||||
bar.
|
||||
|
||||
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:
|
||||
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.prependToBottom` call with this code:
|
||||
|
||||
```coffeescript
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
atom.workspaceView.vertical.children().last().remove()
|
||||
@remove()
|
||||
else
|
||||
atom.workspaceView.vertical.append(this)
|
||||
atom.workspaceView.prependToBottom(this)
|
||||
```
|
||||
|
||||
There are about a hundred different ways to toggle a pane on and off, and this
|
||||
@@ -261,13 +262,13 @@ appending it to `modifiedFilesList`:
|
||||
```coffeescript
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
atom.workspaceView.vertical.children().last().remove()
|
||||
@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)
|
||||
atom.workspaceView.prependToBottom(this)
|
||||
```
|
||||
|
||||
When you toggle the modified files list, your pane is now populated with the
|
||||
@@ -283,13 +284,13 @@ this demonstration, we'll just clear the `modifiedFilesList` each time it's clos
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
@modifiedFilesList.empty() # added this to clear the list on close
|
||||
atom.workspaceView.vertical.children().last().remove()
|
||||
@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)
|
||||
atom.workspaceView.prependToBottom(this)
|
||||
```
|
||||
|
||||
## Coloring UI Elements
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
{Document, Model, 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'
|
||||
Directory: require '../src/directory'
|
||||
Document: Document
|
||||
File: require '../src/file'
|
||||
fs: require 'fs-plus'
|
||||
Git: require '../src/git'
|
||||
Model: Model
|
||||
ConfigObserver: require '../src/config-observer'
|
||||
Point: Point
|
||||
Range: Range
|
||||
|
||||
|
||||
+1
-4
@@ -5,7 +5,7 @@
|
||||
'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'
|
||||
|
||||
|
||||
+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,6 +7,9 @@
|
||||
{ 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 Stylesheet', command: 'application:open-your-stylesheet' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Hide Atom', command: 'application:hide' }
|
||||
{ label: 'Hide Others', command: 'application:hide-other-applications' }
|
||||
@@ -99,6 +102,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' }
|
||||
|
||||
@@ -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' }
|
||||
|
||||
+76
-95
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "0.44.0",
|
||||
"version": "0.48.0",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -16,117 +16,99 @@
|
||||
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
|
||||
}
|
||||
],
|
||||
"atomShellVersion": "0.7.6",
|
||||
"atomShellVersion": "0.8.5",
|
||||
"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",
|
||||
"diff": "git://github.com/benogle/jsdiff.git",
|
||||
"emissary": "0.19.0",
|
||||
"first-mate": "0.5.0",
|
||||
"fs-plus": "0.11.0",
|
||||
"fuzzaldrin": "0.1.0",
|
||||
"git-utils": "0.29.0",
|
||||
"delegato": "1.x",
|
||||
"emissary": "1.x",
|
||||
"first-mate": "1.x",
|
||||
"fs-plus": "1.x",
|
||||
"fstream": "0.1.24",
|
||||
"fuzzaldrin": "0.6.0",
|
||||
"git-utils": "0.34.0",
|
||||
"guid": "0.0.10",
|
||||
"jasmine-focused": "~0.15.0",
|
||||
"jasmine-tagged": "0.3.0",
|
||||
"mkdirp": "0.3.5",
|
||||
"keytar": "0.13.0",
|
||||
"less-cache": "0.10.0",
|
||||
"nslog": "0.1.0",
|
||||
"oniguruma": "0.24.0",
|
||||
"keytar": "0.15.1",
|
||||
"less-cache": "0.11.0",
|
||||
"mixto": "1.x",
|
||||
"nslog": "0.3.0",
|
||||
"oniguruma": "1.x",
|
||||
"optimist": "0.4.0",
|
||||
"pathwatcher": "0.11.0",
|
||||
"pegjs": "0.7.0",
|
||||
"pathwatcher": "0.14.2",
|
||||
"pegjs": "0.8.0",
|
||||
"property-accessors": "1.x",
|
||||
"q": "0.9.7",
|
||||
"scandal": "0.8.0",
|
||||
"scandal": "0.13.0",
|
||||
"season": "0.14.0",
|
||||
"semver": "1.1.4",
|
||||
"space-pen": "2.0.2",
|
||||
"telepath": "0.70.0",
|
||||
"serializable": "1.x",
|
||||
"space-pen": "3.1.1",
|
||||
"temp": "0.5.0",
|
||||
"underscore-plus": "0.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"biscotto": "0.0.17",
|
||||
"formidable": "~1.0.14",
|
||||
"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",
|
||||
"github-releases": "~0.2.0"
|
||||
"text-buffer": "0.16.0",
|
||||
"theorist": "1.x",
|
||||
"underscore-plus": "1.x",
|
||||
"vm-compatibility-layer": "0.1.0"
|
||||
},
|
||||
"packageDependencies": {
|
||||
"atom-dark-syntax": "0.10.0",
|
||||
"atom-dark-ui": "0.17.0",
|
||||
"atom-light-syntax": "0.10.0",
|
||||
"atom-light-ui": "0.16.0",
|
||||
"base16-tomorrow-dark-theme": "0.8.0",
|
||||
"solarized-dark-syntax": "0.6.0",
|
||||
"solarized-light-syntax": "0.2.0",
|
||||
"archive-view": "0.16.0",
|
||||
"autocomplete": "0.18.0",
|
||||
"autoflow": "0.11.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.20.0",
|
||||
"autoflow": "0.12.0",
|
||||
"autosave": "0.10.0",
|
||||
"bookmarks": "0.15.0",
|
||||
"bracket-matcher": "0.15.0",
|
||||
"command-logger": "0.8.0",
|
||||
"command-palette": "0.13.0",
|
||||
"dev-live-reload": "0.20.0",
|
||||
"editor-stats": "0.9.0",
|
||||
"exception-reporting": "0.9.0",
|
||||
"feedback": "0.18.0",
|
||||
"find-and-replace": "0.62.0",
|
||||
"fuzzy-finder": "0.28.0",
|
||||
"gists": "0.13.0",
|
||||
"git-diff": "0.21.0",
|
||||
"github-sign-in": "0.15.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.20.0",
|
||||
"package-generator": "0.23.0",
|
||||
"release-notes": "0.15.0",
|
||||
"settings-view": "0.51.0",
|
||||
"snippets": "0.17.0",
|
||||
"spell-check": "0.17.0",
|
||||
"status-bar": "0.27.0",
|
||||
"styleguide": "0.18.0",
|
||||
"symbols-view": "0.27.0",
|
||||
"tabs": "0.16.0",
|
||||
"terminal": "0.23.0",
|
||||
"timecop": "0.11.0",
|
||||
"to-the-hubs": "0.16.0",
|
||||
"tree-view": "0.50.0",
|
||||
"background-tips": "0.5.0",
|
||||
"bookmarks": "0.17.0",
|
||||
"bracket-matcher": "0.19.0",
|
||||
"command-logger": "0.10.0",
|
||||
"command-palette": "0.14.0",
|
||||
"dev-live-reload": "0.23.0",
|
||||
"editor-stats": "0.12.0",
|
||||
"exception-reporting": "0.12.0",
|
||||
"feedback": "0.22.0",
|
||||
"find-and-replace": "0.79.0",
|
||||
"fuzzy-finder": "0.31.0",
|
||||
"gists": "0.15.0",
|
||||
"git-diff": "0.23.0",
|
||||
"github-sign-in": "0.18.0",
|
||||
"go-to-line": "0.15.0",
|
||||
"grammar-selector": "0.17.0",
|
||||
"image-view": "0.17.0",
|
||||
"keybinding-resolver": "0.9.0",
|
||||
"link": "0.15.0",
|
||||
"markdown-preview": "0.25.1",
|
||||
"metrics": "0.21.0",
|
||||
"package-generator": "0.24.0",
|
||||
"release-notes": "0.17.0",
|
||||
"settings-view": "0.57.0",
|
||||
"snippets": "0.20.0",
|
||||
"spell-check": "0.20.0",
|
||||
"status-bar": "0.32.0",
|
||||
"styleguide": "0.21.0",
|
||||
"symbols-view": "0.29.0",
|
||||
"tabs": "0.18.0",
|
||||
"terminal": "0.26.0",
|
||||
"timecop": "0.13.0",
|
||||
"to-the-hubs": "0.17.0",
|
||||
"tree-view": "0.65.0",
|
||||
"visual-bell": "0.6.0",
|
||||
"welcome": "0.4.0",
|
||||
"whitespace": "0.10.0",
|
||||
"wrap-guide": "0.10.0",
|
||||
"wrap-guide": "0.12.0",
|
||||
"language-c": "0.2.0",
|
||||
"language-clojure": "0.1.0",
|
||||
"language-coffee-script": "0.4.0",
|
||||
"language-css": "0.2.0",
|
||||
"language-gfm": "0.10.0",
|
||||
"language-gfm": "0.12.0",
|
||||
"language-git": "0.3.0",
|
||||
"language-go": "0.2.0",
|
||||
"language-html": "0.2.0",
|
||||
@@ -140,12 +122,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.6.0",
|
||||
"language-ruby-on-rails": "0.3.0",
|
||||
"language-ruby": "0.7.0",
|
||||
"language-ruby-on-rails": "0.4.0",
|
||||
"language-sass": "0.3.0",
|
||||
"language-shellscript": "0.2.0",
|
||||
"language-source": "0.2.0",
|
||||
@@ -154,8 +136,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.4.0"
|
||||
"language-yaml": "0.1.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
+18
-5
@@ -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,16 +23,28 @@ function executeCommands(commands, done, index) {
|
||||
done(null);
|
||||
}
|
||||
|
||||
var apmVendorPath = path.resolve(__dirname, '..', 'vendor', 'apm');
|
||||
var apmInstallPath = path.resolve(__dirname, '..', 'apm');
|
||||
if (!fs.existsSync(apmInstallPath))
|
||||
fs.mkdirSync(apmInstallPath);
|
||||
if (!fs.existsSync(path.join(apmInstallPath, 'node_modules')))
|
||||
fs.mkdirSync(path.join(apmInstallPath, 'node_modules'));
|
||||
|
||||
var apmFlags = process.env.JANKY_SHA1 || process.argv.indexOf('--no-color') !== -1 ? '--no-color' : '';
|
||||
var packagesToDedupe = ['humanize-plus', 'nan', 'oniguruma', 'roaster'];
|
||||
var echoNewLine = process.platform == 'win32' ? 'echo.' : 'echo';
|
||||
var commands = [
|
||||
'git submodule --quiet sync',
|
||||
'git submodule --quiet update --recursive --init',
|
||||
{command: 'npm install --quiet .', options: {cwd: path.resolve(__dirname, '..', 'vendor', 'apm'), ignoreStdout: true}},
|
||||
{command: 'npm install --quiet 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 ' + apmFlags,
|
||||
'node node_modules/atom-package-manager/bin/apm install --quiet ' + apmFlags,
|
||||
'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
-4
@@ -1,11 +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_modules/.bin/grunt "$@"
|
||||
var gruntPath = path.join('node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
|
||||
cp.safeSpawn(gruntPath, process.argv.slice(2), 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
-13
@@ -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,18 +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');
|
||||
var gruntPath = path.join('node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
|
||||
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, gruntPath + ' ci --stack --no-color'),
|
||||
cp.safeExec.bind(global, 'node_modules/.bin/coffee script/upload-release')
|
||||
], 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);
|
||||
});
|
||||
})();
|
||||
|
||||
+4
-1
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env node
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
var cp = require('./utils/child-process-wrapper.js');
|
||||
var path = require('path');
|
||||
var os = require('os');
|
||||
@@ -16,6 +16,9 @@ var killatom = process.platform === 'win32' ? 'START taskkill /F /IM ' + product
|
||||
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'],
|
||||
|
||||
@@ -1,16 +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
|
||||
rm -fr vendor/apm/node_modules
|
||||
./script/bootstrap --no-color
|
||||
./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
|
||||
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/grunt-cli/bin/grunt "$@"
|
||||
var gruntPath = path.resolve(__dirname, '..', 'build', 'node_modules', 'grunt-cli', 'bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
|
||||
var args = [gruntPath, '--gruntfile', path.resolve('build', 'Gruntfile.coffee')];
|
||||
args = args.concat(process.argv.slice(2));
|
||||
cp.safeSpawn(process.execPath, args, process.exit);
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env node
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
var safeExec = require('./utils/child-process-wrapper.js').safeExec;
|
||||
var path = require('path');
|
||||
|
||||
|
||||
@@ -442,7 +442,7 @@ describe "the `atom` global", ->
|
||||
describe ".isReleasedVersion()", ->
|
||||
it "returns false if the version is a SHA and true otherwise", ->
|
||||
version = '0.1.0'
|
||||
spyOn(atom, 'getVersion').andCallFake -> version
|
||||
spyOn(atom.constructor, 'getVersion').andCallFake -> version
|
||||
expect(atom.isReleasedVersion()).toBe true
|
||||
version = '36b5518'
|
||||
expect(atom.isReleasedVersion()).toBe false
|
||||
|
||||
@@ -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", ->
|
||||
|
||||
@@ -7,7 +7,7 @@ describe "DisplayBuffer", ->
|
||||
tabLength = 2
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
displayBuffer = atom.create(new DisplayBuffer({buffer, tabLength}))
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength})
|
||||
changeHandler = jasmine.createSpy 'changeHandler'
|
||||
displayBuffer.on 'changed', changeHandler
|
||||
|
||||
@@ -150,7 +150,7 @@ describe "DisplayBuffer", ->
|
||||
describe "when a newline is inserted, deleted, and re-inserted at the end of a wrapped line (regression)", ->
|
||||
it "correctly renders the original wrapped line", ->
|
||||
buffer = atom.project.buildBufferSync(null, '')
|
||||
displayBuffer = atom.create(new DisplayBuffer({buffer, tabLength, editorWidthInChars: 30, softWrap: true}))
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength, editorWidthInChars: 30, softWrap: true})
|
||||
|
||||
buffer.insert([0, 0], "the quick brown fox jumps over the lazy dog.")
|
||||
buffer.insert([0, Infinity], '\n')
|
||||
@@ -202,7 +202,7 @@ describe "DisplayBuffer", ->
|
||||
displayBuffer.destroy()
|
||||
buffer.release()
|
||||
buffer = atom.project.bufferForPathSync('two-hundred.txt')
|
||||
displayBuffer = atom.create(new DisplayBuffer({buffer, tabLength}))
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength})
|
||||
displayBuffer.on 'changed', changeHandler
|
||||
|
||||
describe "when folds are created and destroyed", ->
|
||||
@@ -308,7 +308,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe "when there is another display buffer pointing to the same buffer", ->
|
||||
it "does not create folds in the other display buffer", ->
|
||||
otherDisplayBuffer = atom.create(new DisplayBuffer({buffer, tabLength}))
|
||||
otherDisplayBuffer = new DisplayBuffer({buffer, tabLength})
|
||||
displayBuffer.createFold(2, 4)
|
||||
expect(otherDisplayBuffer.foldsStartingAtBufferRow(2).length).toBe 0
|
||||
|
||||
|
||||
+119
-10
@@ -28,10 +28,7 @@ describe "Editor", ->
|
||||
editor.foldBufferRow(4)
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
|
||||
# Simulate serialization with replicate
|
||||
editor2 = editor.replicate()
|
||||
# FIXME: The created hook is called manually on deserialization because globals aren't ready otherwise
|
||||
editor2.created()
|
||||
editor2 = editor.testSerialization()
|
||||
|
||||
expect(editor2.id).toBe editor.id
|
||||
expect(editor2.getBuffer().getPath()).toBe editor.getBuffer().getPath()
|
||||
@@ -361,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]
|
||||
|
||||
@@ -375,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]
|
||||
|
||||
@@ -395,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", ->
|
||||
@@ -432,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]
|
||||
@@ -797,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]
|
||||
@@ -1159,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]])
|
||||
@@ -2673,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"]
|
||||
|
||||
+435
-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,274 +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 ".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()
|
||||
+408
-606
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()
|
||||
@@ -403,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 = []
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -5,7 +5,7 @@ describe "Selection", ->
|
||||
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
editor = atom.create(new Editor(buffer: buffer, tabLength: 2))
|
||||
editor = new Editor(buffer: buffer, tabLength: 2)
|
||||
selection = editor.getSelection()
|
||||
|
||||
afterEach ->
|
||||
|
||||
@@ -65,3 +65,19 @@ describe "SpacePen extensions", ->
|
||||
|
||||
it "replaces multiple keystroke", ->
|
||||
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')
|
||||
|
||||
+14
-15
@@ -1,23 +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'
|
||||
|
||||
@@ -28,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
|
||||
@@ -51,15 +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 = {}
|
||||
|
||||
serializedWindowState = null
|
||||
spyOn(atom, 'saveWindowState').andCallFake -> serializedWindowState = @getWindowState().serialize()
|
||||
spyOn(atom, 'loadSerializedWindowState').andCallFake -> serializedWindowState
|
||||
|
||||
spyOn(atom, 'saveSync')
|
||||
atom.syntax.clearGrammarOverrides()
|
||||
atom.syntax.clearProperties()
|
||||
|
||||
@@ -111,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
|
||||
@@ -136,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)", ->
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{_, fs} = require 'atom'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
{Site} = require 'telepath'
|
||||
TextBuffer = require '../src/text-buffer'
|
||||
|
||||
describe 'TextBuffer', ->
|
||||
@@ -971,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()
|
||||
@@ -1005,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
|
||||
@@ -1022,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()
|
||||
@@ -1038,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'
|
||||
@@ -38,8 +40,8 @@ describe "ThemeManager", ->
|
||||
|
||||
# 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'])
|
||||
@@ -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"), 'user.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'
|
||||
|
||||
@@ -21,11 +21,11 @@ describe "TokenizedBuffer", ->
|
||||
describe "when the buffer is destroyed", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = atom.create(new TokenizedBuffer({buffer}))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
|
||||
it "stops tokenization", ->
|
||||
tokenizedBuffer.state.destroy()
|
||||
tokenizedBuffer.destroy()
|
||||
spyOn(tokenizedBuffer, 'tokenizeNextChunk')
|
||||
advanceClock()
|
||||
expect(tokenizedBuffer.tokenizeNextChunk).not.toHaveBeenCalled()
|
||||
@@ -33,7 +33,7 @@ describe "TokenizedBuffer", ->
|
||||
describe "when the buffer contains soft-tabs", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = atom.create(new TokenizedBuffer({buffer}))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
|
||||
|
||||
@@ -313,7 +313,7 @@ describe "TokenizedBuffer", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-coffee-script', sync: true)
|
||||
buffer = atom.project.bufferForPathSync('sample-with-tabs.coffee')
|
||||
tokenizedBuffer = atom.create(new TokenizedBuffer({buffer}))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
|
||||
afterEach ->
|
||||
@@ -347,7 +347,7 @@ describe "TokenizedBuffer", ->
|
||||
'abc\uD835\uDF97def'
|
||||
//\uD835\uDF97xyz
|
||||
"""
|
||||
tokenizedBuffer = atom.create(new TokenizedBuffer({buffer}))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
afterEach ->
|
||||
@@ -384,7 +384,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
buffer = atom.project.bufferForPathSync()
|
||||
buffer.setText "<div class='name'><%= User.find(2).full_name %></div>"
|
||||
tokenizedBuffer = atom.create(new TokenizedBuffer({buffer}))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer.setGrammar(atom.syntax.selectGrammar('test.erb'))
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
@@ -403,7 +403,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
it "returns the correct token (regression)", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = atom.create(new TokenizedBuffer({buffer}))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenForPosition([1,0]).scopes).toEqual ["source.js"]
|
||||
expect(tokenizedBuffer.tokenForPosition([1,1]).scopes).toEqual ["source.js"]
|
||||
@@ -412,7 +412,7 @@ describe "TokenizedBuffer", ->
|
||||
describe ".bufferRangeForScopeAtPosition(selector, position)", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = atom.create(new TokenizedBuffer({buffer}))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
describe "when the selector does not match the token at the position", ->
|
||||
@@ -431,7 +431,7 @@ describe "TokenizedBuffer", ->
|
||||
it "updates the tab length of the tokenized lines", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
buffer.setText('\ttest')
|
||||
tokenizedBuffer = atom.create(new TokenizedBuffer({buffer}))
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenForPosition([0,0]).value).toBe ' '
|
||||
atom.config.set('editor.tabLength', 6)
|
||||
|
||||
@@ -8,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()
|
||||
@@ -85,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')
|
||||
|
||||
+202
-310
@@ -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()
|
||||
@@ -19,22 +21,27 @@ describe "WorkspaceView", ->
|
||||
viewState = null
|
||||
|
||||
simulateReload = ->
|
||||
atom.unloadEditorWindow()
|
||||
atom.deserializeEditorWindow()
|
||||
workspaceState = atom.workspace.serialize()
|
||||
projectState = atom.project.serialize()
|
||||
atom.workspaceView.remove()
|
||||
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]
|
||||
|
||||
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", ->
|
||||
@@ -44,41 +51,41 @@ 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()
|
||||
|
||||
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", ->
|
||||
@@ -91,39 +98,12 @@ describe "WorkspaceView", ->
|
||||
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
|
||||
@@ -183,7 +163,7 @@ 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()
|
||||
@@ -206,280 +186,193 @@ describe "WorkspaceView", ->
|
||||
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()
|
||||
[activePane, initialItemCount] = []
|
||||
beforeEach ->
|
||||
activePane = atom.workspaceView.getActivePane()
|
||||
spyOn(activePane, 'focus')
|
||||
initialItemCount = activePane.getItems().length
|
||||
|
||||
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()
|
||||
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()
|
||||
|
||||
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", ->
|
||||
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
|
||||
|
||||
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()
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor).not.toBe previousEditor
|
||||
|
||||
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()
|
||||
editor = atom.workspaceView.openSync(previousEditor.getPath())
|
||||
expect(editor).toBe previousEditor
|
||||
expect(activePane.activeItem).toBe editor
|
||||
|
||||
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')
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when there is an active pane", ->
|
||||
[activePane, initialItemCount] = []
|
||||
beforeEach ->
|
||||
activePane = atom.workspaceView.getActivePane()
|
||||
spyOn(activePane, 'focus')
|
||||
initialItemCount = activePane.getItems().length
|
||||
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 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
|
||||
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('.pane-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('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
describe ".openSingletonSync(filePath, options)", ->
|
||||
[pane1] = []
|
||||
beforeEach ->
|
||||
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('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
pane1.activate()
|
||||
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('.pane-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('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
pane2.activate()
|
||||
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('.pane-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)", ->
|
||||
[activePane] = []
|
||||
|
||||
beforeEach ->
|
||||
spyOn(PaneView.prototype, 'focus')
|
||||
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 edit session item for the path being opened", ->
|
||||
it "shows the existing edit session in the pane", ->
|
||||
previousEditor = activePane.activeItem
|
||||
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 = 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()
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor).not.toBe previousEditor
|
||||
|
||||
describe "when there is an active pane", ->
|
||||
[activePane] = []
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open(previousEditor.getPath()).then (o) -> editor = o
|
||||
|
||||
beforeEach ->
|
||||
activePane = atom.workspaceView.getActivePane()
|
||||
runs ->
|
||||
expect(editor).toBe previousEditor
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
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", ->
|
||||
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().then (o) -> editor = o
|
||||
atom.workspaceView.open('b').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(activePane.getItems().length).toBe 2
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(activePane.getItems().length).toBe 2
|
||||
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 ->
|
||||
@@ -536,38 +429,37 @@ describe "WorkspaceView", ->
|
||||
expect(workspace.getActivePaneItem().getUri()).not.toBeUndefined()
|
||||
|
||||
# destroy all items
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe 'a'
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe 'file1'
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe 'b'
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe 'file1'
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe 'a'
|
||||
pane.destroyActiveItem()
|
||||
|
||||
# reopens items with uris
|
||||
expect(workspace.getActivePaneItem()).toBeUndefined()
|
||||
workspace.reopenItemSync()
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe 'file1'
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe 'a'
|
||||
|
||||
# does not reopen items that are already open
|
||||
workspace.openSync('b')
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe 'b'
|
||||
workspace.reopenItemSync()
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe 'a'
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe 'file1'
|
||||
|
||||
describe "core:close", ->
|
||||
it "closes the active editor until there are none", ->
|
||||
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()).not.toBeDefined()
|
||||
atom.workspaceView.trigger('core:close')
|
||||
expect(atom.workspaceView.getActivePane()).not.toBeDefined()
|
||||
expect(atom.workspaceView.getActivePane().getItems()).toHaveLength 0
|
||||
|
||||
describe "core:save", ->
|
||||
it "saves active editor until there are none", ->
|
||||
editor = atom.project.openSync('../sample.txt')
|
||||
spyOn(editor, 'save')
|
||||
atom.workspaceView.getActivePane().showItem(editor)
|
||||
atom.workspaceView.getActivePane().activateItem(editor)
|
||||
atom.workspaceView.trigger('core:save')
|
||||
expect(editor.save).toHaveBeenCalled()
|
||||
|
||||
@@ -578,6 +470,6 @@ describe "WorkspaceView", ->
|
||||
it "saves active editor until there are none", ->
|
||||
editor = atom.project.openSync('../sample.txt')
|
||||
spyOn(editor, 'saveAs')
|
||||
atom.workspaceView.getActivePane().showItem(editor)
|
||||
atom.workspaceView.getActivePane().activateItem(editor)
|
||||
atom.workspaceView.trigger('core:save-as')
|
||||
expect(editor.saveAs).toHaveBeenCalled()
|
||||
|
||||
@@ -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
|
||||
|
||||
+156
-168
@@ -4,20 +4,16 @@ keytar = require 'keytar'
|
||||
os = require 'os'
|
||||
path = require 'path'
|
||||
remote = require 'remote'
|
||||
screen = require 'screen'
|
||||
shell = require 'shell'
|
||||
dialog = remote.require 'dialog'
|
||||
app = remote.require 'app'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
telepath = require 'telepath'
|
||||
{Document} = telepath
|
||||
{Model} = require 'theorist'
|
||||
fs = require 'fs-plus'
|
||||
{Subscriber} = require 'emissary'
|
||||
|
||||
{$} = require './space-pen-extensions'
|
||||
DeserializerManager = require './deserializer-manager'
|
||||
Package = require './package'
|
||||
SiteShim = require './site-shim'
|
||||
WindowEventHandler = require './window-event-handler'
|
||||
|
||||
# Public: Atom global for dealing with packages, themes, menus, and the window.
|
||||
@@ -37,24 +33,103 @@ WindowEventHandler = require './window-event-handler'
|
||||
# * `atom.syntax` - A {Syntax} instance
|
||||
# * `atom.themes` - A {ThemeManager} instance
|
||||
module.exports =
|
||||
class Atom
|
||||
Subscriber.includeInto(this)
|
||||
class Atom extends Model
|
||||
# Public: Load or create the Atom environment in the given mode.
|
||||
#
|
||||
# - mode: Pass 'editor' or 'spec' depending on the kind of environment you
|
||||
# want to build.
|
||||
#
|
||||
# Returns an Atom instance, fully initialized
|
||||
@loadOrCreate: (mode) ->
|
||||
@deserialize(@loadState(mode)) ? new this({mode, version: @getVersion()})
|
||||
|
||||
# Private: Deserializes the Atom environment from a state object
|
||||
@deserialize: (state) ->
|
||||
new this(state) if state?.version is @getVersion()
|
||||
|
||||
# Private: Loads and returns the serialized state corresponding to this window
|
||||
# if it exists; otherwise returns undefined.
|
||||
@loadState: (mode) ->
|
||||
statePath = @getStatePath(mode)
|
||||
|
||||
if fs.existsSync(statePath)
|
||||
try
|
||||
stateString = fs.readFileSync(statePath, 'utf8')
|
||||
catch error
|
||||
console.warn "Error reading window state: #{statePath}", error.stack, error
|
||||
else
|
||||
stateString = @getLoadSettings().windowState
|
||||
|
||||
try
|
||||
JSON.parse(stateString) if stateString?
|
||||
catch error
|
||||
console.warn "Error parsing window state: #{statePath} #{error.stack}", error
|
||||
|
||||
# Private: Returns the path where the state for the current window will be
|
||||
# located if it exists.
|
||||
@getStatePath: (mode) ->
|
||||
switch mode
|
||||
when 'spec'
|
||||
filename = 'spec'
|
||||
when 'editor'
|
||||
{initialPath} = @getLoadSettings()
|
||||
if initialPath
|
||||
sha1 = crypto.createHash('sha1').update(initialPath).digest('hex')
|
||||
filename = "editor-#{sha1}"
|
||||
|
||||
if filename
|
||||
path.join(@getStorageDirPath(), filename)
|
||||
else
|
||||
null
|
||||
|
||||
# Private: Get the directory path to Atom's configuration area.
|
||||
#
|
||||
# Returns the absolute path to ~/.atom
|
||||
@getConfigDirPath: ->
|
||||
@configDirPath ?= fs.absolute('~/.atom')
|
||||
|
||||
# Private: Get the path to Atom's storage directory.
|
||||
#
|
||||
# Returns the absolute path to ~/.atom/storage
|
||||
@getStorageDirPath: ->
|
||||
@storageDirPath ?= path.join(@getConfigDirPath(), 'storage')
|
||||
|
||||
# Private: Returns the load settings hash associated with the current window.
|
||||
@getLoadSettings: ->
|
||||
_.deepClone(@loadSettings ?= _.deepClone(@getCurrentWindow().loadSettings))
|
||||
|
||||
# Private:
|
||||
constructor: ->
|
||||
@loadTime = null
|
||||
@workspaceViewParentSelector = 'body'
|
||||
@deserializers = new DeserializerManager()
|
||||
@getCurrentWindow: ->
|
||||
remote.getCurrentWindow()
|
||||
|
||||
# Private: Initialize all the properties in this object.
|
||||
# Private: Get the version of the Atom application.
|
||||
@getVersion: ->
|
||||
@version ?= app.getVersion()
|
||||
|
||||
# Private: Determine whether the current version is an official release.
|
||||
@isReleasedVersion: ->
|
||||
not /\w{7}/.test(@getVersion()) # Check if the release is a 7-character SHA prefix
|
||||
|
||||
workspaceViewParentSelector: 'body'
|
||||
|
||||
# Private: Call .loadOrCreate instead
|
||||
constructor: (@state) ->
|
||||
{@mode} = @state
|
||||
DeserializerManager = require './deserializer-manager'
|
||||
@deserializers = new DeserializerManager(this)
|
||||
|
||||
# Public: Sets up the basic services that should be available in all modes
|
||||
# (both spec and application). Call after this instance has been assigned to
|
||||
# the `atom` global.
|
||||
initialize: ->
|
||||
window.onerror = =>
|
||||
@openDevTools()
|
||||
@emit 'uncaught-error', arguments...
|
||||
|
||||
@unsubscribe()
|
||||
@setBodyPlatformClass()
|
||||
|
||||
{devMode, resourcePath} = atom.getLoadSettings()
|
||||
configDirPath = @getConfigDirPath()
|
||||
|
||||
telepath.devMode = not @isReleasedVersion()
|
||||
@loadTime = null
|
||||
|
||||
Config = require './config'
|
||||
Keymap = require './keymap'
|
||||
@@ -64,40 +139,46 @@ class Atom
|
||||
ThemeManager = require './theme-manager'
|
||||
ContextMenuManager = require './context-menu-manager'
|
||||
MenuManager = require './menu-manager'
|
||||
{devMode, resourcePath} = @getLoadSettings()
|
||||
configDirPath = @getConfigDirPath()
|
||||
|
||||
@config = new Config({configDirPath, resourcePath})
|
||||
@keymap = new Keymap({configDirPath, resourcePath})
|
||||
@packages = new PackageManager({devMode, configDirPath, resourcePath})
|
||||
|
||||
@subscribe @packages, 'activated', => @watchThemes()
|
||||
@themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath})
|
||||
@contextMenu = new ContextMenuManager(devMode)
|
||||
@menu = new MenuManager({resourcePath})
|
||||
@pasteboard = new Pasteboard()
|
||||
@syntax = @deserializers.deserialize(@getWindowState('syntax')) ? new Syntax()
|
||||
@syntax = @deserializers.deserialize(@state.syntax) ? new Syntax()
|
||||
|
||||
# Private: This method is called in any window needing a general environment, including specs
|
||||
setUpEnvironment: (@windowMode) ->
|
||||
@initialize()
|
||||
@subscribe @packages, 'activated', => @watchThemes()
|
||||
|
||||
Project = require './project'
|
||||
TextBuffer = require './text-buffer'
|
||||
TokenizedBuffer = require './tokenized-buffer'
|
||||
DisplayBuffer = require './display-buffer'
|
||||
Editor = require './editor'
|
||||
|
||||
@windowEventHandler = new WindowEventHandler
|
||||
|
||||
# Deprecated: Callers should be converted to use atom.deserializers
|
||||
registerRepresentationClass: ->
|
||||
|
||||
# Deprecated: Callers should be converted to use atom.deserializers
|
||||
registerRepresentationClasses: ->
|
||||
|
||||
# Private:
|
||||
setBodyPlatformClass: ->
|
||||
document.body.classList.add("platform-#{process.platform}")
|
||||
|
||||
# Public: Create a new telepath model. We won't need to define this method when
|
||||
# the atom global is a telepath model itself because all model subclasses inherit
|
||||
# a create method.
|
||||
create: (model) ->
|
||||
@site.createDocument(model)
|
||||
|
||||
# Public: Get the current window
|
||||
getCurrentWindow: ->
|
||||
remote.getCurrentWindow()
|
||||
@constructor.getCurrentWindow()
|
||||
|
||||
# Public: Get the dimensions of this window.
|
||||
#
|
||||
# Returns an object with x, y, width, and height keys.
|
||||
getDimensions: ->
|
||||
getWindowDimensions: ->
|
||||
browserWindow = @getCurrentWindow()
|
||||
[x, y] = browserWindow.getPosition()
|
||||
[width, height] = browserWindow.getSize()
|
||||
@@ -106,65 +187,59 @@ class Atom
|
||||
# Public: Set the dimensions of the window.
|
||||
#
|
||||
# The window will be centered if either the x or y coordinate is not set
|
||||
# in the dimensions parameter.
|
||||
# in the dimensions parameter. If x or y are omitted the window will be
|
||||
# centered. If height or width are omitted only the position will be changed.
|
||||
#
|
||||
# * dimensions:
|
||||
# + x:
|
||||
# The new x coordinate.
|
||||
# + y:
|
||||
# The new y coordinate.
|
||||
# + width:
|
||||
# The new width.
|
||||
# + height:
|
||||
# The new height.
|
||||
setDimensions: ({x, y, width, height}) ->
|
||||
# + x: The new x coordinate.
|
||||
# + y: The new y coordinate.
|
||||
# + width: The new width.
|
||||
# + height: The new height.
|
||||
setWindowDimensions: ({x, y, width, height}) ->
|
||||
browserWindow = @getCurrentWindow()
|
||||
browserWindow.setSize(width, height)
|
||||
if width? and height?
|
||||
browserWindow.setSize(width, height)
|
||||
if x? and y?
|
||||
browserWindow.setPosition(x, y)
|
||||
else
|
||||
browserWindow.center()
|
||||
|
||||
# Private:
|
||||
restoreDimensions: ->
|
||||
dimensions = @getWindowState().getObject('dimensions')
|
||||
unless dimensions?.width and dimensions?.height
|
||||
{height, width} = @getLoadSettings().initialSize ? {}
|
||||
height ?= screen.availHeight
|
||||
width ?= Math.min(screen.availWidth, 1024)
|
||||
dimensions = {width, height}
|
||||
@setDimensions(dimensions)
|
||||
restoreWindowDimensions: ->
|
||||
workAreaSize = screen.getPrimaryDisplay().workAreaSize
|
||||
windowDimensions = @state.windowDimensions ? {}
|
||||
{initialSize} = @getLoadSettings()
|
||||
windowDimensions.height ?= initialSize?.height ? workAreaSize.height
|
||||
windowDimensions.width ?= initialSize?.width ? Math.min(workAreaSize.width, 1024)
|
||||
@setWindowDimensions(windowDimensions)
|
||||
|
||||
# Private:
|
||||
storeWindowDimensions: ->
|
||||
@state.windowDimensions = @getWindowDimensions()
|
||||
|
||||
# Public: Get the load settings for the current window.
|
||||
#
|
||||
# Returns an object containing all the load setting key/value pairs.
|
||||
getLoadSettings: ->
|
||||
@loadSettings ?= _.deepClone(@getCurrentWindow().loadSettings)
|
||||
_.deepClone(@loadSettings)
|
||||
@constructor.getLoadSettings()
|
||||
|
||||
# Private:
|
||||
deserializeProject: ->
|
||||
Project = require './project'
|
||||
@project = @getWindowState('project')
|
||||
unless @project instanceof Project
|
||||
@project = new Project(path: @getLoadSettings().initialPath)
|
||||
@setWindowState('project', @project)
|
||||
@project ?= @deserializers.deserialize(@project) ? new Project(path: @getLoadSettings().initialPath)
|
||||
|
||||
# Private:
|
||||
deserializeWorkspaceView: ->
|
||||
Workspace = require './workspace'
|
||||
WorkspaceView = require './workspace-view'
|
||||
state = @getWindowState()
|
||||
@workspaceView = @deserializers.deserialize(state.get('workspaceView'))
|
||||
unless @workspaceView?
|
||||
@workspaceView = new WorkspaceView()
|
||||
state.set('workspaceView', @workspaceView.getState())
|
||||
@workspace = Workspace.deserialize(@state.workspace) ? new Workspace
|
||||
@workspaceView = new WorkspaceView(@workspace)
|
||||
$(@workspaceViewParentSelector).append(@workspaceView)
|
||||
|
||||
# Private:
|
||||
deserializePackageStates: ->
|
||||
state = @getWindowState()
|
||||
@packages.packageStates = state.getObject('packageStates') ? {}
|
||||
state.remove('packageStates')
|
||||
@packages.packageStates = @state.packageStates ? {}
|
||||
delete @state.packageStates
|
||||
|
||||
# Private:
|
||||
deserializeEditorWindow: ->
|
||||
@@ -172,15 +247,14 @@ class Atom
|
||||
@deserializeProject()
|
||||
@deserializeWorkspaceView()
|
||||
|
||||
# Private: This method is only called when opening a real application window
|
||||
# Private: Call this method when establishing a real application window.
|
||||
startEditorWindow: ->
|
||||
if process.platform is 'darwin'
|
||||
CommandInstaller = require './command-installer'
|
||||
CommandInstaller.installAtomCommand()
|
||||
CommandInstaller.installApmCommand()
|
||||
|
||||
@windowEventHandler = new WindowEventHandler
|
||||
@restoreDimensions()
|
||||
@restoreWindowDimensions()
|
||||
@config.load()
|
||||
@config.setDefaults('core', require('./workspace-view').configDefaults)
|
||||
@config.setDefaults('editor', require('./editor-view').configDefaults)
|
||||
@@ -204,29 +278,18 @@ class Atom
|
||||
unloadEditorWindow: ->
|
||||
return if not @project and not @workspaceView
|
||||
|
||||
windowState = @getWindowState()
|
||||
windowState.set('project', @project)
|
||||
windowState.set('syntax', @syntax.serialize())
|
||||
windowState.set('workspaceView', @workspaceView.serialize())
|
||||
@state.syntax = @syntax.serialize()
|
||||
@state.workspace = @workspace.serialize()
|
||||
@packages.deactivatePackages()
|
||||
windowState.set('packageStates', @packages.packageStates)
|
||||
@saveWindowState()
|
||||
@state.packageStates = @packages.packageStates
|
||||
@saveSync()
|
||||
@workspaceView.remove()
|
||||
@workspaceView = null
|
||||
@project.destroy()
|
||||
@windowEventHandler?.unsubscribe()
|
||||
@keymap.destroy()
|
||||
@windowState = null
|
||||
|
||||
# Set up the default event handlers and menus for a non-editor window.
|
||||
#
|
||||
# This can be used by packages to have a minimum level of keybindings and
|
||||
# menus available when not using the standard editor window.
|
||||
#
|
||||
# This should only be called after setUpEnvironment() has been called.
|
||||
setUpDefaultEvents: ->
|
||||
@windowEventHandler = new WindowEventHandler
|
||||
@keymap.loadBundledKeymaps()
|
||||
@menu.update()
|
||||
|
||||
# Private:
|
||||
loadThemes: ->
|
||||
@themes.load()
|
||||
@@ -331,7 +394,7 @@ class Atom
|
||||
setImmediate =>
|
||||
@show()
|
||||
@focus()
|
||||
@setFullScreen(true) if @workspaceView.getState().get('fullScreen')
|
||||
@setFullScreen(true) if @workspaceView.fullScreen
|
||||
|
||||
# Public: Close the current window.
|
||||
close: ->
|
||||
@@ -362,11 +425,11 @@ class Atom
|
||||
|
||||
# Public: Get the version of the Atom application.
|
||||
getVersion: ->
|
||||
@version ?= app.getVersion()
|
||||
@constructor.getVersion()
|
||||
|
||||
# Public: Determine whether the current version is an official release.
|
||||
isReleasedVersion: ->
|
||||
not /\w{7}/.test(@getVersion()) # Check if the release is a 7-character SHA prefix
|
||||
@constructor.isReleasedVersion()
|
||||
|
||||
getGitHubAuthTokenName: ->
|
||||
'Atom GitHub API Token'
|
||||
@@ -383,86 +446,15 @@ class Atom
|
||||
#
|
||||
# Returns the absolute path to ~/.atom
|
||||
getConfigDirPath: ->
|
||||
@configDirPath ?= fs.absolute('~/.atom')
|
||||
|
||||
# Public: Get the directory path to Atom's storage area.
|
||||
#
|
||||
# Returns the absoluste path to ~/.atom/storage
|
||||
getStorageDirPath: ->
|
||||
@storageDirPath ?= path.join(@getConfigDirPath(), 'storage')
|
||||
@constructor.getConfigDirPath()
|
||||
|
||||
# Private:
|
||||
getWindowStatePath: ->
|
||||
switch @windowMode
|
||||
when 'spec'
|
||||
filename = @windowMode
|
||||
when 'editor'
|
||||
{initialPath} = @getLoadSettings()
|
||||
if initialPath
|
||||
sha1 = crypto.createHash('sha1').update(initialPath).digest('hex')
|
||||
filename = "editor-#{sha1}"
|
||||
|
||||
if filename
|
||||
path.join(@getStorageDirPath(), filename)
|
||||
saveSync: ->
|
||||
stateString = JSON.stringify(@state)
|
||||
if statePath = @constructor.getStatePath(@mode)
|
||||
fs.writeFileSync(statePath, stateString, 'utf8')
|
||||
else
|
||||
null
|
||||
|
||||
# Public: Set the window state of the given keypath to the value.
|
||||
setWindowState: (keyPath, value) ->
|
||||
windowState = @getWindowState()
|
||||
windowState.set(keyPath, value)
|
||||
windowState
|
||||
|
||||
# Private
|
||||
loadSerializedWindowState: ->
|
||||
if windowStatePath = @getWindowStatePath()
|
||||
if fs.existsSync(windowStatePath)
|
||||
try
|
||||
documentStateJson = fs.readFileSync(windowStatePath, 'utf8')
|
||||
catch error
|
||||
console.warn "Error reading window state: #{windowStatePath}", error.stack, error
|
||||
else
|
||||
documentStateJson = @getLoadSettings().windowState
|
||||
|
||||
try
|
||||
documentState = JSON.parse(documentStateJson) if documentStateJson
|
||||
catch error
|
||||
console.warn "Error parsing window state: #{windowStatePath}", error.stack, error
|
||||
|
||||
# Private:
|
||||
loadWindowState: ->
|
||||
serializedWindowState = @loadSerializedWindowState()
|
||||
doc = Document.deserialize(serializedWindowState) if serializedWindowState?
|
||||
doc ?= Document.create()
|
||||
doc.registerModelClasses(
|
||||
require('./text-buffer'),
|
||||
require('./project'),
|
||||
require('./tokenized-buffer'),
|
||||
require('./display-buffer'),
|
||||
require('./editor')
|
||||
)
|
||||
# TODO: Remove this when everything is using telepath models
|
||||
if @site?
|
||||
@site.setRootDocument(doc)
|
||||
else
|
||||
@site = new SiteShim(doc)
|
||||
doc
|
||||
|
||||
# Private:
|
||||
saveWindowState: ->
|
||||
windowState = @getWindowState()
|
||||
if windowStatePath = @getWindowStatePath()
|
||||
windowState.saveSync(windowStatePath)
|
||||
else
|
||||
@getCurrentWindow().loadSettings.windowState = JSON.stringify(windowState.serializeForPersistence())
|
||||
|
||||
# Public: Get the window state for the key path.
|
||||
getWindowState: (keyPath) ->
|
||||
@windowState ?= @loadWindowState()
|
||||
if keyPath
|
||||
@windowState.get(keyPath)
|
||||
else
|
||||
@windowState
|
||||
@getCurrentWindow().loadSettings.windowState = stateString
|
||||
|
||||
# Public: Get the time taken to completely load the current window.
|
||||
#
|
||||
@@ -474,10 +466,6 @@ class Atom
|
||||
getWindowLoadTime: ->
|
||||
@loadTime
|
||||
|
||||
# Private: Returns a replicated copy of the current state.
|
||||
replicate: ->
|
||||
@getWindowState().replicate()
|
||||
|
||||
# Private:
|
||||
crashMainProcess: ->
|
||||
remote.process.crash()
|
||||
|
||||
@@ -39,6 +39,10 @@ class AtomApplication
|
||||
createAtomApplication()
|
||||
return
|
||||
|
||||
# The net.connect is slow in atom-shell for now, use this workaround until
|
||||
# atom/atom-shell#159 is fixed.
|
||||
process.activateUvLoop()
|
||||
|
||||
client = net.connect {path: socketPath}, ->
|
||||
client.write JSON.stringify(options), ->
|
||||
client.end()
|
||||
@@ -52,6 +56,8 @@ class AtomApplication
|
||||
resourcePath: null
|
||||
version: null
|
||||
|
||||
exit: (status) -> app.exit(status)
|
||||
|
||||
constructor: (options) ->
|
||||
{@resourcePath, @version, @devMode} = options
|
||||
global.atomApplication = this
|
||||
@@ -71,9 +77,9 @@ class AtomApplication
|
||||
@openWithOptions(options)
|
||||
|
||||
# Private: Opens a new window based on the options provided.
|
||||
openWithOptions: ({pathsToOpen, urlsToOpen, test, pidToKillWhenClosed, devMode, newWindow, specDirectory}) ->
|
||||
openWithOptions: ({pathsToOpen, urlsToOpen, test, pidToKillWhenClosed, devMode, newWindow, specDirectory, logFile}) ->
|
||||
if test
|
||||
@runSpecs({exitWhenDone: true, @resourcePath, specDirectory})
|
||||
@runSpecs({exitWhenDone: true, @resourcePath, specDirectory, logFile})
|
||||
else if pathsToOpen.length > 0
|
||||
@openPaths({pathsToOpen, pidToKillWhenClosed, newWindow, devMode})
|
||||
else if urlsToOpen.length > 0
|
||||
@@ -107,7 +113,7 @@ class AtomApplication
|
||||
|
||||
# Private: Configures required javascript environment flags.
|
||||
setupJavaScriptArguments: ->
|
||||
app.commandLine.appendSwitch 'js-flags', '--harmony_collections'
|
||||
app.commandLine.appendSwitch 'js-flags', '--harmony_collections --harmony-proxies'
|
||||
|
||||
# Private: Enable updates unless running from a local build of Atom.
|
||||
checkForUpdates: ->
|
||||
@@ -140,11 +146,11 @@ class AtomApplication
|
||||
@on 'application:inspect', ({x,y}) -> @focusedWindow().browserWindow.inspectElement(x, y)
|
||||
@on 'application:open-documentation', -> shell.openExternal('https://www.atom.io/docs/latest/?app')
|
||||
@on 'application:report-issue', -> shell.openExternal('https://github.com/atom/atom/issues/new')
|
||||
@on 'application:show-settings', ->
|
||||
if @focusedWindow()
|
||||
@focusedWindow().openPath("atom://config")
|
||||
else
|
||||
@openPath(pathToOpen: "atom://config")
|
||||
|
||||
@openPathOnEvent('application:show-settings', 'atom://config')
|
||||
@openPathOnEvent('application:open-your-stylesheet', 'atom://.atom/stylesheet')
|
||||
@openPathOnEvent('application:open-your-keymap', 'atom://.atom/keymap')
|
||||
@openPathOnEvent('application:open-your-config', 'atom://.atom/config')
|
||||
|
||||
app.on 'window-all-closed', ->
|
||||
app.quit() if process.platform is 'win32'
|
||||
@@ -197,6 +203,20 @@ class AtomApplication
|
||||
unless @emit(command, args...)
|
||||
@focusedWindow()?.sendCommand(command, args...)
|
||||
|
||||
# Public: Open the given path in the focused window when the event is
|
||||
# triggered.
|
||||
#
|
||||
# A new window will be created if there is no currently focused window.
|
||||
#
|
||||
# * eventName: The event to listen for.
|
||||
# * pathToOpen: The path to open when the event is triggered.
|
||||
openPathOnEvent: (eventName, pathToOpen) ->
|
||||
@on eventName, ->
|
||||
if window = @focusedWindow()
|
||||
window.openPath(pathToOpen)
|
||||
else
|
||||
@openPath({pathToOpen})
|
||||
|
||||
# Private: Returns the {AtomWindow} for the given path.
|
||||
windowForPath: (pathToOpen) ->
|
||||
for atomWindow in @windows
|
||||
@@ -326,7 +346,7 @@ class AtomApplication
|
||||
# The path to include specs from.
|
||||
# + specPath:
|
||||
# The directory to load specs from.
|
||||
runSpecs: ({exitWhenDone, resourcePath, specDirectory}) ->
|
||||
runSpecs: ({exitWhenDone, resourcePath, specDirectory, logFile}) ->
|
||||
if resourcePath isnt @resourcePath and not fs.existsSync(resourcePath)
|
||||
resourcePath = @resourcePath
|
||||
|
||||
@@ -337,7 +357,7 @@ class AtomApplication
|
||||
|
||||
isSpec = true
|
||||
devMode = true
|
||||
new AtomWindow({bootstrapScript, resourcePath, exitWhenDone, isSpec, devMode, specDirectory})
|
||||
new AtomWindow({bootstrapScript, resourcePath, exitWhenDone, isSpec, devMode, specDirectory, logFile})
|
||||
|
||||
runBenchmarks: ->
|
||||
try
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
app = require 'app'
|
||||
fs = require 'fs'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
protocol = require 'protocol'
|
||||
|
||||
@@ -11,8 +11,9 @@ module.exports =
|
||||
class AtomProtocolHandler
|
||||
constructor: (@resourcePath) ->
|
||||
@loadPaths = [
|
||||
path.join(@resourcePath, 'node_modules')
|
||||
path.join(app.getHomeDir(), '.atom', 'dev', 'packages')
|
||||
path.join(app.getHomeDir(), '.atom', 'packages')
|
||||
path.join(@resourcePath, 'node_modules')
|
||||
]
|
||||
|
||||
@registerAtomProtocol()
|
||||
@@ -23,5 +24,5 @@ class AtomProtocolHandler
|
||||
relativePath = path.normalize(request.url.substr(7))
|
||||
for loadPath in @loadPaths
|
||||
filePath = path.join(loadPath, relativePath)
|
||||
break if fs.statSyncNoException(filePath)?
|
||||
break if fs.isFileSync(filePath)
|
||||
return new protocol.RequestFileJob(filePath)
|
||||
|
||||
@@ -18,7 +18,7 @@ class AtomWindow
|
||||
isSpec: null
|
||||
|
||||
constructor: (settings={}) ->
|
||||
{@resourcePath, pathToOpen, initialLine, @isSpec} = settings
|
||||
{@resourcePath, pathToOpen, initialLine, @isSpec, @exitWhenDone} = settings
|
||||
global.atomApplication.addWindow(this)
|
||||
|
||||
@setupNodePath(@resourcePath)
|
||||
@@ -30,7 +30,7 @@ class AtomWindow
|
||||
@handleEvents()
|
||||
|
||||
loadSettings = _.extend({}, settings)
|
||||
loadSettings.windowState ?= ''
|
||||
loadSettings.windowState ?= '{}'
|
||||
|
||||
# Only send to the first non-spec window created
|
||||
if @constructor.includeShellLoadTime and not @isSpec
|
||||
@@ -82,6 +82,8 @@ class AtomWindow
|
||||
@browserWindow.destroy() if chosen is 0
|
||||
|
||||
@browserWindow.on 'crashed', =>
|
||||
global.atomApplication.exit(100) if @exitWhenDone
|
||||
|
||||
chosen = dialog.showMessageBox @browserWindow,
|
||||
type: 'warning'
|
||||
buttons: ['Close Window', 'Reload', 'Keep It Open']
|
||||
|
||||
@@ -2,7 +2,6 @@ global.shellStartTime = Date.now()
|
||||
|
||||
autoUpdater = require 'auto-updater'
|
||||
crashReporter = require 'crash-reporter'
|
||||
delegate = require 'atom-delegate'
|
||||
app = require 'app'
|
||||
fs = require 'fs'
|
||||
module = require 'module'
|
||||
@@ -24,7 +23,7 @@ process.on 'uncaughtException', (error={}) ->
|
||||
nslog(error.message) if error.message?
|
||||
nslog(error.stack) if error.stack?
|
||||
|
||||
delegate.browserMainParts.preMainMessageLoopRun = ->
|
||||
start = ->
|
||||
args = parseCommandLine()
|
||||
|
||||
addPathToOpen = (event, pathToOpen) ->
|
||||
@@ -83,6 +82,7 @@ parseCommandLine = ->
|
||||
options.alias('d', 'dev').boolean('d').describe('d', 'Run in development mode.')
|
||||
options.alias('f', 'foreground').boolean('f').describe('f', 'Keep the browser process in the foreground.')
|
||||
options.alias('h', 'help').boolean('h').describe('h', 'Print this usage message.')
|
||||
options.alias('l', 'log-file').string('l').describe('l', 'Log all output to file.')
|
||||
options.alias('n', 'new-window').boolean('n').describe('n', 'Open a new window.')
|
||||
options.alias('s', 'spec-directory').string('s').describe('s', 'Set the directory from which specs are loaded (default: Atom\'s spec directory).')
|
||||
options.alias('t', 'test').boolean('t').describe('t', 'Run the specified specs and exit with error code on failures.')
|
||||
@@ -106,6 +106,7 @@ parseCommandLine = ->
|
||||
specDirectory = args['spec-directory']
|
||||
newWindow = args['new-window']
|
||||
pidToKillWhenClosed = args['pid'] if args['wait']
|
||||
logFile = args['log-file']
|
||||
|
||||
if args['resource-path']
|
||||
devMode = true
|
||||
@@ -119,4 +120,6 @@ parseCommandLine = ->
|
||||
devMode = false
|
||||
resourcePath = path.dirname(path.dirname(__dirname))
|
||||
|
||||
{resourcePath, pathsToOpen, executedFrom, test, version, pidToKillWhenClosed, devMode, newWindow, specDirectory}
|
||||
{resourcePath, pathsToOpen, executedFrom, test, version, pidToKillWhenClosed, devMode, newWindow, specDirectory, logFile}
|
||||
|
||||
start()
|
||||
|
||||
@@ -6,6 +6,12 @@ path = require 'path'
|
||||
#
|
||||
# This may seem unnecessary but on Windows we have to have separate executables
|
||||
# for each script without this since Windows doesn't support shebang strings.
|
||||
#
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {BufferedNodeProcess} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class BufferedNodeProcess extends BufferedProcess
|
||||
# Executes the given Node script.
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
ChildProcess = require 'child_process'
|
||||
|
||||
# Public: A wrapper which provides line buffering for Node's ChildProcess.
|
||||
#
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {BufferedProcess} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class BufferedProcess
|
||||
process: null
|
||||
|
||||
@@ -22,7 +22,10 @@ getCachedJavaScript = (cachePath) ->
|
||||
fs.readFileSync(cachePath, 'utf8') if stat.isFile()
|
||||
|
||||
compileCoffeeScript = (coffee, filePath, cachePath) ->
|
||||
js = CoffeeScript.compile(coffee, filename: filePath)
|
||||
{js,v3SourceMap} = CoffeeScript.compile(coffee, filename: filePath, sourceMap: true)
|
||||
# Include source map in the web page environment.
|
||||
if btoa? and JSON? and unescape? and encodeURIComponent?
|
||||
js = "#{js}\n//# sourceMappingURL=data:application/json;base64,#{btoa unescape encodeURIComponent v3SourceMap}\n//# sourceURL=#{filePath}"
|
||||
try
|
||||
mkdir(path.dirname(cachePath))
|
||||
fs.writeFileSync(cachePath, js)
|
||||
|
||||
@@ -64,5 +64,5 @@ module.exports =
|
||||
resourcePath = null
|
||||
|
||||
resourcePath ?= atom.getLoadSettings().resourcePath
|
||||
commandPath = path.join(resourcePath, 'node_modules', '.bin', 'apm')
|
||||
commandPath = path.join(resourcePath, 'apm', 'node_modules', '.bin', 'apm')
|
||||
@install(commandPath, callback)
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
Mixin = require 'mixto'
|
||||
|
||||
module.exports =
|
||||
class ConfigObserver extends Mixin
|
||||
observeConfig: (keyPath, args...) ->
|
||||
@configSubscriptions ?= {}
|
||||
@configSubscriptions[keyPath] = atom.config.observe(keyPath, args...)
|
||||
|
||||
@@ -98,6 +98,10 @@ class Config
|
||||
_.extend hash, defaults
|
||||
@update()
|
||||
|
||||
# Public: Get the path to the config file being used.
|
||||
getUserConfigPath: ->
|
||||
@configFilePath
|
||||
|
||||
# Public: Returns a new {Object} containing all of settings and defaults.
|
||||
getSettings: ->
|
||||
_.deepExtend(@settings, @defaultSettings)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{View} = require './space-pen-extensions'
|
||||
{Point, Range} = require 'telepath'
|
||||
{Point, Range} = require 'text-buffer'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
### Internal ###
|
||||
@@ -30,9 +30,6 @@ class CursorView extends View
|
||||
@cursor.on 'destroyed.cursor-view', =>
|
||||
@needsRemoval = true
|
||||
|
||||
if @cursor.marker.isRemote()
|
||||
@addClass("site-#{@cursor.marker.getOriginSiteId()}")
|
||||
|
||||
beforeRemove: ->
|
||||
@editorView.removeCursorView(this)
|
||||
@cursor.off('.cursor-view')
|
||||
@@ -52,7 +49,7 @@ class CursorView extends View
|
||||
else if !@startBlinkingTimeout
|
||||
@startBlinking()
|
||||
|
||||
@setVisible(@cursor.isVisible() and not @editorView.isFoldedAtScreenRow(screenPosition.row))
|
||||
@setVisible(@cursor.isVisible() and not @editorView.getEditor().isFoldedAtScreenRow(screenPosition.row))
|
||||
|
||||
# Override for speed. The base function checks the computedStyle
|
||||
isHidden: ->
|
||||
|
||||
+14
-8
@@ -1,4 +1,4 @@
|
||||
{Point, Range} = require 'telepath'
|
||||
{Point, Range} = require 'text-buffer'
|
||||
{Emitter} = require 'emissary'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
@@ -6,7 +6,7 @@ _ = require 'underscore-plus'
|
||||
# where text can be inserted.
|
||||
#
|
||||
# Cursors belong to {Editor}s and have some metadata attached in the form
|
||||
# of a {StringMarker}.
|
||||
# of a {Marker}.
|
||||
module.exports =
|
||||
class Cursor
|
||||
Emitter.includeInto(this)
|
||||
@@ -250,10 +250,14 @@ class Cursor
|
||||
moveToBottom: ->
|
||||
@setBufferPosition(@editor.getEofBufferPosition())
|
||||
|
||||
# Public: Moves the cursor to the beginning of the screen line.
|
||||
moveToBeginningOfLine: ->
|
||||
# Public: Moves the cursor to the beginning of the line.
|
||||
moveToBeginningOfScreenLine: ->
|
||||
@setScreenPosition([@getScreenRow(), 0])
|
||||
|
||||
# Public: Moves the cursor to the beginning of the buffer line.
|
||||
moveToBeginningOfLine: ->
|
||||
@setBufferPosition([@getBufferRow(), 0])
|
||||
|
||||
# Public: Moves the cursor to the beginning of the first character in the
|
||||
# line.
|
||||
moveToFirstCharacterOfLine: ->
|
||||
@@ -261,9 +265,7 @@ class Cursor
|
||||
screenline = @editor.lineForScreenRow(row)
|
||||
|
||||
goalColumn = screenline.text.search(/\S/)
|
||||
return if goalColumn == -1
|
||||
|
||||
goalColumn = 0 if goalColumn == column
|
||||
goalColumn = 0 if goalColumn == column or goalColumn == -1
|
||||
@setScreenPosition([row, goalColumn])
|
||||
|
||||
# Public: Moves the cursor to the beginning of the buffer line, skipping all
|
||||
@@ -277,9 +279,13 @@ class Cursor
|
||||
|
||||
@setBufferPosition(endOfLeadingWhitespace) if endOfLeadingWhitespace.isGreaterThan(position)
|
||||
|
||||
# Public: Moves the cursor to the end of the line.
|
||||
moveToEndOfScreenLine: ->
|
||||
@setScreenPosition([@getScreenRow(), Infinity])
|
||||
|
||||
# Public: Moves the cursor to the end of the buffer line.
|
||||
moveToEndOfLine: ->
|
||||
@setScreenPosition([@getScreenRow(), Infinity])
|
||||
@setBufferPosition([@getBufferRow(), Infinity])
|
||||
|
||||
# Public: Moves the cursor to the beginning of the word.
|
||||
moveToBeginningOfWord: ->
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
{Document, Model} = require 'telepath'
|
||||
|
||||
# Public: Manages the deserializers used for serialized state
|
||||
#
|
||||
# Should be accessed via `atom.deserializers`
|
||||
module.exports =
|
||||
class DeserializerManager
|
||||
constructor: ->
|
||||
constructor: (@environment) ->
|
||||
@deserializers = {}
|
||||
@deferredDeserializers = {}
|
||||
|
||||
@@ -25,13 +23,9 @@ class DeserializerManager
|
||||
deserialize: (state, params) ->
|
||||
return unless state?
|
||||
|
||||
return state if state instanceof Model
|
||||
|
||||
if deserializer = @get(state)
|
||||
stateVersion = state.get?('version') ? state.version
|
||||
return if deserializer.version? and deserializer.version isnt stateVersion
|
||||
if (state instanceof Document) and not deserializer.acceptsDocuments
|
||||
state = state.toObject()
|
||||
deserializer.deserialize(state, params)
|
||||
else
|
||||
console.warn "No deserializer found for", state
|
||||
|
||||
+56
-19
@@ -1,10 +1,19 @@
|
||||
path = require 'path'
|
||||
|
||||
async = require 'async'
|
||||
{Emitter} = require 'emissary'
|
||||
fs = require 'fs-plus'
|
||||
pathWatcher = require 'pathwatcher'
|
||||
File = require './file'
|
||||
{Emitter} = require 'emissary'
|
||||
|
||||
# Public: Represents a directory using {File}s
|
||||
File = require './file'
|
||||
|
||||
# Public: Represents a directory using {File}s.
|
||||
#
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {Directory} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class Directory
|
||||
Emitter.includeInto(this)
|
||||
@@ -41,7 +50,7 @@ class Directory
|
||||
#
|
||||
# All relative directory entries are removed and symlinks are resolved to
|
||||
# their final destination.
|
||||
getRealPath: ->
|
||||
getRealPathSync: ->
|
||||
unless @realPath?
|
||||
try
|
||||
@realPath = fs.realpathSync(@path)
|
||||
@@ -56,7 +65,7 @@ class Directory
|
||||
|
||||
if pathToCheck.indexOf(path.join(@getPath(), path.sep)) is 0
|
||||
true
|
||||
else if pathToCheck.indexOf(path.join(@getRealPath(), path.sep)) is 0
|
||||
else if pathToCheck.indexOf(path.join(@getRealPathSync(), path.sep)) is 0
|
||||
true
|
||||
else
|
||||
false
|
||||
@@ -70,30 +79,26 @@ class Directory
|
||||
|
||||
if fullPath is @getPath()
|
||||
''
|
||||
else if fullPath.indexOf(path.join(@getPath(), path.sep)) is 0
|
||||
else if @isPathPrefixOf(@getPath(), fullPath)
|
||||
fullPath.substring(@getPath().length + 1)
|
||||
else if fullPath is @getRealPath()
|
||||
else if fullPath is @getRealPathSync()
|
||||
''
|
||||
else if fullPath.indexOf(path.join(@getRealPath(), path.sep)) is 0
|
||||
fullPath.substring(@getRealPath().length + 1)
|
||||
else if @isPathPrefixOf(@getRealPathSync(), fullPath)
|
||||
fullPath.substring(@getRealPathSync().length + 1)
|
||||
else
|
||||
fullPath
|
||||
|
||||
# Public: Reads file entries in this directory from disk.
|
||||
# Public: Reads file entries in this directory from disk synchronously.
|
||||
#
|
||||
# Note: It follows symlinks.
|
||||
#
|
||||
# Returns an Array of {Files}.
|
||||
getEntries: ->
|
||||
# Returns an {Array} of {File} and {Directory} objects.
|
||||
getEntriesSync: ->
|
||||
directories = []
|
||||
files = []
|
||||
for entryPath in fs.listSync(@path)
|
||||
try
|
||||
stat = fs.lstatSync(entryPath)
|
||||
if stat = fs.lstatSyncNoException(entryPath)
|
||||
symlink = stat.isSymbolicLink()
|
||||
stat = fs.statSync(entryPath) if symlink
|
||||
catch e
|
||||
continue
|
||||
stat = fs.statSyncNoException(entryPath) if symlink
|
||||
continue unless stat
|
||||
if stat.isDirectory()
|
||||
directories.push(new Directory(entryPath, symlink))
|
||||
else if stat.isFile()
|
||||
@@ -101,6 +106,34 @@ class Directory
|
||||
|
||||
directories.concat(files)
|
||||
|
||||
# Public: Reads file entries in this directory from disk asynchronously.
|
||||
#
|
||||
# * callback: A function to call with an Error as the first argument and
|
||||
# an {Array} of {File} and {Directory} objects as the second argument.
|
||||
getEntries: (callback) ->
|
||||
fs.list @path, (error, entries) ->
|
||||
return callback(error) if error?
|
||||
|
||||
directories = []
|
||||
files = []
|
||||
addEntry = (entryPath, stat, symlink, callback) ->
|
||||
if stat?.isDirectory()
|
||||
directories.push(new Directory(entryPath, symlink))
|
||||
else if stat?.isFile()
|
||||
files.push(new File(entryPath, symlink))
|
||||
callback()
|
||||
|
||||
statEntry = (entryPath, callback) ->
|
||||
fs.lstat entryPath, (error, stat) ->
|
||||
if stat?.isSymbolicLink()
|
||||
fs.stat entryPath, (error, stat) ->
|
||||
addEntry(entryPath, stat, true, callback)
|
||||
else
|
||||
addEntry(entryPath, stat, false, callback)
|
||||
|
||||
async.eachLimit entries, 1, statEntry, ->
|
||||
callback(null, directories.concat(files))
|
||||
|
||||
# Private:
|
||||
subscribeToNativeChangeEvents: ->
|
||||
unless @watchSubscription?
|
||||
@@ -112,3 +145,7 @@ class Directory
|
||||
if @watchSubscription?
|
||||
@watchSubscription.close()
|
||||
@watchSubscription = null
|
||||
|
||||
# Private: Does given full path start with the given prefix?
|
||||
isPathPrefixOf: (prefix, fullPath) ->
|
||||
fullPath.indexOf(prefix) is 0 and fullPath[prefix.length] is path.sep
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{Range} = require 'telepath'
|
||||
{Range} = require 'text-buffer'
|
||||
_ = require 'underscore-plus'
|
||||
{Emitter, Subscriber} = require 'emissary'
|
||||
|
||||
@@ -42,7 +42,7 @@ class DisplayBufferMarker
|
||||
# Modifies the screen range of the display marker.
|
||||
#
|
||||
# screenRange - The new {Range} to use
|
||||
# options - A hash of options matching those found in {StringMarker.setRange}
|
||||
# options - A hash of options matching those found in {Marker.setRange}
|
||||
setScreenRange: (screenRange, options) ->
|
||||
@setBufferRange(@displayBuffer.bufferRangeForScreenRange(screenRange), options)
|
||||
|
||||
@@ -55,7 +55,7 @@ class DisplayBufferMarker
|
||||
# Modifies the buffer range of the display marker.
|
||||
#
|
||||
# screenRange - The new {Range} to use
|
||||
# options - A hash of options matching those found in {StringMarker.setRange}
|
||||
# options - A hash of options matching those found in {Marker.setRange}
|
||||
setBufferRange: (bufferRange, options) ->
|
||||
@bufferMarker.setRange(bufferRange, options)
|
||||
|
||||
@@ -140,19 +140,10 @@ class DisplayBufferMarker
|
||||
# Returns a {Boolean} indicating whether the marker has been destroyed. A marker
|
||||
# can be invalid without being destroyed, in which case undoing the invalidating
|
||||
# operation would restore the marker. Once a marker is destroyed by calling
|
||||
# {StringMarker.destroy}, no undo/redo operation can ever bring it back.
|
||||
# {Marker.destroy}, no undo/redo operation can ever bring it back.
|
||||
isDestroyed: ->
|
||||
@bufferMarker.isDestroyed()
|
||||
|
||||
getOriginSiteId: ->
|
||||
@bufferMarker.getOriginSiteId()
|
||||
|
||||
isLocal: ->
|
||||
@bufferMarker.isLocal()
|
||||
|
||||
isRemote: ->
|
||||
@bufferMarker.isRemote()
|
||||
|
||||
getAttributes: ->
|
||||
@bufferMarker.getAttributes()
|
||||
|
||||
@@ -160,7 +151,7 @@ class DisplayBufferMarker
|
||||
@bufferMarker.setAttributes(attributes)
|
||||
|
||||
matchesAttributes: (attributes) ->
|
||||
attributes = @displayBuffer.translateToStringMarkerAttributes(attributes)
|
||||
attributes = @displayBuffer.translateToBufferMarkerAttributes(attributes)
|
||||
@bufferMarker.matchesAttributes(attributes)
|
||||
|
||||
# Destroys the marker
|
||||
@@ -177,7 +168,7 @@ class DisplayBufferMarker
|
||||
|
||||
# Returns a {String} representation of the marker
|
||||
inspect: ->
|
||||
"DisplayBufferMarker(id: #{@id}, bufferRange: #{@getBufferRange().inspect()})"
|
||||
"DisplayBufferMarker(id: #{@id}, bufferRange: #{@getBufferRange()})"
|
||||
|
||||
### Internal ###
|
||||
|
||||
@@ -194,13 +185,11 @@ class DisplayBufferMarker
|
||||
newTailScreenPosition = @getTailScreenPosition()
|
||||
isValid = @isValid()
|
||||
|
||||
changed = false
|
||||
changed = true unless _.isEqual(newHeadBufferPosition, @oldHeadBufferPosition)
|
||||
changed = true unless _.isEqual(newHeadScreenPosition, @oldHeadScreenPosition)
|
||||
changed = true unless _.isEqual(newTailBufferPosition, @oldTailBufferPosition)
|
||||
changed = true unless _.isEqual(newTailScreenPosition, @oldTailScreenPosition)
|
||||
changed = true unless _.isEqual(isValid, @wasValid)
|
||||
return unless changed
|
||||
return if _.isEqual(isValid, @wasValid) and
|
||||
_.isEqual(newHeadBufferPosition, @oldHeadBufferPosition) and
|
||||
_.isEqual(newHeadScreenPosition, @oldHeadScreenPosition) and
|
||||
_.isEqual(newTailBufferPosition, @oldTailBufferPosition) and
|
||||
_.isEqual(newTailScreenPosition, @oldTailScreenPosition)
|
||||
|
||||
@emit 'changed', {
|
||||
@oldHeadScreenPosition, newHeadScreenPosition,
|
||||
|
||||
+30
-29
@@ -1,7 +1,9 @@
|
||||
_ = require 'underscore-plus'
|
||||
{Emitter, Subscriber} = require 'emissary'
|
||||
guid = require 'guid'
|
||||
{Model, Point, Range} = require 'telepath'
|
||||
Serializable = require 'serializable'
|
||||
{Model} = require 'theorist'
|
||||
{Point, Range} = require 'text-buffer'
|
||||
TokenizedBuffer = require './tokenized-buffer'
|
||||
RowMap = require './row-map'
|
||||
Fold = require './fold'
|
||||
@@ -12,28 +14,18 @@ ConfigObserver = require './config-observer'
|
||||
# Private:
|
||||
module.exports =
|
||||
class DisplayBuffer extends Model
|
||||
_.extend @prototype, ConfigObserver
|
||||
Serializable.includeInto(this)
|
||||
ConfigObserver.includeInto(this)
|
||||
|
||||
@properties
|
||||
tokenizedBuffer: null
|
||||
softWrap: -> atom.config.get('editor.softWrap') ? false
|
||||
softWrap: null
|
||||
editorWidthInChars: null
|
||||
|
||||
constructor: ->
|
||||
constructor: ({tabLength, @editorWidthInChars, @tokenizedBuffer, buffer}={}) ->
|
||||
super
|
||||
@deserializing = @state?
|
||||
|
||||
created: ->
|
||||
if @deserializing
|
||||
@deserializing = false
|
||||
return
|
||||
|
||||
if @tokenizedBuffer?
|
||||
@tokenizedBuffer?.created()
|
||||
else
|
||||
@tokenizedBuffer = new TokenizedBuffer({@tabLength, @buffer, project: atom.project})
|
||||
@softWrap ?= atom.config.get('editor.softWrap') ? false
|
||||
@tokenizedBuffer ?= new TokenizedBuffer({tabLength, buffer})
|
||||
@buffer = @tokenizedBuffer.buffer
|
||||
|
||||
@markers = {}
|
||||
@foldsByMarkerId = {}
|
||||
@updateAllScreenLines()
|
||||
@@ -43,10 +35,9 @@ class DisplayBuffer extends Model
|
||||
@subscribe @buffer, 'markers-updated', @handleBufferMarkersUpdated
|
||||
@subscribe @buffer, 'marker-created', @handleBufferMarkerCreated
|
||||
|
||||
@subscribe @state, 'changed', ({newValues}) =>
|
||||
if newValues.softWrap?
|
||||
@emit 'soft-wrap-changed', newValues.softWrap
|
||||
@updateWrappedScreenLines()
|
||||
@subscribe @$softWrap, (softWrap) =>
|
||||
@emit 'soft-wrap-changed', softWrap
|
||||
@updateWrappedScreenLines()
|
||||
|
||||
@observeConfig 'editor.preferredLineLength', callNow: false, =>
|
||||
@updateWrappedScreenLines() if @softWrap and atom.config.get('editor.softWrapAtPreferredLineLength')
|
||||
@@ -54,8 +45,18 @@ class DisplayBuffer extends Model
|
||||
@observeConfig 'editor.softWrapAtPreferredLineLength', callNow: false, =>
|
||||
@updateWrappedScreenLines() if @softWrap
|
||||
|
||||
serializeParams: ->
|
||||
id: @id
|
||||
softWrap: @softWrap
|
||||
editorWidthInChars: @editorWidthInChars
|
||||
tokenizedBuffer: @tokenizedBuffer.serialize()
|
||||
|
||||
deserializeParams: (params) ->
|
||||
params.tokenizedBuffer = TokenizedBuffer.deserialize(params.tokenizedBuffer)
|
||||
params
|
||||
|
||||
copy: ->
|
||||
newDisplayBuffer = atom.create(new DisplayBuffer({@buffer, tabLength: @getTabLength()}))
|
||||
newDisplayBuffer = new DisplayBuffer({@buffer, tabLength: @getTabLength()})
|
||||
for marker in @findMarkers(displayBufferId: @id)
|
||||
marker.copy(displayBufferId: newDisplayBuffer.id)
|
||||
newDisplayBuffer
|
||||
@@ -358,7 +359,7 @@ class DisplayBuffer extends Model
|
||||
|
||||
# Get the grammar for this buffer.
|
||||
#
|
||||
# Returns the current {TextMateGrammar} or the {NullGrammar}.
|
||||
# Returns the current {Grammar} or the {NullGrammar}.
|
||||
getGrammar: ->
|
||||
@tokenizedBuffer.grammar
|
||||
|
||||
@@ -468,7 +469,7 @@ class DisplayBuffer extends Model
|
||||
# Constructs a new marker at the given screen range.
|
||||
#
|
||||
# range - The marker {Range} (representing the distance between the head and tail)
|
||||
# options - Options to pass to the {StringMarker} constructor
|
||||
# options - Options to pass to the {Marker} constructor
|
||||
#
|
||||
# Returns a {Number} representing the new marker's ID.
|
||||
markScreenRange: (args...) ->
|
||||
@@ -478,7 +479,7 @@ class DisplayBuffer extends Model
|
||||
# Constructs a new marker at the given buffer range.
|
||||
#
|
||||
# range - The marker {Range} (representing the distance between the head and tail)
|
||||
# options - Options to pass to the {StringMarker} constructor
|
||||
# options - Options to pass to the {Marker} constructor
|
||||
#
|
||||
# Returns a {Number} representing the new marker's ID.
|
||||
markBufferRange: (args...) ->
|
||||
@@ -487,7 +488,7 @@ class DisplayBuffer extends Model
|
||||
# Constructs a new marker at the given screen position.
|
||||
#
|
||||
# range - The marker {Range} (representing the distance between the head and tail)
|
||||
# options - Options to pass to the {StringMarker} constructor
|
||||
# options - Options to pass to the {Marker} constructor
|
||||
#
|
||||
# Returns a {Number} representing the new marker's ID.
|
||||
markScreenPosition: (screenPosition, options) ->
|
||||
@@ -496,7 +497,7 @@ class DisplayBuffer extends Model
|
||||
# Constructs a new marker at the given buffer position.
|
||||
#
|
||||
# range - The marker {Range} (representing the distance between the head and tail)
|
||||
# options - Options to pass to the {StringMarker} constructor
|
||||
# options - Options to pass to the {Marker} constructor
|
||||
#
|
||||
# Returns a {Number} representing the new marker's ID.
|
||||
markBufferPosition: (bufferPosition, options) ->
|
||||
@@ -526,10 +527,10 @@ class DisplayBuffer extends Model
|
||||
#
|
||||
# Returns an {Array} of {DisplayBufferMarker}s
|
||||
findMarkers: (attributes) ->
|
||||
attributes = @translateToStringMarkerAttributes(attributes)
|
||||
attributes = @translateToBufferMarkerAttributes(attributes)
|
||||
@buffer.findMarkers(attributes).map (stringMarker) => @getMarker(stringMarker.id)
|
||||
|
||||
translateToStringMarkerAttributes: (attributes) ->
|
||||
translateToBufferMarkerAttributes: (attributes) ->
|
||||
stringMarkerAttributes = {}
|
||||
for key, value of attributes
|
||||
switch key
|
||||
|
||||
+154
-504
@@ -1,7 +1,7 @@
|
||||
{View, $, $$$} = require './space-pen-extensions'
|
||||
TextBuffer = require './text-buffer'
|
||||
Gutter = require './gutter'
|
||||
{Point, Range} = require 'telepath'
|
||||
{Point, Range} = require 'text-buffer'
|
||||
Editor = require './editor'
|
||||
CursorView = require './cursor-view'
|
||||
SelectionView = require './selection-view'
|
||||
@@ -16,6 +16,12 @@ LongLineLength = 1000
|
||||
# Public: Represents the entire visual pane in Atom.
|
||||
#
|
||||
# The EditorView manages the {Editor}, which manages the file buffers.
|
||||
#
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {EditorView} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class EditorView extends View
|
||||
@characterWidthCache: {}
|
||||
@@ -105,12 +111,12 @@ class EditorView extends View
|
||||
if editor?
|
||||
@edit(editor)
|
||||
else if @mini
|
||||
@edit(atom.create(new Editor
|
||||
buffer: atom.create(new TextBuffer)
|
||||
@edit(new Editor
|
||||
buffer: new TextBuffer
|
||||
softWrap: false
|
||||
tabLength: 2
|
||||
softTabs: true
|
||||
))
|
||||
)
|
||||
else
|
||||
throw new Error("Must supply an Editor or mini: true")
|
||||
|
||||
@@ -119,390 +125,118 @@ class EditorView extends View
|
||||
# Some commands are excluded from mini-editors.
|
||||
bindKeys: ->
|
||||
editorBindings =
|
||||
'core:move-left': @moveCursorLeft
|
||||
'core:move-right': @moveCursorRight
|
||||
'core:select-left': @selectLeft
|
||||
'core:select-right': @selectRight
|
||||
'core:select-all': @selectAll
|
||||
'core:backspace': @backspace
|
||||
'core:delete': @delete
|
||||
'core:undo': @undo
|
||||
'core:redo': @redo
|
||||
'core:cut': @cutSelection
|
||||
'core:copy': @copySelection
|
||||
'core:paste': @paste
|
||||
'editor:move-to-previous-word': @moveCursorToPreviousWord
|
||||
'editor:select-word': @selectWord
|
||||
'editor:consolidate-selections': @consolidateSelections
|
||||
'editor:backspace-to-beginning-of-word': @backspaceToBeginningOfWord
|
||||
'editor:backspace-to-beginning-of-line': @backspaceToBeginningOfLine
|
||||
'editor:delete-to-end-of-word': @deleteToEndOfWord
|
||||
'editor:delete-line': @deleteLine
|
||||
'editor:cut-to-end-of-line': @cutToEndOfLine
|
||||
'editor:move-to-beginning-of-line': @moveCursorToBeginningOfLine
|
||||
'editor:move-to-end-of-line': @moveCursorToEndOfLine
|
||||
'editor:move-to-first-character-of-line': @moveCursorToFirstCharacterOfLine
|
||||
'editor:move-to-beginning-of-word': @moveCursorToBeginningOfWord
|
||||
'editor:move-to-end-of-word': @moveCursorToEndOfWord
|
||||
'editor:move-to-beginning-of-next-word': @moveCursorToBeginningOfNextWord
|
||||
'editor:move-to-previous-word-boundary': @moveCursorToPreviousWordBoundary
|
||||
'editor:move-to-next-word-boundary': @moveCursorToNextWordBoundary
|
||||
'editor:select-to-end-of-line': @selectToEndOfLine
|
||||
'editor:select-to-beginning-of-line': @selectToBeginningOfLine
|
||||
'editor:select-to-end-of-word': @selectToEndOfWord
|
||||
'editor:select-to-beginning-of-word': @selectToBeginningOfWord
|
||||
'editor:select-to-beginning-of-next-word': @selectToBeginningOfNextWord
|
||||
'editor:select-to-next-word-boundary': @selectToNextWordBoundary
|
||||
'editor:select-to-previous-word-boundary': @selectToPreviousWordBoundary
|
||||
'editor:select-to-first-character-of-line': @selectToFirstCharacterOfLine
|
||||
'editor:select-line': @selectLine
|
||||
'editor:transpose': @transpose
|
||||
'editor:upper-case': @upperCase
|
||||
'editor:lower-case': @lowerCase
|
||||
'core:move-left': => @editor.moveCursorLeft()
|
||||
'core:move-right': => @editor.moveCursorRight()
|
||||
'core:select-left': => @editor.selectLeft()
|
||||
'core:select-right': => @editor.selectRight()
|
||||
'core:select-all': => @editor.selectAll()
|
||||
'core:backspace': => @editor.backspace()
|
||||
'core:delete': => @editor.delete()
|
||||
'core:undo': => @editor.undo()
|
||||
'core:redo': => @editor.redo()
|
||||
'core:cut': => @editor.cutSelectedText()
|
||||
'core:copy': => @editor.copySelectedText()
|
||||
'core:paste': => @editor.pasteText()
|
||||
'editor:move-to-previous-word': => @editor.moveCursorToPreviousWord()
|
||||
'editor:select-word': => @editor.selectWord()
|
||||
'editor:consolidate-selections': (event) => @consolidateSelections(event)
|
||||
'editor:backspace-to-beginning-of-word': => @editor.backspaceToBeginningOfWord()
|
||||
'editor:backspace-to-beginning-of-line': => @editor.backspaceToBeginningOfLine()
|
||||
'editor:delete-to-end-of-word': => @editor.deleteToEndOfWord()
|
||||
'editor:delete-line': => @editor.deleteLine()
|
||||
'editor:cut-to-end-of-line': => @editor.cutToEndOfLine()
|
||||
'editor:move-to-beginning-of-screen-line': => @editor.moveCursorToBeginningOfScreenLine()
|
||||
'editor:move-to-beginning-of-line': => @editor.moveCursorToBeginningOfLine()
|
||||
'editor:move-to-end-of-screen-line': => @editor.moveCursorToEndOfScreenLine()
|
||||
'editor:move-to-end-of-line': => @editor.moveCursorToEndOfLine()
|
||||
'editor:move-to-first-character-of-line': => @editor.moveCursorToFirstCharacterOfLine()
|
||||
'editor:move-to-beginning-of-word': => @editor.moveCursorToBeginningOfWord()
|
||||
'editor:move-to-end-of-word': => @editor.moveCursorToEndOfWord()
|
||||
'editor:move-to-beginning-of-next-word': => @editor.moveCursorToBeginningOfNextWord()
|
||||
'editor:move-to-previous-word-boundary': => @editor.moveCursorToPreviousWordBoundary()
|
||||
'editor:move-to-next-word-boundary': => @editor.moveCursorToNextWordBoundary()
|
||||
'editor:select-to-end-of-line': => @editor.selectToEndOfLine()
|
||||
'editor:select-to-beginning-of-line': => @editor.selectToBeginningOfLine()
|
||||
'editor:select-to-end-of-word': => @editor.selectToEndOfWord()
|
||||
'editor:select-to-beginning-of-word': => @editor.selectToBeginningOfWord()
|
||||
'editor:select-to-beginning-of-next-word': => @editor.selectToBeginningOfNextWord()
|
||||
'editor:select-to-next-word-boundary': => @editor.selectToNextWordBoundary()
|
||||
'editor:select-to-previous-word-boundary': => @editor.selectToPreviousWordBoundary()
|
||||
'editor:select-to-first-character-of-line': => @editor.selectToFirstCharacterOfLine()
|
||||
'editor:select-line': => @editor.selectLine()
|
||||
'editor:transpose': => @editor.transpose()
|
||||
'editor:upper-case': => @editor.upperCase()
|
||||
'editor:lower-case': => @editor.lowerCase()
|
||||
|
||||
unless @mini
|
||||
_.extend editorBindings,
|
||||
'core:move-up': @moveCursorUp
|
||||
'core:move-down': @moveCursorDown
|
||||
'core:move-to-top': @moveCursorToTop
|
||||
'core:move-to-bottom': @moveCursorToBottom
|
||||
'core:page-down': @pageDown
|
||||
'core:page-up': @pageUp
|
||||
'core:select-up': @selectUp
|
||||
'core:select-down': @selectDown
|
||||
'core:select-to-top': @selectToTop
|
||||
'core:select-to-bottom': @selectToBottom
|
||||
'editor:indent': @indent
|
||||
'editor:auto-indent': @autoIndent
|
||||
'editor:indent-selected-rows': @indentSelectedRows
|
||||
'editor:outdent-selected-rows': @outdentSelectedRows
|
||||
'editor:newline': @insertNewline
|
||||
'editor:newline-below': @insertNewlineBelow
|
||||
'editor:newline-above': @insertNewlineAbove
|
||||
'editor:add-selection-below': @addSelectionBelow
|
||||
'editor:add-selection-above': @addSelectionAbove
|
||||
'editor:toggle-soft-tabs': @toggleSoftTabs
|
||||
'editor:toggle-soft-wrap': @toggleSoftWrap
|
||||
'editor:fold-all': @foldAll
|
||||
'editor:unfold-all': @unfoldAll
|
||||
'editor:fold-current-row': @foldCurrentRow
|
||||
'editor:unfold-current-row': @unfoldCurrentRow
|
||||
'editor:fold-selection': @foldSelection
|
||||
'editor:fold-at-indent-level-1': => @foldAllAtIndentLevel(0)
|
||||
'editor:fold-at-indent-level-2': => @foldAllAtIndentLevel(1)
|
||||
'editor:fold-at-indent-level-3': => @foldAllAtIndentLevel(2)
|
||||
'editor:fold-at-indent-level-4': => @foldAllAtIndentLevel(3)
|
||||
'editor:fold-at-indent-level-5': => @foldAllAtIndentLevel(4)
|
||||
'editor:fold-at-indent-level-6': => @foldAllAtIndentLevel(5)
|
||||
'editor:fold-at-indent-level-7': => @foldAllAtIndentLevel(6)
|
||||
'editor:fold-at-indent-level-8': => @foldAllAtIndentLevel(7)
|
||||
'editor:fold-at-indent-level-9': => @foldAllAtIndentLevel(8)
|
||||
'editor:toggle-line-comments': @toggleLineCommentsInSelection
|
||||
'editor:log-cursor-scope': @logCursorScope
|
||||
'editor:checkout-head-revision': @checkoutHead
|
||||
'editor:copy-path': @copyPathToPasteboard
|
||||
'editor:move-line-up': @moveLineUp
|
||||
'editor:move-line-down': @moveLineDown
|
||||
'editor:duplicate-line': @duplicateLine
|
||||
'editor:join-line': @joinLine
|
||||
'core:move-up': => @editor.moveCursorUp()
|
||||
'core:move-down': => @editor.moveCursorDown()
|
||||
'core:move-to-top': => @editor.moveCursorToTop()
|
||||
'core:move-to-bottom': => @editor.moveCursorToBottom()
|
||||
'core:page-down': => @pageDown()
|
||||
'core:page-up': => @pageUp()
|
||||
'core:select-up': => @editor.selectUp()
|
||||
'core:select-down': => @editor.selectDown()
|
||||
'core:select-to-top': => @editor.selectToTop()
|
||||
'core:select-to-bottom': => @editor.selectToBottom()
|
||||
'editor:indent': => @editor.indent()
|
||||
'editor:auto-indent': => @editor.autoIndentSelectedRows()
|
||||
'editor:indent-selected-rows': => @editor.indentSelectedRows()
|
||||
'editor:outdent-selected-rows': => @editor.outdentSelectedRows()
|
||||
'editor:newline': => @editor.insertNewline()
|
||||
'editor:newline-below': => @editor.insertNewlineBelow()
|
||||
'editor:newline-above': => @editor.insertNewlineAbove()
|
||||
'editor:add-selection-below': => @editor.addSelectionBelow()
|
||||
'editor:add-selection-above': => @editor.addSelectionAbove()
|
||||
'editor:split-selections-into-lines': => @editor.splitSelectionsIntoLines()
|
||||
'editor:toggle-soft-tabs': => @toggleSoftTabs()
|
||||
'editor:toggle-soft-wrap': => @toggleSoftWrap()
|
||||
'editor:fold-all': => @editor.foldAll()
|
||||
'editor:unfold-all': => @editor.unfoldAll()
|
||||
'editor:fold-current-row': => @editor.foldCurrentRow()
|
||||
'editor:unfold-current-row': => @editor.unfoldCurrentRow()
|
||||
'editor:fold-selection': => @editor.foldSelection()
|
||||
'editor:fold-at-indent-level-1': => @editor.foldAllAtIndentLevel(0)
|
||||
'editor:fold-at-indent-level-2': => @editor.foldAllAtIndentLevel(1)
|
||||
'editor:fold-at-indent-level-3': => @editor.foldAllAtIndentLevel(2)
|
||||
'editor:fold-at-indent-level-4': => @editor.foldAllAtIndentLevel(3)
|
||||
'editor:fold-at-indent-level-5': => @editor.foldAllAtIndentLevel(4)
|
||||
'editor:fold-at-indent-level-6': => @editor.foldAllAtIndentLevel(5)
|
||||
'editor:fold-at-indent-level-7': => @editor.foldAllAtIndentLevel(6)
|
||||
'editor:fold-at-indent-level-8': => @editor.foldAllAtIndentLevel(7)
|
||||
'editor:fold-at-indent-level-9': => @editor.foldAllAtIndentLevel(8)
|
||||
'editor:toggle-line-comments': => @toggleLineCommentsInSelection()
|
||||
'editor:log-cursor-scope': => @logCursorScope()
|
||||
'editor:checkout-head-revision': => @checkoutHead()
|
||||
'editor:copy-path': => @copyPathToPasteboard()
|
||||
'editor:move-line-up': => @editor.moveLineUp()
|
||||
'editor:move-line-down': => @editor.moveLineDown()
|
||||
'editor:duplicate-line': => @editor.duplicateLine()
|
||||
'editor:join-line': => @editor.joinLine()
|
||||
'editor:toggle-indent-guide': => atom.config.toggle('editor.showIndentGuide')
|
||||
'editor:toggle-line-numbers': => atom.config.toggle('editor.showLineNumbers')
|
||||
'editor:scroll-to-cursor': @scrollToCursorPosition
|
||||
'editor:scroll-to-cursor': => @scrollToCursorPosition()
|
||||
|
||||
documentation = {}
|
||||
for name, method of editorBindings
|
||||
do (name, method) =>
|
||||
@command name, (e) => method.call(this, e); false
|
||||
@command name, (e) -> method(e); false
|
||||
|
||||
# {Delegates to: Editor.getCursor}
|
||||
getCursor: -> @editor.getCursor()
|
||||
getEditor: ->
|
||||
@editor
|
||||
|
||||
# {Delegates to: Editor.getCursors}
|
||||
getCursors: -> @editor.getCursors()
|
||||
# {Delegates to: Editor.getText}
|
||||
getText: ->
|
||||
@editor.getText()
|
||||
|
||||
# {Delegates to: Editor.addCursorAtScreenPosition}
|
||||
addCursorAtScreenPosition: (screenPosition) -> @editor.addCursorAtScreenPosition(screenPosition)
|
||||
|
||||
# {Delegates to: Editor.addCursorAtBufferPosition}
|
||||
addCursorAtBufferPosition: (bufferPosition) -> @editor.addCursorAtBufferPosition(bufferPosition)
|
||||
|
||||
# {Delegates to: Editor.moveCursorUp}
|
||||
moveCursorUp: -> @editor.moveCursorUp()
|
||||
|
||||
# {Delegates to: Editor.moveCursorDown}
|
||||
moveCursorDown: -> @editor.moveCursorDown()
|
||||
|
||||
# {Delegates to: Editor.moveCursorLeft}
|
||||
moveCursorLeft: -> @editor.moveCursorLeft()
|
||||
|
||||
# {Delegates to: Editor.moveCursorRight}
|
||||
moveCursorRight: -> @editor.moveCursorRight()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToBeginningOfWord}
|
||||
moveCursorToBeginningOfWord: -> @editor.moveCursorToBeginningOfWord()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToEndOfWord}
|
||||
moveCursorToEndOfWord: -> @editor.moveCursorToEndOfWord()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToBeginningOfNextWord}
|
||||
moveCursorToBeginningOfNextWord: -> @editor.moveCursorToBeginningOfNextWord()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToTop}
|
||||
moveCursorToTop: -> @editor.moveCursorToTop()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToBottom}
|
||||
moveCursorToBottom: -> @editor.moveCursorToBottom()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToBeginningOfLine}
|
||||
moveCursorToBeginningOfLine: -> @editor.moveCursorToBeginningOfLine()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToFirstCharacterOfLine}
|
||||
moveCursorToFirstCharacterOfLine: -> @editor.moveCursorToFirstCharacterOfLine()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToPreviousWordBoundary}
|
||||
moveCursorToPreviousWordBoundary: -> @editor.moveCursorToPreviousWordBoundary()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToNextWordBoundary}
|
||||
moveCursorToNextWordBoundary: -> @editor.moveCursorToNextWordBoundary()
|
||||
|
||||
# {Delegates to: Editor.moveCursorToEndOfLine}
|
||||
moveCursorToEndOfLine: -> @editor.moveCursorToEndOfLine()
|
||||
|
||||
# {Delegates to: Editor.moveLineUp}
|
||||
moveLineUp: -> @editor.moveLineUp()
|
||||
|
||||
# {Delegates to: Editor.moveLineDown}
|
||||
moveLineDown: -> @editor.moveLineDown()
|
||||
|
||||
# {Delegates to: Editor.setCursorScreenPosition}
|
||||
setCursorScreenPosition: (position, options) -> @editor.setCursorScreenPosition(position, options)
|
||||
|
||||
# {Delegates to: Editor.duplicateLine}
|
||||
duplicateLine: -> @editor.duplicateLine()
|
||||
|
||||
# {Delegates to: Editor.joinLine}
|
||||
joinLine: -> @editor.joinLine()
|
||||
|
||||
# {Delegates to: Editor.getCursorScreenPosition}
|
||||
getCursorScreenPosition: -> @editor.getCursorScreenPosition()
|
||||
|
||||
# {Delegates to: Editor.getCursorScreenRow}
|
||||
getCursorScreenRow: -> @editor.getCursorScreenRow()
|
||||
|
||||
# {Delegates to: Editor.setCursorBufferPosition}
|
||||
setCursorBufferPosition: (position, options) -> @editor.setCursorBufferPosition(position, options)
|
||||
|
||||
# {Delegates to: Editor.getCursorBufferPosition}
|
||||
getCursorBufferPosition: -> @editor.getCursorBufferPosition()
|
||||
|
||||
# {Delegates to: Editor.getCurrentParagraphBufferRange}
|
||||
getCurrentParagraphBufferRange: -> @editor.getCurrentParagraphBufferRange()
|
||||
|
||||
# {Delegates to: Editor.getWordUnderCursor}
|
||||
getWordUnderCursor: (options) -> @editor.getWordUnderCursor(options)
|
||||
|
||||
# {Delegates to: Editor.getSelection}
|
||||
getSelection: (index) -> @editor.getSelection(index)
|
||||
|
||||
# {Delegates to: Editor.getSelections}
|
||||
getSelections: -> @editor.getSelections()
|
||||
|
||||
# {Delegates to: Editor.getSelectionsOrderedByBufferPosition}
|
||||
getSelectionsOrderedByBufferPosition: -> @editor.getSelectionsOrderedByBufferPosition()
|
||||
|
||||
# {Delegates to: Editor.getLastSelectionInBuffer}
|
||||
getLastSelectionInBuffer: -> @editor.getLastSelectionInBuffer()
|
||||
|
||||
# {Delegates to: Editor.getSelectedText}
|
||||
getSelectedText: -> @editor.getSelectedText()
|
||||
|
||||
# {Delegates to: Editor.getSelectedBufferRanges}
|
||||
getSelectedBufferRanges: -> @editor.getSelectedBufferRanges()
|
||||
|
||||
# {Delegates to: Editor.getSelectedBufferRange}
|
||||
getSelectedBufferRange: -> @editor.getSelectedBufferRange()
|
||||
|
||||
# {Delegates to: Editor.setSelectedBufferRange}
|
||||
setSelectedBufferRange: (bufferRange, options) -> @editor.setSelectedBufferRange(bufferRange, options)
|
||||
|
||||
# {Delegates to: Editor.setSelectedBufferRanges}
|
||||
setSelectedBufferRanges: (bufferRanges, options) -> @editor.setSelectedBufferRanges(bufferRanges, options)
|
||||
|
||||
# {Delegates to: Editor.addSelectionForBufferRange}
|
||||
addSelectionForBufferRange: (bufferRange, options) -> @editor.addSelectionForBufferRange(bufferRange, options)
|
||||
|
||||
# {Delegates to: Editor.selectRight}
|
||||
selectRight: -> @editor.selectRight()
|
||||
|
||||
# {Delegates to: Editor.selectLeft}
|
||||
selectLeft: -> @editor.selectLeft()
|
||||
|
||||
# {Delegates to: Editor.selectUp}
|
||||
selectUp: -> @editor.selectUp()
|
||||
|
||||
# {Delegates to: Editor.selectDown}
|
||||
selectDown: -> @editor.selectDown()
|
||||
|
||||
# {Delegates to: Editor.selectToTop}
|
||||
selectToTop: -> @editor.selectToTop()
|
||||
|
||||
# {Delegates to: Editor.selectToBottom}
|
||||
selectToBottom: -> @editor.selectToBottom()
|
||||
|
||||
# {Delegates to: Editor.selectAll}
|
||||
selectAll: -> @editor.selectAll()
|
||||
|
||||
# {Delegates to: Editor.selectToBeginningOfLine}
|
||||
selectToBeginningOfLine: -> @editor.selectToBeginningOfLine()
|
||||
|
||||
# {Delegates to: Editor.selectToFirstCharacterOfLine}
|
||||
selectToFirstCharacterOfLine: -> @editor.selectToFirstCharacterOfLine()
|
||||
|
||||
# {Delegates to: Editor.selectToEndOfLine}
|
||||
selectToEndOfLine: -> @editor.selectToEndOfLine()
|
||||
|
||||
# {Delegates to: Editor.selectToPreviousWordBoundary}
|
||||
selectToPreviousWordBoundary: -> @editor.selectToPreviousWordBoundary()
|
||||
|
||||
# {Delegates to: Editor.selectToNextWordBoundary}
|
||||
selectToNextWordBoundary: -> @editor.selectToNextWordBoundary()
|
||||
|
||||
# {Delegates to: Editor.addSelectionBelow}
|
||||
addSelectionBelow: -> @editor.addSelectionBelow()
|
||||
|
||||
# {Delegates to: Editor.addSelectionAbove}
|
||||
addSelectionAbove: -> @editor.addSelectionAbove()
|
||||
|
||||
# {Delegates to: Editor.selectToBeginningOfWord}
|
||||
selectToBeginningOfWord: -> @editor.selectToBeginningOfWord()
|
||||
|
||||
# {Delegates to: Editor.selectToEndOfWord}
|
||||
selectToEndOfWord: -> @editor.selectToEndOfWord()
|
||||
|
||||
# {Delegates to: Editor.selectToBeginningOfNextWord}
|
||||
selectToBeginningOfNextWord: -> @editor.selectToBeginningOfNextWord()
|
||||
|
||||
# {Delegates to: Editor.selectWord}
|
||||
selectWord: -> @editor.selectWord()
|
||||
|
||||
# {Delegates to: Editor.selectLine}
|
||||
selectLine: -> @editor.selectLine()
|
||||
|
||||
# {Delegates to: Editor.selectToScreenPosition}
|
||||
selectToScreenPosition: (position) -> @editor.selectToScreenPosition(position)
|
||||
|
||||
# {Delegates to: Editor.transpose}
|
||||
transpose: -> @editor.transpose()
|
||||
|
||||
# {Delegates to: Editor.upperCase}
|
||||
upperCase: -> @editor.upperCase()
|
||||
|
||||
# {Delegates to: Editor.lowerCase}
|
||||
lowerCase: -> @editor.lowerCase()
|
||||
|
||||
# {Delegates to: Editor.clearSelections}
|
||||
clearSelections: -> @editor.clearSelections()
|
||||
|
||||
# {Delegates to: Editor.backspace}
|
||||
backspace: -> @editor.backspace()
|
||||
|
||||
# {Delegates to: Editor.backspaceToBeginningOfWord}
|
||||
backspaceToBeginningOfWord: -> @editor.backspaceToBeginningOfWord()
|
||||
|
||||
# {Delegates to: Editor.backspaceToBeginningOfLine}
|
||||
backspaceToBeginningOfLine: -> @editor.backspaceToBeginningOfLine()
|
||||
|
||||
# {Delegates to: Editor.delete}
|
||||
delete: -> @editor.delete()
|
||||
|
||||
# {Delegates to: Editor.deleteToEndOfWord}
|
||||
deleteToEndOfWord: -> @editor.deleteToEndOfWord()
|
||||
|
||||
# {Delegates to: Editor.deleteLine}
|
||||
deleteLine: -> @editor.deleteLine()
|
||||
|
||||
# {Delegates to: Editor.cutToEndOfLine}
|
||||
cutToEndOfLine: -> @editor.cutToEndOfLine()
|
||||
# {Delegates to: Editor.setText}
|
||||
setText: (text) ->
|
||||
@editor.setText(text)
|
||||
|
||||
# {Delegates to: Editor.insertText}
|
||||
insertText: (text, options) -> @editor.insertText(text, options)
|
||||
|
||||
# {Delegates to: Editor.insertNewline}
|
||||
insertNewline: -> @editor.insertNewline()
|
||||
|
||||
# {Delegates to: Editor.insertNewlineBelow}
|
||||
insertNewlineBelow: -> @editor.insertNewlineBelow()
|
||||
|
||||
# {Delegates to: Editor.insertNewlineAbove}
|
||||
insertNewlineAbove: -> @editor.insertNewlineAbove()
|
||||
|
||||
# {Delegates to: Editor.indent}
|
||||
indent: (options) -> @editor.indent(options)
|
||||
|
||||
# {Delegates to: Editor.autoIndentSelectedRows}
|
||||
autoIndent: (options) -> @editor.autoIndentSelectedRows()
|
||||
|
||||
# {Delegates to: Editor.indentSelectedRows}
|
||||
indentSelectedRows: -> @editor.indentSelectedRows()
|
||||
|
||||
# {Delegates to: Editor.outdentSelectedRows}
|
||||
outdentSelectedRows: -> @editor.outdentSelectedRows()
|
||||
|
||||
# {Delegates to: Editor.cutSelectedText}
|
||||
cutSelection: -> @editor.cutSelectedText()
|
||||
|
||||
# {Delegates to: Editor.copySelectedText}
|
||||
copySelection: -> @editor.copySelectedText()
|
||||
|
||||
# {Delegates to: Editor.pasteText}
|
||||
paste: (options) -> @editor.pasteText(options)
|
||||
|
||||
# {Delegates to: Editor.undo}
|
||||
undo: -> @editor.undo()
|
||||
|
||||
# {Delegates to: Editor.redo}
|
||||
redo: -> @editor.redo()
|
||||
|
||||
# {Delegates to: Editor.createFold}
|
||||
createFold: (startRow, endRow) -> @editor.createFold(startRow, endRow)
|
||||
|
||||
# {Delegates to: Editor.foldCurrentRow}
|
||||
foldCurrentRow: -> @editor.foldCurrentRow()
|
||||
|
||||
# {Delegates to: Editor.unfoldCurrentRow}
|
||||
unfoldCurrentRow: -> @editor.unfoldCurrentRow()
|
||||
|
||||
# {Delegates to: Editor.foldAll}
|
||||
foldAll: -> @editor.foldAll()
|
||||
|
||||
# {Delegates to: Editor.unfoldAll}
|
||||
unfoldAll: -> @editor.unfoldAll()
|
||||
|
||||
# {Delegates to: Editor.foldSelection}
|
||||
foldSelection: -> @editor.foldSelection()
|
||||
|
||||
# {Delegates to: Editor.destroyFoldsContainingBufferRow}
|
||||
destroyFoldsContainingBufferRow: (bufferRow) -> @editor.destroyFoldsContainingBufferRow(bufferRow)
|
||||
|
||||
# {Delegates to: Editor.isFoldedAtScreenRow}
|
||||
isFoldedAtScreenRow: (screenRow) -> @editor.isFoldedAtScreenRow(screenRow)
|
||||
|
||||
# {Delegates to: Editor.isFoldedAtBufferRow}
|
||||
isFoldedAtBufferRow: (bufferRow) -> @editor.isFoldedAtBufferRow(bufferRow)
|
||||
|
||||
# {Delegates to: Editor.isFoldedAtCursorRow}
|
||||
isFoldedAtCursorRow: -> @editor.isFoldedAtCursorRow()
|
||||
|
||||
foldAllAtIndentLevel: (indentLevel) -> @editor.foldAllAtIndentLevel(indentLevel)
|
||||
|
||||
# {Delegates to: Editor.lineForScreenRow}
|
||||
lineForScreenRow: (screenRow) -> @editor.lineForScreenRow(screenRow)
|
||||
|
||||
# {Delegates to: Editor.linesForScreenRows}
|
||||
linesForScreenRows: (start, end) -> @editor.linesForScreenRows(start, end)
|
||||
|
||||
# {Delegates to: Editor.getScreenLineCount}
|
||||
getScreenLineCount: -> @editor.getScreenLineCount()
|
||||
insertText: (text, options) ->
|
||||
@editor.insertText(text, options)
|
||||
|
||||
# Private:
|
||||
setHeightInLines: (heightInLines)->
|
||||
@@ -514,30 +248,6 @@ class EditorView extends View
|
||||
widthInChars ?= @calculateWidthInChars()
|
||||
@editor.setEditorWidthInChars(widthInChars) if widthInChars
|
||||
|
||||
# {Delegates to: Editor.getMaxScreenLineLength}
|
||||
getMaxScreenLineLength: -> @editor.getMaxScreenLineLength()
|
||||
|
||||
# {Delegates to: Editor.getLastScreenRow}
|
||||
getLastScreenRow: -> @editor.getLastScreenRow()
|
||||
|
||||
# {Delegates to: Editor.clipScreenPosition}
|
||||
clipScreenPosition: (screenPosition, options={}) -> @editor.clipScreenPosition(screenPosition, options)
|
||||
|
||||
# {Delegates to: Editor.screenPositionForBufferPosition}
|
||||
screenPositionForBufferPosition: (position, options) -> @editor.screenPositionForBufferPosition(position, options)
|
||||
|
||||
# {Delegates to: Editor.bufferPositionForScreenPosition}
|
||||
bufferPositionForScreenPosition: (position, options) -> @editor.bufferPositionForScreenPosition(position, options)
|
||||
|
||||
# {Delegates to: Editor.screenRangeForBufferRange}
|
||||
screenRangeForBufferRange: (range) -> @editor.screenRangeForBufferRange(range)
|
||||
|
||||
# {Delegates to: Editor.bufferRangeForScreenRange}
|
||||
bufferRangeForScreenRange: (range) -> @editor.bufferRangeForScreenRange(range)
|
||||
|
||||
# {Delegates to: Editor.bufferRowsForScreenRows}
|
||||
bufferRowsForScreenRows: (startRow, endRow) -> @editor.bufferRowsForScreenRows(startRow, endRow)
|
||||
|
||||
# Public: Emulates the "page down" key, where the last row of a buffer scrolls to become the first.
|
||||
pageDown: ->
|
||||
newScrollTop = @scrollTop() + @scrollView[0].clientHeight
|
||||
@@ -597,51 +307,9 @@ class EditorView extends View
|
||||
|
||||
# Checkout the HEAD revision of this editor's file.
|
||||
checkoutHead: ->
|
||||
if path = @getPath()
|
||||
if path = @editor.getPath()
|
||||
atom.project.getRepo()?.checkoutHead(path)
|
||||
|
||||
# {Delegates to: Editor.setText}
|
||||
setText: (text) -> @editor.setText(text)
|
||||
|
||||
# {Delegates to: Editor.save}
|
||||
save: -> @editor.save()
|
||||
|
||||
# {Delegates to: Editor.getText}
|
||||
getText: -> @editor.getText()
|
||||
|
||||
# {Delegates to: Editor.getPath}
|
||||
getPath: -> @editor?.getPath()
|
||||
|
||||
# {Delegates to: Editor.transact}
|
||||
transact: (fn) -> @editor.transact(fn)
|
||||
|
||||
# {Delegates to: TextBuffer.getLineCount}
|
||||
getLineCount: -> @getBuffer().getLineCount()
|
||||
|
||||
# {Delegates to: TextBuffer.getLastRow}
|
||||
getLastBufferRow: -> @getBuffer().getLastRow()
|
||||
|
||||
# {Delegates to: TextBuffer.getTextInRange}
|
||||
getTextInRange: (range) -> @getBuffer().getTextInRange(range)
|
||||
|
||||
# {Delegates to: TextBuffer.getEofPosition}
|
||||
getEofPosition: -> @getBuffer().getEofPosition()
|
||||
|
||||
# {Delegates to: TextBuffer.lineForRow}
|
||||
lineForBufferRow: (row) -> @getBuffer().lineForRow(row)
|
||||
|
||||
# {Delegates to: TextBuffer.lineLengthForRow}
|
||||
lineLengthForBufferRow: (row) -> @getBuffer().lineLengthForRow(row)
|
||||
|
||||
# {Delegates to: TextBuffer.rangeForRow}
|
||||
rangeForBufferRow: (row) -> @getBuffer().rangeForRow(row)
|
||||
|
||||
# {Delegates to: TextBuffer.scanInRange}
|
||||
scanInBufferRange: (args...) -> @getBuffer().scanInRange(args...)
|
||||
|
||||
# {Delegates to: TextBuffer.backwardsScanInRange}
|
||||
backwardsScanInBufferRange: (args...) -> @getBuffer().backwardsScanInRange(args...)
|
||||
|
||||
### Internal ###
|
||||
|
||||
configure: ->
|
||||
@@ -686,17 +354,22 @@ class EditorView extends View
|
||||
@editor.destroyFoldWithId(id)
|
||||
false
|
||||
|
||||
@gutter.on 'mousedown', '.foldable .icon-right', (e) =>
|
||||
bufferRow = $(e.target).parent().data('bufferRow')
|
||||
@editor.toggleFoldAtBufferRow(bufferRow)
|
||||
false
|
||||
|
||||
@renderedLines.on 'mousedown', (e) =>
|
||||
clickCount = e.originalEvent.detail
|
||||
|
||||
screenPosition = @screenPositionFromMouseEvent(e)
|
||||
if clickCount == 1
|
||||
if e.metaKey
|
||||
@addCursorAtScreenPosition(screenPosition)
|
||||
@editor.addCursorAtScreenPosition(screenPosition)
|
||||
else if e.shiftKey
|
||||
@selectToScreenPosition(screenPosition)
|
||||
@editor.selectToScreenPosition(screenPosition)
|
||||
else
|
||||
@setCursorScreenPosition(screenPosition)
|
||||
@editor.setCursorScreenPosition(screenPosition)
|
||||
else if clickCount == 2
|
||||
@editor.selectWord() unless e.shiftKey
|
||||
else if clickCount == 3
|
||||
@@ -738,12 +411,12 @@ class EditorView extends View
|
||||
|
||||
selectedText = null
|
||||
@hiddenInput.on 'compositionstart', =>
|
||||
selectedText = @getSelectedText()
|
||||
selectedText = @editor.getSelectedText()
|
||||
@hiddenInput.css('width', '100%')
|
||||
@hiddenInput.on 'compositionupdate', (e) =>
|
||||
@insertText(e.originalEvent.data, {select: true, undo: 'skip'})
|
||||
@editor.insertText(e.originalEvent.data, {select: true, undo: 'skip'})
|
||||
@hiddenInput.on 'compositionend', =>
|
||||
@insertText(selectedText, {select: true, undo: 'skip'})
|
||||
@editor.insertText(selectedText, {select: true, undo: 'skip'})
|
||||
@hiddenInput.css('width', '1px')
|
||||
|
||||
lastInput = ''
|
||||
@@ -751,10 +424,10 @@ class EditorView extends View
|
||||
# Work around of the accented character suggestion feature in OS X.
|
||||
selectedLength = @hiddenInput[0].selectionEnd - @hiddenInput[0].selectionStart
|
||||
if selectedLength is 1 and lastInput is @hiddenInput.val()
|
||||
@selectLeft()
|
||||
@editor.selectLeft()
|
||||
|
||||
lastInput = e.originalEvent.data
|
||||
@insertText(lastInput)
|
||||
@editor.insertText(lastInput)
|
||||
@hiddenInput.val(lastInput)
|
||||
false
|
||||
|
||||
@@ -765,7 +438,7 @@ class EditorView extends View
|
||||
lastMoveEvent = null
|
||||
moveHandler = (event = lastMoveEvent) =>
|
||||
if event
|
||||
@selectToScreenPosition(@screenPositionFromMouseEvent(event))
|
||||
@editor.selectToScreenPosition(@screenPositionFromMouseEvent(event))
|
||||
lastMoveEvent = event
|
||||
|
||||
$(document).on "mousemove.editor-#{@id}", moveHandler
|
||||
@@ -811,6 +484,7 @@ class EditorView extends View
|
||||
|
||||
@trigger 'editor:attached', [this]
|
||||
|
||||
# TODO: This should be private and only called from the constructor
|
||||
edit: (editor) ->
|
||||
return if editor is @editor
|
||||
|
||||
@@ -824,11 +498,14 @@ class EditorView extends View
|
||||
|
||||
@editor.setVisible(true)
|
||||
|
||||
@editor.on "destroyed", =>
|
||||
@remove()
|
||||
|
||||
@editor.on "contents-conflicted.editor", =>
|
||||
@showBufferConflictAlert(@editor)
|
||||
|
||||
@editor.on "path-changed.editor", =>
|
||||
@reloadGrammar()
|
||||
@editor.reloadGrammar()
|
||||
@trigger 'editor:path-changed'
|
||||
|
||||
@editor.on "grammar-changed.editor", =>
|
||||
@@ -911,20 +588,15 @@ class EditorView extends View
|
||||
|
||||
### Public ###
|
||||
|
||||
# Retrieves the {Editor}'s buffer.
|
||||
#
|
||||
# Returns the current {TextBuffer}.
|
||||
getBuffer: -> @editor.buffer
|
||||
|
||||
# Scrolls the editor to the bottom.
|
||||
scrollToBottom: ->
|
||||
@scrollBottom(@getScreenLineCount() * @lineHeight)
|
||||
@scrollBottom(@editor.getScreenLineCount() * @lineHeight)
|
||||
|
||||
# Scrolls the editor to the position of the most recently added cursor.
|
||||
#
|
||||
# The editor is also centered.
|
||||
scrollToCursorPosition: ->
|
||||
@scrollToBufferPosition(@getCursorBufferPosition(), center: true)
|
||||
@scrollToBufferPosition(@editor.getCursorBufferPosition(), center: true)
|
||||
|
||||
# Scrolls the editor to the given buffer position.
|
||||
#
|
||||
@@ -960,7 +632,7 @@ class EditorView extends View
|
||||
#
|
||||
# bufferRange - The {Range} to check.
|
||||
highlightFoldsContainingBufferRange: (bufferRange) ->
|
||||
screenLines = @linesForScreenRows(@firstRenderedScreenRow, @lastRenderedScreenRow)
|
||||
screenLines = @editor.linesForScreenRows(@firstRenderedScreenRow, @lastRenderedScreenRow)
|
||||
for screenLine, i in screenLines
|
||||
if fold = screenLine.fold
|
||||
screenRow = @firstRenderedScreenRow + i
|
||||
@@ -1007,7 +679,7 @@ class EditorView extends View
|
||||
#
|
||||
# fontSize - A {Number} indicating the font size in pixels.
|
||||
setFontSize: (fontSize) ->
|
||||
@css('font-size', "#{fontSize}px}")
|
||||
@css('font-size', "#{fontSize}px")
|
||||
|
||||
@clearCharacterWidthCache()
|
||||
|
||||
@@ -1067,7 +739,7 @@ class EditorView extends View
|
||||
#
|
||||
# Returns a {Pane}.
|
||||
getPane: ->
|
||||
@parent('.item-views').parent('.pane').view()
|
||||
@parent('.item-views').parents('.pane').view()
|
||||
|
||||
remove: (selector, keepData) ->
|
||||
return super if keepData or @removed
|
||||
@@ -1174,7 +846,7 @@ class EditorView extends View
|
||||
@setHeightInLines()
|
||||
|
||||
updateLayerDimensions: ->
|
||||
height = @lineHeight * @getScreenLineCount()
|
||||
height = @lineHeight * @editor.getScreenLineCount()
|
||||
unless @layerHeight == height
|
||||
@layerHeight = height
|
||||
@underlayer.height(@layerHeight)
|
||||
@@ -1183,7 +855,7 @@ class EditorView extends View
|
||||
@verticalScrollbarContent.height(@layerHeight)
|
||||
@scrollBottom(height) if @scrollBottom() > height
|
||||
|
||||
minWidth = Math.max(@charWidth * @getMaxScreenLineLength() + 20, @scrollView.width())
|
||||
minWidth = Math.max(@charWidth * @editor.getMaxScreenLineLength() + 20, @scrollView.width())
|
||||
unless @layerMinWidth == minWidth
|
||||
@renderedLines.css('min-width', minWidth)
|
||||
@underlayer.css('min-width', minWidth)
|
||||
@@ -1215,8 +887,8 @@ class EditorView extends View
|
||||
@scrollTop(editorScrollTop)
|
||||
@scrollLeft(editorScrollLeft)
|
||||
@setSoftWrap(@editor.getSoftWrap())
|
||||
@newCursors = @editor.getAllCursors()
|
||||
@newSelections = @editor.getAllSelections()
|
||||
@newCursors = @editor.getCursors()
|
||||
@newSelections = @editor.getSelections()
|
||||
@updateDisplay(suppressAutoScroll: true)
|
||||
|
||||
requestDisplayUpdate: ->
|
||||
@@ -1229,7 +901,7 @@ class EditorView extends View
|
||||
|
||||
updateDisplay: (options={}) ->
|
||||
return unless @attached and @editor
|
||||
return if @editor.destroyed
|
||||
return if @editor.isDestroyed()
|
||||
unless @isOnDom() and @isVisible()
|
||||
@redrawOnReattach = true
|
||||
return
|
||||
@@ -1297,9 +969,9 @@ class EditorView extends View
|
||||
|
||||
updatePlaceholderText: ->
|
||||
return unless @mini
|
||||
if (not @placeholderText) or @getText()
|
||||
if (not @placeholderText) or @editor.getText()
|
||||
@find('.placeholder-text').remove()
|
||||
else if @placeholderText and not @getText()
|
||||
else if @placeholderText and not @editor.getText()
|
||||
element = @find('.placeholder-text')
|
||||
if element.length
|
||||
element.text(@placeholderText)
|
||||
@@ -1309,7 +981,7 @@ class EditorView extends View
|
||||
updateRenderedLines: ->
|
||||
firstVisibleScreenRow = @getFirstVisibleScreenRow()
|
||||
lastScreenRowToRender = firstVisibleScreenRow + @heightInLines - 1
|
||||
lastScreenRow = @getLastScreenRow()
|
||||
lastScreenRow = @editor.getLastScreenRow()
|
||||
|
||||
if @firstRenderedScreenRow? and firstVisibleScreenRow >= @firstRenderedScreenRow and lastScreenRowToRender <= @lastRenderedScreenRow
|
||||
renderFrom = Math.min(lastScreenRow, @firstRenderedScreenRow)
|
||||
@@ -1338,15 +1010,15 @@ class EditorView extends View
|
||||
|
||||
if change.bufferDelta?
|
||||
afterStart = change.end + change.bufferDelta + 1
|
||||
if @lineForBufferRow(afterStart) is ''
|
||||
if @editor.lineForBufferRow(afterStart) is ''
|
||||
afterEnd = afterStart
|
||||
afterEnd++ while @lineForBufferRow(afterEnd + 1) is ''
|
||||
afterEnd++ while @editor.lineForBufferRow(afterEnd + 1) is ''
|
||||
emptyLineChanges.push({start: afterStart, end: afterEnd, screenDelta: 0})
|
||||
|
||||
beforeEnd = change.start - 1
|
||||
if @lineForBufferRow(beforeEnd) is ''
|
||||
if @editor.lineForBufferRow(beforeEnd) is ''
|
||||
beforeStart = beforeEnd
|
||||
beforeStart-- while @lineForBufferRow(beforeStart - 1) is ''
|
||||
beforeStart-- while @editor.lineForBufferRow(beforeStart - 1) is ''
|
||||
emptyLineChanges.push({start: beforeStart, end: beforeEnd, screenDelta: 0})
|
||||
|
||||
emptyLineChanges
|
||||
@@ -1455,7 +1127,7 @@ class EditorView extends View
|
||||
@renderedLines.css('padding-top', paddingTop)
|
||||
@gutter.lineNumbers.css('padding-top', paddingTop)
|
||||
|
||||
paddingBottom = (@getLastScreenRow() - @lastRenderedScreenRow) * @lineHeight
|
||||
paddingBottom = (@editor.getLastScreenRow() - @lastRenderedScreenRow) * @lineHeight
|
||||
@renderedLines.css('padding-bottom', paddingBottom)
|
||||
@gutter.lineNumbers.css('padding-bottom', paddingBottom)
|
||||
|
||||
@@ -1474,7 +1146,7 @@ class EditorView extends View
|
||||
# Returns a {Number}.
|
||||
getLastVisibleScreenRow: ->
|
||||
calculatedRow = Math.ceil((@scrollTop() + @scrollView.height()) / @lineHeight) - 1
|
||||
screenRow = Math.max(0, Math.min(@getScreenLineCount() - 1, calculatedRow))
|
||||
screenRow = Math.max(0, Math.min(@editor.getScreenLineCount() - 1, calculatedRow))
|
||||
screenRow = 0 if isNaN(screenRow)
|
||||
screenRow
|
||||
|
||||
@@ -1579,7 +1251,7 @@ class EditorView extends View
|
||||
#
|
||||
# Returns an object with two values: `top` and `left`, representing the pixel positions.
|
||||
pixelPositionForBufferPosition: (position) ->
|
||||
@pixelPositionForScreenPosition(@screenPositionForBufferPosition(position))
|
||||
@pixelPositionForScreenPosition(@editor.screenPositionForBufferPosition(position))
|
||||
|
||||
# Converts a screen position to a pixel position.
|
||||
#
|
||||
@@ -1728,31 +1400,15 @@ class EditorView extends View
|
||||
return if @mini
|
||||
|
||||
@highlightedLine?.removeClass('cursor-line')
|
||||
if @getSelection().isEmpty()
|
||||
@highlightedLine = @lineElementForScreenRow(@getCursorScreenRow())
|
||||
if @editor.getSelection().isEmpty()
|
||||
@highlightedLine = @lineElementForScreenRow(@editor.getCursorScreenRow())
|
||||
@highlightedLine.addClass('cursor-line')
|
||||
else
|
||||
@highlightedLine = null
|
||||
|
||||
# {Delegates to: Editor.getGrammar}
|
||||
getGrammar: ->
|
||||
@editor.getGrammar()
|
||||
|
||||
# {Delegates to: Editor.setGrammar}
|
||||
setGrammar: (grammar) ->
|
||||
@editor.setGrammar(grammar)
|
||||
|
||||
# {Delegates to: Editor.reloadGrammar}
|
||||
reloadGrammar: ->
|
||||
@editor.reloadGrammar()
|
||||
|
||||
# {Delegates to: Editor.scopesForBufferPosition}
|
||||
scopesForBufferPosition: (bufferPosition) ->
|
||||
@editor.scopesForBufferPosition(bufferPosition)
|
||||
|
||||
# Copies the current file path to the native clipboard.
|
||||
copyPathToPasteboard: ->
|
||||
path = @getPath()
|
||||
path = @editor.getPath()
|
||||
atom.pasteboard.write(path) if path?
|
||||
|
||||
### Internal ###
|
||||
@@ -1839,13 +1495,13 @@ class EditorView extends View
|
||||
' '
|
||||
|
||||
replaceSelectedText: (replaceFn) ->
|
||||
selection = @getSelection()
|
||||
selection = @editor.getSelection()
|
||||
return false if selection.isEmpty()
|
||||
|
||||
text = replaceFn(@getTextInRange(selection.getBufferRange()))
|
||||
text = replaceFn(@editor.getTextInRange(selection.getBufferRange()))
|
||||
return false if text is null or text is undefined
|
||||
|
||||
@insertText(text, select: true)
|
||||
@editor.insertText(text, select: true)
|
||||
true
|
||||
|
||||
consolidateSelections: (e) -> e.abortKeyBinding() unless @editor.consolidateSelections()
|
||||
@@ -1853,12 +1509,6 @@ class EditorView extends View
|
||||
logCursorScope: ->
|
||||
console.log @editor.getCursorScopes()
|
||||
|
||||
beginTransaction: -> @editor.beginTransaction()
|
||||
|
||||
commitTransaction: -> @editor.commitTransaction()
|
||||
|
||||
abortTransaction: -> @editor.abortTransaction()
|
||||
|
||||
logScreenLines: (start, end) ->
|
||||
@editor.logScreenLines(start, end)
|
||||
|
||||
|
||||
+109
-154
@@ -1,6 +1,9 @@
|
||||
_ = require 'underscore-plus'
|
||||
path = require 'path'
|
||||
{Model, Point, Range} = require 'telepath'
|
||||
Serializable = require 'serializable'
|
||||
Delegator = require 'delegato'
|
||||
{Model} = require 'theorist'
|
||||
{Point, Range} = require 'text-buffer'
|
||||
LanguageMode = require './language-mode'
|
||||
DisplayBuffer = require './display-buffer'
|
||||
Cursor = require './cursor'
|
||||
@@ -27,17 +30,13 @@ TextMateScopeSelector = require('first-mate').ScopeSelector
|
||||
# atom.workspaceView.eachEditorView (editorView) ->
|
||||
# editorView.insertText('Hello World')
|
||||
# ```
|
||||
#
|
||||
# ## Collaboration builtin
|
||||
#
|
||||
# FIXME: Describe how there are both local and remote cursors and selections and
|
||||
# why that is.
|
||||
module.exports =
|
||||
class Editor extends Model
|
||||
Serializable.includeInto(this)
|
||||
atom.deserializers.add(this)
|
||||
Delegator.includeInto(this)
|
||||
|
||||
@properties
|
||||
displayBuffer: null
|
||||
softTabs: null
|
||||
scrollTop: 0
|
||||
scrollLeft: 0
|
||||
|
||||
@@ -47,33 +46,23 @@ class Editor extends Model
|
||||
buffer: null
|
||||
languageMode: null
|
||||
cursors: null
|
||||
remoteCursors: null
|
||||
selections: null
|
||||
remoteSelections: null
|
||||
suppressSelectionMerging: false
|
||||
|
||||
constructor: ->
|
||||
super
|
||||
@deserializing = @state?
|
||||
@delegatesMethods 'foldAll', 'unfoldAll', 'foldAllAtIndentLevel', 'foldBufferRow',
|
||||
'unfoldBufferRow', 'suggestedIndentForBufferRow', 'autoIndentBufferRow', 'autoIndentBufferRows',
|
||||
'autoDecreaseIndentForBufferRow', 'toggleLineCommentForBufferRow', 'toggleLineCommentsForBufferRows',
|
||||
'isFoldableAtBufferRow', toProperty: 'languageMode'
|
||||
|
||||
created: ->
|
||||
if @deserializing
|
||||
@deserializing = false
|
||||
@callDisplayBufferCreatedHook = true
|
||||
@registerEditor = true
|
||||
return
|
||||
constructor: ({@softTabs, initialLine, tabLength, softWrap, @displayBuffer, buffer, registerEditor, suppressCursorCreation}) ->
|
||||
super
|
||||
|
||||
@cursors = []
|
||||
@remoteCursors = []
|
||||
@selections = []
|
||||
@remoteSelections = []
|
||||
|
||||
unless @displayBuffer?
|
||||
@displayBuffer = new DisplayBuffer({@buffer, @tabLength, @softWrap})
|
||||
@softTabs = @buffer.usesSoftTabs() ? @softTabs ? atom.config.get('editor.softTabs') ? true
|
||||
|
||||
@displayBuffer.created() if @callDisplayBufferCreatedHook
|
||||
@displayBuffer ?= new DisplayBuffer({buffer, tabLength, softWrap})
|
||||
@buffer = @displayBuffer.buffer
|
||||
@softTabs = @buffer.usesSoftTabs() ? @softTabs ? atom.config.get('editor.softTabs') ? true
|
||||
|
||||
for marker in @findMarkers(@getSelectionMarkerAttributes())
|
||||
marker.setAttributes(preserveFolds: true)
|
||||
@@ -82,22 +71,31 @@ class Editor extends Model
|
||||
@subscribeToBuffer()
|
||||
@subscribeToDisplayBuffer()
|
||||
|
||||
if @getCursors().length is 0 and not @suppressCursorCreation
|
||||
if @initialLine
|
||||
position = [@initialLine, 0]
|
||||
if @getCursors().length is 0 and not suppressCursorCreation
|
||||
if initialLine
|
||||
position = [initialLine, 0]
|
||||
else
|
||||
position = _.last(@getRemoteCursors())?.getBufferPosition() ? [0, 0]
|
||||
position = [0, 0]
|
||||
@addCursorAtBufferPosition(position)
|
||||
|
||||
@languageMode = new LanguageMode(this, @buffer.getExtension())
|
||||
|
||||
@subscribe @$scrollTop, 'value', (scrollTop) => @emit 'scroll-top-changed', scrollTop
|
||||
@subscribe @$scrollLeft, 'value', (scrollLeft) => @emit 'scroll-left-changed', scrollLeft
|
||||
@subscribe @$scrollTop, (scrollTop) => @emit 'scroll-top-changed', scrollTop
|
||||
@subscribe @$scrollLeft, (scrollLeft) => @emit 'scroll-left-changed', scrollLeft
|
||||
|
||||
atom.project.addEditor(this) if @registerEditor
|
||||
atom.project.addEditor(this) if registerEditor
|
||||
|
||||
# Deprecated: The goal is a world where we don't call serialize explicitly
|
||||
serialize: -> this
|
||||
serializeParams: ->
|
||||
id: @id
|
||||
softTabs: @softTabs
|
||||
scrollTop: @scrollTop
|
||||
scrollLeft: @scrollLeft
|
||||
displayBuffer: @displayBuffer.serialize()
|
||||
|
||||
deserializeParams: (params) ->
|
||||
params.displayBuffer = DisplayBuffer.deserialize(params.displayBuffer)
|
||||
params.registerEditor = true
|
||||
params
|
||||
|
||||
# Private:
|
||||
subscribeToBuffer: ->
|
||||
@@ -126,24 +124,20 @@ class Editor extends Model
|
||||
require './editor-view'
|
||||
|
||||
# Private:
|
||||
destroy: ->
|
||||
return if @destroyed
|
||||
@destroyed = true
|
||||
destroyed: ->
|
||||
@unsubscribe()
|
||||
selection.destroy() for selection in @getSelections()
|
||||
@buffer.release()
|
||||
@displayBuffer.destroy()
|
||||
@languageMode.destroy()
|
||||
atom.project?.removeEditor(this)
|
||||
@emit 'destroyed'
|
||||
@off()
|
||||
|
||||
# Private: Creates an {Editor} with the same initial state
|
||||
copy: ->
|
||||
tabLength = @getTabLength()
|
||||
displayBuffer = @displayBuffer.copy()
|
||||
softTabs = @getSoftTabs()
|
||||
newEditor = @create(new Editor({@buffer, displayBuffer, tabLength, softTabs, suppressCursorCreation: true}))
|
||||
newEditor = new Editor({@buffer, displayBuffer, tabLength, softTabs, suppressCursorCreation: true})
|
||||
newEditor.setScrollTop(@getScrollTop())
|
||||
newEditor.setScrollLeft(@getScrollLeft())
|
||||
for marker in @findMarkers(editorId: @id)
|
||||
@@ -187,7 +181,8 @@ class Editor extends Model
|
||||
# Returns a {Boolean}.
|
||||
isEqual: (other) ->
|
||||
return false unless other instanceof Editor
|
||||
@buffer == other.buffer and
|
||||
@isAlive() == other.isAlive() and
|
||||
@buffer.getPath() == other.buffer.getPath() and
|
||||
@getScrollTop() == other.getScrollTop() and
|
||||
@getScrollLeft() == other.getScrollLeft() and
|
||||
@getCursorScreenPosition().isEqual(other.getCursorScreenPosition())
|
||||
@@ -207,8 +202,8 @@ class Editor extends Model
|
||||
# Deprecated: Use the ::scrollLeft property directly
|
||||
getScrollLeft: -> @scrollLeft
|
||||
|
||||
# Set the number of characters that can be displayed horizontally in the
|
||||
# editor that contains this edit session.
|
||||
# Public: Set the number of characters that can be displayed horizontally in
|
||||
# the editor.
|
||||
#
|
||||
# editorWidthInChars - A {Number} of characters
|
||||
setEditorWidthInChars: (editorWidthInChars) ->
|
||||
@@ -281,10 +276,9 @@ class Editor extends Model
|
||||
# * newLevel:
|
||||
# A {Number} indicating the new indentation level.
|
||||
setIndentationForBufferRow: (bufferRow, newLevel) ->
|
||||
currentLevel = @indentationForBufferRow(bufferRow)
|
||||
currentIndentString = @buildIndentString(currentLevel)
|
||||
currentIndentLength = @lineForBufferRow(bufferRow).match(/^\s*/)[0].length
|
||||
newIndentString = @buildIndentString(newLevel)
|
||||
@buffer.change([[bufferRow, 0], [bufferRow, currentIndentString.length]], newIndentString)
|
||||
@buffer.change([[bufferRow, 0], [bufferRow, currentIndentLength]], newIndentString)
|
||||
|
||||
# Public: Returns the indentation level of the given line of text.
|
||||
#
|
||||
@@ -320,12 +314,18 @@ class Editor extends Model
|
||||
# {Delegates to: TextBuffer.getPath}
|
||||
getPath: -> @buffer.getPath()
|
||||
|
||||
# {Delegates to: TextBuffer.getText}
|
||||
# Public: Returns a {String} representing the entire contents of the editor.
|
||||
getText: -> @buffer.getText()
|
||||
|
||||
# {Delegates to: TextBuffer.setText}
|
||||
# Public: Replaces the entire contents of the buffer with the given {String}.
|
||||
setText: (text) -> @buffer.setText(text)
|
||||
|
||||
# Public: Returns a {String} of text in the given {Range}.
|
||||
getTextInRange: (range) -> @buffer.getTextInRange(range)
|
||||
|
||||
# Public: Returns a {Number} representing the number of lines in the editor.
|
||||
getLineCount: -> @buffer.getLineCount()
|
||||
|
||||
# Private: Retrieves the current {TextBuffer}.
|
||||
getBuffer: -> @buffer
|
||||
|
||||
@@ -347,16 +347,28 @@ class Editor extends Model
|
||||
# {Delegates to: TextBuffer.getEofPosition}
|
||||
getEofBufferPosition: -> @buffer.getEofPosition()
|
||||
|
||||
# {Delegates to: TextBuffer.getLastRow}
|
||||
# Public: Returns a {Number} representing the last zero-indexed buffer row
|
||||
# number of the editor.
|
||||
getLastBufferRow: -> @buffer.getLastRow()
|
||||
|
||||
# {Delegates to: TextBuffer.rangeForRow}
|
||||
# Public: Returns the range for the given buffer row.
|
||||
#
|
||||
# * row: A row {Number}.
|
||||
# * options: An options hash with an `includeNewline` key.
|
||||
#
|
||||
# Returns a {Range}.
|
||||
bufferRangeForBufferRow: (row, options) -> @buffer.rangeForRow(row, options)
|
||||
|
||||
# {Delegates to: TextBuffer.lineForRow}
|
||||
# Public: Returns a {String} representing the contents of the line at the
|
||||
# given buffer row.
|
||||
#
|
||||
# * row - A {Number} representing a zero-indexed buffer row.
|
||||
lineForBufferRow: (row) -> @buffer.lineForRow(row)
|
||||
|
||||
# {Delegates to: TextBuffer.lineLengthForRow}
|
||||
# Public: Returns a {Number} representing the line length for the given
|
||||
# buffer row, exclusive of its line-ending character(s).
|
||||
#
|
||||
# row - A {Number} indicating the buffer row.
|
||||
lineLengthForBufferRow: (row) -> @buffer.lineLengthForRow(row)
|
||||
|
||||
# {Delegates to: TextBuffer.scan}
|
||||
@@ -567,36 +579,16 @@ class Editor extends Model
|
||||
@getCursor().needsAutoscroll = true
|
||||
@buffer.redo(this)
|
||||
|
||||
# Public: Folds all the rows.
|
||||
foldAll: ->
|
||||
@languageMode.foldAll()
|
||||
|
||||
# Public: Unfolds all the rows.
|
||||
unfoldAll: ->
|
||||
@languageMode.unfoldAll()
|
||||
|
||||
# Public: Creates a fold for each section at the given indent level.
|
||||
foldAllAtIndentLevel: (indentLevel) ->
|
||||
@languageMode.foldAllAtIndentLevel(indentLevel)
|
||||
|
||||
# Public: Folds the current row.
|
||||
foldCurrentRow: ->
|
||||
bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row
|
||||
@foldBufferRow(bufferRow)
|
||||
|
||||
# Public: Folds a give buffer row.
|
||||
foldBufferRow: (bufferRow) ->
|
||||
@languageMode.foldBufferRow(bufferRow)
|
||||
|
||||
# Public: Unfolds the current row.
|
||||
unfoldCurrentRow: ->
|
||||
bufferRow = @bufferPositionForScreenPosition(@getCursorScreenPosition()).row
|
||||
@unfoldBufferRow(bufferRow)
|
||||
|
||||
# Public: Unfolds a given a buffer row.
|
||||
unfoldBufferRow: (bufferRow) ->
|
||||
@languageMode.unfoldBufferRow(bufferRow)
|
||||
|
||||
# Public: Folds all selections.
|
||||
foldSelection: ->
|
||||
selection.fold() for selection in @getSelections()
|
||||
@@ -618,6 +610,14 @@ class Editor extends Model
|
||||
for row in [bufferRange.start.row..bufferRange.end.row]
|
||||
@destroyFoldsContainingBufferRow(row)
|
||||
|
||||
# Public: Folds the given buffer row if it's not currently folded, and unfolds
|
||||
# it otherwise.
|
||||
toggleFoldAtBufferRow: (bufferRow) ->
|
||||
if @isFoldedAtBufferRow(bufferRow)
|
||||
@unfoldBufferRow(bufferRow)
|
||||
else
|
||||
@foldBufferRow(bufferRow)
|
||||
|
||||
# Public: Returns whether the current row is folded.
|
||||
isFoldedAtCursorRow: ->
|
||||
@isFoldedAtScreenRow(@getCursorScreenRow())
|
||||
@@ -638,38 +638,6 @@ class Editor extends Model
|
||||
largestFoldStartingAtScreenRow: (screenRow) ->
|
||||
@displayBuffer.largestFoldStartingAtScreenRow(screenRow)
|
||||
|
||||
# Public: Suggests the indent for the given buffer row.
|
||||
suggestedIndentForBufferRow: (bufferRow) ->
|
||||
@languageMode.suggestedIndentForBufferRow(bufferRow)
|
||||
|
||||
# Public: Indents all the rows between two buffer rows.
|
||||
#
|
||||
# * startRow: The row {Number} to start at (inclusive)
|
||||
# * endRow: The row {Number} to end at (inclusive)
|
||||
autoIndentBufferRows: (startRow, endRow) ->
|
||||
@languageMode.autoIndentBufferRows(startRow, endRow)
|
||||
|
||||
# Public: Indents the given buffer row to it's suggested level.
|
||||
autoIndentBufferRow: (bufferRow) ->
|
||||
@languageMode.autoIndentBufferRow(bufferRow)
|
||||
|
||||
# Public:
|
||||
#
|
||||
# FIXME: What does this do?
|
||||
autoDecreaseIndentForBufferRow: (bufferRow) ->
|
||||
@languageMode.autoDecreaseIndentForBufferRow(bufferRow)
|
||||
|
||||
# Public: Wraps the lines between two rows in comments.
|
||||
#
|
||||
# If the language doesn't have comments, nothing happens.
|
||||
#
|
||||
# startRow - The row {Number} to start at (inclusive)
|
||||
# endRow - The row {Number} to end at (inclusive)
|
||||
#
|
||||
# Returns an {Array} of the commented {Ranges}.
|
||||
toggleLineCommentsForBufferRows: (start, end) ->
|
||||
@languageMode.toggleLineCommentsForBufferRows(start, end)
|
||||
|
||||
# Public: Moves the selected line up one row.
|
||||
moveLineUp: ->
|
||||
selection = @getSelectedBufferRange()
|
||||
@@ -797,27 +765,27 @@ class Editor extends Model
|
||||
findMarkers: (attributes) ->
|
||||
@displayBuffer.findMarkers(attributes)
|
||||
|
||||
# {Delegates to: DisplayBuffer.markScreenRange}
|
||||
# Public: {Delegates to: DisplayBuffer.markScreenRange}
|
||||
markScreenRange: (args...) ->
|
||||
@displayBuffer.markScreenRange(args...)
|
||||
|
||||
# {Delegates to: DisplayBuffer.markBufferRange}
|
||||
# Public: {Delegates to: DisplayBuffer.markBufferRange}
|
||||
markBufferRange: (args...) ->
|
||||
@displayBuffer.markBufferRange(args...)
|
||||
|
||||
# {Delegates to: DisplayBuffer.markScreenPosition}
|
||||
# Public: {Delegates to: DisplayBuffer.markScreenPosition}
|
||||
markScreenPosition: (args...) ->
|
||||
@displayBuffer.markScreenPosition(args...)
|
||||
|
||||
# {Delegates to: DisplayBuffer.markBufferPosition}
|
||||
# Public: {Delegates to: DisplayBuffer.markBufferPosition}
|
||||
markBufferPosition: (args...) ->
|
||||
@displayBuffer.markBufferPosition(args...)
|
||||
|
||||
# {Delegates to: DisplayBuffer.destroyMarker}
|
||||
# Public: {Delegates to: DisplayBuffer.destroyMarker}
|
||||
destroyMarker: (args...) ->
|
||||
@displayBuffer.destroyMarker(args...)
|
||||
|
||||
# {Delegates to: DisplayBuffer.getMarkerCount}
|
||||
# Public: {Delegates to: DisplayBuffer.getMarkerCount}
|
||||
getMarkerCount: ->
|
||||
@buffer.getMarkerCount()
|
||||
|
||||
@@ -825,11 +793,6 @@ class Editor extends Model
|
||||
hasMultipleCursors: ->
|
||||
@getCursors().length > 1
|
||||
|
||||
# Public: Returns an Array of all {Cursor}s, including cursors representing
|
||||
# remote users.
|
||||
getAllCursors: ->
|
||||
@getCursors().concat(@getRemoteCursors())
|
||||
|
||||
# Public: Returns an Array of all local {Cursor}s.
|
||||
getCursors: -> new Array(@cursors...)
|
||||
|
||||
@@ -837,9 +800,6 @@ class Editor extends Model
|
||||
getCursor: ->
|
||||
_.last(@cursors)
|
||||
|
||||
# Public: Returns an Array of all remove {Cursor}s.
|
||||
getRemoteCursors: -> new Array(@remoteCursors...)
|
||||
|
||||
# Public: Adds and returns a cursor at the given screen position.
|
||||
addCursorAtScreenPosition: (screenPosition) ->
|
||||
@markScreenPosition(screenPosition, @getSelectionMarkerAttributes())
|
||||
@@ -854,10 +814,7 @@ class Editor extends Model
|
||||
# position.
|
||||
addCursor: (marker) ->
|
||||
cursor = new Cursor(editor: this, marker: marker)
|
||||
if marker.isLocal()
|
||||
@cursors.push(cursor)
|
||||
else
|
||||
@remoteCursors.push(cursor)
|
||||
@cursors.push(cursor)
|
||||
@emit 'cursor-added', cursor
|
||||
cursor
|
||||
|
||||
@@ -878,12 +835,7 @@ class Editor extends Model
|
||||
@destroyFoldsIntersectingBufferRange(marker.getBufferRange())
|
||||
cursor = @addCursor(marker)
|
||||
selection = new Selection(_.extend({editor: this, marker, cursor}, options))
|
||||
|
||||
if marker.isLocal()
|
||||
@selections.push(selection)
|
||||
else
|
||||
@remoteSelections.push(selection)
|
||||
|
||||
@selections.push(selection)
|
||||
selectionBufferRange = selection.getBufferRange()
|
||||
@mergeIntersectingSelections()
|
||||
if selection.destroyed
|
||||
@@ -941,10 +893,7 @@ class Editor extends Model
|
||||
#
|
||||
# * selection - The {Selection} to remove.
|
||||
removeSelection: (selection) ->
|
||||
if selection.isLocal()
|
||||
_.remove(@selections, selection)
|
||||
else
|
||||
_.remove(@remoteSelections, selection)
|
||||
_.remove(@selections, selection)
|
||||
|
||||
# Public: Clears every selection.
|
||||
#
|
||||
@@ -955,7 +904,7 @@ class Editor extends Model
|
||||
|
||||
# Public:
|
||||
#
|
||||
# FIXME: What does this do?
|
||||
# Removes all but one cursor (if there are multiple cursors)
|
||||
consolidateSelections: ->
|
||||
selections = @getSelections()
|
||||
if selections.length > 1
|
||||
@@ -964,10 +913,6 @@ class Editor extends Model
|
||||
else
|
||||
false
|
||||
|
||||
# Public: Returns all selections, including remote selections.
|
||||
getAllSelections: ->
|
||||
@getSelections().concat(@getRemoteSelections())
|
||||
|
||||
# Public: Gets all local selections.
|
||||
#
|
||||
# Returns an {Array} of {Selection}s.
|
||||
@@ -982,21 +927,12 @@ class Editor extends Model
|
||||
getLastSelection: ->
|
||||
_.last(@selections)
|
||||
|
||||
# Public: Returns all remote selections.
|
||||
getRemoteSelections: -> new Array(@remoteSelections...)
|
||||
|
||||
# Public: Gets all local selections, ordered by their position in the buffer.
|
||||
#
|
||||
# Returns an {Array} of {Selection}s.
|
||||
getSelectionsOrderedByBufferPosition: ->
|
||||
@getSelections().sort (a, b) -> a.compare(b)
|
||||
|
||||
# Public: Gets all remote selections, ordered by their position in the buffer.
|
||||
#
|
||||
# Returns an {Array} of {Selection}s.
|
||||
getRemoteSelectionsOrderedByBufferPosition: ->
|
||||
@getRemoteSelections().sort (a, b) -> a.compare(b)
|
||||
|
||||
# Public: Gets the very last local selection in the buffer.
|
||||
#
|
||||
# Returns a {Selection}.
|
||||
@@ -1066,12 +1002,6 @@ class Editor extends Model
|
||||
getSelectedBufferRanges: ->
|
||||
selection.getBufferRange() for selection in @getSelectionsOrderedByBufferPosition()
|
||||
|
||||
# Public: Gets an Array of buffer {Range}s of all the remote {Selection}s.
|
||||
#
|
||||
# Sorted by their position in the file itself.
|
||||
getRemoteSelectedBufferRanges: ->
|
||||
selection.getBufferRange() for selection in @getRemoteSelectionsOrderedByBufferPosition()
|
||||
|
||||
# Public: Returns the selected text of the most recently added local {Selection}.
|
||||
getSelectedText: ->
|
||||
@getLastSelection().getText()
|
||||
@@ -1120,6 +1050,10 @@ class Editor extends Model
|
||||
@moveCursors (cursor) -> cursor.moveToBottom()
|
||||
|
||||
# Public: Moves every local cursor to the beginning of the line.
|
||||
moveCursorToBeginningOfScreenLine: ->
|
||||
@moveCursors (cursor) -> cursor.moveToBeginningOfScreenLine()
|
||||
|
||||
# Public: Moves every local cursor to the beginning of the buffer line.
|
||||
moveCursorToBeginningOfLine: ->
|
||||
@moveCursors (cursor) -> cursor.moveToBeginningOfLine()
|
||||
|
||||
@@ -1128,6 +1062,10 @@ class Editor extends Model
|
||||
@moveCursors (cursor) -> cursor.moveToFirstCharacterOfLine()
|
||||
|
||||
# Public: Moves every local cursor to the end of the line.
|
||||
moveCursorToEndOfScreenLine: ->
|
||||
@moveCursors (cursor) -> cursor.moveToEndOfScreenLine()
|
||||
|
||||
# Public: Moves every local cursor to the end of the buffer line.
|
||||
moveCursorToEndOfLine: ->
|
||||
@moveCursors (cursor) -> cursor.moveToEndOfLine()
|
||||
|
||||
@@ -1233,6 +1171,23 @@ class Editor extends Model
|
||||
addSelectionAbove: ->
|
||||
@expandSelectionsBackward (selection) => selection.addSelectionAbove()
|
||||
|
||||
# Public: Split any multi-line selections into one selection per line.
|
||||
#
|
||||
# This methods break apart all multi-line selections to create multiple
|
||||
# single-line selections that cumulatively cover the same original area.
|
||||
splitSelectionsIntoLines: ->
|
||||
for selection in @getSelections()
|
||||
range = selection.getBufferRange()
|
||||
continue if range.isSingleLine()
|
||||
|
||||
selection.destroy()
|
||||
{start, end} = range
|
||||
@addSelectionForBufferRange([start, [start.row, Infinity]])
|
||||
{row} = start
|
||||
while ++row < end.row
|
||||
@addSelectionForBufferRange([[row, 0], [row, Infinity]])
|
||||
@addSelectionForBufferRange([[end.row, 0], [end.row, end.column]])
|
||||
|
||||
# Public: Transposes the current text selections.
|
||||
#
|
||||
# The text in each selection is reversed so `abcd` would become `dcba`. The
|
||||
@@ -1401,7 +1356,7 @@ class Editor extends Model
|
||||
|
||||
# Private:
|
||||
inspect: ->
|
||||
JSON.stringify @state.toObject()
|
||||
"<Editor #{@id}>"
|
||||
|
||||
# Private:
|
||||
logScreenLines: (start, end) -> @displayBuffer.logLines(start, end)
|
||||
|
||||
@@ -10,6 +10,12 @@ fs = require 'fs-plus'
|
||||
#
|
||||
# You should probably create a {Directory} and access the {File} objects that
|
||||
# it creates, rather than instantiating the {File} class directly.
|
||||
#
|
||||
# ## Requiring in packages
|
||||
#
|
||||
# ```coffee
|
||||
# {File} = require 'atom'
|
||||
# ```
|
||||
module.exports =
|
||||
class File
|
||||
Emitter.includeInto(this)
|
||||
|
||||
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