Comparar commits
1135 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| ede03fd634 | |||
| 926493f05c | |||
| 4f82662263 | |||
| dfa3d8a1c7 | |||
| 677ec44b37 | |||
| 0359e933d1 | |||
| ff88c1c41f | |||
| 0acc632cf5 | |||
| 2c65a30d4a | |||
| 590b8ec221 | |||
| cff1a012af | |||
| 3ea057a145 | |||
| b30ea21ab9 | |||
| 44d42380b8 | |||
| f906afa8ec | |||
| 815a282146 | |||
| 9fe24acbe9 | |||
| f2f3745022 | |||
| d2ecc8c2b6 | |||
| 9166a563c2 | |||
| 7388468b5a | |||
| 21bc658230 | |||
| 72f85aeae4 | |||
| 26b3cfc3b2 | |||
| c91eb1fbbe | |||
| 75ed3aa03b | |||
| 00d783fb23 | |||
| b80e7fc137 | |||
| e1d2bbabfb | |||
| 9de5adf572 | |||
| 2a3eab945d | |||
| df8d77cc47 | |||
| 9d11ea8bcd | |||
| f19d2d7369 | |||
| 7f790137ed | |||
| c56dce0c80 | |||
| 1f105542a3 | |||
| ec499e2bde | |||
| 59202f9a56 | |||
| e437673fbc | |||
| ded222551a | |||
| fe2def8321 | |||
| e19368a291 | |||
| 3980082e3c | |||
| 9f29e423db | |||
| 23d9f6e41f | |||
| 649f0ac246 | |||
| b9dcb569b1 | |||
| 6538ee47aa | |||
| 043a50012e | |||
| 9ed0ded91b | |||
| ad5c0f93ed | |||
| 4265de2232 | |||
| b7ae12f86f | |||
| 20e7fea3ab | |||
| 910469d373 | |||
| ff9c9adeff | |||
| ceac8ba0e1 | |||
| 24f386df2d | |||
| 1006706cf6 | |||
| 0b0a3ae4c2 | |||
| 276de1e5ae | |||
| fcffcc83f9 | |||
| 4122b5a43f | |||
| 2430edbcb8 | |||
| e2269d5d51 | |||
| 2d04c50184 | |||
| 2dc8c94165 | |||
| c1022d15ba | |||
| b620be7780 | |||
| 70c89dfefa | |||
| ace61c330a | |||
| 0626df5cdd | |||
| b1679a03d6 | |||
| af95abe185 | |||
| a23561f9ce | |||
| a93048d3ae | |||
| 1814b187e9 | |||
| d7d7bd2db5 | |||
| f86ca9b59a | |||
| 58cccd76a4 | |||
| fd1477fc92 | |||
| eca9ba107c | |||
| 94126942eb | |||
| e6697ce3a7 | |||
| ccd86d91de | |||
| 2f8ec967f6 | |||
| 709ae6a1bc | |||
| 5e14d44d6a | |||
| 41dd4a386a | |||
| 9af4b14716 | |||
| aed9f18457 | |||
| edf2fbe0e5 | |||
| ca71bf224b | |||
| 012363a785 | |||
| 1a48903f96 | |||
| 44331d0ba6 | |||
| fe7c5b4bc1 | |||
| 1825af1309 | |||
| b5947f5a56 | |||
| 864d6ce8f2 | |||
| ac07cf3d9a | |||
| 6ba3698547 | |||
| d57e5f389d | |||
| 2023aeef97 | |||
| 1c6564f7a5 | |||
| d9d28b5236 | |||
| 8734eab8cb | |||
| 1ad5158f19 | |||
| 453e034a5f | |||
| 9841a3588f | |||
| dd0ae8a8ea | |||
| d8ddd52df8 | |||
| 83696bb9c7 | |||
| 243c4efe20 | |||
| 22c65f2407 | |||
| e553fefc25 | |||
| cf6fc22c87 | |||
| a6d8f588c3 | |||
| 2ef74de0f8 | |||
| 2ee6469b17 | |||
| 9977884a2c | |||
| c331723c55 | |||
| dd3ca1fc2f | |||
| f7f2da6ad2 | |||
| eba74d1420 | |||
| 91c9f6ffa5 | |||
| db319c9f9c | |||
| 134affcd8f | |||
| 9bfa7704e6 | |||
| 8acd84c7e2 | |||
| a53d223b6f | |||
| 673b78561a | |||
| 57c56fc46e | |||
| d81fb8cabf | |||
| 1dec9d0ad2 | |||
| bda75c1a8f | |||
| c0c2d797b1 | |||
| a342a18440 | |||
| 047425c564 | |||
| 8374911dab | |||
| 6ca3c90abe | |||
| 96cb266c27 | |||
| 21723b155e | |||
| 14c3feee17 | |||
| 0255f1e223 | |||
| 76a5912126 | |||
| 848dc12f28 | |||
| 7a41fabf60 | |||
| f4b82fe3a4 | |||
| aec32b65a0 | |||
| 01e0970406 | |||
| b08978a431 | |||
| e4b934d3fa | |||
| c350285044 | |||
| 4a7b43f609 | |||
| 0d66c68fe8 | |||
| b72028c68d | |||
| 332b918c01 | |||
| bc94dc63e6 | |||
| a1d540d288 | |||
| af4034ba39 | |||
| 89cbd6b834 | |||
| fe01ded75b | |||
| aab4f3b761 | |||
| adf5cfc78c | |||
| 66bb9dab93 | |||
| c8e4535e8b | |||
| 7199cda549 | |||
| 1eb9c3d0d5 | |||
| 27c03ae3f3 | |||
| a8ddc530a2 | |||
| 8e78583521 | |||
| 7317d52896 | |||
| 54dd5ea28e | |||
| 22fe04fb27 | |||
| adb174ef5f | |||
| d3a016bf69 | |||
| 46c5f09721 | |||
| 6401213510 | |||
| 5f92c6df6c | |||
| 75e3659809 | |||
| b458b86bbd | |||
| 645f950368 | |||
| 8dd183fc78 | |||
| 6ef6fb77f9 | |||
| 159adcb00c | |||
| 06ccb48efe | |||
| 95f2d02d18 | |||
| 63df8dfcdb | |||
| 12bf0f5381 | |||
| 637acf63ce | |||
| 9ff23e6bed | |||
| d96f15e8b5 | |||
| 5bf654f6cc | |||
| 16dba3e68e | |||
| 08716fd888 | |||
| 43e30ad466 | |||
| 16c86047eb | |||
| 0d36eec288 | |||
| 2e697f65e8 | |||
| edab3e5a27 | |||
| 97999f1080 | |||
| a268b3e963 | |||
| 8931bf8f3a | |||
| 918c31905f | |||
| c7ff4f9897 | |||
| 4f604ced3c | |||
| 8c3147f975 | |||
| a29c18f8c0 | |||
| 43f517abff | |||
| 8b901f81e0 | |||
| 4182ee4f12 | |||
| 601b314236 | |||
| 7d596355b9 | |||
| fe0cdbf349 | |||
| c2381ba61c | |||
| ccc5f8fa66 | |||
| e29ffbea8c | |||
| efbd508163 | |||
| 4179d9d268 | |||
| 3fc3d48def | |||
| b438b311f3 | |||
| 561e31c0c5 | |||
| 2a8a5268c6 | |||
| 28b085be1c | |||
| f5bc71e559 | |||
| 2188dd201d | |||
| b21eb6f934 | |||
| 9b0d8ec242 | |||
| 3c32a8e8fb | |||
| 3ab7836ab2 | |||
| ef8b7531b0 | |||
| b04f9f9488 | |||
| 452d86ac0c | |||
| 6fe1bae40d | |||
| e2170ea907 | |||
| 545bf4bd98 | |||
| 1fdc78a157 | |||
| 738bfd7253 | |||
| 8b0b997db6 | |||
| 263ab3b4a6 | |||
| 3afbcbe25f | |||
| 578ca8b197 | |||
| 5e674fe29e | |||
| 72fe586101 | |||
| 82d5577bdc | |||
| c703cf2050 | |||
| c19c8c5341 | |||
| 32b5592475 | |||
| 993f1ac2d6 | |||
| 21b40acdf1 | |||
| ddf7c04e66 | |||
| c127237cc6 | |||
| 9694d255f0 | |||
| 81b4803d56 | |||
| 893e9873a1 | |||
| 23e805fe9e | |||
| 43257de7cd | |||
| c7fded0d7f | |||
| 284d823ad5 | |||
| 164a121de9 | |||
| 50ab49aedb | |||
| a127d3c4eb | |||
| cac5c6e3a5 | |||
| 1ee783fdb9 | |||
| 47870a1214 | |||
| 7801d8562f | |||
| 104271861c | |||
| de25b8ea37 | |||
| ba9f353c4a | |||
| 7665cd1a6a | |||
| c39d8d9aa7 | |||
| 4b0d22917b | |||
| 720b2ad47d | |||
| d34327a667 | |||
| d2146f9b2e | |||
| 732d36af28 | |||
| edfc86f153 | |||
| 339e30d973 | |||
| 5e1e092650 | |||
| c69febd44d | |||
| a9d7564f3e | |||
| a0b733b53d | |||
| 8cb565ad2a | |||
| 32158711ce | |||
| e099f00739 | |||
| e707ab5441 | |||
| 3b7f1467e1 | |||
| 1c7eef89c3 | |||
| 4fc1f5b248 | |||
| 6eacfa7077 | |||
| 9aefafb831 | |||
| 5a3353ec28 | |||
| e87b8dc463 | |||
| 60daa483e6 | |||
| cd699d8b9b | |||
| d33a962848 | |||
| 0d2067e1a4 | |||
| 8647137952 | |||
| fbcfad28c3 | |||
| 5e1b2e2696 | |||
| a0346e95cf | |||
| 20ee7b432c | |||
| 4026e6ca5c | |||
| 378901e0d2 | |||
| 964abd3141 | |||
| 5ca7ad3bce | |||
| 4dcba4bb27 | |||
| 073ea84d69 | |||
| 2965d2e974 | |||
| a67f0d4d57 | |||
| 5309d5f24d | |||
| af3ca57094 | |||
| db375cd190 | |||
| d96b63d791 | |||
| e435b48750 | |||
| a1f8a21c7c | |||
| 69f9f10c6b | |||
| d3c6bd2f98 | |||
| 466868e639 | |||
| 2317c6835e | |||
| 4e99d003ee | |||
| 1a5e10c1d2 | |||
| 8efcb1abfa | |||
| f031a9706d | |||
| eb7f3ff5af | |||
| bb595ab08a | |||
| 101326a130 | |||
| ad60594c13 | |||
| ca6da5f9c1 | |||
| c397b3cc60 | |||
| 1074c6c34e | |||
| 6cb0f1ff78 | |||
| 371e31c786 | |||
| dce70b35b5 | |||
| 99a67ca1ab | |||
| 9dc1758d76 | |||
| 196942d126 | |||
| d95aa0aac1 | |||
| 27f0d11039 | |||
| a69e6136ea | |||
| 0fd8b6be8d | |||
| 8e51a7f6d6 | |||
| bdb4cd5247 | |||
| 3afe750a66 | |||
| c8f3e056e9 | |||
| 32fdf0b681 | |||
| 82c73c9911 | |||
| fe0184d067 | |||
| 0ef6757e65 | |||
| deb4365d65 | |||
| 750f4ee410 | |||
| 1382bd3b3b | |||
| d908c8b026 | |||
| 6f766acac8 | |||
| 7eba9d3a23 | |||
| cde5861cb8 | |||
| 765c15829d | |||
| 0471619269 | |||
| d8ba8f13d8 | |||
| c089429c14 | |||
| b22f850e03 | |||
| cf47ee5063 | |||
| 9d6a01d11c | |||
| bcf9dfd236 | |||
| c281eb9596 | |||
| 2acde6a727 | |||
| ee9b78afb6 | |||
| a379d47230 | |||
| 57c23e1b5f | |||
| f8d959ae16 | |||
| 831454bd30 | |||
| 13abb28486 | |||
| 717704c8ed | |||
| eab26fb3a6 | |||
| 17947d0f99 | |||
| 626e22e4ae | |||
| 5837b7cfda | |||
| 02a9d11bb5 | |||
| 0d89be26be | |||
| 1a487db29f | |||
| 1144e52fea | |||
| 2938a8e650 | |||
| cd97de76fc | |||
| 25c099f3a2 | |||
| eb39b8505e | |||
| 7a90cc46ad | |||
| c6c1cb233d | |||
| f9e37c9b47 | |||
| dfb6835449 | |||
| bf13b426c5 | |||
| 14175d80ef | |||
| c99e211144 | |||
| 1d04cbf584 | |||
| 92650e079f | |||
| f29ce127db | |||
| b29f1965f0 | |||
| 48a2a1934b | |||
| bb65a1a47e | |||
| f6a8b72fd4 | |||
| 196e908961 | |||
| 72744494b8 | |||
| c287be1725 | |||
| 28ec1f3e2d | |||
| d0bf769896 | |||
| 8fe60b5838 | |||
| 484a7c95ee | |||
| 44a3365787 | |||
| f3e88b56f3 | |||
| d618472f95 | |||
| a45ffb3aaa | |||
| 1d2b2eec4c | |||
| 5c1f750f94 | |||
| 85a6db253b | |||
| 72caf279a6 | |||
| 05ff058ca5 | |||
| 022ead9228 | |||
| 14fd9aac7e | |||
| 5975884a0e | |||
| e8ce559034 | |||
| 9add438ea4 | |||
| d65a6c3fc4 | |||
| a493359b58 | |||
| d6de973500 | |||
| 25cc37bc86 | |||
| 0e6bc275b7 | |||
| cec731b697 | |||
| 6942b82a70 | |||
| b6d5a1ddd8 | |||
| 8d4ae33134 | |||
| cbec03c158 | |||
| 1fe0a1ad7b | |||
| 8b9ede8414 | |||
| 194094043e | |||
| 9068f28e83 | |||
| ebb5d38e1a | |||
| aa1ed1dfcc | |||
| 0292c66f93 | |||
| 4ce68fe5d8 | |||
| 289e7d56c3 | |||
| cb7471945c | |||
| 8da9e8ddc1 | |||
| a68694e6e2 | |||
| 98b5a400e6 | |||
| 0bbd9630f5 | |||
| c6770aa83e | |||
| e26d97d5ac | |||
| b2177cbc09 | |||
| c8663541f0 | |||
| 9f32a24e7e | |||
| 914a87290b | |||
| 112b8bfa29 | |||
| 5d99acd8c5 | |||
| 066d8dc944 | |||
| 9af6e99682 | |||
| bc65137911 | |||
| 02dfe074e6 | |||
| 2b4c2f1758 | |||
| a7494cf649 | |||
| 3d494ed9fa | |||
| 47a14bb2e6 | |||
| 7a71f26345 | |||
| 870c4d4214 | |||
| ac5d10fae2 | |||
| 2963fe177f | |||
| 0947947a9d | |||
| 0112e8887f | |||
| 3a7ecccec7 | |||
| f9a9712f54 | |||
| 5e5ba63c59 | |||
| 2c58d1a2b7 | |||
| 5e2e5a4b58 | |||
| 344d237a42 | |||
| 04eef20c84 | |||
| 77dba8d19b | |||
| 704294a2d5 | |||
| 847a8165e0 | |||
| 18f2f5f821 | |||
| 7f2e0e2317 | |||
| 963513e840 | |||
| b1470fc1b5 | |||
| 43482ea78e | |||
| 167e6dc1bf | |||
| 088a627468 | |||
| 4f6c655294 | |||
| e2db58c6ee | |||
| 64aba6ec24 | |||
| 2910170eeb | |||
| 9bc24b8736 | |||
| 401ef87bb2 | |||
| 0c5ed1eee4 | |||
| 790c227924 | |||
| 822f13d6e3 | |||
| 70c14eb4f5 | |||
| 673d2330f0 | |||
| e187604942 | |||
| c2cd1cd13d | |||
| c720a6a029 | |||
| 601466782f | |||
| 92ef8f22e4 | |||
| 690ffab9c0 | |||
| 512f373ca6 | |||
| e8f4da54a6 | |||
| d56137e3c0 | |||
| 9629afb145 | |||
| 5ecbd024cf | |||
| b4974eee41 | |||
| 3ca3b23ef8 | |||
| 9b48df928c | |||
| 884d276458 | |||
| 441e179c48 | |||
| e1248561b6 | |||
| 7b5055f974 | |||
| 206db1c271 | |||
| 821debcb85 | |||
| b9395d2946 | |||
| 44e6e7f45d | |||
| 88ee021b4d | |||
| b5c8e3e1fe | |||
| 9962ce9859 | |||
| 882d766689 | |||
| 959401f5a7 | |||
| fa9aa3691b | |||
| a95fdce85f | |||
| e8edc83e39 | |||
| 3ff702581a | |||
| 88c9275bff | |||
| 826d536c09 | |||
| c99c2af6ae | |||
| ccc6eed3da | |||
| e1aec57ffe | |||
| c1fc09e510 | |||
| 19212f99ee | |||
| 2681dcc63c | |||
| 6f5d85edb9 | |||
| a59c01c6be | |||
| a78613b7e5 | |||
| 33c1353500 | |||
| 22a7c25104 | |||
| 902406c572 | |||
| b0077986b4 | |||
| 686ebf8759 | |||
| 66831ce8b9 | |||
| dd2c6d2f24 | |||
| 30a175230a | |||
| fe3e71cbd7 | |||
| 8d2e1b7e43 | |||
| c37b884007 | |||
| 033d85fd4f | |||
| 963aef70e8 | |||
| 1dce2c439d | |||
| b3ed9d468b | |||
| 878eca30e9 | |||
| 97aed1f680 | |||
| bac76784e0 | |||
| 8e970b64b8 | |||
| dfe9f5684e | |||
| f0f477d481 | |||
| 470ce7bd22 | |||
| f59080ec74 | |||
| f5ca836e49 | |||
| 319f9a22d8 | |||
| 458d3b3d3c | |||
| 11ec939924 | |||
| e14019e2dd | |||
| 180912db61 | |||
| aba1900d13 | |||
| 5fdb3196a3 | |||
| 8763a49dc6 | |||
| ffbd15eb98 | |||
| 871b7406cd | |||
| 66fa9d6a42 | |||
| 8c7649dd57 | |||
| 38c4fb3884 | |||
| 2d17ffc792 | |||
| 1d70e12594 | |||
| 8fb729d000 | |||
| b9fd05ba1e | |||
| 2ad9fb52ae | |||
| 91778cb566 | |||
| a76cda4564 | |||
| 7cb1ea038c | |||
| fa18a5cb33 | |||
| a012248316 | |||
| 53ccf7cf87 | |||
| fbda0028ca | |||
| 9e3648c22f | |||
| d8374eb251 | |||
| fd6e5e7a13 | |||
| 1da6bca2ae | |||
| 40630114e7 | |||
| 6736b6af3e | |||
| 9c7747efbf | |||
| 42b203d502 | |||
| d7d4a990a5 | |||
| 8597951f0c | |||
| 7d47527b17 | |||
| 197d185ea4 | |||
| 91d1c2914e | |||
| da2487ba84 | |||
| 171c3e018b | |||
| bda8397d3d | |||
| c9aa082e63 | |||
| 6c4eb7439a | |||
| 76922c2d46 | |||
| fd7c3f3980 | |||
| 6b750c82ca | |||
| 4d643242a1 | |||
| e974e61012 | |||
| 1ce4f3c552 | |||
| 3ac2cae355 | |||
| 2ffa989ba4 | |||
| 341454cd81 | |||
| 4507981f2a | |||
| 14f2444883 | |||
| 267b1bdce6 | |||
| 53451e0bcd | |||
| 4223ea25ee | |||
| bb517467eb | |||
| dd16aefbb6 | |||
| 471c323ca5 | |||
| bf021ab7f7 | |||
| 9198f3b809 | |||
| b7c227dbfc | |||
| 0dc031140c | |||
| e853bbfcb6 | |||
| cef7577826 | |||
| 8366887df6 | |||
| e7f63b7c62 | |||
| 81a04769fb | |||
| d4fcfbd034 | |||
| bab92c6d0a | |||
| 72523de046 | |||
| 82fbba4547 | |||
| 8b112cfd65 | |||
| ec83c1061b | |||
| 75c573fe61 | |||
| ed8800d182 | |||
| 0aabfddfcb | |||
| 1bf1785885 | |||
| 20df9f4666 | |||
| e40b7b1412 | |||
| 611559ecd6 | |||
| c74783ebbf | |||
| 9df69f801b | |||
| 105f74e15e | |||
| d7e56c447b | |||
| cecee6a430 | |||
| e3e83918e5 | |||
| 0438565c43 | |||
| cbfa87a3b4 | |||
| 3187013eac | |||
| e7b632eb18 | |||
| cdce91157f | |||
| a566bd469d | |||
| 389586bb41 | |||
| 51b39500fe | |||
| b5a6f0db0f | |||
| 259faa5b5a | |||
| c8477c2f01 | |||
| f6b00fc90f | |||
| b3246d63fb | |||
| 07eb2d014a | |||
| bf0015f6cc | |||
| ca3d1e869c | |||
| a57083a48b | |||
| 2ae46734db | |||
| 3918435c7f | |||
| 180e5d4164 | |||
| d8f5ef71cd | |||
| 2e2bab7778 | |||
| b10ef9454b | |||
| 31fc4fcde8 | |||
| f48514114d | |||
| fd929364d1 | |||
| 4f758f1fe2 | |||
| 35e1940984 | |||
| bdadf43341 | |||
| 92b829c89b | |||
| 3db9e16637 | |||
| 29d7735848 | |||
| 5b4b57f10f | |||
| ed4b50e3b4 | |||
| d526cfcba0 | |||
| 0cf2cdceea | |||
| 8239bb7e05 | |||
| 6d3e137db8 | |||
| fa34eea27a | |||
| 3e41b0967f | |||
| bd3cfda2bb | |||
| 386b8a9e46 | |||
| 099b5186ab | |||
| deab6027fb | |||
| 590582782b | |||
| abb0a3c792 | |||
| 43baee5abd | |||
| 75153388cc | |||
| b959fc0a6a | |||
| 77b5fd47ca | |||
| f3a9500e61 | |||
| dadb0d5427 | |||
| 9cdc15b161 | |||
| 7c27cbd9f5 | |||
| 010931391e | |||
| eaf6aba943 | |||
| d60d710325 | |||
| bb1bcc233a | |||
| f0813dbca8 | |||
| 9bded1cd19 | |||
| c6e96a6188 | |||
| 09ff90bfc4 | |||
| 2bf9036578 | |||
| 6a7e4c30fb | |||
| ed41cc3cad | |||
| 9aee5adeb6 | |||
| 70f3527123 | |||
| e67e8ff0f5 | |||
| 4448a7419c | |||
| e18a0f045a | |||
| e8e84cbd2f | |||
| e8c5027026 | |||
| da964a8f15 | |||
| 2c4aee1181 | |||
| 24d3f1daeb | |||
| beb2fb08ea | |||
| 7b86891642 | |||
| 87e325dce1 | |||
| ee87c757ed | |||
| a25812dca7 | |||
| 1f9a7599b7 | |||
| 07dee8838c | |||
| 8367b020ec | |||
| 9fce6a2f1c | |||
| 165a417a9d | |||
| 39fe0c418b | |||
| 75cee638bc | |||
| ae72d4ad56 | |||
| bfcb24f517 | |||
| 208ed09109 | |||
| 0b7f291e17 | |||
| a61b057aea | |||
| 4c817baf4c | |||
| a564cc66f6 | |||
| 14c58c4517 | |||
| ebe77065cc | |||
| e1d35ed90f | |||
| 5d23a8fceb | |||
| 30b0fed60f | |||
| 90f3726c5c | |||
| 5f10c48219 | |||
| 23957d7f66 | |||
| 58bf19cc83 | |||
| f439b04978 | |||
| f6d1647021 | |||
| 0ccaf9049f | |||
| 132194337e | |||
| f0198944b5 | |||
| f414c0955b | |||
| 4ffa5bb90e | |||
| 4dbca94d32 | |||
| 7111961929 | |||
| 5d46d7a881 | |||
| 8d1d64d9d3 | |||
| 60498616b7 | |||
| ed745d2072 | |||
| 1f69963982 | |||
| 787b6fb677 | |||
| 2d5b04579f | |||
| 9fbfeb970b | |||
| 63cac904ae | |||
| 04ad048e1f | |||
| b9b8c61c11 | |||
| 6a8697a357 | |||
| bb35655e67 | |||
| 865d94d758 | |||
| 965e146bf2 | |||
| d97b394000 | |||
| 29b85a1bb9 | |||
| dda65d82c3 | |||
| 56de32f433 | |||
| 3cb751723d | |||
| 344e6d15ce | |||
| 8e7de0dc89 | |||
| 3b388f16fd | |||
| c64b1a7628 | |||
| 5ce71f5ded | |||
| 02415ca400 | |||
| 69f357b538 | |||
| 780441bdb8 | |||
| e0ad22d0bd | |||
| 115203cbd2 | |||
| 0b5155f496 | |||
| 4f18baaed5 | |||
| 095320680f | |||
| 8a8df8d6cb | |||
| 6bb4eaee93 | |||
| 7710845cdc | |||
| 8471ffed29 | |||
| ce6d793528 | |||
| e5bf70544e | |||
| c591911455 | |||
| 44955e536b | |||
| 6f25945911 | |||
| e5bd341f05 | |||
| e9d4563084 | |||
| b67ce8f4f8 | |||
| 6a90bc1b17 | |||
| 6ba8855d51 | |||
| e6b55cd521 | |||
| 59f453bb06 | |||
| e275b6c414 | |||
| f3cdb6e0a2 | |||
| c4fc75215b | |||
| 68e7c209bd | |||
| 1737df6ff9 | |||
| 897bf2f8e0 | |||
| 1851c81e60 | |||
| 938c85e616 | |||
| 350602a740 | |||
| f1ed51a823 | |||
| 086f0a0684 | |||
| fbd8f7a1a7 | |||
| 3e359e3c4e | |||
| 425c076221 | |||
| 351c1f1ab8 | |||
| 1efde26230 | |||
| 527aa61fa1 | |||
| 18a712a1f7 | |||
| 6c8db8ee49 | |||
| 4bfcdf4d5b | |||
| c1f3499359 | |||
| 201717a9aa | |||
| 3e83364e38 | |||
| ba681023da | |||
| 2ae7775caf | |||
| b2ab9d3246 | |||
| 7cc61c5252 | |||
| b3b2be2430 | |||
| 1674f16a0e | |||
| 24c84fe09c | |||
| 2136fdaa60 | |||
| 347eb9ce83 | |||
| a3eb76d630 | |||
| 163994340d | |||
| 8ffa8fed12 | |||
| 2141d06f61 | |||
| e1a1a7efc9 | |||
| f3f94a2af0 | |||
| 3d8e304b1c | |||
| 2d0ba3e1f9 | |||
| be0ebab841 | |||
| 8c1d49c703 | |||
| 4af71391a6 | |||
| 90802e3725 | |||
| a9d1afdf65 | |||
| 2a51eb7614 | |||
| 96d38d7252 | |||
| 4705e382bf | |||
| 11f8eba2c3 | |||
| d638fe9ca0 | |||
| 3f69197a08 | |||
| 43bf47fa26 | |||
| 092ed37ed9 | |||
| 19da22d440 | |||
| 51ff3ec46a | |||
| 36b5518add | |||
| dbdb51db2f | |||
| 7db11ea3c6 | |||
| f6ce92e52d | |||
| 6c1999ab60 | |||
| 3dde91dd3d | |||
| 9deec49f81 | |||
| 7c3106d9d9 | |||
| 7138d5b888 | |||
| 43495f05ba | |||
| 518a929880 | |||
| dd00f0d4ea | |||
| 20d8458009 | |||
| 13f45c615a | |||
| 998d542825 | |||
| fccc47023c | |||
| a2f2efe8b3 | |||
| c6bdc59fef | |||
| 047e5e696f | |||
| 72191f3d3d | |||
| 5ced583a7c | |||
| c326988a7d | |||
| 5ff47f3aa1 | |||
| 308bb95ea2 | |||
| 22ec8c3a75 | |||
| 3490022180 | |||
| ec3e139632 | |||
| 63e3be8630 | |||
| e61d482384 | |||
| b108b5030a | |||
| 943fe29ca3 | |||
| 2bbf7fd0d7 | |||
| 42e9c612b2 | |||
| 33778cefdd | |||
| 39fb5dc134 | |||
| ffedaa1bc1 | |||
| 3402504779 | |||
| c8253be1ae | |||
| 9ede5f85e3 | |||
| 640fd0051b | |||
| 779c918179 | |||
| ee1f21439d | |||
| f25d8d83c5 | |||
| b4270b1e9e | |||
| 98701283e8 | |||
| 34f71a2623 | |||
| af06bf8984 | |||
| 796632c36c | |||
| 65fa5bf880 | |||
| 1ccc93c50b | |||
| f4d19810b8 | |||
| 1c74925637 | |||
| a4d2b4d21a | |||
| 88eb803d91 | |||
| b61654b52f | |||
| db1768a9c7 | |||
| 6156dac3de | |||
| d5a7f7396e | |||
| ca65ff6c55 | |||
| 49782d3335 | |||
| e45aeecb0a | |||
| d3edf33dc4 | |||
| c54c925793 | |||
| e3b89aca93 | |||
| 9183d4968f | |||
| 1aa96fc37a | |||
| d75d3b6d09 | |||
| f60856eca7 | |||
| 3a7049a1d9 | |||
| 29d1b42d04 | |||
| 80552675d0 | |||
| ff696355ee | |||
| 3a2c4ca74d | |||
| 777cc4b561 | |||
| c0fe3c01de | |||
| 97dbe0d3b2 | |||
| 250938869e | |||
| 6092de9af8 | |||
| 01855901e7 | |||
| 161542a4af | |||
| 6d2bd7756e | |||
| f0fece2cf0 | |||
| c36ee1dbe6 | |||
| 76ec5a98c4 | |||
| ec022b5c77 | |||
| 912636a599 | |||
| 011c3cc223 | |||
| 433b64d581 | |||
| 365d69a19c | |||
| 5d1e507988 | |||
| 776b267396 | |||
| 77e4b7f90f | |||
| 2501b98e8f | |||
| 32d301ef34 | |||
| f4861b7919 | |||
| 94e4fef108 | |||
| 7ae432fad5 | |||
| 18057e303b | |||
| 71064adae2 | |||
| 43d964c357 | |||
| 5f4115cb81 | |||
| 8cf2cdb2a9 | |||
| a460530770 | |||
| 162bab506d | |||
| 6c5669b53d | |||
| a7db8229d5 | |||
| b7848d760f | |||
| e6d8f3b9bf | |||
| 0e270f85ba | |||
| 9e3f4dc033 | |||
| c1a744b29c | |||
| a283161c1e | |||
| 9ede9571bc | |||
| dbc21befec | |||
| ca0f0357d7 | |||
| be1c1f8719 | |||
| e95c60b4ec | |||
| 847dd6d93b | |||
| 7f62720350 | |||
| 8af0a59c52 | |||
| 31a909828d | |||
| 78562dcf15 | |||
| 4869fad7f6 | |||
| 77da136d17 | |||
| 67b1d0d22a | |||
| 23fcb59c5d | |||
| 16ef30dc82 | |||
| e3a4f450ad | |||
| 847578398d | |||
| 63dc8863e3 | |||
| 58d09ec5a0 | |||
| bb5d70fa2e | |||
| 4a904673e5 | |||
| 4c1d47779b | |||
| a0adc32e04 | |||
| 2434db4dc2 | |||
| e2ee99556d | |||
| cececc2297 | |||
| f0825ca526 | |||
| 57c0261f96 | |||
| 6891922e07 | |||
| e03a7c336d | |||
| 49a32dd2dc | |||
| 94d4ad618c | |||
| 9b0f8ccee0 | |||
| fa963f0583 | |||
| d36c4f0ec9 | |||
| e6e72d99f3 | |||
| 9ce22fd6d3 | |||
| 5166ca274c | |||
| 6f1dd702b0 | |||
| 1d4f447975 | |||
| 4998fdda85 | |||
| aa52428c26 | |||
| 9c1127dd1b | |||
| 91d4ef2653 | |||
| 80cc881912 | |||
| 867aaa8f95 | |||
| 28a1dc58e7 | |||
| ee7ef0f893 | |||
| eaf60a00b3 | |||
| e01c19e6b7 | |||
| 930e479cdb | |||
| 46c6d63dcd | |||
| 1b502c6349 | |||
| 07eb01016d | |||
| 99d6955644 | |||
| 3f05ff9b14 | |||
| 9caf3ca349 | |||
| 25a1b77048 | |||
| 866c0c0668 | |||
| 6094efde76 | |||
| b4099ba66f | |||
| 10b560b5a7 | |||
| 305c331777 | |||
| 0b5cad27d2 | |||
| 7d967e71f4 | |||
| d36a0aa437 | |||
| 8316da3338 | |||
| a0b200565c | |||
| 8f4de0b568 | |||
| 45cd3e9335 | |||
| c58b30bba9 | |||
| 0093f39102 | |||
| b1f041fa6e | |||
| ca11661f6d | |||
| bc4ceb189c | |||
| 98694e5407 | |||
| 86ae743e9d | |||
| dddd515c35 | |||
| 85d46004fd | |||
| 5cb6aa0538 | |||
| d228cfe1ee | |||
| a8f7c5201b | |||
| e15b96691e | |||
| 3053c4a585 | |||
| 776d58bf21 | |||
| 1b5762274c | |||
| 1f20dbf16d | |||
| 9fe507d675 | |||
| a190a069c1 | |||
| c697e58a71 | |||
| 1f36d0ca02 | |||
| 235a1e46ab | |||
| 1197dc1979 | |||
| 94e94506b5 | |||
| 163c800d55 | |||
| 3f5a6184b6 | |||
| 77819616a8 | |||
| 3749726742 | |||
| d22d6e9af2 | |||
| 7ad6f0b18b | |||
| cdfd27be3e | |||
| 4e554a4941 | |||
| 3db5cad927 | |||
| 1a21600bde | |||
| 3c3811c527 | |||
| 16c0ef935f | |||
| 6cc897ac67 | |||
| 80782feb24 | |||
| 3cfe50585a | |||
| 4c336ed0ee | |||
| c1cf8936eb | |||
| d131b3e39c | |||
| 043ce8f972 | |||
| e4cac4de70 | |||
| c5cc0706e0 | |||
| 45ddece673 | |||
| 2901a04843 | |||
| 0259536c5d | |||
| 48692e5127 | |||
| 19dc6b3523 | |||
| d1496a0634 | |||
| d8c4fa902d | |||
| f1e768767e | |||
| c03e9ad815 | |||
| ebe3d0d885 | |||
| 46f73d037f | |||
| e7e0f081f4 | |||
| dde46ca931 | |||
| 65746521a6 | |||
| 5352f7322c | |||
| f395905d4e | |||
| f84635766c | |||
| 413078a493 | |||
| d3c9b6e547 | |||
| 078fffa7c1 | |||
| 8472ac4fc8 | |||
| 1bdfb004ef | |||
| e42079c762 | |||
| a0ceb78627 | |||
| eb985a9880 | |||
| d0eb26c35f | |||
| 1489488159 | |||
| a0a90ca26a | |||
| 21f6676094 | |||
| ad9721a893 | |||
| 12ae7abc38 | |||
| ee0814313f | |||
| a3c9e01595 | |||
| 2e801fbf87 | |||
| 40d7fb3c45 | |||
| 14ad277012 | |||
| 6a9f29024a | |||
| 3ce6176313 | |||
| 7d7ed79fde | |||
| 8aa32fc8d1 | |||
| f8ed6eadc8 | |||
| 0c2c739741 |
@@ -10,3 +10,4 @@ debug.log
|
||||
/atom-shell/
|
||||
docs/output
|
||||
spec/fixtures/evil-files/
|
||||
/apm
|
||||
|
||||
+45
-49
@@ -1,67 +1,63 @@
|
||||
# :rotating_light: Contributing to Atom :rotating_light:
|
||||
# :tada: Contributing to Atom :tada:
|
||||
|
||||
These are just guidelines, not rules, use your best judgement and feel free
|
||||
to propose changes to this document in a pull request.
|
||||
|
||||
## Issues
|
||||
* Include screenshots and animated GIFs whenever possible, they are immensely
|
||||
helpful
|
||||
helpful.
|
||||
* Include the behavior you expected to happen and other places you've seen
|
||||
that behavior such as Emacs, vi, Xcode, etc.
|
||||
* Check the Console app for stack traces to include if reporting a crash
|
||||
* Check the Dev tools (`alt-cmd-i`) for errors and stack traces to include
|
||||
* Check the Console app for stack traces to include if reporting a crash.
|
||||
* Check the Dev tools (`alt-cmd-i`) for errors and stack traces to include.
|
||||
|
||||
### Package Repositories
|
||||
|
||||
## Code
|
||||
* Follow the [JavaScript](https://github.com/styleguide/javascript) and
|
||||
[CSS](https://github.com/styleguide/css) styleguides
|
||||
This is the repository for the core Atom editor only. Atom comes bundled with
|
||||
many packages and themes that are stored in other repos under the
|
||||
[atom org](https://github.com/atom) such as [tabs](https://github.com/atom/tabs),
|
||||
[find-and-replace](https://github.com/atom/find-and-replace),
|
||||
[language-javascript](https://github.com/atom/language-javascript),
|
||||
and [atom-light-ui](http://github.com/atom/atom-light-ui).
|
||||
|
||||
If you think you know which package is causing the issue you are reporting, feel
|
||||
free to open up the issue in that specific repository instead. When in doubt
|
||||
just open the issue here but be aware that it may get closed here and reopened
|
||||
in the proper package's repository.
|
||||
|
||||
## Pull Requests
|
||||
* Include screenshots and animated GIFs whenever possible.
|
||||
* Follow the [CoffeeScript](#coffeescript-styleguide),
|
||||
[JavaScript](https://github.com/styleguide/javascript),
|
||||
and [CSS](https://github.com/styleguide/css) styleguides
|
||||
* Include thoughtfully worded [Jasmine](http://pivotal.github.com/jasmine/)
|
||||
specs
|
||||
* Add 3rd-party packages as a `package.json` dependency
|
||||
* Commit messages are in the present tense
|
||||
* Commit messages that improve the format of the code start with :lipstick:
|
||||
* Commit messages that improve the performance start with :racehorse:
|
||||
* Commit messages that remove memory leaks start with :non-potable_water:
|
||||
* Commit messages that improve documentation start with :memo:
|
||||
* Files end with a newline
|
||||
* Avoid placing files in `vendor`. 3rd-party packages should be added as a
|
||||
`package.json` dependency.
|
||||
* Files end with a newline.
|
||||
* Requires should be in the following order:
|
||||
* Built in Node Modules (such as `path`)
|
||||
* Built in Atom and Atom Shell Modules (such as `atom`, `shell`)
|
||||
* Local Modules (using relative paths)
|
||||
* Class variables and methods should be in the following order:
|
||||
* Class variables (variables starting with a `@`)
|
||||
* Class methods (methods starting with a `@`)
|
||||
* Instance variables
|
||||
* Instance methods
|
||||
* Beware of platform differences
|
||||
* The home directory is `process.env.USERPROFILE` on Windows, while on OS X
|
||||
and Linux it's `process.env.HOME`
|
||||
* Path separator is `\` on Windows, and is `/` on OS X and Linux, so use
|
||||
`path.join` to concatenate filenames
|
||||
* Use `require('atom').fs.getHomeDirectory()` to get the home directory.
|
||||
* Use `path.join()` to concatenate filenames.
|
||||
* Temporary directory is not `/tmp` on Windows, use `os.tmpdir()` when
|
||||
possible
|
||||
|
||||
## Philosophy
|
||||
## Git Commit Messages
|
||||
* Use the present tense
|
||||
* Reference issues and pull requests liberally
|
||||
* Consider starting the commit message with an applicable emoji:
|
||||
* :lipstick: when improving the format/structure of the code
|
||||
* :racehorse: when improving performance
|
||||
* :non-potable_water: when plugging memory leaks
|
||||
* :memo: when writing docs
|
||||
|
||||
### Write Beautiful Code
|
||||
Once you get something working, take the time to consider whether you can achieve it in a more elegant way. We're planning on open-sourcing Atom, so let's put our best foot forward.
|
||||
## CoffeeScript Styleguide
|
||||
|
||||
### When in doubt, pair-up
|
||||
Pairing can be an effective and fun way to pass on culture, knowledge, and taste. If you can find the time, we encourage you to work synchronously with other community members of all experience levels to help the knowledge-mulching process. It doesn't have to be all the time; a little pairing goes a long way.
|
||||
|
||||
### Write tests, and write them first
|
||||
The test suite keeps protects our codebase from the ravages of entropy, but it only works when we have thorough coverage. Before you write implementation code, write a failing test proving that it's needed.
|
||||
|
||||
### Leave the test suite better than you found it
|
||||
Consider how the specs you are adding fit into the spec-file as a whole. Is this the right place for your spec? Does the spec need to be reorganized now that you're adding this extra dimension? Specs are only as useful as the next person's ability to understand them.
|
||||
|
||||
### Solve today's problem
|
||||
Avoid adding flexibility that isn't needed *today*. Nothing is ever set in stone, and we can always go back and add flexibility later. Adding it early just means we have to pay for complexity that we might not end up using.
|
||||
|
||||
### Favor clarity over brevity or cleverness.
|
||||
Three lines that someone else can read are better than one line that's tricky.
|
||||
|
||||
### Don't be defensive
|
||||
Only catch exceptions that are truly exceptional. Assume that components we control will honor their contracts. If they don't, the solution is to find and fix the problem in code rather than cluttering the code with attempts to foresee all potential issues at runtime.
|
||||
|
||||
### Don't be afraid to add classes and methods
|
||||
Code rarely suffers from too many methods and classes, and often suffers from too few. Err on the side of numerous short, well-named methods. Pull out classes with well-defined roles.
|
||||
|
||||
### Rip shit out
|
||||
Don't be afraid to delete code. Don't be afraid to rewrite something that needs to be refreshed. If it's in version control, we can always resurrect it.
|
||||
|
||||
### Maintain a consistent level of abstraction
|
||||
Every line in a method should read at the same basic level of abstraction. If there's a section of a method that goes into a lot more detail than the rest of the method, consider extracting a new method and giving it a clear name.
|
||||
* Set parameter defaults without spaces around the equal sign
|
||||
* `clear = (count=1) ->` instead of `clear = (count = 1) ->`
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
Copyright 2013 GitHub Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+6
-19
@@ -1,30 +1,17 @@
|
||||
# Atom — Futuristic Text Editing
|
||||
# Atom — The hackable, ~~collaborative~~ editor
|
||||
|
||||

|
||||

|
||||
|
||||
Check out our [guides](https://www.atom.io/docs/latest/) and [API documentation](https://www.atom.io/docs/api/v34.0.0/api/)
|
||||
Check out our [guides and API documentation](https://www.atom.io/docs/latest/).
|
||||
|
||||
## Installing
|
||||
|
||||
Download the latest Atom release from [speakeasy](https://speakeasy.githubapp.com/apps/27).
|
||||
Download the latest [Atom release](https://github.com/atom/atom/releases/latest).
|
||||
|
||||
It will automatically update when a new release is available.
|
||||
Atom will automatically update when a new release is available.
|
||||
|
||||
## Building
|
||||
|
||||
### Requirements
|
||||
|
||||
* Mountain Lion
|
||||
* Looking for Windows support? Read [here][building].
|
||||
* Boxen (Obviously Atom won't release with this requirement)
|
||||
|
||||
### Installation
|
||||
|
||||
1. `gh-setup atom`
|
||||
|
||||
2. `cd ~/github/atom`
|
||||
|
||||
3. `script/build`
|
||||
|
||||
Follow the instructions in the [build docs][building].
|
||||
|
||||
[building]: https://github.com/atom/atom/blob/master/docs/building-atom.md
|
||||
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'Atom',
|
||||
'type': 'none',
|
||||
'postbuilds': [
|
||||
{
|
||||
'postbuild_name': 'Create Atom, basically do everything',
|
||||
'action': ['script/constructicon/build'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
require './benchmark-helper'
|
||||
{$, _, RootView} = require 'atom'
|
||||
{$, _, WorkspaceView} = require 'atom'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
|
||||
describe "editorView.", ->
|
||||
editorView = null
|
||||
|
||||
beforeEach ->
|
||||
window.rootViewParentSelector = '#jasmine-content'
|
||||
window.rootView = new RootView
|
||||
window.rootView.attachToDom()
|
||||
atom.workspaceViewParentSelector = '#jasmine-content'
|
||||
atom.workspaceView = new WorkspaceView
|
||||
atom.workspaceView.attachToDom()
|
||||
|
||||
rootView.width(1024)
|
||||
rootView.height(768)
|
||||
rootView.openSync()
|
||||
editorView = rootView.getActiveView()
|
||||
atom.workspaceView.width(1024)
|
||||
atom.workspaceView.height(768)
|
||||
atom.workspaceView.openSync()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
|
||||
afterEach ->
|
||||
if editorView.pendingDisplayUpdate
|
||||
@@ -40,7 +40,7 @@ describe "editorView.", ->
|
||||
|
||||
describe "300-line-file.", ->
|
||||
beforeEach ->
|
||||
rootView.openSync('medium.coffee')
|
||||
atom.workspaceView.openSync('medium.coffee')
|
||||
|
||||
describe "at-begining.", ->
|
||||
benchmark "insert-delete", ->
|
||||
@@ -171,11 +171,11 @@ describe "editorView.", ->
|
||||
|
||||
describe "9000-line-file.", ->
|
||||
benchmark "opening.", 5, ->
|
||||
rootView.openSync('huge.js')
|
||||
atom.workspaceView.openSync('huge.js')
|
||||
|
||||
describe "after-opening.", ->
|
||||
beforeEach ->
|
||||
rootView.openSync('huge.js')
|
||||
atom.workspaceView.openSync('huge.js')
|
||||
|
||||
benchmark "moving-to-eof.", 1, ->
|
||||
editorView.moveCursorToBottom()
|
||||
|
||||
@@ -2,12 +2,39 @@ fs = require 'fs'
|
||||
path = require 'path'
|
||||
os = require 'os'
|
||||
|
||||
# Add support for obselete APIs of vm module so we can make some third-party
|
||||
# modules work under node v0.11.x.
|
||||
require 'vm-compatibility-layer'
|
||||
|
||||
fm = require 'json-front-matter'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
packageJson = require './package.json'
|
||||
packageJson = require '../package.json'
|
||||
|
||||
# OAuth token for atom-bot
|
||||
# TODO Remove once all repositories are public
|
||||
process.env.ATOM_ACCESS_TOKEN ?= '362295be4c5258d3f7b967bbabae662a455ca2a7'
|
||||
|
||||
# Shim harmony collections in case grunt was invoked without harmony
|
||||
# collections enabled
|
||||
_.extend(global, require('harmony-collections')) unless global.WeakMap?
|
||||
|
||||
module.exports = (grunt) ->
|
||||
grunt.loadNpmTasks('grunt-coffeelint')
|
||||
grunt.loadNpmTasks('grunt-lesslint')
|
||||
grunt.loadNpmTasks('grunt-cson')
|
||||
grunt.loadNpmTasks('grunt-contrib-csslint')
|
||||
grunt.loadNpmTasks('grunt-contrib-coffee')
|
||||
grunt.loadNpmTasks('grunt-contrib-less')
|
||||
grunt.loadNpmTasks('grunt-markdown')
|
||||
grunt.loadNpmTasks('grunt-shell')
|
||||
grunt.loadNpmTasks('grunt-download-atom-shell')
|
||||
grunt.loadNpmTasks('grunt-peg')
|
||||
grunt.loadTasks('tasks')
|
||||
|
||||
# This allows all subsequent paths to the relative to the root of the repo
|
||||
grunt.file.setBase(path.resolve('..'))
|
||||
|
||||
if not grunt.option('verbose')
|
||||
grunt.log.writeln = (args...) -> grunt.log
|
||||
grunt.log.write = (args...) -> grunt.log
|
||||
@@ -19,7 +46,9 @@ 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
|
||||
appName = 'Atom.app'
|
||||
tmpDir = '/tmp'
|
||||
@@ -28,6 +57,7 @@ module.exports = (grunt) ->
|
||||
shellAppDir = path.join(buildDir, appName)
|
||||
contentsDir = path.join(shellAppDir, 'Contents')
|
||||
appDir = path.join(contentsDir, 'Resources', 'app')
|
||||
atomShellDownloadDir = '/tmp/atom-cached-atom-shells'
|
||||
|
||||
installDir = path.join(installRoot, appName)
|
||||
|
||||
@@ -77,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'))
|
||||
@@ -85,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')
|
||||
@@ -99,6 +137,8 @@ module.exports = (grunt) ->
|
||||
|
||||
cson: csonConfig
|
||||
|
||||
peg: pegConfig
|
||||
|
||||
coffeelint:
|
||||
options:
|
||||
no_empty_param_list:
|
||||
@@ -111,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'
|
||||
@@ -168,6 +210,12 @@ module.exports = (grunt) ->
|
||||
_.extend(context, parsed.attributes)
|
||||
parsed.body
|
||||
|
||||
'download-atom-shell':
|
||||
version: packageJson.atomShellVersion
|
||||
outputDir: 'atom-shell'
|
||||
downloadDir: atomShellDownloadDir
|
||||
rebuild: true # rebuild native modules after atom-shell is updated
|
||||
|
||||
shell:
|
||||
'kill-atom':
|
||||
command: 'pkill -9 Atom'
|
||||
@@ -176,20 +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-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', ['update-atom-shell', 'build', 'set-development-version', 'lint', 'test'])
|
||||
grunt.registerTask('deploy', ['partial-clean', 'update-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', ['update-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,35 @@
|
||||
{
|
||||
"name": "atom-build",
|
||||
"description": "Atom build",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"biscotto": "0.0.17",
|
||||
"first-mate": "~0.13.0",
|
||||
"formidable": "~1.0.14",
|
||||
"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,15 +21,14 @@ module.exports = (grunt) ->
|
||||
|
||||
cp 'atom.sh', path.join(appDir, 'atom.sh')
|
||||
cp 'package.json', path.join(appDir, 'package.json')
|
||||
|
||||
iconPath = path.resolve(__dirname, '..', 'resources', 'atom.png')
|
||||
cp iconPath, path.join(appDir, 'atom.png')
|
||||
cp 'apm', path.join(appDir, 'apm')
|
||||
|
||||
packageDirectories = []
|
||||
nonPackageDirectories = [
|
||||
'benchmark'
|
||||
'dot-atom'
|
||||
'vendor'
|
||||
'resources'
|
||||
]
|
||||
|
||||
{devDependencies} = grunt.file.readJSON('package.json')
|
||||
@@ -40,17 +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)
|
||||
@@ -3,13 +3,13 @@ fs = require 'fs'
|
||||
|
||||
module.exports = (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', ->
|
||||
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']
|
||||
args = [commonArgs..., '--title', 'Atom API Documentation', '-o', 'docs/output/api', 'src/', '../text-buffer/src/range.coffee', '../text-buffer/src/point.coffee', '../text-buffer/src/marker.coffee']
|
||||
grunt.util.spawn({cmd, args, opts}, done)
|
||||
|
||||
grunt.registerTask 'lint-docs', 'Generate stats about the doc coverage', ->
|
||||
@@ -0,0 +1,25 @@
|
||||
module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'output-disk-space', 'Print diskspace available', ->
|
||||
return unless process.platform is 'darwin'
|
||||
|
||||
done = @async()
|
||||
|
||||
cmd = 'df'
|
||||
args = ['-Hl']
|
||||
spawn {cmd, args}, (error, result, code) ->
|
||||
return done(error) if error?
|
||||
|
||||
lines = result.stdout.split("\n")
|
||||
|
||||
for line in lines[1..]
|
||||
[filesystem, size, used, avail, capacity, extra] = line.split(/\s+/)
|
||||
capacity = parseInt(capacity)
|
||||
|
||||
if capacity > 90
|
||||
grunt.log.error("#{filesystem} is at #{capacity}% capacity!")
|
||||
else if capacity > 80
|
||||
grunt.log.ok("#{filesystem} is at #{capacity}% capacity.")
|
||||
|
||||
done()
|
||||
@@ -0,0 +1,172 @@
|
||||
child_process = require 'child_process'
|
||||
path = require 'path'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
GitHub = require 'github-releases'
|
||||
request = require 'request'
|
||||
|
||||
grunt = null
|
||||
maxReleases = 10
|
||||
assetName = 'atom-mac.zip'
|
||||
assetPath = "/tmp/atom-build/#{assetName}"
|
||||
commitSha = process.env.JANKY_SHA1
|
||||
token = process.env.ATOM_ACCESS_TOKEN
|
||||
defaultHeaders =
|
||||
Authorization: "token #{token}"
|
||||
'User-Agent': 'Atom'
|
||||
|
||||
module.exports = (gruntObject) ->
|
||||
grunt = gruntObject
|
||||
grunt.registerTask 'publish-build', 'Publish the built app', ->
|
||||
return unless process.platform is 'darwin'
|
||||
return if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH isnt 'master'
|
||||
|
||||
done = @async()
|
||||
|
||||
createBuildRelease (error, release) ->
|
||||
return done(error) if error?
|
||||
zipApp (error) ->
|
||||
return done(error) if error?
|
||||
uploadAsset release, (error) ->
|
||||
return done(error) if error?
|
||||
publishRelease release, (error) ->
|
||||
return done(error) if error?
|
||||
getAtomDraftRelease (error, release) ->
|
||||
return done(error) if error?
|
||||
deleteExistingAsset release, (error) ->
|
||||
return done(error) if error?
|
||||
uploadAsset(release, done)
|
||||
|
||||
logError = (message, error, details) ->
|
||||
grunt.log.error(message)
|
||||
grunt.log.error(error.message ? error) if error?
|
||||
grunt.log.error(details) if details
|
||||
|
||||
zipApp = (callback) ->
|
||||
fs.removeSync(assetPath)
|
||||
|
||||
options = {cwd: path.dirname(assetPath), maxBuffer: Infinity}
|
||||
child_process.exec "zip -r --symlinks #{assetName} Atom.app", options, (error, stdout, stderr) ->
|
||||
if error?
|
||||
logError('Zipping Atom.app failed', error, stderr)
|
||||
callback(error)
|
||||
|
||||
getRelease = (callback) ->
|
||||
options =
|
||||
uri: 'https://api.github.com/repos/atom/atom-master-builds/releases'
|
||||
method: 'GET'
|
||||
headers: defaultHeaders
|
||||
json: true
|
||||
request options, (error, response, releases=[]) ->
|
||||
if error? or response.statusCode isnt 200
|
||||
logError('Fetching releases failed', error, releases)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
if releases.length > maxReleases
|
||||
deleteRelease(release) for release in releases[maxReleases..]
|
||||
|
||||
for release in releases when release.name is commitSha
|
||||
callback(null, release)
|
||||
return
|
||||
callback()
|
||||
|
||||
getAtomDraftRelease = (callback) ->
|
||||
atomRepo = new GitHub({repo: 'atom/atom', token})
|
||||
atomRepo.getReleases (error, releases=[]) ->
|
||||
if error?
|
||||
logError('Fetching atom/atom releases failed', error, releases)
|
||||
callback(error)
|
||||
else
|
||||
for release in releases when release.draft
|
||||
callback(null, release)
|
||||
return
|
||||
callback(new Error('No draft release in atom/atom repo'))
|
||||
|
||||
deleteRelease = (release) ->
|
||||
options =
|
||||
uri: release.url
|
||||
method: 'DELETE'
|
||||
headers: defaultHeaders
|
||||
json: true
|
||||
request options, (error, response, body='') ->
|
||||
if error? or response.statusCode isnt 204
|
||||
logError('Deleting release failed', error, body)
|
||||
|
||||
deleteExistingAsset = (release, callback) ->
|
||||
for asset in release.assets when asset.name is assetName
|
||||
options =
|
||||
uri: asset.url
|
||||
method: 'DELETE'
|
||||
headers: defaultHeaders
|
||||
request options, (error, response, body='') ->
|
||||
if error? or response.statusCode isnt 204
|
||||
logError('Deleting existing release asset failed', error, body)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
callback()
|
||||
|
||||
return
|
||||
|
||||
callback()
|
||||
|
||||
createBuildRelease = (callback) ->
|
||||
getRelease (error, release) ->
|
||||
if error?
|
||||
callback(error)
|
||||
return
|
||||
|
||||
if release?
|
||||
deleteExistingAsset release, (error) ->
|
||||
callback(error, release)
|
||||
return
|
||||
|
||||
options =
|
||||
uri: 'https://api.github.com/repos/atom/atom-master-builds/releases'
|
||||
method: 'POST'
|
||||
headers: defaultHeaders
|
||||
json:
|
||||
tag_name: "v#{commitSha}"
|
||||
target_commitish: 'master'
|
||||
name: commitSha
|
||||
body: "Build of [atom@#{commitSha.substring(0, 7)}](https://github.com/atom/atom/commits/#{commitSha})"
|
||||
draft: true
|
||||
prerelease: true
|
||||
request options, (error, response, release={}) ->
|
||||
if error? or response.statusCode isnt 201
|
||||
logError('Creating release failed', error, release)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
callback(null, release)
|
||||
|
||||
uploadAsset = (release, callback) ->
|
||||
options =
|
||||
uri: release.upload_url.replace(/\{.*$/, "?name=#{assetName}")
|
||||
method: 'POST'
|
||||
headers: _.extend({
|
||||
'Content-Type': 'application/zip'
|
||||
'Content-Length': fs.getSizeSync(assetPath)
|
||||
}, defaultHeaders)
|
||||
|
||||
assetRequest = request options, (error, response, body='') ->
|
||||
if error? or response.statusCode >= 400
|
||||
logError('Upload release asset failed', error, body)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
callback(null, release)
|
||||
|
||||
fs.createReadStream(assetPath).pipe(assetRequest)
|
||||
|
||||
publishRelease = (release, callback) ->
|
||||
options =
|
||||
uri: release.url
|
||||
method: 'POST'
|
||||
headers: defaultHeaders
|
||||
json:
|
||||
draft: false
|
||||
request options, (error, response, body={}) ->
|
||||
if error? or response.statusCode isnt 200
|
||||
logError('Creating release failed', error, body)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
callback()
|
||||
@@ -6,7 +6,7 @@ module.exports = (grunt) ->
|
||||
|
||||
shellAppDir = grunt.config.get('atom.shellAppDir')
|
||||
shellExePath = path.join(shellAppDir, 'atom.exe')
|
||||
iconPath = path.resolve(__dirname, '..', 'resources', 'win', 'atom.ico')
|
||||
iconPath = path.resolve('resources', 'win', 'atom.ico')
|
||||
|
||||
rcedit = require('rcedit')
|
||||
rcedit(shellExePath, {'icon': iconPath}, done)
|
||||
@@ -0,0 +1,50 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
getVersion = (callback) ->
|
||||
if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH is 'master'
|
||||
{version} = require(path.join(grunt.config.get('atom.appDir'), 'package.json'))
|
||||
callback(null, version)
|
||||
else
|
||||
cmd = 'git'
|
||||
args = ['rev-parse', '--short', 'HEAD']
|
||||
spawn {cmd, args}, (error, {stdout}={}, code) ->
|
||||
callback(error, stdout?.trim?())
|
||||
|
||||
grunt.registerTask 'set-version', 'Set the version in the plist and package.json', ->
|
||||
done = @async()
|
||||
|
||||
getVersion (error, version) ->
|
||||
if error?
|
||||
done(error)
|
||||
return
|
||||
|
||||
appDir = grunt.config.get('atom.appDir')
|
||||
|
||||
# Replace version field of package.json.
|
||||
packageJsonPath = path.join(appDir, 'package.json')
|
||||
packageJson = require(packageJsonPath)
|
||||
packageJson.version = version
|
||||
packageJsonString = JSON.stringify(packageJson, null, 2)
|
||||
fs.writeFileSync(packageJsonPath, packageJsonString)
|
||||
|
||||
if process.platform is 'darwin'
|
||||
cmd = 'script/set-version'
|
||||
args = [grunt.config.get('atom.buildDir'), version]
|
||||
spawn {cmd, args}, (error, result, code) -> done(error)
|
||||
else if process.platform is 'win32'
|
||||
shellAppDir = grunt.config.get('atom.shellAppDir')
|
||||
shellExePath = path.join(shellAppDir, 'atom.exe')
|
||||
|
||||
strings =
|
||||
CompanyName: 'GitHub, Inc.'
|
||||
FileDescription: 'The hackable, collaborative editor'
|
||||
LegalCopyright: 'Copyright (C) 2013 GitHub, Inc. All rights reserved'
|
||||
ProductName: 'Atom'
|
||||
ProductVersion: version
|
||||
|
||||
rcedit = require('rcedit')
|
||||
rcedit(shellExePath, {'version-string': strings}, done)
|
||||
@@ -0,0 +1,112 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
async = require 'async'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{isAtomPackage, spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
packageSpecQueue = null
|
||||
|
||||
runPackageSpecs = (callback) ->
|
||||
failedPackages = []
|
||||
rootDir = grunt.config.get('atom.shellAppDir')
|
||||
contentsDir = grunt.config.get('atom.contentsDir')
|
||||
resourcePath = process.cwd()
|
||||
if process.platform is 'darwin'
|
||||
appPath = path.join(contentsDir, 'MacOS', 'Atom')
|
||||
else if process.platform is 'win32'
|
||||
appPath = path.join(contentsDir, 'atom.exe')
|
||||
|
||||
packageSpecQueue = async.queue (packagePath, callback) ->
|
||||
if process.platform is 'darwin'
|
||||
options =
|
||||
cmd: appPath
|
||||
args: ['--test', "--resource-path=#{resourcePath}", "--spec-directory=#{path.join(packagePath, 'spec')}"]
|
||||
opts:
|
||||
cwd: packagePath
|
||||
env: _.extend({}, process.env, ATOM_PATH: rootDir)
|
||||
else if process.platform is 'win32'
|
||||
options =
|
||||
cmd: process.env.comspec
|
||||
args: ['/c', appPath, '--test', "--resource-path=#{resourcePath}", "--spec-directory=#{path.join(packagePath, 'spec')}", "--log-file=ci.log"]
|
||||
opts:
|
||||
cwd: packagePath
|
||||
env: _.extend({}, process.env, ATOM_PATH: rootDir)
|
||||
|
||||
grunt.verbose.writeln "Launching #{path.basename(packagePath)} specs."
|
||||
spawn options, (error, results, code) ->
|
||||
if process.platform is 'win32'
|
||||
process.stderr.write(fs.readFileSync(path.join(packagePath, 'ci.log')))
|
||||
fs.unlinkSync(path.join(packagePath, 'ci.log'))
|
||||
|
||||
failedPackages.push path.basename(packagePath) if error
|
||||
callback()
|
||||
|
||||
modulesDirectory = path.resolve('node_modules')
|
||||
for packageDirectory in fs.readdirSync(modulesDirectory)
|
||||
packagePath = path.join(modulesDirectory, packageDirectory)
|
||||
continue unless grunt.file.isDir(path.join(packagePath, 'spec'))
|
||||
continue unless isAtomPackage(packagePath)
|
||||
packageSpecQueue.push(packagePath)
|
||||
|
||||
# TODO: Restore concurrency on Windows
|
||||
packageSpecQueue.concurrency = 1 unless process.platform is 'win32'
|
||||
|
||||
packageSpecQueue.drain = -> callback(null, failedPackages)
|
||||
|
||||
runCoreSpecs = (callback) ->
|
||||
contentsDir = grunt.config.get('atom.contentsDir')
|
||||
if process.platform is 'darwin'
|
||||
appPath = path.join(contentsDir, 'MacOS', 'Atom')
|
||||
else if process.platform is 'win32'
|
||||
appPath = path.join(contentsDir, 'atom.exe')
|
||||
resourcePath = process.cwd()
|
||||
coreSpecsPath = path.resolve('spec')
|
||||
|
||||
if process.platform is 'darwin'
|
||||
options =
|
||||
cmd: appPath
|
||||
args: ['--test', "--resource-path=#{resourcePath}", "--spec-directory=#{coreSpecsPath}"]
|
||||
else if process.platform is 'win32'
|
||||
options =
|
||||
cmd: process.env.comspec
|
||||
args: ['/c', appPath, '--test', "--resource-path=#{resourcePath}", "--spec-directory=#{coreSpecsPath}", "--log-file=ci.log"]
|
||||
|
||||
spawn options, (error, results, code) ->
|
||||
if process.platform is 'win32'
|
||||
process.stderr.write(fs.readFileSync('ci.log'))
|
||||
fs.unlinkSync('ci.log')
|
||||
else
|
||||
# TODO: Restore concurrency on Windows
|
||||
packageSpecQueue.concurrency = 2
|
||||
|
||||
callback(null, error)
|
||||
|
||||
grunt.registerTask 'run-specs', 'Run the specs', ->
|
||||
done = @async()
|
||||
startTime = Date.now()
|
||||
|
||||
# TODO: This should really be parallel on both platforms, however our
|
||||
# fixtures step on each others toes currently.
|
||||
if process.platform is 'darwin'
|
||||
method = async.parallel
|
||||
else if process.platform is 'win32'
|
||||
method = async.series
|
||||
|
||||
method [runCoreSpecs, runPackageSpecs], (error, results) ->
|
||||
[coreSpecFailed, failedPackages] = results
|
||||
elapsedTime = Math.round((Date.now() - startTime) / 100) / 10
|
||||
grunt.verbose.writeln("Total spec time: #{elapsedTime}s")
|
||||
failures = failedPackages
|
||||
failures.push "atom core" if coreSpecFailed
|
||||
|
||||
grunt.log.error("[Error]".red + " #{failures.join(', ')} spec(s) failed") if failures.length > 0
|
||||
|
||||
# TODO: Mark the build as green on Windows until specs pass.
|
||||
if process.platform is 'darwin'
|
||||
done(!coreSpecFailed and failedPackages.length == 0)
|
||||
else if process.platform is 'win32'
|
||||
done(true)
|
||||
@@ -39,7 +39,7 @@ module.exports = (grunt) ->
|
||||
proc = childProcess.spawn(options.cmd, options.args, options.opts)
|
||||
proc.stdout.on 'data', (data) -> stdout.push(data.toString())
|
||||
proc.stderr.on 'data', (data) -> stderr.push(data.toString())
|
||||
proc.on 'exit', (exitCode, signal) ->
|
||||
proc.on 'close', (exitCode, signal) ->
|
||||
error = new Error(signal) if exitCode != 0
|
||||
results = {stderr: stderr.join(''), stdout: stdout.join(''), code: exitCode}
|
||||
grunt.log.error results.stderr if exitCode != 0
|
||||
@@ -6,10 +6,10 @@ atom][download].
|
||||
|
||||
## OSX
|
||||
|
||||
* Use Mountain Lion
|
||||
* Use OS X 10.8 or later
|
||||
* Install the latest node 0.10.x release (32bit preferable)
|
||||
* Clone [atom][atom-git] to `~/github/atom`
|
||||
* Run `~/github/atom/script/bootstrap`
|
||||
* Run `~/github/atom/script/build`
|
||||
|
||||
## Windows
|
||||
|
||||
@@ -26,7 +26,7 @@ atom][download].
|
||||
* Use the Windows GitHub shell and cd into `C:\Users\<user>\github\atom`
|
||||
* Run `script\bootstrap`
|
||||
|
||||
[download]: http://www.atom.io
|
||||
[download]: https://github.com/atom/atom/releases/latest
|
||||
[win-node]: http://nodejs.org/download/
|
||||
[win-python]: http://www.python.org/download/
|
||||
[win-github]: http://windows.github.com/
|
||||
|
||||
@@ -67,11 +67,11 @@ object.
|
||||
|
||||
Your package's top-level module should implement the following methods:
|
||||
|
||||
- `activate(rootView, state)`: This **required** method is called when your
|
||||
package is loaded. It is always passed the window's global `rootView`, and is
|
||||
sometimes passed state data if the window has been reloaded and your module
|
||||
implements the `serialize` method. Use this to do initialization work when your
|
||||
package is started (like setting up DOM elements or binding events).
|
||||
- `activate(state)`: This **required** method is called when your
|
||||
package is activated. It is passed the state data from the last time the window
|
||||
was serialized if your module implements the `serialize()` method. Use this to
|
||||
do initialization work when your package is started (like setting up DOM
|
||||
elements or binding events).
|
||||
|
||||
- `serialize()`: This **optional** method is called when the window is shutting
|
||||
down, allowing you to return JSON to represent the state of your component. When
|
||||
@@ -104,7 +104,7 @@ module.exports = require "./lib/my-package"
|
||||
`my-package/my-package.coffee` might start:
|
||||
```coffeescript
|
||||
module.exports =
|
||||
activate: (rootView, state) -> # ...
|
||||
activate: (state) -> # ...
|
||||
deactivate: -> # ...
|
||||
serialize: -> # ...
|
||||
```
|
||||
@@ -126,7 +126,7 @@ is recommended).
|
||||
Ideally, you won't need much in the way of styling. We've provided a standard
|
||||
set of components which define both the colors and UI elements for any package
|
||||
that fits into Atom seamlessly. You can view all of Atom's UI components by opening
|
||||
the styleguide: open the command palette (`cmd-p`) and search for _styleguide_,
|
||||
the styleguide: open the command palette (`cmd-shift-P`) and search for _styleguide_,
|
||||
or just type `cmd-ctrl-G`.
|
||||
|
||||
If you _do_ need special styling, try to keep only structural styles in the package
|
||||
@@ -206,7 +206,7 @@ specific parts of the interface, like adding a file in the tree-view:
|
||||
'context-menu':
|
||||
'.tree-view':
|
||||
'Add file': 'tree-view:add-file'
|
||||
'#root-view':
|
||||
'.workspace':
|
||||
'Inspect Element': 'core:inspect'
|
||||
```
|
||||
|
||||
@@ -311,7 +311,7 @@ protocol URLs to load resources in the package.
|
||||
|
||||
The URLs should be in the format of
|
||||
`atom://package-name/relative-path-to-package-of-resource`, for example, the
|
||||
`atom://image-view/images/transparent-background.gif` would be equivablent to
|
||||
`atom://image-view/images/transparent-background.gif` would be equivalent to
|
||||
`~/.atom/packages/image-view/images/transparent-background.gif`.
|
||||
|
||||
You can also use the `atom` protocol URLs in themes.
|
||||
|
||||
@@ -34,7 +34,7 @@ target elements which are outside of the editor.
|
||||
|
||||
Let's create your first theme.
|
||||
|
||||
To get started, hit `cmd-p`, and start typing "Generate Theme" to generate
|
||||
To get started, hit `cmd-shift-P`, and start typing "Generate Theme" to generate
|
||||
a package. Select "Generate Theme," and you'll be asked for a theme name. Let's
|
||||
call ours _motif_.
|
||||
|
||||
@@ -114,7 +114,7 @@ If you are creating an interface theme, you'll want a way to see how your theme
|
||||
changes affect all the components in the system. The [styleguide] is a page with
|
||||
every component Atom supports rendered.
|
||||
|
||||
To open the styleguide, open the command palette (`cmd-p`) and search for
|
||||
To open the styleguide, open the command palette (`cmd-shift-P`) and search for
|
||||
_styleguide_, or use the shortcut `cmd-ctrl-shift-g`.
|
||||
|
||||
![styleguide-img]
|
||||
|
||||
@@ -19,6 +19,16 @@ Themes` section on the `Packages` tab within the Settings panel.
|
||||
You can install non-bundled packages by going to the `Available Packages`
|
||||
section on the `Packages` tab within the Settings panel (`cmd-,`).
|
||||
|
||||
You can also install packages from the command line using the
|
||||
[apm](https://github.com/atom/apm) command:
|
||||
|
||||
`apm install <package_name>` to install the latest version.
|
||||
|
||||
`apm install <package_name>@<package_version>` to install a specific version.
|
||||
|
||||
For example `apm install emmet@0.1.5` installs the `0.1.5` release of the
|
||||
[Emmet](https://github.com/atom/emmet) package into `~/.atom/packages`.
|
||||
|
||||
## Customizing Key Bindings
|
||||
|
||||
Atom keymaps work similarly to stylesheets. Just as stylesheets use selectors
|
||||
@@ -56,7 +66,7 @@ directory, which contains CoffeeScript-style JSON:
|
||||
|
||||
```coffeescript
|
||||
core:
|
||||
hideGitIgnoredFiles: true
|
||||
excludeVcsIgnoredPaths: true
|
||||
editor:
|
||||
fontSize: 18
|
||||
```
|
||||
@@ -69,7 +79,6 @@ namespaces: `core` and `editor`.
|
||||
- `core`
|
||||
- `disabledPackages`: An array of package names to disable
|
||||
- `excludeVcsIgnoredPaths`: Don't search within files specified by _.gitignore_
|
||||
- `hideGitIgnoredFiles`: Whether files in the _.gitignore_ should be hidden
|
||||
- `ignoredNames`: File names to ignore across all of Atom
|
||||
- `projectHome`: The directory where projects are assumed to be located
|
||||
- `themes`: An array of theme names to load, in cascading order
|
||||
@@ -97,7 +106,7 @@ namespaces: `core` and `editor`.
|
||||
- `whitespace`
|
||||
- `ensureSingleTrailingNewline`: Whether to reduce multiple newlines to one at the end of files
|
||||
- `removeTrailingWhitespace`: Enable/disable striping of whitespace at the end of lines (defaults to `true`)
|
||||
- `wrapGuide`
|
||||
- `wrap-guide`
|
||||
- `columns`: Array of hashes with a `pattern` and `column` key to match the
|
||||
the path of the current editor to a column position.
|
||||
|
||||
|
||||
+38
-39
@@ -6,13 +6,12 @@ productive as quickly as possible. There are also guides which cover
|
||||
|
||||
## The Command Palette
|
||||
|
||||
If there's one key-command you must remember in Atom, it should be `cmd-p`. You
|
||||
can always hit `cmd-p` to bring up a list of commands that are relevant to the
|
||||
currently focused interface element. If there is a key binding for a given
|
||||
command, it is also displayed. This is a great way to explore the system and get
|
||||
to know the key commands interactively. If you'd like to learn about adding or
|
||||
changing a binding for a command, refer to the [key bindings][key-bindings]
|
||||
section below.
|
||||
If there's one key-command you remember in Atom, it should be `cmd-shift-P`. You
|
||||
can always press `cmd-shift-P` to bring up a list of commands (and key bindings)
|
||||
that are relevant to the currently focused interface element. This is a great
|
||||
way to explore the system and learn key bindings interactively. For information
|
||||
about adding or changing a key binding refer to the [customizing key
|
||||
bindings][key-bindings] section.
|
||||
|
||||
![Command Palette]
|
||||
|
||||
@@ -20,24 +19,24 @@ section below.
|
||||
|
||||
### Working With Files
|
||||
|
||||
Atom windows are scoped to the directory in which they're opened from. So if you
|
||||
launch Atom from the command line, everything will be relative to the current
|
||||
directory. This means that the tree view on the left will only show files
|
||||
contained within that directory.
|
||||
Atom windows are scoped to the directory they're opened from. If you launch Atom
|
||||
from the command line everything will be relative to the current directory. This
|
||||
means that the tree view on the left will only show files contained within that
|
||||
directory.
|
||||
|
||||
This can be a useful way to organize multiple projects, as each project will be
|
||||
contained within it's own window and it's state will be unique to that window.
|
||||
contained within its own window.
|
||||
|
||||
#### Finding Files
|
||||
|
||||
The fastest way to find a file in your project is to use the fuzzy finder. Just
|
||||
hit `cmd-t` and start typing the name of the file you're looking for. If you
|
||||
already have the file open as a tab and want to jump to it, hit `cmd-b` to bring
|
||||
up a searchable list of open buffers.
|
||||
The fastest way to find a file is to use the fuzzy finder. Press `cmd-t` and
|
||||
begin typing the name of the file you're looking for. If you are looking for a
|
||||
file that is already open press `cmd-b` to bring up a searchable list of open
|
||||
files.
|
||||
|
||||
You can also use the tree view to navigate to a file. To open or move focus to
|
||||
the tree view, hit `cmd-\`. You can then navigate to a file and select it with
|
||||
`return`.
|
||||
the tree view, press `cmd-\`. You can then navigate to a file using the arrow
|
||||
keys and select it with `return`.
|
||||
|
||||
#### Adding, Moving, Deleting Files
|
||||
|
||||
@@ -46,50 +45,50 @@ select a directory in the tree view and press `a`. Then type the name of the
|
||||
file. Any intermediate directories you type will be created automatically if
|
||||
needed.
|
||||
|
||||
To move or rename a file or directory, select it in the tree view and hit `m`.
|
||||
To delete a file, select it in the tree view and hit `delete`.
|
||||
To move or rename a file or directory, select it in the tree view and press `m`.
|
||||
|
||||
To delete a file, select it in the tree view and press `delete`.
|
||||
|
||||
### Searching
|
||||
|
||||
#### Find and Replace
|
||||
|
||||
To search within a buffer use `cmd-f`. To search the entire project use
|
||||
`cmd-shift-f`. To find and replace within the current buffer use `cmd-alt-f`.
|
||||
`cmd-shift-f`.
|
||||
|
||||
#### Navigating By Symbols
|
||||
|
||||
If you want to jump to a method, the `cmd-j` binding opens a list of all symbols
|
||||
in the current file. `cmd-.` jumps to the tag for the word currently under the
|
||||
cursor.
|
||||
If you want to jump to a method press `cmd-r`. It opens a list of all symbols
|
||||
in the current file.
|
||||
|
||||
To search for symbols across your project use `cmd-shift-j`, but you'll need to
|
||||
make sure you have a tags file generated for the project Also, if you're editing
|
||||
CoffeeScript, it's a good idea to update your `~/.ctags` file to understand the
|
||||
language. Here is [a good example][ctags].
|
||||
To search for symbols across your project use `cmd-shift-r`, but you'll need to
|
||||
make sure you have a ctags installed and a tags file generated for your project.
|
||||
Also, if you're editing CoffeeScript, it's a good idea to update your `~/.ctags`
|
||||
file to understand the language. Here is [a good example][ctags].
|
||||
|
||||
### Split Panes
|
||||
|
||||
You can split any editor pane horizontally or vertically by using `ctrl-w s` or
|
||||
`ctrl-w v`. Once you have a split pane, you can move focus between them with
|
||||
`ctrl-tab` or `ctrl-w w`. To close a pane, close all tabs inside it.
|
||||
You can split any editor pane horizontally or vertically by using `cmd-k right` or
|
||||
`cmd-k down`. Once you have a split pane, you can move focus between them with
|
||||
`cmd-k cmd-right` or `cmd-k cmd-down`. To close a pane, close all tabs inside it.
|
||||
|
||||
### Folding
|
||||
|
||||
You can fold everything with `ctrl-{` and unfold everything with
|
||||
`ctrl-}`. Or, you can fold / unfold by a single level with `ctrl-[` and
|
||||
`ctrl-]`.
|
||||
You can fold everything with `alt-cmd-{` and unfold everything with
|
||||
`alt-cmd-}`. Or, you can fold / unfold by a single level with `alt-cmd-[` and
|
||||
`alt-cmd-]`.
|
||||
|
||||
### Soft-Wrap
|
||||
|
||||
If you want to toggle soft wrap, trigger the command from the command palette.
|
||||
Hit `cmd-p` to open the palette, then type "wrap" to find the correct
|
||||
Press `cmd-shift-P` to open the palette, then type "wrap" to find the correct
|
||||
command.
|
||||
|
||||
## Configuration
|
||||
|
||||
If you press `cmd-,`, a configuration panel will appear in the currently focused
|
||||
pane. This serves as the primary interface for adjusting settings, installing
|
||||
packages and changing themes.
|
||||
Press `cmd-,` to display the a settings pane. This serves as the primary
|
||||
interface for adjusting config settings, installing packages and changing
|
||||
themes.
|
||||
|
||||
For more advanced configuration see the [customization guide][customization].
|
||||
|
||||
@@ -97,6 +96,6 @@ For more advanced configuration see the [customization guide][customization].
|
||||
[theming]: creating-a-theme.md
|
||||
[extending]: creating-a-package.md
|
||||
[customization]: customizing-atom.md
|
||||
[key-bindings]: #customizing-key-bindings
|
||||
[key-bindings]: customizing-atom.md#customizing-key-bindings
|
||||
[command palette]: https://f.cloud.github.com/assets/1424/1091618/ee7c3554-166a-11e3-9955-aaa61bb5509c.png
|
||||
[ctags]: https://github.com/kevinsawicki/dotfiles/blob/master/.ctags
|
||||
|
||||
@@ -23,7 +23,7 @@ The `observeConfig` method will call the given callback immediately with the
|
||||
current value for the specified key path, and it will also call it in the future
|
||||
whenever the value of that key path changes.
|
||||
|
||||
Subscriptions made with `observeConfig` are automatically cancelled when the
|
||||
Subscriptions made with `observeConfig` are automatically canceled when the
|
||||
view is removed. You can cancel config subscriptions manually via the
|
||||
`unobserveConfig` method.
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ Note that the last example describes multiple keystrokes in succession:
|
||||
|
||||
A semantic event is the name of the custom event that will be triggered on the
|
||||
target of the keydown event when a key binding matches. You can use the command
|
||||
palette (bound to `cmd-p`), to get a list of relevant events and their bindings
|
||||
palette (bound to `cmd-shift-P`), to get a list of relevant events and their bindings
|
||||
in any focused context in Atom.
|
||||
|
||||
### Rules for Mapping A Keydown Event to A Semantic Event
|
||||
|
||||
@@ -8,11 +8,11 @@ view objects inherit from the jQuery prototype, and wrap DOM nodes
|
||||
View objects are actually jQuery wrappers around DOM fragments, supporting all
|
||||
the typical jQuery traversal and manipulation methods. In addition, view objects
|
||||
have methods that are view-specific. For example, you could call both general
|
||||
and view-specific on the global `rootView` instance:
|
||||
and view-specific on the global `atom.workspaceView` instance:
|
||||
|
||||
```coffeescript
|
||||
rootView.find('.editor.active') # standard jQuery method
|
||||
rootView.getActiveEditor() # view-specific method
|
||||
atom.workspaceView.find('.editor.active') # standard jQuery method
|
||||
atom.workspaceView.getActiveEditor() # view-specific method
|
||||
```
|
||||
|
||||
If you retrieve a jQuery wrapper for an element associated with a view, use the
|
||||
@@ -20,7 +20,7 @@ If you retrieve a jQuery wrapper for an element associated with a view, use the
|
||||
|
||||
```coffeescript
|
||||
# this is a plain jQuery object; you can't call view-specific methods
|
||||
editorElement = rootView.find('.editor.active')
|
||||
editorElement = atom.workspaceView.find('.editor.active')
|
||||
|
||||
# get the view object by calling `.view()` to call view-specific methods
|
||||
editorView = editorElement.view()
|
||||
@@ -29,37 +29,30 @@ editorView.setCursorBufferPosition([1, 2])
|
||||
|
||||
Refer to the [SpacePen] documentation for more details.
|
||||
|
||||
### RootView
|
||||
### WorkspaceView
|
||||
|
||||
The root of Atom's view hierarchy is a global called `rootView`, which is a
|
||||
singleton instance of the `RootView` view class. The root view fills the entire
|
||||
The root of Atom's view hierarchy is a global called `atom.workspaceView`, which is a
|
||||
singleton instance of the `WorkspaceView` view class. The root view fills the entire
|
||||
window, and contains every other view. If you open Atom's inspector with
|
||||
`alt-cmd-i`, you can see the internal structure of `RootView`:
|
||||
`alt-cmd-i`, you can see the internal structure of `WorkspaceView`:
|
||||
|
||||
![RootView in the inspector][rootview-inspector]
|
||||
![WorkspaceView in the inspector][workspaceview-inspector]
|
||||
|
||||
#### Panes
|
||||
|
||||
The `RootView` contains a `#horizontal` and a `#vertical` axis surrounding
|
||||
`#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)
|
||||
rootView.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)
|
||||
rootView.vertical.append(new MyOtherView)
|
||||
# place a view below the panes
|
||||
atom.workspaceView.appendToBottom(new MyOtherView)
|
||||
```
|
||||
|
||||
[spacepen]: http://github.com/nathansobo/space-pen
|
||||
[rootview-inspector]: https://f.cloud.github.com/assets/1424/1091631/1932c2d6-166b-11e3-8adf-9690fe82d3b8.png
|
||||
[workspaceView-inspector]: https://f.cloud.github.com/assets/1424/1091631/1932c2d6-166b-11e3-8adf-9690fe82d3b8.png
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# Setting up Travis CI
|
||||
|
||||
Packages under the [atom org][atom-org] should use [Travis CI][travis-ci] for
|
||||
builds.
|
||||
|
||||
Currently we have a [Travis Pro][travis-pro] account since the repositories
|
||||
are private. This process will be simpler and have fewer steps once the
|
||||
package repos are made public.
|
||||
|
||||
Each package builds using the [build-package][build-package] script, any
|
||||
changes to the build should be made in that script and will affect all
|
||||
package builds immediately.
|
||||
|
||||
Each package builds against the latest successful build of atom@master. The
|
||||
master builds are stored in the [atom-master-builds][atom-master-builds] repo
|
||||
as releases named by the SHA-1 built.
|
||||
|
||||
## Configuring a package
|
||||
|
||||
* Run `cd ~/github/my-package` to navigate to the package repo locally
|
||||
* Run `apm test` to verify that the package currently builds via [apm][apm]
|
||||
* Add the package repo to the [Travis CI team][travis-ci-team]
|
||||
* Run `gem install travis` to install the [travis gem][travis-gem]
|
||||
* Run `travis login --pro` and log in using the [atom-build][atom-build] user
|
||||
and the password from the *Shared-Developers* folder in LastPass
|
||||
* Run `apm ci` to add a `.travis.yml` file to the repo and to configure Travis
|
||||
* Log into [Travis][travis-ci] as the `atom-build` user and you should now see
|
||||
the package listed and building
|
||||
|
||||
|
||||
[apm]: https://github.com/atom/apm
|
||||
[build-package]: https://github.com/atom/apm/blob/master/script/build-package
|
||||
[atom-build]: https://github.com/atom-build
|
||||
[atom-master-builds]: https://github.com/atom/atom-master-builds/releases
|
||||
[atom-org]: https://github.com/atom
|
||||
[travis-ci]: https://magnum.travis-ci.com
|
||||
[travis-ci-team]: https://github.com/organizations/atom/teams/596636
|
||||
[travis-gem]: https://rubygems.org/gems/travis
|
||||
[travis-pro]: http://about.travis-ci.org/docs/user/travis-pro
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
Let's take a look at creating your first package.
|
||||
|
||||
To get started, hit `cmd-p`, and start typing "Generate Package" to generate
|
||||
To get started, hit `cmd-shift-P`, and start typing "Generate Package" to generate
|
||||
a package. Once you select the "Generate Package" command, it'll ask you for a
|
||||
name for your new package. Let's call ours _changer_.
|
||||
|
||||
Atom will pop open a new window, showing the _changer_ package with a default set of
|
||||
folders and files created for us. Hit `cmd-p` and start typing "Changer." You'll
|
||||
folders and files created for us. Hit `cmd-shift-P` and start typing "Changer." You'll
|
||||
see a new `Changer:Toggle` command which, if selected, pops up a greeting. So far,
|
||||
so good!
|
||||
|
||||
@@ -44,12 +44,12 @@ you can map to `body` if you want to scope to anywhere in Atom, or just `.editor
|
||||
for the editor portion.
|
||||
|
||||
To bind keybindings to a command, we'll need to do a bit of association in our
|
||||
CoffeeScript code using the `rootView.command` method. This method takes a command
|
||||
CoffeeScript code using the `atom.workspaceView.command` method. This method takes a command
|
||||
name and executes a callback function. Open up _lib/changer-view.coffee_, and
|
||||
change `rootView.command "changer:toggle"` to look like this:
|
||||
change `atom.workspaceView.command "changer:toggle"` to look like this:
|
||||
|
||||
```coffeescript
|
||||
rootView.command "changer:magic", => @magic()
|
||||
atom.workspaceView.command "changer:magic", => @magic()
|
||||
```
|
||||
|
||||
It's common practice to namespace your commands with your package name, separated
|
||||
@@ -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
|
||||
rootView.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, `rootView.vertical.append` tells Atom to append `this`
|
||||
item (_i.e._, whatever is defined by`@content`) _vertically_ to the editor. If
|
||||
we had called `rootView.horizontal.append`, the pane would be attached to the
|
||||
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 `rootView.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()
|
||||
rootView.vertical.children().last().remove()
|
||||
@remove()
|
||||
else
|
||||
rootView.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()
|
||||
rootView.vertical.children().last().remove()
|
||||
@remove()
|
||||
else
|
||||
for file in modifiedFiles
|
||||
stat = fs.lstatSync(file)
|
||||
mtime = stat.mtime
|
||||
@modifiedFilesList.append("<li>#{file} - Modified at #{mtime}")
|
||||
rootView.vertical.append(this)
|
||||
atom.workspaceView.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
|
||||
rootView.vertical.children().last().remove()
|
||||
@remove()
|
||||
else
|
||||
for file in modifiedFiles
|
||||
stat = fs.lstatSync(file)
|
||||
mtime = stat.mtime
|
||||
@modifiedFilesList.append("<li>#{file} - Modified at #{mtime}")
|
||||
rootView.vertical.append(this)
|
||||
atom.workspaceView.prependToBottom(this)
|
||||
```
|
||||
|
||||
## Coloring UI Elements
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
{Document, Point, Range} = require 'telepath'
|
||||
{Point, Range} = require 'text-buffer'
|
||||
|
||||
module.exports =
|
||||
_: require 'underscore-plus'
|
||||
BufferedNodeProcess: require '../src/buffered-node-process'
|
||||
BufferedProcess: require '../src/buffered-process'
|
||||
Directory: require '../src/directory'
|
||||
Document: Document
|
||||
File: require '../src/file'
|
||||
fs: require 'fs-plus'
|
||||
Git: require '../src/git'
|
||||
@@ -20,8 +19,8 @@ unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
|
||||
module.exports.$ = $
|
||||
module.exports.$$ = $$
|
||||
module.exports.$$$ = $$$
|
||||
module.exports.Editor = require '../src/editor-view'
|
||||
module.exports.RootView = require '../src/root-view'
|
||||
module.exports.EditorView = require '../src/editor-view'
|
||||
module.exports.WorkspaceView = require '../src/workspace-view'
|
||||
module.exports.SelectList = require '../src/select-list'
|
||||
module.exports.ScrollView = require '../src/scroll-view'
|
||||
module.exports.Task = require '../src/task'
|
||||
|
||||
+38
-9
@@ -1,11 +1,11 @@
|
||||
'.editor':
|
||||
# Platform Bindings
|
||||
'alt-left': 'editor:move-to-previous-word-boundary'
|
||||
'alt-right': 'editor:move-to-next-word-boundary'
|
||||
'alt-shift-left': 'editor:select-to-previous-word-boundary'
|
||||
'alt-shift-right': 'editor:select-to-next-word-boundary'
|
||||
'alt-left': 'editor:move-to-beginning-of-word'
|
||||
'alt-right': 'editor:move-to-end-of-word'
|
||||
'alt-shift-left': 'editor:select-to-beginning-of-word'
|
||||
'alt-shift-right': 'editor:select-to-end-of-word'
|
||||
'home': 'editor:move-to-first-character-of-line'
|
||||
'end': 'editor:move-to-end-of-line'
|
||||
'end': 'editor:move-to-end-of-screen-line'
|
||||
'shift-home': 'editor:select-to-first-character-of-line'
|
||||
'shift-end': 'editor:select-to-end-of-line'
|
||||
|
||||
@@ -34,16 +34,45 @@
|
||||
'escape': 'editor:consolidate-selections'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
'input:not(.hidden-input), .native-key-bindings':
|
||||
'body .native-key-bindings':
|
||||
'tab': 'core:focus-next'
|
||||
'shift-tab': 'core:focus-previous'
|
||||
'enter': 'native!'
|
||||
'backspace': 'native!'
|
||||
'shift-backspace': 'native!'
|
||||
'delete': 'native!'
|
||||
'up': 'native!'
|
||||
'down': 'native!'
|
||||
'shift-up': 'native!'
|
||||
'shift-down': 'native!'
|
||||
'alt-up': 'native!'
|
||||
'alt-down': 'native!'
|
||||
'alt-shift-up': 'native!'
|
||||
'alt-shift-down': 'native!'
|
||||
'cmd-up': 'native!'
|
||||
'cmd-down': 'native!'
|
||||
'cmd-shift-up': 'native!'
|
||||
'cmd-shift-down': 'native!'
|
||||
'ctrl-up': 'native!'
|
||||
'ctrl-down': 'native!'
|
||||
'ctrl-shift-up': 'native!'
|
||||
'ctrl-shift-down': 'native!'
|
||||
'left': 'native!'
|
||||
'right': 'native!'
|
||||
'shift-left': 'native!'
|
||||
'shift-right': 'native!'
|
||||
'backspace': 'native!'
|
||||
'shift-backspace': 'native!'
|
||||
'delete': 'native!'
|
||||
'alt-left': 'native!'
|
||||
'alt-right': 'native!'
|
||||
'alt-shift-left': 'native!'
|
||||
'alt-shift-right': 'native!'
|
||||
'cmd-left': 'native!'
|
||||
'cmd-right': 'native!'
|
||||
'cmd-shift-left': 'native!'
|
||||
'cmd-shift-right': 'native!'
|
||||
'ctrl-left': 'native!'
|
||||
'ctrl-right': 'native!'
|
||||
'ctrl-shift-left': 'native!'
|
||||
'ctrl-shift-right': 'native!'
|
||||
'ctrl-b': 'native!'
|
||||
'ctrl-f': 'native!'
|
||||
'ctrl-F': 'native!'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# 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'
|
||||
|
||||
@@ -94,12 +94,12 @@
|
||||
'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,6 +111,7 @@
|
||||
'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)':
|
||||
# Atom specific
|
||||
@@ -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 input:not(.hidden-input), body.platform-darwin .native-key-bindings':
|
||||
'body.platform-darwin .native-key-bindings':
|
||||
'cmd-z': 'native!'
|
||||
'cmd-Z': 'native!'
|
||||
'cmd-x': 'native!'
|
||||
|
||||
+1
-17
@@ -99,6 +99,7 @@
|
||||
submenu: [
|
||||
{ label: 'Add Selection Above', command: 'editor:add-selection-above' }
|
||||
{ label: 'Add Selection Below', command: 'editor:add-selection-below' }
|
||||
{ label: 'Split into Lines', command: 'editor:split-selections-into-lines'}
|
||||
{ type: 'separator' }
|
||||
{ label: 'Select to Top', command: 'core:select-to-top' }
|
||||
{ label: 'Select to Bottom', command: 'core:select-to-bottom' }
|
||||
@@ -113,23 +114,6 @@
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: 'Movement'
|
||||
submenu: [
|
||||
{ label: 'Move to Top', command: 'core:move-to-top' }
|
||||
{ label: 'Move to Bottom', command: 'core:move-to-bottom' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Move to Beginning of Line', command: 'editor:move-to-beginning-of-line' }
|
||||
{ label: 'Move to First Character of Line', command: 'editor:move-to-first-character-of-line' }
|
||||
{ label: 'Move to End of Line', command: 'editor:move-to-end-of-line' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Move to Beginning of Word', command: 'editor:move-to-beginning-of-word' }
|
||||
{ label: 'Move to End of Word', command: 'editor:move-to-end-of-word' }
|
||||
{ label: 'Move to Next Word', command: 'editor:move-to-next-word-boundary' }
|
||||
{ label: 'Move to Previous Word', command: 'editor:move-to-previous-word-boundary' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: 'Find'
|
||||
submenu: []
|
||||
|
||||
+1
-18
@@ -106,6 +106,7 @@
|
||||
submenu: [
|
||||
{ label: 'Add Selection &Above', command: 'editor:add-selection-above' }
|
||||
{ label: 'Add Selection &Below', command: 'editor:add-selection-below' }
|
||||
{ label: 'S&plit into Lines', command: 'editor:split-selections-into-lines'}
|
||||
{ type: 'separator' }
|
||||
{ label: 'Select to &Top', command: 'core:select-to-top' }
|
||||
{ label: 'Select to Botto&m', command: 'core:select-to-bottom' }
|
||||
@@ -120,23 +121,6 @@
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Movement'
|
||||
submenu: [
|
||||
{ label: 'Move to &Top', command: 'core:move-to-top' }
|
||||
{ label: 'Move to &Bottom', command: 'core:move-to-bottom' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Move to Beginning of &Line', command: 'editor:move-to-beginning-of-line' }
|
||||
{ label: 'Move to &First Character of Line', command: 'editor:move-to-first-character-of-line' }
|
||||
{ label: 'Move to &End of Line', command: 'editor:move-to-end-of-line' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Move to Beginning of &Word', command: 'editor:move-to-beginning-of-word' }
|
||||
{ label: 'Move to End of Wor&d', command: 'editor:move-to-end-of-word' }
|
||||
{ label: 'Move to &Next Word', command: 'editor:move-to-next-word-boundary' }
|
||||
{ label: 'Move to &Previous Word', command: 'editor:move-to-previous-word-boundary' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: 'F&ind'
|
||||
submenu: []
|
||||
@@ -165,7 +149,6 @@
|
||||
{ label: "Install &update", command: 'application:install-update', visible: false }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Documentation', command: 'application:open-documentation' }
|
||||
{ label: 'Report an &Issue', command: 'application:report-issue' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
}
|
||||
|
||||
+88
-93
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"version": "39.0.0",
|
||||
"productName": "Atom",
|
||||
"version": "0.47.0",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -9,117 +10,111 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/atom/atom/issues"
|
||||
},
|
||||
"atomShellVersion": "0.6.11",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "Apache",
|
||||
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
|
||||
}
|
||||
],
|
||||
"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",
|
||||
"emissary": "0.19.0",
|
||||
"first-mate": "0.5.0",
|
||||
"fs-plus": "0.9.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.33.1",
|
||||
"guid": "0.0.10",
|
||||
"jasmine-focused": "~0.15.0",
|
||||
"jasmine-tagged": "0.3.0",
|
||||
"mkdirp": "0.3.5",
|
||||
"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.10.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.0",
|
||||
"telepath": "0.45.0",
|
||||
"serializable": "1.x",
|
||||
"space-pen": "3.1.1",
|
||||
"temp": "0.5.0",
|
||||
"underscore-plus": "0.3.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"
|
||||
"text-buffer": "0.15.0",
|
||||
"theorist": "1.x",
|
||||
"underscore-plus": "1.x",
|
||||
"vm-compatibility-layer": "0.1.0"
|
||||
},
|
||||
"packageDependencies": {
|
||||
"atom-light-ui": "0.8.0",
|
||||
"atom-light-syntax": "0.6.0",
|
||||
"atom-dark-ui": "0.8.0",
|
||||
"atom-dark-syntax": "0.6.0",
|
||||
"base16-tomorrow-dark-theme": "0.6.0",
|
||||
"solarized-dark-syntax": "0.4.0",
|
||||
"archive-view": "0.14.0",
|
||||
"autocomplete": "0.15.0",
|
||||
"autoflow": "0.9.0",
|
||||
"autosave": "0.7.0",
|
||||
"bookmarks": "0.13.0",
|
||||
"bracket-matcher": "0.12.0",
|
||||
"command-logger": "0.7.0",
|
||||
"command-palette": "0.10.0",
|
||||
"dev-live-reload": "0.17.0",
|
||||
"editor-stats": "0.7.0",
|
||||
"exception-reporting": "0.8.0",
|
||||
"find-and-replace": "0.49.0",
|
||||
"fuzzy-finder": "0.25.0",
|
||||
"gists": "0.11.0",
|
||||
"git-diff": "0.16.0",
|
||||
"github-sign-in": "0.11.0",
|
||||
"go-to-line": "0.10.0",
|
||||
"grammar-selector": "0.11.0",
|
||||
"image-view": "0.9.0",
|
||||
"keybinding-resolver": "0.5.0",
|
||||
"link": "0.9.0",
|
||||
"markdown-preview": "0.19.0",
|
||||
"metrics": "0.12.0",
|
||||
"package-generator": "0.21.0",
|
||||
"release-notes": "0.12.0",
|
||||
"settings-view": "0.47.0",
|
||||
"snippets": "0.15.0",
|
||||
"spell-check": "0.15.0",
|
||||
"status-bar": "0.22.0",
|
||||
"styleguide": "0.15.0",
|
||||
"symbols-view": "0.24.0",
|
||||
"tabs": "0.12.0",
|
||||
"terminal": "0.22.0",
|
||||
"timecop": "0.10.0",
|
||||
"to-the-hubs": "0.13.0",
|
||||
"tree-view": "0.39.0",
|
||||
"visual-bell": "0.4.0",
|
||||
"whitespace": "0.9.0",
|
||||
"wrap-guide": "0.6.0",
|
||||
"atom-dark-syntax": "0.10.0",
|
||||
"atom-dark-ui": "0.21.0",
|
||||
"atom-light-syntax": "0.10.0",
|
||||
"atom-light-ui": "0.20.0",
|
||||
"base16-tomorrow-dark-theme": "0.8.0",
|
||||
"solarized-dark-syntax": "0.6.0",
|
||||
"solarized-light-syntax": "0.2.0",
|
||||
"archive-view": "0.21.0",
|
||||
"autocomplete": "0.20.0",
|
||||
"autoflow": "0.12.0",
|
||||
"autosave": "0.10.0",
|
||||
"background-tips": "0.5.0",
|
||||
"bookmarks": "0.16.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.77.0",
|
||||
"fuzzy-finder": "0.31.0",
|
||||
"gists": "0.15.0",
|
||||
"git-diff": "0.23.0",
|
||||
"github-sign-in": "0.16.0",
|
||||
"go-to-line": "0.15.0",
|
||||
"grammar-selector": "0.17.0",
|
||||
"image-view": "0.17.0",
|
||||
"keybinding-resolver": "0.9.0",
|
||||
"link": "0.14.0",
|
||||
"markdown-preview": "0.25.1",
|
||||
"metrics": "0.21.0",
|
||||
"package-generator": "0.24.0",
|
||||
"release-notes": "0.15.1",
|
||||
"settings-view": "0.57.0",
|
||||
"snippets": "0.20.0",
|
||||
"spell-check": "0.20.0",
|
||||
"status-bar": "0.32.0",
|
||||
"styleguide": "0.19.0",
|
||||
"symbols-view": "0.29.0",
|
||||
"tabs": "0.17.0",
|
||||
"terminal": "0.26.0",
|
||||
"timecop": "0.13.0",
|
||||
"to-the-hubs": "0.17.0",
|
||||
"tree-view": "0.63.0",
|
||||
"visual-bell": "0.6.0",
|
||||
"welcome": "0.4.0",
|
||||
"whitespace": "0.10.0",
|
||||
"wrap-guide": "0.12.0",
|
||||
"language-c": "0.2.0",
|
||||
"language-clojure": "0.1.0",
|
||||
"language-coffee-script": "0.3.0",
|
||||
"language-coffee-script": "0.4.0",
|
||||
"language-css": "0.2.0",
|
||||
"language-gfm": "0.9.0",
|
||||
"language-gfm": "0.11.0",
|
||||
"language-git": "0.3.0",
|
||||
"language-go": "0.2.0",
|
||||
"language-html": "0.2.0",
|
||||
"language-hyperlink": "0.3.0",
|
||||
"language-java": "0.2.0",
|
||||
"language-javascript": "0.3.0",
|
||||
"language-javascript": "0.4.0",
|
||||
"language-json": "0.2.0",
|
||||
"language-less": "0.1.0",
|
||||
"language-make": "0.1.0",
|
||||
@@ -127,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.4.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",
|
||||
|
||||
Arquivo binário não exibido.
+23
-10
@@ -1,7 +1,13 @@
|
||||
#!/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
|
||||
// TODO Remove once all repositories are public
|
||||
if (!process.env.ATOM_ACCESS_TOKEN)
|
||||
process.env.ATOM_ACCESS_TOKEN = '362295be4c5258d3f7b967bbabae662a455ca2a7';
|
||||
|
||||
// Executes an array of commands one by one.
|
||||
function executeCommands(commands, done, index) {
|
||||
index = (index == undefined ? 0 : index);
|
||||
@@ -17,21 +23,28 @@ function executeCommands(commands, done, index) {
|
||||
done(null);
|
||||
}
|
||||
|
||||
// Join multiple commands into one line.
|
||||
function joinCommands() {
|
||||
var commandSeparator = process.platform == 'win32' ? '&' : ';';
|
||||
return Array.prototype.slice.call(arguments, 0).join(commandSeparator);
|
||||
}
|
||||
var apmVendorPath = path.resolve(__dirname, '..', 'vendor', 'apm');
|
||||
var apmInstallPath = path.resolve(__dirname, '..', 'apm');
|
||||
if (!fs.existsSync(apmInstallPath))
|
||||
fs.mkdirSync(apmInstallPath);
|
||||
if (!fs.existsSync(path.join(apmInstallPath, 'node_modules')))
|
||||
fs.mkdirSync(path.join(apmInstallPath, 'node_modules'));
|
||||
|
||||
var apmFlags = process.env.JANKY_SHA1 || process.argv.indexOf('--no-color') !== -1 ? '--no-color' : '';
|
||||
var packagesToDedupe = ['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: joinCommands('cd vendor/apm', 'npm install --silent .'), options: {ignoreStdout: true}},
|
||||
{command: 'npm install --silent vendor/apm', options: {ignoreStdout: true}},
|
||||
{command: 'npm install --quiet', options: {cwd: path.resolve(__dirname, '..', 'build'), ignoreStdout: true}},
|
||||
{command: 'npm install --quiet', options: {cwd: apmVendorPath, ignoreStdout: true}},
|
||||
{command: 'npm install --quiet ' + apmVendorPath, options: {cwd: apmInstallPath, ignoreStdout: true}},
|
||||
{command: 'npm install --quiet ' + apmVendorPath, options: {ignoreStdout: true}},
|
||||
{command: 'node ../../apm/node_modules/atom-package-manager/bin/apm rebuild', options: {cwd: path.resolve('node_modules', 'atom-package-manager'), ignoreStdout: true}},
|
||||
echoNewLine,
|
||||
'node node_modules/atom-package-manager/bin/apm clean',
|
||||
'node node_modules/atom-package-manager/bin/apm install --silent',
|
||||
'node apm/node_modules/atom-package-manager/bin/apm clean ' + apmFlags,
|
||||
'node apm/node_modules/atom-package-manager/bin/apm install --quiet ' + apmFlags,
|
||||
'node apm/node_modules/atom-package-manager/bin/apm dedupe --quiet ' + apmFlags + ' ' + packagesToDedupe.join(' '),
|
||||
];
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
||||
+6
-5
@@ -1,12 +1,13 @@
|
||||
#!/usr/bin/env node
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
var cp = require('./utils/child-process-wrapper.js');
|
||||
var path = require('path');
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
||||
cp.safeExec('node script/bootstrap', function() {
|
||||
// node node_modules/grunt-cli/bin/grunt "$@"
|
||||
var gruntPath = path.join('node_modules', 'grunt-cli', 'bin', 'grunt');
|
||||
var args = [gruntPath].concat(process.argv.slice(2));
|
||||
cp.safeSpawn(process.execPath, args, process.exit);
|
||||
// build/node_modules/.bin/grunt "$@"
|
||||
var gruntPath = path.join('build', 'node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
|
||||
var args = ['--gruntfile', path.resolve('build', 'Gruntfile.coffee')];
|
||||
args = args.concat(process.argv.slice(2));
|
||||
cp.safeSpawn(gruntPath, args, process.exit);
|
||||
});
|
||||
|
||||
+16
-11
@@ -1,20 +1,18 @@
|
||||
#!/usr/bin/env node
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
var cp = require('./utils/child-process-wrapper.js');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
||||
if (process.platform != 'darwin')
|
||||
throw new Error('cibuild can not run on ' + process.platform + ' yet!');
|
||||
if (process.platform == 'linux')
|
||||
throw new Error('cibuild can not run on linux yet!');
|
||||
|
||||
var homeDir = process.platform == 'win32' ? process.env.USERPROFILE : process.env.HOME;
|
||||
|
||||
function readEnvironmentVariables() {
|
||||
var credentialsPath = '/var/lib/jenkins/config/atomcredentials';
|
||||
function loadEnvironmentVariables(filePath) {
|
||||
try {
|
||||
var credentials = fs.readFileSync(credentialsPath, 'utf8');
|
||||
var lines = credentials.trim().split('\n');
|
||||
var lines = fs.readFileSync(filePath, 'utf8').trim().split('\n');
|
||||
for (i in lines) {
|
||||
var parts = lines[i].split('=');
|
||||
var key = parts[0].trim();
|
||||
@@ -24,16 +22,23 @@ function readEnvironmentVariables() {
|
||||
} catch(error) { }
|
||||
}
|
||||
|
||||
function readEnvironmentVariables() {
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/atomcredentials')
|
||||
loadEnvironmentVariables('/var/lib/jenkins/config/xcodekeychain')
|
||||
}
|
||||
|
||||
readEnvironmentVariables();
|
||||
cp.safeExec.bind(global, 'node script/bootstrap', function(error) {
|
||||
if (error)
|
||||
process.exit(1);
|
||||
require('fs-plus').removeSync.bind(global, path.join(homeDir, '.atom'))
|
||||
var async = require('async');
|
||||
async.series([
|
||||
require('rimraf').bind(global, path.join(homeDir, '.atom')),
|
||||
var gruntPath = path.join('build', 'node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
|
||||
var tasks = [
|
||||
cp.safeExec.bind(global, 'git clean -dff'),
|
||||
cp.safeExec.bind(global, 'node node_modules/grunt-cli/bin/grunt ci --stack --no-color'),
|
||||
], function(error) {
|
||||
cp.safeExec.bind(global, gruntPath + ' ci --gruntfile build/Gruntfile.coffee --stack --no-color'),
|
||||
]
|
||||
async.series(tasks, function(error) {
|
||||
process.exit(error ? 1 : 0);
|
||||
});
|
||||
})();
|
||||
|
||||
Arquivo executável
+37
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
var cp = require('./utils/child-process-wrapper.js');
|
||||
var path = require('path');
|
||||
var os = require('os');
|
||||
|
||||
var removeCommand = process.platform === 'win32' ? 'del /F /Q /S ' : 'rm -rf ';
|
||||
var productName = require('../package.json').productName;
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
var home = process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];
|
||||
var tmpdir = process.platform === 'win32' ? os.tmpdir() : '/tmp';
|
||||
|
||||
// Windows: Use START as a way to ignore error if Atom.exe isnt running
|
||||
var killatom = process.platform === 'win32' ? 'START taskkill /F /IM ' + productName + '.exe' : 'pkill -9 ' + productName + ' || true';
|
||||
|
||||
var commands = [
|
||||
killatom,
|
||||
[__dirname, '..', 'node_modules'],
|
||||
[__dirname, '..', 'build', 'node_modules'],
|
||||
[__dirname, '..', 'apm', 'node_modules'],
|
||||
[__dirname, '..', 'vendor', 'apm', 'node_modules'],
|
||||
[__dirname, '..', 'atom-shell'],
|
||||
[home, '.atom', '.node-gyp'],
|
||||
[home, '.atom', 'storage'],
|
||||
[tmpdir, 'atom-build'],
|
||||
[tmpdir, 'atom-cached-atom-shells'],
|
||||
[tmpdir, 'atom-compile-cache'],
|
||||
];
|
||||
var run = function() {
|
||||
var next = commands.shift();
|
||||
if (!next)
|
||||
process.exit(0);
|
||||
if (Array.isArray(next))
|
||||
next = removeCommand + path.resolve.apply(path.resolve, next);
|
||||
cp.safeExec(next, run);
|
||||
};
|
||||
run();
|
||||
@@ -0,0 +1,5 @@
|
||||
@IF EXIST "%~dp0\node.exe" (
|
||||
"%~dp0\node.exe" "%~dp0\clean" %*
|
||||
) ELSE (
|
||||
node "%~dp0\clean" %*
|
||||
)
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
|
||||
# This entire file is a hack so that constructicon can build Atom via
|
||||
# xcode
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
rm -fr node_modules
|
||||
./script/bootstrap
|
||||
./node_modules/.bin/grunt --no-color --build-dir="$BUILT_PRODUCTS_DIR" deploy
|
||||
|
||||
echo "TARGET_BUILD_DIR=$BUILT_PRODUCTS_DIR"
|
||||
echo "FULL_PRODUCT_NAME=Atom.app"
|
||||
echo "PRODUCT_NAME=Atom"
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
cd "$(dirname "$0")/../.."
|
||||
export PATH="atom-shell/Atom.app/Contents/Resources/:${PATH}"
|
||||
|
||||
rm -rf atom.xcodeproj
|
||||
gyp --depth=. atom.gyp
|
||||
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);
|
||||
Arquivo executável
+11
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env coffee
|
||||
|
||||
path = require 'path'
|
||||
CommandInstaller = require '../src/command-installer'
|
||||
|
||||
callback = (error, sourcePath, destinationPath) ->
|
||||
unless error?
|
||||
console.log "#{sourcePath} intalled to #{destinationPath}"
|
||||
|
||||
CommandInstaller.installAtomCommand(path.resolve(__dirname, '..'), callback)
|
||||
CommandInstaller.installApmCommand(path.resolve(__dirname, '..'), callback)
|
||||
@@ -3,11 +3,11 @@
|
||||
set -e
|
||||
|
||||
BUILT_PRODUCTS_DIR=$1
|
||||
VERSION=$2
|
||||
PLIST_PATH="$BUILT_PRODUCTS_DIR/Atom.app/Contents/Info.plist"
|
||||
HELPER_PLIST_PATH="$BUILT_PRODUCTS_DIR/Atom.app/Contents/Frameworks/Atom Helper.app/Contents/Info.plist"
|
||||
|
||||
# Update version
|
||||
VERSION=$(git rev-parse --short HEAD | tr -d "\n")
|
||||
/usr/libexec/PlistBuddy -c "Set CFBundleShortVersionString $VERSION" "$PLIST_PATH"
|
||||
/usr/libexec/PlistBuddy -c "Set CFBundleVersion $VERSION" "$PLIST_PATH"
|
||||
/usr/libexec/PlistBuddy -c "Set CFBundleShortVersionString $VERSION" "$HELPER_PLIST_PATH"
|
||||
|
||||
+3
-2
@@ -1,9 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
var safeExec = require('./utils/child-process-wrapper.js').safeExec;
|
||||
var path = require('path');
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
||||
safeExec('node script/bootstrap', function() {
|
||||
safeExec('node node_modules/grunt-cli/bin/grunt ci --stack --no-color', process.exit);
|
||||
var gruntPath = path.join('node_modules', '.bin', 'grunt') + (process.platform === 'win32' ? '.cmd' : '');
|
||||
safeExec(gruntPath + ' ci --stack --no-color', process.exit);
|
||||
});
|
||||
|
||||
+13
-5
@@ -1,11 +1,11 @@
|
||||
{$, $$, fs, RootView} = require 'atom'
|
||||
{$, $$, fs, WorkspaceView} = require 'atom'
|
||||
Exec = require('child_process').exec
|
||||
path = require 'path'
|
||||
ThemeManager = require '../src/theme-manager'
|
||||
|
||||
describe "the `atom` global", ->
|
||||
beforeEach ->
|
||||
atom.rootView = new RootView
|
||||
atom.workspaceView = new WorkspaceView
|
||||
|
||||
describe "package lifecycle methods", ->
|
||||
describe ".loadPackage(name)", ->
|
||||
@@ -90,12 +90,12 @@ describe "the `atom` global", ->
|
||||
it "defers requiring/activating the main module until an activation event bubbles to the root view", ->
|
||||
expect(pack.requireMainModule).not.toHaveBeenCalled()
|
||||
expect(mainModule.activate).not.toHaveBeenCalled()
|
||||
atom.rootView.trigger 'activation-event'
|
||||
atom.workspaceView.trigger 'activation-event'
|
||||
expect(mainModule.activate).toHaveBeenCalled()
|
||||
|
||||
it "triggers the activation event on all handlers registered during activation", ->
|
||||
atom.rootView.openSync()
|
||||
editorView = atom.rootView.getActiveView()
|
||||
atom.workspaceView.openSync()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
eventHandler = jasmine.createSpy("activation-event")
|
||||
editorView.command 'activation-event', eventHandler
|
||||
editorView.trigger 'activation-event'
|
||||
@@ -438,3 +438,11 @@ describe "the `atom` global", ->
|
||||
expect(atom.config.get('core.themes')).not.toContain packageName
|
||||
expect(atom.config.get('core.themes')).not.toContain packageName
|
||||
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
|
||||
|
||||
describe ".isReleasedVersion()", ->
|
||||
it "returns false if the version is a SHA and true otherwise", ->
|
||||
version = '0.1.0'
|
||||
spyOn(atom.constructor, 'getVersion').andCallFake -> version
|
||||
expect(atom.isReleasedVersion()).toBe true
|
||||
version = '36b5518'
|
||||
expect(atom.isReleasedVersion()).toBe false
|
||||
|
||||
@@ -108,10 +108,10 @@ describe "Config", ->
|
||||
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
|
||||
describe ".save()", ->
|
||||
nodeFs = require 'fs'
|
||||
CSON = require 'season'
|
||||
|
||||
beforeEach ->
|
||||
spyOn(nodeFs, 'writeFileSync')
|
||||
spyOn(CSON, 'writeFileSync')
|
||||
jasmine.unspy atom.config, 'save'
|
||||
|
||||
describe "when ~/.atom/config.json exists", ->
|
||||
@@ -122,12 +122,12 @@ describe "Config", ->
|
||||
atom.config.set("x.y.z", 3)
|
||||
atom.config.setDefaults("a.b", e: 4, f: 5)
|
||||
|
||||
nodeFs.writeFileSync.reset()
|
||||
CSON.writeFileSync.reset()
|
||||
atom.config.save()
|
||||
|
||||
expect(nodeFs.writeFileSync.argsForCall[0][0]).toBe(path.join(atom.config.configDirPath, "atom.config.json"))
|
||||
writtenConfig = JSON.parse(nodeFs.writeFileSync.argsForCall[0][1])
|
||||
expect(writtenConfig).toEqual atom.config.settings
|
||||
expect(CSON.writeFileSync.argsForCall[0][0]).toBe(path.join(atom.config.configDirPath, "atom.config.json"))
|
||||
writtenConfig = CSON.writeFileSync.argsForCall[0][1]
|
||||
expect(writtenConfig).toBe atom.config.settings
|
||||
|
||||
describe "when ~/.atom/config.json doesn't exist", ->
|
||||
it "writes any non-default properties to ~/.atom/config.cson", ->
|
||||
@@ -137,12 +137,12 @@ describe "Config", ->
|
||||
atom.config.set("x.y.z", 3)
|
||||
atom.config.setDefaults("a.b", e: 4, f: 5)
|
||||
|
||||
nodeFs.writeFileSync.reset()
|
||||
CSON.writeFileSync.reset()
|
||||
atom.config.save()
|
||||
|
||||
expect(nodeFs.writeFileSync.argsForCall[0][0]).toBe(path.join(atom.config.configDirPath, "atom.config.cson"))
|
||||
expect(CSON.writeFileSync.argsForCall[0][0]).toBe(path.join(atom.config.configDirPath, "atom.config.cson"))
|
||||
CoffeeScript = require 'coffee-script'
|
||||
writtenConfig = CoffeeScript.eval(nodeFs.writeFileSync.argsForCall[0][1], bare: true)
|
||||
writtenConfig = CSON.writeFileSync.argsForCall[0][1]
|
||||
expect(writtenConfig).toEqual atom.config.settings
|
||||
|
||||
describe ".setDefaults(keyPath, defaults)", ->
|
||||
|
||||
@@ -68,7 +68,7 @@ describe "Directory", ->
|
||||
|
||||
describe "on #darwin or #linux", ->
|
||||
it "includes symlink information about entries", ->
|
||||
entries = directory.getEntries()
|
||||
entries = directory.getEntriesSync()
|
||||
for entry in entries
|
||||
name = entry.getBaseName()
|
||||
if name is 'symlink-to-dir' or name is 'symlink-to-file'
|
||||
@@ -76,6 +76,20 @@ describe "Directory", ->
|
||||
else
|
||||
expect(entry.symlink).toBeFalsy()
|
||||
|
||||
callback = jasmine.createSpy('getEntries')
|
||||
directory.getEntries(callback)
|
||||
|
||||
waitsFor -> callback.callCount is 1
|
||||
|
||||
runs ->
|
||||
entries = callback.mostRecentCall.args[1]
|
||||
for entry in entries
|
||||
name = entry.getBaseName()
|
||||
if name is 'symlink-to-dir' or name is 'symlink-to-file'
|
||||
expect(entry.symlink).toBeTruthy()
|
||||
else
|
||||
expect(entry.symlink).toBeFalsy()
|
||||
|
||||
describe ".relativize(path)", ->
|
||||
describe "on #darwin or #linux", ->
|
||||
it "returns a relative path based on the directory's path", ->
|
||||
|
||||
@@ -15,19 +15,6 @@ describe "DisplayBuffer", ->
|
||||
displayBuffer.destroy()
|
||||
buffer.release()
|
||||
|
||||
describe "@deserialize(state)", ->
|
||||
it "constructs a display buffer with the same buffer, folds, editorWidthInChars, and tabLength", ->
|
||||
displayBuffer.setTabLength(4)
|
||||
displayBuffer.setEditorWidthInChars(64)
|
||||
displayBuffer.createFold(2, 4)
|
||||
displayBuffer2 = atom.deserializers.deserialize(displayBuffer.serialize())
|
||||
expect(displayBuffer2.id).toBe displayBuffer.id
|
||||
expect(displayBuffer2.buffer).toBe displayBuffer.buffer
|
||||
expect(displayBuffer2.tokenizedBuffer.buffer).toBe displayBuffer.tokenizedBuffer.buffer
|
||||
expect(displayBuffer2.isFoldedAtBufferRow(2)).toBeTruthy()
|
||||
expect(displayBuffer2.getSoftWrapColumn()).toBe displayBuffer.getSoftWrapColumn()
|
||||
expect(displayBuffer2.getTabLength()).toBe displayBuffer.getTabLength()
|
||||
|
||||
describe ".copy()", ->
|
||||
it "creates a new DisplayBuffer with the same initial state", ->
|
||||
marker1 = displayBuffer.markBufferRange([[1, 2], [3, 4]], id: 1)
|
||||
@@ -103,6 +90,14 @@ describe "DisplayBuffer", ->
|
||||
expect(displayBuffer.lineForRow(3).text).toBe ' var pivot = items.shift(), current, left = [], '
|
||||
expect(displayBuffer.lineForRow(4).text).toBe 'right = [];'
|
||||
|
||||
describe "when there are hard tabs", ->
|
||||
beforeEach ->
|
||||
buffer.setText(buffer.getText().replace(new RegExp(' ', 'g'), '\t'))
|
||||
|
||||
it "correctly tokenizes the hard tabs", ->
|
||||
expect(displayBuffer.lineForRow(3).tokens[0].isHardTab).toBeTruthy()
|
||||
expect(displayBuffer.lineForRow(3).tokens[1].isHardTab).toBeTruthy()
|
||||
|
||||
describe "when the buffer changes", ->
|
||||
describe "when buffer lines are updated", ->
|
||||
describe "when whitespace is added after the max line length", ->
|
||||
|
||||
+137
-14
@@ -21,20 +21,21 @@ describe "Editor", ->
|
||||
buffer = editor.buffer
|
||||
lineLengths = buffer.getLines().map (line) -> line.length
|
||||
|
||||
describe "@deserialize(state)", ->
|
||||
describe "when the editor is deserialized", ->
|
||||
it "restores selections and folds based on markers in the buffer", ->
|
||||
editor.setSelectedBufferRange([[1, 2], [3, 4]])
|
||||
editor.addSelectionForBufferRange([[5, 6], [7, 5]], isReversed: true)
|
||||
editor.foldBufferRow(4)
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
|
||||
editor2 = atom.deserializers.deserialize(editor.serialize())
|
||||
editor2 = editor.testSerialization()
|
||||
|
||||
expect(editor2.id).toBe editor.id
|
||||
expect(editor2.getBuffer().getPath()).toBe editor.getBuffer().getPath()
|
||||
expect(editor2.getSelectedBufferRanges()).toEqual [[[1, 2], [3, 4]], [[5, 6], [7, 5]]]
|
||||
expect(editor2.getSelection(1).isReversed()).toBeTruthy()
|
||||
expect(editor2.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
editor2.destroy()
|
||||
|
||||
describe ".copy()", ->
|
||||
it "returns a different edit session with the same initial state", ->
|
||||
@@ -101,6 +102,16 @@ describe "Editor", ->
|
||||
lastCursor = editor.addCursorAtScreenPosition([2, 0])
|
||||
expect(editor.getCursor()).toBe lastCursor
|
||||
|
||||
describe "when the cursor moves", ->
|
||||
it "clears a goal column established by vertical movement", ->
|
||||
editor.setText('b')
|
||||
editor.setCursorBufferPosition([0,0])
|
||||
editor.insertNewline()
|
||||
editor.moveCursorUp()
|
||||
editor.insertText('a')
|
||||
editor.moveCursorDown()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [1, 1]
|
||||
|
||||
describe ".setCursorScreenPosition(screenPosition)", ->
|
||||
it "clears a goal column established by vertical movement", ->
|
||||
# set a goal column by moving down
|
||||
@@ -347,13 +358,13 @@ describe "Editor", ->
|
||||
expect(editor.getCursors().length).toBe 1
|
||||
expect(editor.getCursorBufferPosition()).toEqual [12,2]
|
||||
|
||||
describe ".moveCursorToBeginningOfLine()", ->
|
||||
describe ".moveCursorToBeginningOfScreenLine()", ->
|
||||
describe "when soft wrap is on", ->
|
||||
it "moves cursor to the beginning of the screen line", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setEditorWidthInChars(10)
|
||||
editor.setCursorScreenPosition([1, 2])
|
||||
editor.moveCursorToBeginningOfLine()
|
||||
editor.moveCursorToBeginningOfScreenLine()
|
||||
cursor = editor.getCursor()
|
||||
expect(cursor.getScreenPosition()).toEqual [1, 0]
|
||||
|
||||
@@ -361,19 +372,19 @@ describe "Editor", ->
|
||||
it "moves cursor to the beginning of then line", ->
|
||||
editor.setCursorScreenPosition [0,5]
|
||||
editor.addCursorAtScreenPosition [1,7]
|
||||
editor.moveCursorToBeginningOfLine()
|
||||
editor.moveCursorToBeginningOfScreenLine()
|
||||
expect(editor.getCursors().length).toBe 2
|
||||
[cursor1, cursor2] = editor.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,0]
|
||||
|
||||
describe ".moveCursorToEndOfLine()", ->
|
||||
describe ".moveCursorToEndOfScreenLine()", ->
|
||||
describe "when soft wrap is on", ->
|
||||
it "moves cursor to the beginning of the screen line", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setEditorWidthInChars(10)
|
||||
editor.setCursorScreenPosition([1, 2])
|
||||
editor.moveCursorToEndOfLine()
|
||||
editor.moveCursorToEndOfScreenLine()
|
||||
cursor = editor.getCursor()
|
||||
expect(cursor.getScreenPosition()).toEqual [1, 9]
|
||||
|
||||
@@ -381,12 +392,30 @@ describe "Editor", ->
|
||||
it "moves cursor to the end of line", ->
|
||||
editor.setCursorScreenPosition [0,0]
|
||||
editor.addCursorAtScreenPosition [1,0]
|
||||
editor.moveCursorToEndOfLine()
|
||||
editor.moveCursorToEndOfScreenLine()
|
||||
expect(editor.getCursors().length).toBe 2
|
||||
[cursor1, cursor2] = editor.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,29]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,30]
|
||||
|
||||
describe ".moveCursorToBeginningOfLine()", ->
|
||||
it "moves cursor to the beginning of the buffer line", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setEditorWidthInChars(10)
|
||||
editor.setCursorScreenPosition([1, 2])
|
||||
editor.moveCursorToBeginningOfLine()
|
||||
cursor = editor.getCursor()
|
||||
expect(cursor.getScreenPosition()).toEqual [0, 0]
|
||||
|
||||
describe ".moveCursorToEndOfLine()", ->
|
||||
it "moves cursor to the end of the buffer line", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setEditorWidthInChars(10)
|
||||
editor.setCursorScreenPosition([0, 2])
|
||||
editor.moveCursorToEndOfLine()
|
||||
cursor = editor.getCursor()
|
||||
expect(cursor.getScreenPosition()).toEqual [3, 4]
|
||||
|
||||
describe ".moveCursorToFirstCharacterOfLine()", ->
|
||||
describe "when soft wrap is on", ->
|
||||
it "moves to the first character of the current screen line or the beginning of the screen line if it's already on the first character", ->
|
||||
@@ -418,6 +447,13 @@ describe "Editor", ->
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,0]
|
||||
|
||||
it "moves to the beginning of the line if it only contains whitespace ", ->
|
||||
editor.setText("first\n \nthird")
|
||||
editor.setCursorScreenPosition [1,2]
|
||||
editor.moveCursorToFirstCharacterOfLine()
|
||||
cursor = editor.getCursor()
|
||||
expect(cursor.getBufferPosition()).toEqual [1,0]
|
||||
|
||||
describe ".moveCursorToBeginningOfWord()", ->
|
||||
it "moves the cursor to the beginning of the word", ->
|
||||
editor.setCursorBufferPosition [0, 8]
|
||||
@@ -783,6 +819,11 @@ describe "Editor", ->
|
||||
editor.selectLine()
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[12,0], [12,2]]
|
||||
|
||||
editor.setCursorBufferPosition([0, 2])
|
||||
editor.selectLine()
|
||||
editor.selectLine()
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[0,0], [2,0]]
|
||||
|
||||
describe ".selectToBeginningOfWord()", ->
|
||||
it "selects text from cusor position to beginning of word", ->
|
||||
editor.setCursorScreenPosition [0,13]
|
||||
@@ -1145,6 +1186,27 @@ describe "Editor", ->
|
||||
[[10, 0], [10, 0]]
|
||||
]
|
||||
|
||||
describe ".splitSelectionsIntoLines()", ->
|
||||
it "splits all multi-line selections into one selection per line", ->
|
||||
editor.setSelectedBufferRange([[0, 3], [2, 4]])
|
||||
editor.splitSelectionsIntoLines()
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [
|
||||
[[0, 3], [0, 29]]
|
||||
[[1, 0], [1, 30]]
|
||||
[[2, 0], [2, 4]]
|
||||
]
|
||||
|
||||
editor.setSelectedBufferRange([[0, 3], [1, 10]])
|
||||
editor.splitSelectionsIntoLines()
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [
|
||||
[[0, 3], [0, 29]]
|
||||
[[1, 0], [1, 10]]
|
||||
]
|
||||
|
||||
editor.setSelectedBufferRange([[0, 0], [0, 3]])
|
||||
editor.splitSelectionsIntoLines()
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 3]]]
|
||||
|
||||
describe ".consolidateSelections()", ->
|
||||
it "destroys all selections but the most recent, returning true if any selections were destroyed", ->
|
||||
editor.setSelectedBufferRange([[3, 16], [3, 21]])
|
||||
@@ -2389,17 +2451,16 @@ describe "Editor", ->
|
||||
|
||||
describe "when a better-matched grammar is added to syntax", ->
|
||||
it "switches to the better-matched grammar and re-tokenizes the buffer", ->
|
||||
editor.destroy()
|
||||
jsGrammar = atom.syntax.selectGrammar('a.js')
|
||||
atom.syntax.removeGrammar(jsGrammar)
|
||||
|
||||
editor = atom.project.openSync('sample.js', autoIndent: false)
|
||||
expect(editor.getGrammar()).toBe atom.syntax.nullGrammar
|
||||
expect(editor.lineForScreenRow(0).tokens.length).toBe 1
|
||||
editor2 = atom.project.openSync('sample.js', autoIndent: false)
|
||||
expect(editor2.getGrammar()).toBe atom.syntax.nullGrammar
|
||||
expect(editor2.lineForScreenRow(0).tokens.length).toBe 1
|
||||
|
||||
atom.syntax.addGrammar(jsGrammar)
|
||||
expect(editor.getGrammar()).toBe jsGrammar
|
||||
expect(editor.lineForScreenRow(0).tokens.length).toBeGreaterThan 1
|
||||
expect(editor2.getGrammar()).toBe jsGrammar
|
||||
expect(editor2.lineForScreenRow(0).tokens.length).toBeGreaterThan 1
|
||||
|
||||
describe "auto-indent", ->
|
||||
copyText = (text, {startColumn}={}) ->
|
||||
@@ -2611,6 +2672,7 @@ describe "Editor", ->
|
||||
|
||||
describe ".shouldPromptToSave()", ->
|
||||
it "returns false when an edit session's buffer is in use by more than one session", ->
|
||||
jasmine.unspy(editor, 'shouldPromptToSave')
|
||||
expect(editor.shouldPromptToSave()).toBeFalsy()
|
||||
buffer.setText('changed')
|
||||
expect(editor.shouldPromptToSave()).toBeTruthy()
|
||||
@@ -2659,3 +2721,64 @@ describe "Editor", ->
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 2]
|
||||
editor.moveCursorLeft()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 0]
|
||||
|
||||
describe ".setIndentationForBufferRow", ->
|
||||
describe "when the editor uses soft tabs but the row has hard tabs", ->
|
||||
it "only replaces whitespace charachters", ->
|
||||
editor.setSoftWrap(true)
|
||||
editor.setText(" 1\n 2")
|
||||
editor.setCursorBufferPosition([0, 0])
|
||||
editor.setIndentationForBufferRow(0, 2)
|
||||
expect(editor.getText()).toBe(" 1\n 2")
|
||||
|
||||
describe "when the editor's grammar has an injection selector", ->
|
||||
beforeEach ->
|
||||
atom.packages.activatePackage('language-text', sync: true)
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
|
||||
it "includes the grammar's patterns when the selector matches the current scope in other grammars", ->
|
||||
atom.packages.activatePackage('language-hyperlink', sync: true)
|
||||
grammar = atom.syntax.selectGrammar("text.js")
|
||||
{tokens} = grammar.tokenizeLine("var i; // http://github.com")
|
||||
|
||||
expect(tokens[0].value).toBe "var"
|
||||
expect(tokens[0].scopes).toEqual ["source.js", "storage.modifier.js"]
|
||||
|
||||
expect(tokens[6].value).toBe "http://github.com"
|
||||
expect(tokens[6].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
|
||||
describe "when the grammar is added", ->
|
||||
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor.setText("// http://github.com")
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " http://github.com"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
atom.packages.activatePackage('language-hyperlink', sync: true)
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "http://github.com"
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
|
||||
describe "when the grammar is updated", ->
|
||||
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor.setText("// SELECT * FROM OCTOCATS")
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
atom.packages.activatePackage('package-with-injection-selector', sync: true)
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
atom.packages.activatePackage('language-sql', sync: true)
|
||||
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "SELECT"
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "keyword.other.DML.sql"]
|
||||
|
||||
+499
-450
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -73,7 +73,7 @@ describe 'File', ->
|
||||
|
||||
fs.moveSync(filePath, newPath)
|
||||
|
||||
waitsFor "move event", ->
|
||||
waitsFor "move event", 30000, ->
|
||||
moveHandler.callCount > 0
|
||||
|
||||
runs ->
|
||||
|
||||
@@ -9,5 +9,5 @@ module.exports =
|
||||
|
||||
activate: ->
|
||||
@activateCallCount++
|
||||
atom.rootView.getActiveView()?.command 'activation-event', =>
|
||||
atom.workspaceView.getActiveView()?.command 'activation-event', =>
|
||||
@activationEventCallCount++
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
'name': 'test'
|
||||
'scopeName': 'source.test'
|
||||
'injectionSelector': 'comment'
|
||||
'patterns': [{'include': 'source.sql'}]
|
||||
@@ -1,5 +1,5 @@
|
||||
# This package loads async, otherwise it would log errors when it
|
||||
# is automatically serialized when rootView is deactivatated
|
||||
# is automatically serialized when workspaceView is deactivatated
|
||||
|
||||
'main': 'index.coffee'
|
||||
'activationEvents': ['activation-event']
|
||||
|
||||
@@ -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,15 +1,18 @@
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
|
||||
temp = require 'temp'
|
||||
Keymap = require '../src/keymap'
|
||||
{$, $$, RootView} = require 'atom'
|
||||
{$, $$, WorkspaceView} = require 'atom'
|
||||
|
||||
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
|
||||
@@ -158,12 +164,12 @@ describe "Keymap", ->
|
||||
expect(bazHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when the event's target is the document body", ->
|
||||
it "triggers the mapped event on the rootView", ->
|
||||
atom.rootView = new RootView
|
||||
atom.rootView.attachToDom()
|
||||
it "triggers the mapped event on the workspaceView", ->
|
||||
atom.workspaceView = new WorkspaceView
|
||||
atom.workspaceView.attachToDom()
|
||||
keymap.bindKeys 'name', 'body', 'x': 'foo'
|
||||
fooHandler = jasmine.createSpy("fooHandler")
|
||||
atom.rootView.on 'foo', fooHandler
|
||||
atom.workspaceView.on 'foo', fooHandler
|
||||
|
||||
result = keymap.handleKeyEvent(keydownEvent('x', target: document.body))
|
||||
expect(result).toBe(false)
|
||||
@@ -347,3 +353,19 @@ describe "Keymap", ->
|
||||
el = $$ -> @div class: 'brown'
|
||||
bindings = keymap.keyBindingsForCommandMatchingElement('cultivate', el)
|
||||
expect(bindings).toHaveLength 0
|
||||
|
||||
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'
|
||||
|
||||
@@ -1,341 +1,67 @@
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
PaneContainer = require '../src/pane-container'
|
||||
Pane = require '../src/pane'
|
||||
{_, $, View, $$} = require 'atom'
|
||||
|
||||
describe "PaneContainer", ->
|
||||
[TestView, container, pane1, pane2, pane3] = []
|
||||
|
||||
beforeEach ->
|
||||
class TestView extends View
|
||||
atom.deserializers.add(this)
|
||||
@deserialize: ({name}) -> new TestView(name)
|
||||
@content: -> @div tabindex: -1
|
||||
initialize: (@name) -> @text(@name)
|
||||
serialize: -> { deserializer: 'TestView', @name }
|
||||
getUri: -> path.join(temp.dir, @name)
|
||||
save: -> @saved = true
|
||||
isEqual: (other) -> @name is other.name
|
||||
|
||||
container = new PaneContainer
|
||||
pane1 = new Pane(new TestView('1'))
|
||||
container.setRoot(pane1)
|
||||
pane2 = pane1.splitRight(new TestView('2'))
|
||||
pane3 = pane2.splitDown(new TestView('3'))
|
||||
|
||||
afterEach ->
|
||||
atom.deserializers.remove(TestView)
|
||||
|
||||
describe ".focusNextPane()", ->
|
||||
it "focuses the pane following the focused pane or the first pane if no pane has focus", ->
|
||||
container.attachToDom()
|
||||
container.focusNextPane()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPane()
|
||||
expect(pane2.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPane()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPane()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".focusPreviousPane()", ->
|
||||
it "focuses the pane preceding the focused pane or the last pane if no pane has focus", ->
|
||||
container.attachToDom()
|
||||
container.focusPreviousPane()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPane()
|
||||
expect(pane2.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPane()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPane()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".getActivePane()", ->
|
||||
it "returns the most-recently focused pane", ->
|
||||
focusStealer = $$ -> @div tabindex: -1, "focus stealer"
|
||||
focusStealer.attachToDom()
|
||||
container.attachToDom()
|
||||
|
||||
pane2.focus()
|
||||
expect(container.getFocusedPane()).toBe pane2
|
||||
expect(container.getActivePane()).toBe pane2
|
||||
|
||||
focusStealer.focus()
|
||||
expect(container.getFocusedPane()).toBeUndefined()
|
||||
expect(container.getActivePane()).toBe pane2
|
||||
|
||||
pane3.focus()
|
||||
expect(container.getFocusedPane()).toBe pane3
|
||||
expect(container.getActivePane()).toBe pane3
|
||||
|
||||
# returns the first pane if none have been set to active
|
||||
container.find('.pane.active').removeClass('active')
|
||||
expect(container.getActivePane()).toBe pane1
|
||||
|
||||
describe ".eachPane(callback)", ->
|
||||
it "runs the callback with all current and future panes until the subscription is cancelled", ->
|
||||
panes = []
|
||||
subscription = container.eachPane (pane) -> panes.push(pane)
|
||||
expect(panes).toEqual [pane1, pane2, pane3]
|
||||
|
||||
panes = []
|
||||
pane4 = pane3.splitRight(pane3.copyActiveItem())
|
||||
expect(panes).toEqual [pane4]
|
||||
|
||||
panes = []
|
||||
subscription.off()
|
||||
pane4.splitDown()
|
||||
expect(panes).toEqual []
|
||||
|
||||
describe ".reopenItem()", ->
|
||||
describe "when there is an active pane", ->
|
||||
it "reconstructs and shows the last-closed pane item", ->
|
||||
expect(container.getActivePane()).toBe pane3
|
||||
item3 = pane3.activeItem
|
||||
item4 = new TestView('4')
|
||||
pane3.showItem(item4)
|
||||
|
||||
pane3.destroyItem(item3)
|
||||
pane3.destroyItem(item4)
|
||||
expect(container.getActivePane()).toBe pane1
|
||||
|
||||
expect(container.reopenItem()).toBeTruthy()
|
||||
expect(pane1.activeItem).toEqual item4
|
||||
|
||||
expect(container.reopenItem()).toBeTruthy()
|
||||
expect(pane1.activeItem).toEqual item3
|
||||
|
||||
expect(container.reopenItem()).toBeFalsy()
|
||||
expect(pane1.activeItem).toEqual item3
|
||||
|
||||
describe "when the last-closed pane item is an edit session", ->
|
||||
it "reopens the edit session (regression)", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
pane3.showItem(editor)
|
||||
pane3.destroyItem(editor)
|
||||
expect(container.reopenItem()).toBeTruthy()
|
||||
expect(pane3.activeItem.getPath()).toBe editor.getPath()
|
||||
expect(container.reopenItem()).toBeFalsy()
|
||||
|
||||
describe "when there is no active pane", ->
|
||||
it "attaches a new pane with the reconstructed last pane item and focuses it", ->
|
||||
container.attachToDom()
|
||||
pane1.remove()
|
||||
pane2.remove()
|
||||
item3 = pane3.activeItem
|
||||
pane3.destroyItem(item3)
|
||||
expect(container.getActivePane()).toBeUndefined()
|
||||
|
||||
container.reopenItem()
|
||||
|
||||
expect(container.getActivePane().activeItem).toEqual item3
|
||||
expect(container.getActivePane().activeView).toMatchSelector ':focus'
|
||||
|
||||
it "does not reopen an item that is already open", ->
|
||||
item3 = pane3.activeItem
|
||||
item4 = new TestView('4')
|
||||
pane3.showItem(item4)
|
||||
pane3.destroyItem(item3)
|
||||
pane3.destroyItem(item4)
|
||||
|
||||
expect(container.getActivePane()).toBe pane1
|
||||
pane1.showItem(new TestView('4'))
|
||||
|
||||
expect(container.reopenItem()).toBeTruthy()
|
||||
expect(_.pluck(pane1.getItems(), 'name')).toEqual ['1', '4', '3']
|
||||
expect(pane1.activeItem).toEqual item3
|
||||
|
||||
expect(container.reopenItem()).toBeFalsy()
|
||||
expect(pane1.activeItem).toEqual item3
|
||||
|
||||
pane1.destroyItem(item3)
|
||||
container.setRoot(new Pane(item3))
|
||||
expect(container.reopenItem()).toBeFalsy()
|
||||
expect(container.getActivePane().getItems().length).toBe 1
|
||||
expect(container.getActivePaneItem()).toEqual item3
|
||||
|
||||
describe ".saveAll()", ->
|
||||
it "saves all open pane items", ->
|
||||
pane1.showItem(new TestView('4'))
|
||||
|
||||
container.saveAll()
|
||||
|
||||
for pane in container.getPanes()
|
||||
for item in pane.getItems()
|
||||
expect(item.saved).toBeTruthy()
|
||||
|
||||
describe ".confirmClose()", ->
|
||||
it "returns true after modified files are saved", ->
|
||||
pane1.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
pane2.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
spyOn(atom, "confirm").andReturn(0)
|
||||
|
||||
saved = container.confirmClose()
|
||||
|
||||
runs ->
|
||||
expect(saved).toBeTruthy()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "returns false if the user cancels saving", ->
|
||||
pane1.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
pane2.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
spyOn(atom, "confirm").andReturn(1)
|
||||
|
||||
saved = container.confirmClose()
|
||||
|
||||
runs ->
|
||||
expect(saved).toBeFalsy()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
describe "serialization", ->
|
||||
it "can be serialized and deserialized, and correctly adjusts dimensions of deserialized panes after attach", ->
|
||||
newContainer = atom.deserializers.deserialize(container.serialize())
|
||||
expect(newContainer.find('.row > :contains(1)')).toExist()
|
||||
expect(newContainer.find('.row > .column > :contains(2)')).toExist()
|
||||
expect(newContainer.find('.row > .column > :contains(3)')).toExist()
|
||||
[containerA, pane1A, pane2A, pane3A] = []
|
||||
|
||||
newContainer.height(200).width(300).attachToDom()
|
||||
expect(newContainer.find('.row > :contains(1)').width()).toBe 150
|
||||
expect(newContainer.find('.row > .column > :contains(2)').height()).toBe 100
|
||||
|
||||
xit "removes empty panes on deserialization", ->
|
||||
# only deserialize pane 1's view successfully
|
||||
TestView.deserialize = ({name}) -> new TestView(name) if name is '1'
|
||||
newContainer = atom.deserializers.deserialize(container.serialize())
|
||||
expect(newContainer.find('.row, .column')).not.toExist()
|
||||
expect(newContainer.find('> :contains(1)')).toExist()
|
||||
|
||||
describe "pane-container:active-pane-item-changed", ->
|
||||
[pane1, item1a, item1b, item2a, item2b, item3a, container, activeItemChangedHandler] = []
|
||||
beforeEach ->
|
||||
item1a = new TestView('1a')
|
||||
item1b = new TestView('1b')
|
||||
item2a = new TestView('2a')
|
||||
item2b = new TestView('2b')
|
||||
item3a = new TestView('3a')
|
||||
# This is a dummy item to prevent panes from being empty on deserialization
|
||||
class Item
|
||||
atom.deserializers.add(this)
|
||||
@deserialize: -> new this
|
||||
serialize: -> deserializer: 'Item'
|
||||
|
||||
pane1A = new Pane(items: [new Item])
|
||||
containerA = new PaneContainer(root: pane1A)
|
||||
pane2A = pane1A.splitRight(items: [new Item])
|
||||
pane3A = pane2A.splitDown(items: [new Item])
|
||||
|
||||
it "preserves the focused pane across serialization", ->
|
||||
expect(pane3A.focused).toBe true
|
||||
|
||||
containerB = containerA.testSerialization()
|
||||
[pane1B, pane2B, pane3B] = containerB.getPanes()
|
||||
expect(pane3B.focused).toBe true
|
||||
|
||||
it "preserves the active pane across serialization, independent of focus", ->
|
||||
pane3A.activate()
|
||||
expect(containerA.activePane).toBe pane3A
|
||||
|
||||
containerB = containerA.testSerialization()
|
||||
[pane1B, pane2B, pane3B] = containerB.getPanes()
|
||||
expect(containerB.activePane).toBe pane3B
|
||||
|
||||
describe "::activePane", ->
|
||||
[container, pane1, pane2] = []
|
||||
|
||||
beforeEach ->
|
||||
container = new PaneContainer
|
||||
container.attachToDom()
|
||||
pane1 = new Pane(item1a)
|
||||
container.setRoot(pane1)
|
||||
pane1 = container.root
|
||||
|
||||
activeItemChangedHandler = jasmine.createSpy("activeItemChangedHandler")
|
||||
container.on 'pane-container:active-pane-item-changed', activeItemChangedHandler
|
||||
it "references the first pane if no pane has been made active", ->
|
||||
expect(container.activePane).toBe pane1
|
||||
expect(pane1.active).toBe true
|
||||
|
||||
describe "when there are no panes", ->
|
||||
it "is triggered when a new pane containing a pane item is added", ->
|
||||
container.setRoot()
|
||||
expect(container.getPanes().length).toBe 0
|
||||
activeItemChangedHandler.reset()
|
||||
it "references the most pane on which ::activate was most recently called", ->
|
||||
pane2 = pane1.splitRight()
|
||||
pane2.activate()
|
||||
expect(container.activePane).toBe pane2
|
||||
expect(pane1.active).toBe false
|
||||
expect(pane2.active).toBe true
|
||||
pane1.activate()
|
||||
expect(container.activePane).toBe pane1
|
||||
expect(pane1.active).toBe true
|
||||
expect(pane2.active).toBe false
|
||||
|
||||
pane = new Pane(item1a)
|
||||
container.setRoot(pane)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
it "is reassigned to the next pane if the current active pane is destroyed", ->
|
||||
pane2 = pane1.splitRight()
|
||||
pane2.activate()
|
||||
pane2.destroy()
|
||||
expect(container.activePane).toBe pane1
|
||||
expect(pane1.active).toBe true
|
||||
|
||||
describe "when there is one pane", ->
|
||||
it "is triggered when a new pane item is added", ->
|
||||
pane1.showItem(item1b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1b
|
||||
|
||||
it "is not triggered when the active pane item is shown again", ->
|
||||
pane1.showItem(item1a)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when switching to an existing pane item", ->
|
||||
pane1.showItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.showItem(item1a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is triggered when the active pane item is removed", ->
|
||||
pane1.showItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.removeItem(item1b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is not triggered when an inactive pane item is removed", ->
|
||||
pane1.showItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.removeItem(item1a)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when all pane items are removed", ->
|
||||
pane1.removeItem(item1a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toBe undefined
|
||||
|
||||
it "is triggered when the pane is removed", ->
|
||||
pane1.remove()
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toBe undefined
|
||||
|
||||
describe "when there are two panes", ->
|
||||
[pane2] = []
|
||||
|
||||
beforeEach ->
|
||||
pane2 = pane1.splitLeft(item2a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
it "is triggered when a new pane item is added to the active pane", ->
|
||||
pane2.showItem(item2b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item2b
|
||||
|
||||
it "is not triggered when a new pane item is added to an inactive pane", ->
|
||||
pane1.showItem(item1b)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane item removed from the active pane", ->
|
||||
pane2.showItem(item2b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane2.removeItem(item2b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item2a
|
||||
|
||||
it "is not triggered when the active pane item removed from an inactive pane", ->
|
||||
pane1.showItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.removeItem(item1b)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane is removed", ->
|
||||
pane2.remove()
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is not triggered when an inactive pane is removed", ->
|
||||
pane1.remove()
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane is changed", ->
|
||||
pane1.makeActive()
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
describe "when there are multiple panes", ->
|
||||
beforeEach ->
|
||||
pane2 = pane1.splitRight(item2a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
it "is triggered when a new pane is added", ->
|
||||
pane2.splitDown(item3a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item3a
|
||||
|
||||
it "is not triggered when the non active pane is removed", ->
|
||||
pane3 = pane2.splitDown(item3a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.remove()
|
||||
pane2.remove()
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
it "does not allow the root pane to be destroyed", ->
|
||||
pane1.destroy()
|
||||
expect(container.root).toBe pane1
|
||||
expect(pane1.isDestroyed()).toBe false
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
PaneContainerView = require '../src/pane-container-view'
|
||||
PaneView = require '../src/pane-view'
|
||||
{_, $, View, $$} = require 'atom'
|
||||
|
||||
describe "PaneContainerView", ->
|
||||
[TestView, container, pane1, pane2, pane3] = []
|
||||
|
||||
beforeEach ->
|
||||
class TestView extends View
|
||||
atom.deserializers.add(this)
|
||||
@deserialize: ({name}) -> new TestView(name)
|
||||
@content: -> @div tabindex: -1
|
||||
initialize: (@name) -> @text(@name)
|
||||
serialize: -> { deserializer: 'TestView', @name }
|
||||
getUri: -> path.join(temp.dir, @name)
|
||||
save: -> @saved = true
|
||||
isEqual: (other) -> @name is other?.name
|
||||
|
||||
container = new PaneContainerView
|
||||
pane1 = container.getRoot()
|
||||
pane1.activateItem(new TestView('1'))
|
||||
pane2 = pane1.splitRight(new TestView('2'))
|
||||
pane3 = pane2.splitDown(new TestView('3'))
|
||||
|
||||
afterEach ->
|
||||
atom.deserializers.remove(TestView)
|
||||
|
||||
describe ".focusNextPane()", ->
|
||||
it "focuses the pane following the focused pane or the first pane if no pane has focus", ->
|
||||
container.attachToDom()
|
||||
container.focusNextPane()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPane()
|
||||
expect(pane2.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPane()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPane()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".focusPreviousPane()", ->
|
||||
it "focuses the pane preceding the focused pane or the last pane if no pane has focus", ->
|
||||
container.attachToDom()
|
||||
container.getPanes()[0].focus() # activate first pane
|
||||
|
||||
container.focusPreviousPane()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPane()
|
||||
expect(pane2.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPane()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPane()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".getActivePane()", ->
|
||||
it "returns the most-recently focused pane", ->
|
||||
focusStealer = $$ -> @div tabindex: -1, "focus stealer"
|
||||
focusStealer.attachToDom()
|
||||
container.attachToDom()
|
||||
|
||||
pane2.focus()
|
||||
expect(container.getFocusedPane()).toBe pane2
|
||||
expect(container.getActivePane()).toBe pane2
|
||||
|
||||
focusStealer.focus()
|
||||
expect(container.getFocusedPane()).toBeUndefined()
|
||||
expect(container.getActivePane()).toBe pane2
|
||||
|
||||
pane3.focus()
|
||||
expect(container.getFocusedPane()).toBe pane3
|
||||
expect(container.getActivePane()).toBe pane3
|
||||
|
||||
describe ".eachPane(callback)", ->
|
||||
it "runs the callback with all current and future panes until the subscription is cancelled", ->
|
||||
panes = []
|
||||
subscription = container.eachPane (pane) -> panes.push(pane)
|
||||
expect(panes).toEqual [pane1, pane2, pane3]
|
||||
|
||||
panes = []
|
||||
pane4 = pane3.splitRight(pane3.copyActiveItem())
|
||||
expect(panes).toEqual [pane4]
|
||||
|
||||
panes = []
|
||||
subscription.off()
|
||||
pane4.splitDown()
|
||||
expect(panes).toEqual []
|
||||
|
||||
describe ".saveAll()", ->
|
||||
it "saves all open pane items", ->
|
||||
pane1.activateItem(new TestView('4'))
|
||||
|
||||
container.saveAll()
|
||||
|
||||
for pane in container.getPanes()
|
||||
for item in pane.getItems()
|
||||
expect(item.saved).toBeTruthy()
|
||||
|
||||
describe ".confirmClose()", ->
|
||||
it "returns true after modified files are saved", ->
|
||||
pane1.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
pane2.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
spyOn(atom, "confirm").andReturn(0)
|
||||
|
||||
saved = container.confirmClose()
|
||||
|
||||
runs ->
|
||||
expect(saved).toBeTruthy()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "returns false if the user cancels saving", ->
|
||||
pane1.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
pane2.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
spyOn(atom, "confirm").andReturn(1)
|
||||
|
||||
saved = container.confirmClose()
|
||||
|
||||
runs ->
|
||||
expect(saved).toBeFalsy()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
describe "serialization", ->
|
||||
it "can be serialized and deserialized, and correctly adjusts dimensions of deserialized panes after attach", ->
|
||||
newContainer = new PaneContainerView(container.model.testSerialization())
|
||||
expect(newContainer.find('.pane-row > :contains(1)')).toExist()
|
||||
expect(newContainer.find('.pane-row > .pane-column > :contains(2)')).toExist()
|
||||
expect(newContainer.find('.pane-row > .pane-column > :contains(3)')).toExist()
|
||||
|
||||
newContainer.height(200).width(300).attachToDom()
|
||||
expect(newContainer.find('.pane-row > :contains(1)').width()).toBe 150
|
||||
expect(newContainer.find('.pane-row > .pane-column > :contains(2)').height()).toBe 100
|
||||
|
||||
describe "if there are empty panes after deserialization", ->
|
||||
beforeEach ->
|
||||
# only deserialize pane 1's view successfully
|
||||
TestView.deserialize = ({name}) -> new TestView(name) if name is '1'
|
||||
|
||||
describe "if the 'core.destroyEmptyPanes' config option is false (the default)", ->
|
||||
it "leaves the empty panes intact", ->
|
||||
newContainer = new PaneContainerView(container.model.testSerialization())
|
||||
expect(newContainer.find('.pane-row > :contains(1)')).toExist()
|
||||
expect(newContainer.find('.pane-row > .pane-column > .pane').length).toBe 2
|
||||
|
||||
describe "if the 'core.destroyEmptyPanes' config option is true", ->
|
||||
it "removes empty panes on deserialization", ->
|
||||
atom.config.set('core.destroyEmptyPanes', true)
|
||||
newContainer = new PaneContainerView(container.model.testSerialization())
|
||||
expect(newContainer.find('.pane-row, .pane-column')).not.toExist()
|
||||
expect(newContainer.find('> :contains(1)')).toExist()
|
||||
|
||||
describe "pane-container:active-pane-item-changed", ->
|
||||
[pane1, item1a, item1b, item2a, item2b, item3a, container, activeItemChangedHandler] = []
|
||||
beforeEach ->
|
||||
item1a = new TestView('1a')
|
||||
item1b = new TestView('1b')
|
||||
item2a = new TestView('2a')
|
||||
item2b = new TestView('2b')
|
||||
item3a = new TestView('3a')
|
||||
|
||||
container = new PaneContainerView
|
||||
pane1 = container.getRoot()
|
||||
pane1.activateItem(item1a)
|
||||
container.attachToDom()
|
||||
|
||||
activeItemChangedHandler = jasmine.createSpy("activeItemChangedHandler")
|
||||
container.on 'pane-container:active-pane-item-changed', activeItemChangedHandler
|
||||
|
||||
describe "when there is one pane", ->
|
||||
it "is triggered when a new pane item is added", ->
|
||||
pane1.activateItem(item1b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1b
|
||||
|
||||
it "is not triggered when the active pane item is shown again", ->
|
||||
pane1.activateItem(item1a)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when switching to an existing pane item", ->
|
||||
pane1.activateItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.activateItem(item1a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is triggered when the active pane item is destroyed", ->
|
||||
pane1.activateItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.destroyItem(item1b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is not triggered when an inactive pane item is destroyed", ->
|
||||
pane1.activateItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.destroyItem(item1a)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when all pane items are destroyed", ->
|
||||
pane1.destroyItem(item1a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toBe undefined
|
||||
|
||||
describe "when there are two panes", ->
|
||||
[pane2] = []
|
||||
|
||||
beforeEach ->
|
||||
pane2 = pane1.splitLeft(item2a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
it "is triggered when a new pane item is added to the active pane", ->
|
||||
pane2.activateItem(item2b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item2b
|
||||
|
||||
it "is not triggered when a new pane item is added to an inactive pane", ->
|
||||
pane1.activateItem(item1b)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane's active item is destroyed", ->
|
||||
pane2.activateItem(item2b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane2.destroyItem(item2b)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item2a
|
||||
|
||||
it "is not triggered when an inactive pane's active item is destroyed", ->
|
||||
pane1.activateItem(item1b)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.destroyItem(item1b)
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane is destroyed", ->
|
||||
pane2.remove()
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
it "is not triggered when an inactive pane is destroyed", ->
|
||||
pane1.remove()
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "is triggered when the active pane is changed", ->
|
||||
pane1.activate()
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a
|
||||
|
||||
describe "when there are multiple panes", ->
|
||||
beforeEach ->
|
||||
pane2 = pane1.splitRight(item2a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
it "is triggered when a new pane is added", ->
|
||||
pane2.splitDown(item3a)
|
||||
expect(activeItemChangedHandler.callCount).toBe 1
|
||||
expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item3a
|
||||
|
||||
it "is not triggered when an inactive pane is destroyed", ->
|
||||
pane3 = pane2.splitDown(item3a)
|
||||
activeItemChangedHandler.reset()
|
||||
|
||||
pane1.remove()
|
||||
pane2.remove()
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
+373
-593
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)')
|
||||
+68
-45
@@ -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()
|
||||
@@ -39,15 +35,15 @@ describe "Project", ->
|
||||
describe "when an edit session is destroyed", ->
|
||||
it "removes edit session and calls destroy on buffer (if buffer is not referenced by other edit sessions)", ->
|
||||
editor = atom.project.openSync("a")
|
||||
anotherEditSession = atom.project.openSync("a")
|
||||
anotherEditor = atom.project.openSync("a")
|
||||
|
||||
expect(atom.project.editors.length).toBe 2
|
||||
expect(editor.buffer).toBe anotherEditSession.buffer
|
||||
expect(editor.buffer).toBe anotherEditor.buffer
|
||||
|
||||
editor.destroy()
|
||||
expect(atom.project.editors.length).toBe 1
|
||||
|
||||
anotherEditSession.destroy()
|
||||
anotherEditor.destroy()
|
||||
expect(atom.project.editors.length).toBe 0
|
||||
|
||||
describe "when an edit session is saved and the project has no path", ->
|
||||
@@ -59,22 +55,6 @@ describe "Project", ->
|
||||
editor.saveAs(tempFile)
|
||||
expect(atom.project.getPath()).toBe path.dirname(tempFile)
|
||||
|
||||
describe "when an edit session is deserialized", ->
|
||||
it "emits an 'editor-created' event and stores the edit session", ->
|
||||
handler = jasmine.createSpy('editorCreatedHandler')
|
||||
atom.project.on 'editor-created', handler
|
||||
|
||||
editor1 = atom.project.openSync("a")
|
||||
expect(handler.callCount).toBe 1
|
||||
expect(atom.project.getEditSessions().length).toBe 1
|
||||
expect(atom.project.getEditSessions()[0]).toBe editor1
|
||||
|
||||
editor2 = atom.deserializers.deserialize(editor1.serialize())
|
||||
expect(handler.callCount).toBe 2
|
||||
expect(atom.project.getEditSessions().length).toBe 2
|
||||
expect(atom.project.getEditSessions()[0]).toBe editor1
|
||||
expect(atom.project.getEditSessions()[1]).toBe editor2
|
||||
|
||||
describe "when an edit session is copied", ->
|
||||
it "emits an 'editor-created' event and stores the edit session", ->
|
||||
handler = jasmine.createSpy('editorCreatedHandler')
|
||||
@@ -82,23 +62,23 @@ describe "Project", ->
|
||||
|
||||
editor1 = atom.project.openSync("a")
|
||||
expect(handler.callCount).toBe 1
|
||||
expect(atom.project.getEditSessions().length).toBe 1
|
||||
expect(atom.project.getEditSessions()[0]).toBe editor1
|
||||
expect(atom.project.getEditors().length).toBe 1
|
||||
expect(atom.project.getEditors()[0]).toBe editor1
|
||||
|
||||
editor2 = editor1.copy()
|
||||
expect(handler.callCount).toBe 2
|
||||
expect(atom.project.getEditSessions().length).toBe 2
|
||||
expect(atom.project.getEditSessions()[0]).toBe editor1
|
||||
expect(atom.project.getEditSessions()[1]).toBe editor2
|
||||
expect(atom.project.getEditors().length).toBe 2
|
||||
expect(atom.project.getEditors()[0]).toBe editor1
|
||||
expect(atom.project.getEditors()[1]).toBe editor2
|
||||
|
||||
describe ".openSync(path)", ->
|
||||
[fooOpener, barOpener, absolutePath, newBufferHandler, newEditSessionHandler] = []
|
||||
[fooOpener, barOpener, absolutePath, newBufferHandler, newEditorHandler] = []
|
||||
beforeEach ->
|
||||
absolutePath = require.resolve('./fixtures/dir/a')
|
||||
newBufferHandler = jasmine.createSpy('newBufferHandler')
|
||||
atom.project.on 'buffer-created', newBufferHandler
|
||||
newEditSessionHandler = jasmine.createSpy('newEditSessionHandler')
|
||||
atom.project.on 'editor-created', newEditSessionHandler
|
||||
newEditorHandler = jasmine.createSpy('newEditorHandler')
|
||||
atom.project.on 'editor-created', newEditorHandler
|
||||
|
||||
fooOpener = (pathToOpen, options) -> { foo: pathToOpen, options } if pathToOpen?.match(/\.foo/)
|
||||
barOpener = (pathToOpen) -> { bar: pathToOpen } if pathToOpen?.match(/^bar:\/\//)
|
||||
@@ -115,14 +95,14 @@ describe "Project", ->
|
||||
editor = atom.project.openSync(absolutePath)
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editor
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when given a relative path that hasn't been opened previously", ->
|
||||
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = atom.project.openSync('a')
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editor
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed the path to a buffer that has already been opened", ->
|
||||
it "returns a new edit session containing previously opened buffer and emits a 'editor-created' event", ->
|
||||
@@ -131,14 +111,14 @@ describe "Project", ->
|
||||
expect(atom.project.openSync(absolutePath).buffer).toBe editor.buffer
|
||||
expect(atom.project.openSync('a').buffer).toBe editor.buffer
|
||||
expect(newBufferHandler).not.toHaveBeenCalled()
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editor
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when not passed a path", ->
|
||||
it "returns a new edit session and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = atom.project.openSync()
|
||||
expect(editor.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer)
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editor
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed a path that matches a custom opener", ->
|
||||
it "returns the resource returned by the custom opener", ->
|
||||
@@ -147,14 +127,14 @@ describe "Project", ->
|
||||
expect(atom.project.openSync("bar://baz")).toEqual { bar: "bar://baz" }
|
||||
|
||||
describe ".open(path)", ->
|
||||
[fooOpener, barOpener, absolutePath, newBufferHandler, newEditSessionHandler] = []
|
||||
[fooOpener, barOpener, absolutePath, newBufferHandler, newEditorHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
absolutePath = require.resolve('./fixtures/dir/a')
|
||||
newBufferHandler = jasmine.createSpy('newBufferHandler')
|
||||
atom.project.on 'buffer-created', newBufferHandler
|
||||
newEditSessionHandler = jasmine.createSpy('newEditSessionHandler')
|
||||
atom.project.on 'editor-created', newEditSessionHandler
|
||||
newEditorHandler = jasmine.createSpy('newEditorHandler')
|
||||
atom.project.on 'editor-created', newEditorHandler
|
||||
|
||||
fooOpener = (pathToOpen, options) -> { foo: pathToOpen, options } if pathToOpen?.match(/\.foo/)
|
||||
barOpener = (pathToOpen) -> { bar: pathToOpen } if pathToOpen?.match(/^bar:\/\//)
|
||||
@@ -175,7 +155,7 @@ describe "Project", ->
|
||||
runs ->
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editor
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when given a relative path that isn't currently opened", ->
|
||||
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'editor-created' events", ->
|
||||
@@ -186,7 +166,7 @@ describe "Project", ->
|
||||
runs ->
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editor
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed the path to a buffer that is currently opened", ->
|
||||
it "returns a new edit session containing currently opened buffer and emits a 'editor-created' event", ->
|
||||
@@ -199,7 +179,7 @@ describe "Project", ->
|
||||
expect(atom.project.openSync(absolutePath).buffer).toBe editor.buffer
|
||||
expect(atom.project.openSync('a').buffer).toBe editor.buffer
|
||||
expect(newBufferHandler).not.toHaveBeenCalled()
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editor
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when not passed a path", ->
|
||||
it "returns a new edit session and emits 'buffer-created' and 'editor-created' events", ->
|
||||
@@ -210,7 +190,7 @@ describe "Project", ->
|
||||
runs ->
|
||||
expect(editor.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer)
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editor
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed a path that matches a custom opener", ->
|
||||
it "returns the resource returned by the custom opener", ->
|
||||
@@ -353,6 +333,19 @@ describe "Project", ->
|
||||
|
||||
expect(editor.isModified()).toBeFalsy()
|
||||
|
||||
it "does not replace when the path is not specified", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor = atom.project.openSync('sample-with-comments.js')
|
||||
|
||||
results = []
|
||||
waitsForPromise ->
|
||||
atom.project.replace /items/gi, 'items', [commentFilePath], (result) ->
|
||||
results.push(result)
|
||||
|
||||
runs ->
|
||||
expect(results).toHaveLength 1
|
||||
expect(results[0].filePath).toBe commentFilePath
|
||||
|
||||
it "does NOT save when modified", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor.buffer.change([[0,0],[0,0]], 'omg')
|
||||
@@ -406,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 = []
|
||||
@@ -532,3 +526,32 @@ describe "Project", ->
|
||||
resultForA = _.find results, ({filePath}) -> path.basename(filePath) == 'a'
|
||||
expect(resultForA.matches).toHaveLength 1
|
||||
expect(resultForA.matches[0].matchText).toBe 'Elephant'
|
||||
|
||||
describe ".eachBuffer(callback)", ->
|
||||
beforeEach ->
|
||||
atom.project.bufferForPathSync('a')
|
||||
|
||||
it "invokes the callback for existing buffer", ->
|
||||
count = 0
|
||||
count = 0
|
||||
callbackBuffer = null
|
||||
callback = (buffer) ->
|
||||
callbackBuffer = buffer
|
||||
count++
|
||||
atom.project.eachBuffer(callback)
|
||||
expect(count).toBe 1
|
||||
expect(callbackBuffer).toBe atom.project.getBuffers()[0]
|
||||
|
||||
it "invokes the callback for new buffers", ->
|
||||
count = 0
|
||||
callbackBuffer = null
|
||||
callback = (buffer) ->
|
||||
callbackBuffer = buffer
|
||||
count++
|
||||
|
||||
atom.project.eachBuffer(callback)
|
||||
count = 0
|
||||
callbackBuffer = null
|
||||
atom.project.bufferForPathSync(require.resolve('./fixtures/sample.txt'))
|
||||
expect(count).toBe 1
|
||||
expect(callbackBuffer).toBe atom.project.getBuffers()[1]
|
||||
|
||||
@@ -1,557 +0,0 @@
|
||||
{$, $$, fs, RootView, View} = require 'atom'
|
||||
Q = require 'q'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
Pane = require '../src/pane'
|
||||
|
||||
describe "RootView", ->
|
||||
pathToOpen = null
|
||||
|
||||
beforeEach ->
|
||||
atom.project.setPath(atom.project.resolve('dir'))
|
||||
pathToOpen = atom.project.resolve('a')
|
||||
atom.rootView = new RootView
|
||||
atom.rootView.enableKeymap()
|
||||
atom.rootView.openSync(pathToOpen)
|
||||
atom.rootView.focus()
|
||||
|
||||
describe "@deserialize()", ->
|
||||
viewState = null
|
||||
|
||||
refreshRootViewAndProject = ->
|
||||
rootViewState = atom.rootView.serialize()
|
||||
atom.project.getState().serializeForPersistence()
|
||||
project2 = atom.replicate().get('project')
|
||||
atom.rootView.remove()
|
||||
atom.project.destroy()
|
||||
atom.project = project2
|
||||
atom.rootView = atom.deserializers.deserialize(rootViewState)
|
||||
atom.rootView.attachToDom()
|
||||
|
||||
describe "when the serialized RootView has an unsaved buffer", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
atom.rootView.attachToDom()
|
||||
atom.rootView.openSync()
|
||||
editor1 = atom.rootView.getActiveView()
|
||||
buffer = editor1.getBuffer()
|
||||
editor1.splitRight()
|
||||
expect(atom.rootView.getActiveView()).toBe atom.rootView.getEditors()[2]
|
||||
|
||||
refreshRootViewAndProject()
|
||||
|
||||
expect(atom.rootView.getEditors().length).toBe 2
|
||||
expect(atom.rootView.getActiveView()).toBe atom.rootView.getEditors()[1]
|
||||
expect(atom.rootView.title).toBe "untitled - #{atom.project.getPath()}"
|
||||
|
||||
describe "when there are open editors", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
atom.rootView.attachToDom()
|
||||
pane1 = atom.rootView.getActivePane()
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitRight()
|
||||
pane4 = pane2.splitDown()
|
||||
pane2.showItem(atom.project.openSync('b'))
|
||||
pane3.showItem(atom.project.openSync('../sample.js'))
|
||||
pane3.activeItem.setCursorScreenPosition([2, 4])
|
||||
pane4.showItem(atom.project.openSync('../sample.txt'))
|
||||
pane4.activeItem.setCursorScreenPosition([0, 2])
|
||||
pane2.focus()
|
||||
|
||||
refreshRootViewAndProject()
|
||||
|
||||
expect(atom.rootView.getEditors().length).toBe 4
|
||||
editor1 = atom.rootView.panes.find('.row > .pane .editor:eq(0)').view()
|
||||
editor3 = atom.rootView.panes.find('.row > .pane .editor:eq(1)').view()
|
||||
editor2 = atom.rootView.panes.find('.row > .column > .pane .editor:eq(0)').view()
|
||||
editor4 = atom.rootView.panes.find('.row > .column > .pane .editor:eq(1)').view()
|
||||
|
||||
expect(editor1.getPath()).toBe atom.project.resolve('a')
|
||||
expect(editor2.getPath()).toBe atom.project.resolve('b')
|
||||
expect(editor3.getPath()).toBe atom.project.resolve('../sample.js')
|
||||
expect(editor3.getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(editor4.getPath()).toBe atom.project.resolve('../sample.txt')
|
||||
expect(editor4.getCursorScreenPosition()).toEqual [0, 2]
|
||||
|
||||
# ensure adjust pane dimensions is called
|
||||
expect(editor1.width()).toBeGreaterThan 0
|
||||
expect(editor2.width()).toBeGreaterThan 0
|
||||
expect(editor3.width()).toBeGreaterThan 0
|
||||
expect(editor4.width()).toBeGreaterThan 0
|
||||
|
||||
# ensure correct editor is focused again
|
||||
expect(editor2.isFocused).toBeTruthy()
|
||||
expect(editor1.isFocused).toBeFalsy()
|
||||
expect(editor3.isFocused).toBeFalsy()
|
||||
expect(editor4.isFocused).toBeFalsy()
|
||||
|
||||
expect(atom.rootView.title).toBe "#{path.basename(editor2.getPath())} - #{atom.project.getPath()}"
|
||||
|
||||
describe "where there are no open editors", ->
|
||||
it "constructs the view with no open editors", ->
|
||||
atom.rootView.getActivePane().remove()
|
||||
expect(atom.rootView.getEditors().length).toBe 0
|
||||
refreshRootViewAndProject()
|
||||
expect(atom.rootView.getEditors().length).toBe 0
|
||||
|
||||
describe "focus", ->
|
||||
beforeEach ->
|
||||
atom.rootView.attachToDom()
|
||||
|
||||
describe "when there is an active view", ->
|
||||
it "hands off focus to the active view", ->
|
||||
editorView = atom.rootView.getActiveView()
|
||||
editorView.isFocused = false
|
||||
atom.rootView.focus()
|
||||
expect(editorView.isFocused).toBeTruthy()
|
||||
|
||||
describe "when there is no active view", ->
|
||||
beforeEach ->
|
||||
atom.rootView.getActivePane().remove()
|
||||
expect(atom.rootView.getActiveView()).toBeUndefined()
|
||||
atom.rootView.attachToDom()
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
describe "when are visible focusable elements (with a -1 tabindex)", ->
|
||||
it "passes focus to the first focusable element", ->
|
||||
focusable1 = $$ -> @div "One", id: 'one', tabindex: -1
|
||||
focusable2 = $$ -> @div "Two", id: 'two', tabindex: -1
|
||||
atom.rootView.horizontal.append(focusable1, focusable2)
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
atom.rootView.focus()
|
||||
expect(document.activeElement).toBe focusable1[0]
|
||||
|
||||
describe "when there are no visible focusable elements", ->
|
||||
it "surrenders focus to the body", ->
|
||||
focusable = $$ -> @div "One", id: 'one', tabindex: -1
|
||||
atom.rootView.horizontal.append(focusable)
|
||||
focusable.hide()
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
atom.rootView.focus()
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
describe "keymap wiring", ->
|
||||
commandHandler = null
|
||||
beforeEach ->
|
||||
commandHandler = jasmine.createSpy('commandHandler')
|
||||
atom.rootView.on('foo-command', commandHandler)
|
||||
|
||||
atom.keymap.bindKeys('name', '*', 'x': 'foo-command')
|
||||
|
||||
describe "when a keydown event is triggered in the RootView", ->
|
||||
it "triggers matching keybindings for that event", ->
|
||||
event = keydownEvent 'x', target: atom.rootView[0]
|
||||
|
||||
atom.rootView.trigger(event)
|
||||
expect(commandHandler).toHaveBeenCalled()
|
||||
|
||||
describe "window title", ->
|
||||
describe "when the project has no path", ->
|
||||
it "sets the title to 'untitled'", ->
|
||||
atom.project.setPath(undefined)
|
||||
expect(atom.rootView.title).toBe 'untitled'
|
||||
|
||||
describe "when the project has a path", ->
|
||||
beforeEach ->
|
||||
atom.rootView.openSync('b')
|
||||
|
||||
describe "when there is an active pane item", ->
|
||||
it "sets the title to the pane item's title plus the project path", ->
|
||||
item = atom.rootView.getActivePaneItem()
|
||||
expect(atom.rootView.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
|
||||
describe "when the title of the active pane item changes", ->
|
||||
it "updates the window title based on the item's new title", ->
|
||||
editor = atom.rootView.getActivePaneItem()
|
||||
editor.buffer.setPath(path.join(temp.dir, 'hi'))
|
||||
expect(atom.rootView.title).toBe "#{editor.getTitle()} - #{atom.project.getPath()}"
|
||||
|
||||
describe "when the active pane's item changes", ->
|
||||
it "updates the title to the new item's title plus the project path", ->
|
||||
atom.rootView.getActivePane().showNextItem()
|
||||
item = atom.rootView.getActivePaneItem()
|
||||
expect(atom.rootView.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
|
||||
describe "when the last pane item is removed", ->
|
||||
it "updates the title to contain the project's path", ->
|
||||
atom.rootView.getActivePane().remove()
|
||||
expect(atom.rootView.getActivePaneItem()).toBeUndefined()
|
||||
expect(atom.rootView.title).toBe atom.project.getPath()
|
||||
|
||||
describe "when an inactive pane's item changes", ->
|
||||
it "does not update the title", ->
|
||||
pane = atom.rootView.getActivePane()
|
||||
pane.splitRight()
|
||||
initialTitle = atom.rootView.title
|
||||
pane.showNextItem()
|
||||
expect(atom.rootView.title).toBe initialTitle
|
||||
|
||||
describe "when the root view is deserialized", ->
|
||||
it "updates the title to contain the project's path", ->
|
||||
rootView2 = atom.deserializers.deserialize(atom.rootView.serialize())
|
||||
item = atom.rootView.getActivePaneItem()
|
||||
expect(rootView2.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
rootView2.remove()
|
||||
|
||||
describe "font size adjustment", ->
|
||||
it "increases/decreases font size when increase/decrease-font-size events are triggered", ->
|
||||
fontSizeBefore = atom.config.get('editor.fontSize')
|
||||
atom.rootView.trigger 'window:increase-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 1
|
||||
atom.rootView.trigger 'window:increase-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 2
|
||||
atom.rootView.trigger 'window:decrease-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 1
|
||||
atom.rootView.trigger 'window:decrease-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore
|
||||
|
||||
it "does not allow the font size to be less than 1", ->
|
||||
atom.config.set("editor.fontSize", 1)
|
||||
atom.rootView.trigger 'window:decrease-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe 1
|
||||
|
||||
describe ".openSync(filePath, options)", ->
|
||||
describe "when there is no active pane", ->
|
||||
beforeEach ->
|
||||
spyOn(Pane.prototype, 'focus')
|
||||
atom.rootView.getActivePane().remove()
|
||||
expect(atom.rootView.getActivePane()).toBeUndefined()
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "creates a empty edit session as an item on a new pane, and focuses the pane", ->
|
||||
editor = atom.rootView.openSync()
|
||||
expect(atom.rootView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(atom.rootView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
it "can create multiple empty edit sessions as an item on a new pane", ->
|
||||
editor = atom.rootView.openSync()
|
||||
editor2 = atom.rootView.openSync()
|
||||
expect(atom.rootView.getActivePane().getItems().length).toBe 2
|
||||
expect(editor).not.toBe editor2
|
||||
|
||||
describe "when called with a path", ->
|
||||
it "creates an edit session for the given path as an item on a new pane, and focuses the pane", ->
|
||||
editor = atom.rootView.openSync('b')
|
||||
expect(atom.rootView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
expect(atom.rootView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the changeFocus option is false", ->
|
||||
it "does not focus the new pane", ->
|
||||
editor = atom.rootView.openSync('b', changeFocus: false)
|
||||
expect(atom.rootView.getActivePane().focus).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the split option is 'right'", ->
|
||||
it "creates a new pane and opens the file in said pane", ->
|
||||
editor = atom.rootView.openSync('b', split: 'right')
|
||||
expect(atom.rootView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
|
||||
describe "when there is an active pane", ->
|
||||
[activePane, initialItemCount] = []
|
||||
beforeEach ->
|
||||
activePane = atom.rootView.getActivePane()
|
||||
spyOn(activePane, 'focus')
|
||||
initialItemCount = activePane.getItems().length
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "opens an edit session with an empty buffer as an item in the active pane and focuses it", ->
|
||||
editor = atom.rootView.openSync()
|
||||
expect(activePane.getItems().length).toBe initialItemCount + 1
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a path", ->
|
||||
describe "when the active pane already has an edit session item for the path being opened", ->
|
||||
it "shows the existing edit session in the pane", ->
|
||||
previousEditSession = activePane.activeItem
|
||||
|
||||
editor = atom.rootView.openSync('b')
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor).not.toBe previousEditSession
|
||||
|
||||
editor = atom.rootView.openSync(previousEditSession.getPath())
|
||||
expect(editor).toBe previousEditSession
|
||||
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.rootView.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.rootView.openSync('b', changeFocus: false)
|
||||
expect(activePane.focus).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the split option is 'right'", ->
|
||||
it "creates a new pane and opens the file in said pane", ->
|
||||
pane1 = atom.rootView.getActivePane()
|
||||
|
||||
editor = atom.rootView.openSync('b', split: 'right')
|
||||
pane2 = atom.rootView.getActivePane()
|
||||
expect(pane2[0]).not.toBe pane1[0]
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
|
||||
expect(atom.rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
editor = atom.rootView.openSync('file1', split: 'right')
|
||||
pane3 = atom.rootView.getActivePane()
|
||||
expect(pane3[0]).toBe pane2[0]
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/file1')
|
||||
|
||||
expect(atom.rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
describe ".openSingletonSync(filePath, options)", ->
|
||||
describe "when there is an active pane", ->
|
||||
[pane1] = []
|
||||
beforeEach ->
|
||||
spyOn(Pane.prototype, 'focus').andCallFake -> @makeActive()
|
||||
pane1 = atom.rootView.getActivePane()
|
||||
|
||||
it "creates a new pane and reuses the file when already open", ->
|
||||
atom.rootView.openSingletonSync('b', split: 'right')
|
||||
pane2 = atom.rootView.getActivePane()
|
||||
expect(pane2[0]).not.toBe pane1[0]
|
||||
expect(pane1.itemForUri('b')).toBeFalsy()
|
||||
expect(pane2.itemForUri('b')).not.toBeFalsy()
|
||||
expect(atom.rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
pane1.focus()
|
||||
expect(atom.rootView.getActivePane()[0]).toBe pane1[0]
|
||||
|
||||
atom.rootView.openSingletonSync('b', split: 'right')
|
||||
pane3 = atom.rootView.getActivePane()
|
||||
expect(pane3[0]).toBe pane2[0]
|
||||
expect(pane1.itemForUri('b')).toBeFalsy()
|
||||
expect(pane2.itemForUri('b')).not.toBeFalsy()
|
||||
expect(atom.rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
it "handles split: left by opening to the left pane when necessary", ->
|
||||
atom.rootView.openSingletonSync('b', split: 'right')
|
||||
pane2 = atom.rootView.getActivePane()
|
||||
expect(pane2[0]).not.toBe pane1[0]
|
||||
|
||||
atom.rootView.openSingletonSync('file1', split: 'left')
|
||||
|
||||
activePane = atom.rootView.getActivePane()
|
||||
expect(activePane[0]).toBe pane1[0]
|
||||
|
||||
expect(pane1.itemForUri('file1')).toBeTruthy()
|
||||
expect(pane2.itemForUri('file1')).toBeFalsy()
|
||||
expect(atom.rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
pane2.focus()
|
||||
expect(atom.rootView.getActivePane()[0]).toBe pane2[0]
|
||||
|
||||
atom.rootView.openSingletonSync('file1', split: 'left')
|
||||
activePane = atom.rootView.getActivePane()
|
||||
expect(activePane[0]).toBe pane1[0]
|
||||
expect(atom.rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
it "reuses the file when already open", ->
|
||||
atom.rootView.openSync('b')
|
||||
atom.rootView.openSingletonSync('b', split: 'right')
|
||||
expect(atom.rootView.panes.find('.pane').toArray()).toEqual [pane1[0]]
|
||||
|
||||
describe ".open(filePath)", ->
|
||||
beforeEach ->
|
||||
spyOn(Pane.prototype, 'focus')
|
||||
|
||||
describe "when there is no active pane", ->
|
||||
beforeEach ->
|
||||
atom.rootView.getActivePane().remove()
|
||||
expect(atom.rootView.getActivePane()).toBeUndefined()
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "creates a empty edit session as an item on a new pane, and focuses the pane", ->
|
||||
editor = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.rootView.open().then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(atom.rootView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(atom.rootView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
it "can create multiple empty edit sessions as items on a pane", ->
|
||||
editor1 = null
|
||||
editor2 = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.rootView.open()
|
||||
.then (o) ->
|
||||
editor1 = o
|
||||
atom.rootView.open()
|
||||
.then (o) ->
|
||||
editor2 = o
|
||||
|
||||
runs ->
|
||||
expect(atom.rootView.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.rootView.open('b').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(atom.rootView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
expect(atom.rootView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
describe "when there is an active pane", ->
|
||||
[activePane] = []
|
||||
|
||||
beforeEach ->
|
||||
activePane = atom.rootView.getActivePane()
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "opens an edit session with an empty buffer as an item in the active pane and focuses it", ->
|
||||
editor = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.rootView.open().then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(activePane.getItems().length).toBe 2
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a path", ->
|
||||
describe "when the active pane already has an item for the given path", ->
|
||||
it "shows the existing edit session in the pane", ->
|
||||
previousEditSession = activePane.activeItem
|
||||
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
atom.rootView.open('b').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor).not.toBe previousEditSession
|
||||
|
||||
waitsForPromise ->
|
||||
atom.rootView.open(previousEditSession.getPath()).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor).toBe previousEditSession
|
||||
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.rootView.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.rootView.height(200)
|
||||
atom.rootView.attachToDom()
|
||||
rightEditor = atom.rootView.getActiveView()
|
||||
rightEditor.setText(" \t ")
|
||||
leftEditor = rightEditor.splitLeft()
|
||||
expect(rightEditor.find(".line:first").text()).toBe " "
|
||||
expect(leftEditor.find(".line:first").text()).toBe " "
|
||||
|
||||
withInvisiblesShowing = "#{rightEditor.invisibles.space}#{rightEditor.invisibles.tab} #{rightEditor.invisibles.space}#{rightEditor.invisibles.eol}"
|
||||
|
||||
atom.rootView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
expect(leftEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
lowerLeftEditor = leftEditor.splitDown()
|
||||
expect(lowerLeftEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
atom.rootView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditor.find(".line:first").text()).toBe " "
|
||||
expect(leftEditor.find(".line:first").text()).toBe " "
|
||||
|
||||
lowerRightEditor = rightEditor.splitDown()
|
||||
expect(lowerRightEditor.find(".line:first").text()).toBe " "
|
||||
|
||||
describe ".eachEditor(callback)", ->
|
||||
beforeEach ->
|
||||
atom.rootView.attachToDom()
|
||||
|
||||
it "invokes the callback for existing editor", ->
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
callback = (editor) ->
|
||||
callbackEditor = editor
|
||||
count++
|
||||
atom.rootView.eachEditor(callback)
|
||||
expect(count).toBe 1
|
||||
expect(callbackEditor).toBe atom.rootView.getActiveView()
|
||||
|
||||
it "invokes the callback for new editor", ->
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
callback = (editor) ->
|
||||
callbackEditor = editor
|
||||
count++
|
||||
|
||||
atom.rootView.eachEditor(callback)
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
atom.rootView.getActiveView().splitRight()
|
||||
expect(count).toBe 1
|
||||
expect(callbackEditor).toBe atom.rootView.getActiveView()
|
||||
|
||||
it "returns a subscription that can be disabled", ->
|
||||
count = 0
|
||||
callback = (editor) -> count++
|
||||
|
||||
subscription = atom.rootView.eachEditor(callback)
|
||||
expect(count).toBe 1
|
||||
atom.rootView.getActiveView().splitRight()
|
||||
expect(count).toBe 2
|
||||
subscription.off()
|
||||
atom.rootView.getActiveView().splitRight()
|
||||
expect(count).toBe 2
|
||||
|
||||
describe ".eachBuffer(callback)", ->
|
||||
beforeEach ->
|
||||
atom.rootView.attachToDom()
|
||||
|
||||
it "invokes the callback for existing buffer", ->
|
||||
count = 0
|
||||
count = 0
|
||||
callbackBuffer = null
|
||||
callback = (buffer) ->
|
||||
callbackBuffer = buffer
|
||||
count++
|
||||
atom.rootView.eachBuffer(callback)
|
||||
expect(count).toBe 1
|
||||
expect(callbackBuffer).toBe atom.rootView.getActiveView().getBuffer()
|
||||
|
||||
it "invokes the callback for new buffer", ->
|
||||
count = 0
|
||||
callbackBuffer = null
|
||||
callback = (buffer) ->
|
||||
callbackBuffer = buffer
|
||||
count++
|
||||
|
||||
atom.rootView.eachBuffer(callback)
|
||||
count = 0
|
||||
callbackBuffer = null
|
||||
atom.rootView.openSync(require.resolve('./fixtures/sample.txt'))
|
||||
expect(count).toBe 1
|
||||
expect(callbackBuffer).toBe atom.rootView.getActiveView().getBuffer()
|
||||
@@ -33,7 +33,7 @@ describe "SelectList", ->
|
||||
selectList.attachToDom()
|
||||
|
||||
it "filters the elements in the list based on the scoreElement function and selects the first item", ->
|
||||
miniEditor.insertText('la')
|
||||
miniEditor.getEditor().insertText('la')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 2
|
||||
@@ -43,13 +43,13 @@ describe "SelectList", ->
|
||||
expect(selectList.error).not.toBeVisible()
|
||||
|
||||
it "displays an error if there are no matches, removes error when there are matches", ->
|
||||
miniEditor.insertText('nothing will match this')
|
||||
miniEditor.getEditor().insertText('nothing will match this')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 0
|
||||
expect(selectList.error).not.toBeHidden()
|
||||
|
||||
miniEditor.setText('la')
|
||||
miniEditor.getEditor().setText('la')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 2
|
||||
@@ -58,7 +58,7 @@ describe "SelectList", ->
|
||||
it "displays no elements until the array has been set on the list", ->
|
||||
selectList.array = null
|
||||
selectList.list.empty()
|
||||
miniEditor.insertText('la')
|
||||
miniEditor.getEditor().insertText('la')
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li').length).toBe 0
|
||||
@@ -124,7 +124,7 @@ describe "SelectList", ->
|
||||
selectList.attachToDom()
|
||||
|
||||
it "does not trigger the confirmed hook", ->
|
||||
miniEditor.insertText("i will never match anything")
|
||||
miniEditor.getEditor().insertText("i will never match anything")
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li')).not.toExist()
|
||||
@@ -132,7 +132,7 @@ describe "SelectList", ->
|
||||
expect(selectList.confirmed).not.toHaveBeenCalled()
|
||||
|
||||
it "does trigger the cancelled hook", ->
|
||||
miniEditor.insertText("i will never match anything")
|
||||
miniEditor.getEditor().insertText("i will never match anything")
|
||||
window.advanceClock(selectList.inputThrottle)
|
||||
|
||||
expect(list.find('li')).not.toExist()
|
||||
|
||||
@@ -53,15 +53,15 @@ describe "SpacePen extensions", ->
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
|
||||
describe "tooltips", ->
|
||||
describe "replaceModifiers", ->
|
||||
replaceModifiers = $.fn.setTooltip.replaceModifiers
|
||||
describe "humanizeKeystrokes", ->
|
||||
humanizeKeystrokes = $.fn.setTooltip.humanizeKeystrokes
|
||||
|
||||
it "replaces single keystroke", ->
|
||||
expect(replaceModifiers('cmd-O')).toEqual '⌘⇧O'
|
||||
expect(replaceModifiers('cmd-shift-up')).toEqual '⌘⇧↑'
|
||||
expect(replaceModifiers('cmd-option-down')).toEqual '⌘⌥↓'
|
||||
expect(replaceModifiers('cmd-option-left')).toEqual '⌘⌥←'
|
||||
expect(replaceModifiers('cmd-option-right')).toEqual '⌘⌥→'
|
||||
expect(humanizeKeystrokes('cmd-O')).toEqual '⌘⇧O'
|
||||
expect(humanizeKeystrokes('cmd-shift-up')).toEqual '⌘⇧↑'
|
||||
expect(humanizeKeystrokes('cmd-option-down')).toEqual '⌘⌥↓'
|
||||
expect(humanizeKeystrokes('cmd-option-left')).toEqual '⌘⌥←'
|
||||
expect(humanizeKeystrokes('cmd-option-right')).toEqual '⌘⌥→'
|
||||
|
||||
it "replaces multiple keystroke", ->
|
||||
expect(replaceModifiers('cmd-o ctrl-2')).toEqual '⌘O ⌃2'
|
||||
expect(humanizeKeystrokes('cmd-o ctrl-2')).toEqual '⌘O ⌃2'
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
# Start the crash reporter before anything else.
|
||||
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')
|
||||
|
||||
+23
-19
@@ -1,22 +1,20 @@
|
||||
require '../src/window'
|
||||
atom.setUpEnvironment('spec')
|
||||
atom.restoreDimensions()
|
||||
atom.initialize()
|
||||
atom.restoreWindowDimensions()
|
||||
|
||||
require '../vendor/jasmine-jquery'
|
||||
path = require 'path'
|
||||
{_, $, File, RootView, fs} = require 'atom'
|
||||
{_, $, File, WorkspaceView, fs} = require 'atom'
|
||||
Keymap = require '../src/keymap'
|
||||
Config = require '../src/config'
|
||||
{Point} = require 'telepath'
|
||||
{Point} = require 'text-buffer'
|
||||
Project = require '../src/project'
|
||||
Editor = require '../src/editor'
|
||||
EditorView = require '../src/editor-view'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
pathwatcher = require 'pathwatcher'
|
||||
platform = require './spec-helper-platform'
|
||||
clipboard = require 'clipboard'
|
||||
|
||||
platform.generateEvilFiles()
|
||||
|
||||
atom.themes.loadBaseStylesheets()
|
||||
atom.themes.requireStylesheet '../static/jasmine'
|
||||
|
||||
@@ -27,9 +25,8 @@ keyBindingsToRestore = atom.keymap.getKeyBindings()
|
||||
|
||||
$(window).on 'core:close', -> window.close()
|
||||
$(window).on 'unload', ->
|
||||
atom.windowMode = 'spec'
|
||||
atom.getWindowState().set('dimensions', atom.getDimensions())
|
||||
atom.saveWindowState()
|
||||
atom.storeWindowDimensions()
|
||||
atom.saveSync()
|
||||
$('html,body').css('overflow', 'auto')
|
||||
|
||||
jasmine.getEnv().addEqualityTester(_.isEqual) # Use underscore's definition of equality for toEqual assertions
|
||||
@@ -50,12 +47,15 @@ if specDirectory
|
||||
beforeEach ->
|
||||
$.fx.off = true
|
||||
projectPath = specProjectPath ? path.join(@specDirectory, 'fixtures')
|
||||
atom.project = atom.getWindowState().set('project', new Project(path: projectPath))
|
||||
atom.project = new Project(path: projectPath)
|
||||
atom.keymap.keyBindings = _.clone(keyBindingsToRestore)
|
||||
|
||||
window.resetTimeouts()
|
||||
atom.packages.packageStates = {}
|
||||
spyOn(atom, 'saveWindowState')
|
||||
|
||||
serializedWindowState = null
|
||||
|
||||
spyOn(atom, 'saveSync')
|
||||
atom.syntax.clearGrammarOverrides()
|
||||
atom.syntax.clearProperties()
|
||||
|
||||
@@ -73,7 +73,7 @@ beforeEach ->
|
||||
config = new Config({resourcePath, configDirPath: atom.getConfigDirPath()})
|
||||
spyOn(config, 'load')
|
||||
spyOn(config, 'save')
|
||||
config.setDefaults('core', RootView.configDefaults)
|
||||
config.setDefaults('core', WorkspaceView.configDefaults)
|
||||
config.setDefaults('editor', EditorView.configDefaults)
|
||||
config.set "editor.fontFamily", "Courier"
|
||||
config.set "editor.fontSize", 16
|
||||
@@ -85,10 +85,11 @@ beforeEach ->
|
||||
|
||||
# make editor display updates synchronous
|
||||
spyOn(EditorView.prototype, 'requestDisplayUpdate').andCallFake -> @updateDisplay()
|
||||
spyOn(RootView.prototype, 'setTitle').andCallFake (@title) ->
|
||||
spyOn(WorkspaceView.prototype, 'setTitle').andCallFake (@title) ->
|
||||
spyOn(window, "setTimeout").andCallFake window.fakeSetTimeout
|
||||
spyOn(window, "clearTimeout").andCallFake window.fakeClearTimeout
|
||||
spyOn(File.prototype, "detectResurrectionAfterDelay").andCallFake -> @detectResurrection()
|
||||
spyOn(Editor.prototype, "shouldPromptToSave").andReturn false
|
||||
|
||||
# make tokenization synchronous
|
||||
TokenizedBuffer.prototype.chunkSize = Infinity
|
||||
@@ -104,15 +105,18 @@ afterEach ->
|
||||
atom.packages.deactivatePackages()
|
||||
atom.menu.template = []
|
||||
|
||||
atom.rootView?.remove?()
|
||||
atom.rootView = null
|
||||
atom.workspaceView?.remove?()
|
||||
atom.workspaceView = null
|
||||
delete atom.state.workspace
|
||||
|
||||
atom.project?.destroy?()
|
||||
atom.project = null
|
||||
|
||||
delete atom.state.packageStates
|
||||
|
||||
$('#jasmine-content').empty() unless window.debugContent
|
||||
delete atom.windowState
|
||||
jasmine.unspy(atom, 'saveWindowState')
|
||||
|
||||
jasmine.unspy(atom, 'saveSync')
|
||||
ensureNoPathSubscriptions()
|
||||
atom.syntax.off()
|
||||
waits(0) # yield to ui thread to make screen update more frequently
|
||||
@@ -131,7 +135,7 @@ jasmine.StringPrettyPrinter.prototype.emitObject = (obj) ->
|
||||
emitObject.call(this, obj)
|
||||
|
||||
jasmine.unspy = (object, methodName) ->
|
||||
throw new Error("Not a spy") unless object[methodName].originalValue?
|
||||
throw new Error("Not a spy") unless object[methodName].hasOwnProperty('originalValue')
|
||||
object[methodName] = object[methodName].originalValue
|
||||
|
||||
addCustomMatchers = (spec) ->
|
||||
|
||||
+12
-10
@@ -1,7 +1,6 @@
|
||||
{fs} = require 'atom'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
TextMateGrammar = require '../src/text-mate-grammar'
|
||||
|
||||
describe "the `syntax` global", ->
|
||||
beforeEach ->
|
||||
@@ -62,20 +61,23 @@ describe "the `syntax` global", ->
|
||||
|
||||
describe "when multiple grammars have matching fileTypes", ->
|
||||
it "selects the grammar with the longest fileType match", ->
|
||||
grammar1 = new TextMateGrammar
|
||||
grammarPath1 = temp.path(suffix: '.json')
|
||||
fs.writeFileSync grammarPath1, JSON.stringify(
|
||||
name: 'test1'
|
||||
scopeName: 'source1'
|
||||
fileTypes: ['test', 'more.test']
|
||||
fileTypes: ['test']
|
||||
)
|
||||
grammar1 = atom.syntax.loadGrammarSync(grammarPath1)
|
||||
expect(atom.syntax.selectGrammar('more.test', '')).toBe grammar1
|
||||
|
||||
grammar2 = new TextMateGrammar
|
||||
grammarPath2 = temp.path(suffix: '.json')
|
||||
fs.writeFileSync grammarPath2, JSON.stringify(
|
||||
name: 'test2'
|
||||
scopeName: 'source2'
|
||||
fileTypes: ['test']
|
||||
|
||||
atom.syntax.addGrammar(grammar1)
|
||||
atom.syntax.addGrammar(grammar2)
|
||||
|
||||
expect(atom.syntax.selectGrammar('more.test', '')).toBe grammar1
|
||||
fileTypes: ['test', 'more.test']
|
||||
)
|
||||
grammar2 = atom.syntax.loadGrammarSync(grammarPath2)
|
||||
expect(atom.syntax.selectGrammar('more.test', '')).toBe grammar2
|
||||
|
||||
describe "when there is no file path", ->
|
||||
it "does not throw an exception (regression)", ->
|
||||
|
||||
+109
-46
@@ -1,7 +1,6 @@
|
||||
{_, fs} = require 'atom'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
{Site} = require 'telepath'
|
||||
TextBuffer = require '../src/text-buffer'
|
||||
|
||||
describe 'TextBuffer', ->
|
||||
@@ -34,11 +33,11 @@ describe 'TextBuffer', ->
|
||||
expect(buffer.getText()).toBe fs.readFileSync(filePath, 'utf8')
|
||||
|
||||
describe "when no file exists for the path", ->
|
||||
it "is modified and is initially empty", ->
|
||||
it "is not modified and is initially empty", ->
|
||||
filePath = "does-not-exist.txt"
|
||||
expect(fs.existsSync(filePath)).toBeFalsy()
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
expect(buffer.isModified()).toBeTruthy()
|
||||
expect(buffer.isModified()).not.toBeTruthy()
|
||||
expect(buffer.getText()).toBe ''
|
||||
|
||||
describe "when no path is given", ->
|
||||
@@ -113,10 +112,17 @@ describe 'TextBuffer', ->
|
||||
|
||||
runs ->
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
expect(event.oldRange).toEqual [[0, 0], [0, 5]]
|
||||
expect(event.oldRange).toEqual [[0, 0], [0, 0]]
|
||||
expect(event.newRange).toEqual [[0, 0], [0, 6]]
|
||||
expect(event.oldText).toBe "first"
|
||||
expect(event.oldText).toBe ""
|
||||
expect(event.newText).toBe "second"
|
||||
|
||||
[event] = changeHandler.argsForCall[1]
|
||||
expect(event.oldRange).toEqual [[0, 6], [0, 11]]
|
||||
expect(event.newRange).toEqual [[0, 6], [0, 6]]
|
||||
expect(event.oldText).toBe "first"
|
||||
expect(event.newText).toBe ""
|
||||
|
||||
expect(buffer.isModified()).toBeFalsy()
|
||||
|
||||
describe "when the buffer's memory contents differ from the *previous* disk contents", ->
|
||||
@@ -160,20 +166,38 @@ describe 'TextBuffer', ->
|
||||
filePath = bufferToDelete.getPath() # symlinks may have been converted
|
||||
|
||||
expect(bufferToDelete.getPath()).toBe filePath
|
||||
expect(bufferToDelete.isModified()).toBeFalsy()
|
||||
|
||||
removeHandler = jasmine.createSpy('removeHandler')
|
||||
bufferToDelete.file.on 'removed', removeHandler
|
||||
fs.removeSync(filePath)
|
||||
waitsFor "file to be removed", ->
|
||||
removeHandler.callCount > 0
|
||||
|
||||
afterEach ->
|
||||
bufferToDelete.destroy()
|
||||
|
||||
it "retains its path and reports the buffer as modified", ->
|
||||
expect(bufferToDelete.getPath()).toBe filePath
|
||||
expect(bufferToDelete.isModified()).toBeTruthy()
|
||||
describe "when the file is modified", ->
|
||||
beforeEach ->
|
||||
bufferToDelete.setText("I WAS MODIFIED")
|
||||
expect(bufferToDelete.isModified()).toBeTruthy()
|
||||
|
||||
removeHandler = jasmine.createSpy('removeHandler')
|
||||
bufferToDelete.file.on 'removed', removeHandler
|
||||
fs.removeSync(filePath)
|
||||
waitsFor "file to be removed", ->
|
||||
removeHandler.callCount > 0
|
||||
|
||||
it "retains its path and reports the buffer as modified", ->
|
||||
expect(bufferToDelete.getPath()).toBe filePath
|
||||
expect(bufferToDelete.isModified()).toBeTruthy()
|
||||
|
||||
describe "when the file is not modified", ->
|
||||
beforeEach ->
|
||||
expect(bufferToDelete.isModified()).toBeFalsy()
|
||||
|
||||
removeHandler = jasmine.createSpy('removeHandler')
|
||||
bufferToDelete.file.on 'removed', removeHandler
|
||||
fs.removeSync(filePath)
|
||||
waitsFor "file to be removed", ->
|
||||
removeHandler.callCount > 0
|
||||
|
||||
it "retains its path and reports the buffer as not modified", ->
|
||||
expect(bufferToDelete.getPath()).toBe filePath
|
||||
expect(bufferToDelete.isModified()).toBeFalsy()
|
||||
|
||||
it "resumes watching of the file when it is re-saved", ->
|
||||
bufferToDelete.save()
|
||||
@@ -210,19 +234,6 @@ describe 'TextBuffer', ->
|
||||
advanceClock(buffer.stoppedChangingDelay)
|
||||
expect(modifiedHandler).toHaveBeenCalledWith(false)
|
||||
|
||||
it "reports the modified status changing to true after the underlying file is deleted", ->
|
||||
buffer.release()
|
||||
filePath = path.join(temp.dir, 'atom-tmp-file')
|
||||
fs.writeFileSync(filePath, 'delete me')
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
modifiedHandler = jasmine.createSpy("modifiedHandler")
|
||||
buffer.on 'modified-status-changed', modifiedHandler
|
||||
|
||||
fs.removeSync(filePath)
|
||||
|
||||
waitsFor "modified status to change", -> modifiedHandler.callCount
|
||||
runs -> expect(buffer.isModified()).toBe true
|
||||
|
||||
it "reports the modified status changing to false after a modified buffer is saved", ->
|
||||
filePath = path.join(temp.dir, 'atom-tmp-file')
|
||||
fs.writeFileSync(filePath, '')
|
||||
@@ -454,6 +465,68 @@ describe 'TextBuffer', ->
|
||||
expect(event.oldRange).toEqual expectedPreRange
|
||||
expect(event.newRange).toEqual [[0, 0], [1, 14]]
|
||||
|
||||
describe ".setTextViaDiff(text)", ->
|
||||
it "can change the entire contents of the buffer when there are no newlines", ->
|
||||
buffer.setText('BUFFER CHANGE')
|
||||
newText = 'DISK CHANGE'
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
describe "with standard newlines", ->
|
||||
it "can change the entire contents of the buffer with no newline at the end", ->
|
||||
newText = "I know you are.\nBut what am I?"
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
it "can change the entire contents of the buffer with a newline at the end", ->
|
||||
newText = "I know you are.\nBut what am I?\n"
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
it "can change a few lines at the beginning in the buffer", ->
|
||||
newText = buffer.getText().replace(/function/g, 'omgwow')
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
it "can change a few lines in the middle of the buffer", ->
|
||||
newText = buffer.getText().replace(/shift/g, 'omgwow')
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
it "can adds a newline at the end", ->
|
||||
newText = buffer.getText() + '\n'
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
describe "with windows newlines", ->
|
||||
beforeEach ->
|
||||
buffer.setText(buffer.getText().replace(/\n/g, '\r\n'))
|
||||
|
||||
it "adds a newline at the end", ->
|
||||
newText = buffer.getText() + '\r\n'
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
it "changes the entire contents of the buffer with smaller content with no newline at the end", ->
|
||||
newText = "I know you are.\r\nBut what am I?"
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
it "changes the entire contents of the buffer with smaller content with newline at the end", ->
|
||||
newText = "I know you are.\r\nBut what am I?\r\n"
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
it "changes a few lines at the beginning in the buffer", ->
|
||||
newText = buffer.getText().replace(/function/g, 'omgwow')
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
it "changes a few lines in the middle of the buffer", ->
|
||||
newText = buffer.getText().replace(/shift/g, 'omgwow')
|
||||
buffer.setTextViaDiff(newText)
|
||||
expect(buffer.getText()).toBe newText
|
||||
|
||||
describe ".save()", ->
|
||||
saveBuffer = null
|
||||
|
||||
@@ -897,25 +970,22 @@ describe 'TextBuffer', ->
|
||||
expect(buffer.getText()).toBe "\ninitialtexthello\n1\n2\n"
|
||||
|
||||
describe "serialization", ->
|
||||
[buffer2, project2] = []
|
||||
buffer2 = null
|
||||
|
||||
beforeEach ->
|
||||
buffer.destroy()
|
||||
|
||||
filePath = temp.openSync('atom').path
|
||||
fs.writeFileSync(filePath, "words")
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
buffer = atom.project.bufferForPathSync(filePath).retain()
|
||||
|
||||
afterEach ->
|
||||
buffer2?.release()
|
||||
project2?.destroy()
|
||||
buffer2?.destroy()
|
||||
|
||||
describe "when the serialized buffer had no unsaved changes", ->
|
||||
it "loads the current contents of the file at the serialized path", ->
|
||||
expect(buffer.isModified()).toBeFalsy()
|
||||
|
||||
project2 = atom.replicate().get('project')
|
||||
buffer2 = project2.getBuffers()[0]
|
||||
buffer2 = buffer.testSerialization()
|
||||
|
||||
waitsForPromise ->
|
||||
buffer2.load()
|
||||
@@ -931,8 +1001,7 @@ describe 'TextBuffer', ->
|
||||
buffer.setText("BUFFER CHANGE")
|
||||
fs.writeFileSync(filePath, "DISK CHANGE")
|
||||
|
||||
project2 = atom.replicate().get('project')
|
||||
buffer2 = project2.getBuffers()[0]
|
||||
buffer2 = buffer.testSerialization()
|
||||
|
||||
waitsFor ->
|
||||
buffer2.cachedDiskContents
|
||||
@@ -948,9 +1017,7 @@ describe 'TextBuffer', ->
|
||||
buffer.setText("abc")
|
||||
buffer.retain()
|
||||
|
||||
buffer.getState().serializeForPersistence()
|
||||
project2 = atom.replicate().get('project')
|
||||
buffer2 = project2.getBuffers()[0]
|
||||
buffer2 = buffer.testSerialization()
|
||||
|
||||
waitsForPromise ->
|
||||
buffer2.load()
|
||||
@@ -964,15 +1031,11 @@ describe 'TextBuffer', ->
|
||||
|
||||
describe "when the serialized buffer was unsaved and had no path", ->
|
||||
it "restores the previous unsaved state of the buffer", ->
|
||||
buffer.release()
|
||||
buffer.destroy()
|
||||
|
||||
buffer = atom.project.bufferForPathSync()
|
||||
buffer.setText("abc")
|
||||
|
||||
state = buffer.getState().clone()
|
||||
expect(state.get('path')).toBeUndefined()
|
||||
expect(state.getObject('text')).toBe 'abc'
|
||||
|
||||
buffer2 = atom.project.addBuffer(new TextBuffer(state))
|
||||
buffer2 = buffer.testSerialization()
|
||||
expect(buffer2.getPath()).toBeUndefined()
|
||||
expect(buffer2.getText()).toBe("abc")
|
||||
|
||||
@@ -1,696 +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)", ->
|
||||
beforeEach ->
|
||||
grammar = atom.syntax.selectGrammar('page.erb')
|
||||
lines = grammar.tokenizeLines '<% page_title "My Page" %>'
|
||||
|
||||
it "correctly parses strings inside tags", ->
|
||||
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"]
|
||||
|
||||
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,5 @@
|
||||
path = require 'path'
|
||||
{$, $$, fs, RootView} = require 'atom'
|
||||
{$, $$, fs, WorkspaceView} = require 'atom'
|
||||
|
||||
ThemeManager = require '../src/theme-manager'
|
||||
AtomPackage = require '../src/atom-package'
|
||||
@@ -32,14 +32,14 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "getImportPaths()", ->
|
||||
it "returns the theme directories before the themes are loaded", ->
|
||||
atom.config.set('core.themes', ['atom-dark-syntax', 'atom-dark-ui', 'atom-light-ui'])
|
||||
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui', 'atom-light-ui'])
|
||||
|
||||
paths = themeManager.getImportPaths()
|
||||
|
||||
# syntax theme is not a dir at this time, so only two.
|
||||
expect(paths.length).toBe 2
|
||||
expect(paths[0]).toContain 'atom-dark-ui'
|
||||
expect(paths[1]).toContain 'atom-light-ui'
|
||||
expect(paths[0]).toContain 'atom-light-ui'
|
||||
expect(paths[1]).toContain 'atom-dark-ui'
|
||||
|
||||
it "ignores themes that cannot be resolved to a directory", ->
|
||||
atom.config.set('core.themes', ['definitely-not-a-theme'])
|
||||
@@ -67,8 +67,8 @@ describe "ThemeManager", ->
|
||||
atom.config.set('core.themes', [])
|
||||
expect($('style.theme').length).toBe 0
|
||||
|
||||
# atom-dark-ui has an directory path, the syntax ones dont.
|
||||
atom.config.set('core.themes', ['atom-light-syntax', 'atom-dark-ui', 'atom-dark-syntax'])
|
||||
# atom-dark-ui has an directory path, the syntax one doesn't
|
||||
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui'])
|
||||
importPaths = themeManager.getImportPaths()
|
||||
expect(importPaths.length).toBe 1
|
||||
expect(importPaths[0]).toContain 'atom-dark-ui'
|
||||
@@ -140,16 +140,16 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "base stylesheet loading", ->
|
||||
beforeEach ->
|
||||
atom.rootView = new RootView
|
||||
atom.rootView.append $$ -> @div class: 'editor'
|
||||
atom.rootView.attachToDom()
|
||||
atom.workspaceView = new WorkspaceView
|
||||
atom.workspaceView.append $$ -> @div class: 'editor'
|
||||
atom.workspaceView.attachToDom()
|
||||
themeManager.activateThemes()
|
||||
|
||||
it "loads the correct values from the theme's ui-variables file", ->
|
||||
atom.config.set('core.themes', ['theme-with-ui-variables'])
|
||||
|
||||
# an override loaded in the base css
|
||||
expect(atom.rootView.css("background-color")).toBe "rgb(0, 0, 255)"
|
||||
expect(atom.workspaceView.css("background-color")).toBe "rgb(0, 0, 255)"
|
||||
|
||||
# from within the theme itself
|
||||
expect($(".editor").css("padding-top")).toBe "150px"
|
||||
|
||||
@@ -18,14 +18,6 @@ describe "TokenizedBuffer", ->
|
||||
advanceClock() while tokenizedBuffer.firstInvalidRow()?
|
||||
changeHandler?.reset()
|
||||
|
||||
describe "@deserialize(state)", ->
|
||||
it "constructs a tokenized buffer with the same buffer and tabLength setting", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer1 = new TokenizedBuffer(buffer: buffer, tabLength: 4)
|
||||
tokenizedBuffer2 = atom.deserializers.deserialize(tokenizedBuffer1.serialize())
|
||||
expect(tokenizedBuffer2.buffer).toBe tokenizedBuffer1.buffer
|
||||
expect(tokenizedBuffer2.getTabLength()).toBe tokenizedBuffer1.getTabLength()
|
||||
|
||||
describe "when the buffer is destroyed", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
@@ -434,3 +426,13 @@ describe "TokenizedBuffer", ->
|
||||
describe "when the selector matches a run of multiple tokens at the position", ->
|
||||
it "returns the range covered by all contigous tokens (within a single line)", ->
|
||||
expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.function', [1, 18])).toEqual [[1, 6], [1, 28]]
|
||||
|
||||
describe "when the editor.tabLength config value changes", ->
|
||||
it "updates the tab length of the tokenized lines", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
buffer.setText('\ttest')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenForPosition([0,0]).value).toBe ' '
|
||||
atom.config.set('editor.tabLength', 6)
|
||||
expect(tokenizedBuffer.tokenForPosition([0,0]).value).toBe ' '
|
||||
|
||||
+21
-16
@@ -1,5 +1,6 @@
|
||||
{$, $$, fs} = require 'atom'
|
||||
path = require 'path'
|
||||
Editor = require '../src/editor'
|
||||
WindowEventHandler = require '../src/window-event-handler'
|
||||
|
||||
describe "Window", ->
|
||||
@@ -7,8 +8,11 @@ describe "Window", ->
|
||||
|
||||
beforeEach ->
|
||||
spyOn(atom, 'hide')
|
||||
atom.getLoadSettings() # Causes atom.loadSettings to be initialized
|
||||
atom.loadSettings.initialPath = atom.project.getPath()
|
||||
initialPath = atom.project.getPath()
|
||||
spyOn(atom, 'getLoadSettings').andCallFake ->
|
||||
loadSettings = atom.getLoadSettings.originalValue.call(atom)
|
||||
loadSettings.initialPath = initialPath
|
||||
loadSettings
|
||||
atom.project.destroy()
|
||||
windowEventHandler = new WindowEventHandler()
|
||||
atom.deserializeEditorWindow()
|
||||
@@ -55,49 +59,50 @@ describe "Window", ->
|
||||
[beforeUnloadEvent] = []
|
||||
|
||||
beforeEach ->
|
||||
jasmine.unspy(Editor.prototype, "shouldPromptToSave")
|
||||
beforeUnloadEvent = $.Event(new Event('beforeunload'))
|
||||
|
||||
describe "when pane items are are modified", ->
|
||||
it "prompts user to save and and calls rootView.confirmClose", ->
|
||||
spyOn(atom.rootView, 'confirmClose').andCallThrough()
|
||||
it "prompts user to save and and calls workspaceView.confirmClose", ->
|
||||
spyOn(atom.workspaceView, 'confirmClose').andCallThrough()
|
||||
spyOn(atom, "confirm").andReturn(2)
|
||||
editor = atom.rootView.openSync("sample.js")
|
||||
editor = atom.workspaceView.openSync("sample.js")
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.rootView.confirmClose).toHaveBeenCalled()
|
||||
expect(atom.workspaceView.confirmClose).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "prompts user to save and handler returns true if don't save", ->
|
||||
spyOn(atom, "confirm").andReturn(2)
|
||||
editor = atom.rootView.openSync("sample.js")
|
||||
editor = atom.workspaceView.openSync("sample.js")
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "prompts user to save and handler returns false if dialog is canceled", ->
|
||||
spyOn(atom, "confirm").andReturn(1)
|
||||
editor = atom.rootView.openSync("sample.js")
|
||||
editor = atom.workspaceView.openSync("sample.js")
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
describe ".unloadEditorWindow()", ->
|
||||
it "saves the serialized state of the window so it can be deserialized after reload", ->
|
||||
rootViewState = atom.rootView.serialize()
|
||||
workspaceState = atom.workspace.serialize()
|
||||
syntaxState = atom.syntax.serialize()
|
||||
|
||||
atom.unloadEditorWindow()
|
||||
|
||||
expect(atom.getWindowState().getObject('rootView')).toEqual rootViewState.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.rootView.openSync('sample.js')
|
||||
buffer = atom.rootView.getActivePaneItem().buffer
|
||||
pane = atom.rootView.getActivePane()
|
||||
atom.workspaceView.openSync('sample.js')
|
||||
buffer = atom.workspaceView.getActivePaneItem().buffer
|
||||
pane = atom.workspaceView.getActivePane()
|
||||
pane.splitRight(pane.copyActiveItem())
|
||||
expect(atom.rootView.find('.editor').length).toBe 2
|
||||
expect(atom.workspaceView.find('.editor').length).toBe 2
|
||||
|
||||
atom.unloadEditorWindow()
|
||||
|
||||
|
||||
@@ -0,0 +1,475 @@
|
||||
{$, $$, fs, WorkspaceView, View} = require 'atom'
|
||||
Q = require 'q'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
PaneView = require '../src/pane-view'
|
||||
Workspace = require '../src/workspace'
|
||||
|
||||
describe "WorkspaceView", ->
|
||||
pathToOpen = null
|
||||
|
||||
beforeEach ->
|
||||
atom.project.setPath(atom.project.resolve('dir'))
|
||||
pathToOpen = atom.project.resolve('a')
|
||||
atom.workspace = new Workspace
|
||||
atom.workspaceView = new WorkspaceView(atom.workspace)
|
||||
atom.workspaceView.enableKeymap()
|
||||
atom.workspaceView.openSync(pathToOpen)
|
||||
atom.workspaceView.focus()
|
||||
|
||||
describe "@deserialize()", ->
|
||||
viewState = null
|
||||
|
||||
simulateReload = ->
|
||||
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()
|
||||
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.getActivePane()).toBe atom.workspaceView.getPanes()[1]
|
||||
expect(atom.workspaceView.title).toBe "untitled - #{atom.project.getPath()}"
|
||||
|
||||
describe "when there are open editors", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
atom.workspaceView.attachToDom()
|
||||
pane1 = atom.workspaceView.getActivePane()
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitRight()
|
||||
pane4 = pane2.splitDown()
|
||||
pane2.activateItem(atom.project.openSync('b'))
|
||||
pane3.activateItem(atom.project.openSync('../sample.js'))
|
||||
pane3.activeItem.setCursorScreenPosition([2, 4])
|
||||
pane4.activateItem(atom.project.openSync('../sample.txt'))
|
||||
pane4.activeItem.setCursorScreenPosition([0, 2])
|
||||
pane2.focus()
|
||||
|
||||
simulateReload()
|
||||
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 4
|
||||
editorView1 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(0)').view()
|
||||
editorView3 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(1)').view()
|
||||
editorView2 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(0)').view()
|
||||
editorView4 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(1)').view()
|
||||
|
||||
expect(editorView1.getEditor().getPath()).toBe atom.project.resolve('a')
|
||||
expect(editorView2.getEditor().getPath()).toBe atom.project.resolve('b')
|
||||
expect(editorView3.getEditor().getPath()).toBe atom.project.resolve('../sample.js')
|
||||
expect(editorView3.getEditor().getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(editorView4.getEditor().getPath()).toBe atom.project.resolve('../sample.txt')
|
||||
expect(editorView4.getEditor().getCursorScreenPosition()).toEqual [0, 2]
|
||||
|
||||
# ensure adjust pane dimensions is called
|
||||
expect(editorView1.width()).toBeGreaterThan 0
|
||||
expect(editorView2.width()).toBeGreaterThan 0
|
||||
expect(editorView3.width()).toBeGreaterThan 0
|
||||
expect(editorView4.width()).toBeGreaterThan 0
|
||||
|
||||
# ensure correct editorView is focused again
|
||||
expect(editorView2.isFocused).toBeTruthy()
|
||||
expect(editorView1.isFocused).toBeFalsy()
|
||||
expect(editorView3.isFocused).toBeFalsy()
|
||||
expect(editorView4.isFocused).toBeFalsy()
|
||||
|
||||
expect(atom.workspaceView.title).toBe "#{path.basename(editorView2.getEditor().getPath())} - #{atom.project.getPath()}"
|
||||
|
||||
describe "where there are no open editors", ->
|
||||
it "constructs the view with no open editors", ->
|
||||
atom.workspaceView.getActivePane().remove()
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 0
|
||||
simulateReload()
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 0
|
||||
|
||||
describe "focus", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView.attachToDom()
|
||||
|
||||
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
|
||||
beforeEach ->
|
||||
commandHandler = jasmine.createSpy('commandHandler')
|
||||
atom.workspaceView.on('foo-command', commandHandler)
|
||||
|
||||
atom.keymap.bindKeys('name', '*', 'x': 'foo-command')
|
||||
|
||||
describe "when a keydown event is triggered in the WorkspaceView", ->
|
||||
it "triggers matching keybindings for that event", ->
|
||||
event = keydownEvent 'x', target: atom.workspaceView[0]
|
||||
|
||||
atom.workspaceView.trigger(event)
|
||||
expect(commandHandler).toHaveBeenCalled()
|
||||
|
||||
describe "window title", ->
|
||||
describe "when the project has no path", ->
|
||||
it "sets the title to 'untitled'", ->
|
||||
atom.project.setPath(undefined)
|
||||
expect(atom.workspaceView.title).toBe 'untitled'
|
||||
|
||||
describe "when the project has a path", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView.openSync('b')
|
||||
|
||||
describe "when there is an active pane item", ->
|
||||
it "sets the title to the pane item's title plus the project path", ->
|
||||
item = atom.workspaceView.getActivePaneItem()
|
||||
expect(atom.workspaceView.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
|
||||
describe "when the title of the active pane item changes", ->
|
||||
it "updates the window title based on the item's new title", ->
|
||||
editor = atom.workspaceView.getActivePaneItem()
|
||||
editor.buffer.setPath(path.join(temp.dir, 'hi'))
|
||||
expect(atom.workspaceView.title).toBe "#{editor.getTitle()} - #{atom.project.getPath()}"
|
||||
|
||||
describe "when the active pane's item changes", ->
|
||||
it "updates the title to the new item's title plus the project path", ->
|
||||
atom.workspaceView.getActivePane().showNextItem()
|
||||
item = atom.workspaceView.getActivePaneItem()
|
||||
expect(atom.workspaceView.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
|
||||
describe "when the last pane item is removed", ->
|
||||
it "updates the title to contain the project's path", ->
|
||||
atom.workspaceView.getActivePane().remove()
|
||||
expect(atom.workspaceView.getActivePaneItem()).toBeUndefined()
|
||||
expect(atom.workspaceView.title).toBe atom.project.getPath()
|
||||
|
||||
describe "when an inactive pane's item changes", ->
|
||||
it "does not update the title", ->
|
||||
pane = atom.workspaceView.getActivePane()
|
||||
pane.splitRight()
|
||||
initialTitle = atom.workspaceView.title
|
||||
pane.showNextItem()
|
||||
expect(atom.workspaceView.title).toBe initialTitle
|
||||
|
||||
describe "when the root view is deserialized", ->
|
||||
it "updates the title to contain the project's path", ->
|
||||
workspaceView2 = new WorkspaceView(atom.workspace.testSerialization())
|
||||
item = atom.workspaceView.getActivePaneItem()
|
||||
expect(workspaceView2.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
workspaceView2.remove()
|
||||
|
||||
describe "font size adjustment", ->
|
||||
it "increases/decreases font size when increase/decrease-font-size events are triggered", ->
|
||||
fontSizeBefore = atom.config.get('editor.fontSize')
|
||||
atom.workspaceView.trigger 'window:increase-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 1
|
||||
atom.workspaceView.trigger 'window:increase-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 2
|
||||
atom.workspaceView.trigger 'window:decrease-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 1
|
||||
atom.workspaceView.trigger 'window:decrease-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore
|
||||
|
||||
it "does not allow the font size to be less than 1", ->
|
||||
atom.config.set("editor.fontSize", 1)
|
||||
atom.workspaceView.trigger 'window:decrease-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe 1
|
||||
|
||||
describe ".openSync(filePath, options)", ->
|
||||
[activePane, initialItemCount] = []
|
||||
beforeEach ->
|
||||
activePane = atom.workspaceView.getActivePane()
|
||||
spyOn(activePane, 'focus')
|
||||
initialItemCount = activePane.getItems().length
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "opens an edit session with an empty buffer as an item in the active pane and focuses it", ->
|
||||
editor = atom.workspaceView.openSync()
|
||||
expect(activePane.getItems().length).toBe initialItemCount + 1
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a path", ->
|
||||
describe "when the active pane already has an edit session item for the path being opened", ->
|
||||
it "shows the existing edit session in the pane", ->
|
||||
previousEditor = activePane.activeItem
|
||||
|
||||
editor = atom.workspaceView.openSync('b')
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor).not.toBe previousEditor
|
||||
|
||||
editor = atom.workspaceView.openSync(previousEditor.getPath())
|
||||
expect(editor).toBe previousEditor
|
||||
expect(activePane.activeItem).toBe editor
|
||||
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the active pane does not have an edit session item for the path being opened", ->
|
||||
it "creates a new edit session for the given path in the active editor", ->
|
||||
editor = atom.workspaceView.openSync('b')
|
||||
expect(activePane.items.length).toBe 2
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the changeFocus option is false", ->
|
||||
it "does not focus the active pane", ->
|
||||
editor = atom.workspaceView.openSync('b', changeFocus: false)
|
||||
expect(activePane.focus).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the split option is 'right'", ->
|
||||
it "creates a new pane and opens the file in said pane", ->
|
||||
pane1 = atom.workspaceView.getActivePane()
|
||||
|
||||
editor = atom.workspaceView.openSync('b', split: 'right')
|
||||
pane2 = atom.workspaceView.getActivePane()
|
||||
expect(pane2[0]).not.toBe pane1[0]
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
|
||||
expect(atom.workspaceView.panes.find('.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 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()
|
||||
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 = "#{rightEditorView.invisibles.space}#{rightEditorView.invisibles.tab} #{rightEditorView.invisibles.space}#{rightEditorView.invisibles.eol}"
|
||||
|
||||
atom.workspaceView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
expect(leftEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
lowerLeftEditorView = leftEditorView.splitDown()
|
||||
expect(lowerLeftEditorView.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
atom.workspaceView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditorView.find(".line:first").text()).toBe " "
|
||||
expect(leftEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
lowerRightEditorView = rightEditorView.splitDown()
|
||||
expect(lowerRightEditorView.find(".line:first").text()).toBe " "
|
||||
|
||||
describe ".eachEditorView(callback)", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView.attachToDom()
|
||||
|
||||
it "invokes the callback for existing editor", ->
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
callback = (editor) ->
|
||||
callbackEditor = editor
|
||||
count++
|
||||
atom.workspaceView.eachEditorView(callback)
|
||||
expect(count).toBe 1
|
||||
expect(callbackEditor).toBe atom.workspaceView.getActiveView()
|
||||
|
||||
it "invokes the callback for new editor", ->
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
callback = (editor) ->
|
||||
callbackEditor = editor
|
||||
count++
|
||||
|
||||
atom.workspaceView.eachEditorView(callback)
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
atom.workspaceView.getActiveView().splitRight()
|
||||
expect(count).toBe 1
|
||||
expect(callbackEditor).toBe atom.workspaceView.getActiveView()
|
||||
|
||||
it "returns a subscription that can be disabled", ->
|
||||
count = 0
|
||||
callback = (editor) -> count++
|
||||
|
||||
subscription = atom.workspaceView.eachEditorView(callback)
|
||||
expect(count).toBe 1
|
||||
atom.workspaceView.getActiveView().splitRight()
|
||||
expect(count).toBe 2
|
||||
subscription.off()
|
||||
atom.workspaceView.getActiveView().splitRight()
|
||||
expect(count).toBe 2
|
||||
|
||||
describe ".reopenItemSync()", ->
|
||||
it "opens the uri associated with the last closed pane that isn't currently open", ->
|
||||
workspace = atom.workspaceView
|
||||
pane = workspace.getActivePane()
|
||||
workspace.openSync('b')
|
||||
workspace.openSync('file1')
|
||||
workspace.openSync()
|
||||
|
||||
# does not reopen items with no uri
|
||||
expect(workspace.getActivePaneItem().getUri()).toBeUndefined()
|
||||
pane.destroyActiveItem()
|
||||
workspace.reopenItemSync()
|
||||
expect(workspace.getActivePaneItem().getUri()).not.toBeUndefined()
|
||||
|
||||
# destroy all items
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe 'a'
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe 'b'
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe 'file1'
|
||||
pane.destroyActiveItem()
|
||||
|
||||
# reopens items with uris
|
||||
expect(workspace.getActivePaneItem()).toBeUndefined()
|
||||
workspace.reopenItemSync()
|
||||
expect(workspace.getActivePaneItem().getUri()).toBe 'file1'
|
||||
|
||||
# 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'
|
||||
|
||||
describe "core:close", ->
|
||||
it "closes the active pane item until all that remains is a single empty pane", ->
|
||||
atom.config.set('core.destroyEmptyPanes', true)
|
||||
atom.project.openSync('../sample.txt')
|
||||
expect(atom.workspaceView.getActivePane().getItems()).toHaveLength 1
|
||||
atom.workspaceView.trigger('core:close')
|
||||
expect(atom.workspaceView.getActivePane().getItems()).toHaveLength 0
|
||||
|
||||
describe "core:save", ->
|
||||
it "saves active editor until there are none", ->
|
||||
editor = atom.project.openSync('../sample.txt')
|
||||
spyOn(editor, 'save')
|
||||
atom.workspaceView.getActivePane().activateItem(editor)
|
||||
atom.workspaceView.trigger('core:save')
|
||||
expect(editor.save).toHaveBeenCalled()
|
||||
|
||||
describe "core:save-as", ->
|
||||
beforeEach ->
|
||||
spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path')
|
||||
|
||||
it "saves active editor until there are none", ->
|
||||
editor = atom.project.openSync('../sample.txt')
|
||||
spyOn(editor, 'saveAs')
|
||||
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
|
||||
@@ -219,11 +212,11 @@ class AtomPackage extends Package
|
||||
subscribeToActivationEvents: ->
|
||||
return unless @metadata.activationEvents?
|
||||
if _.isArray(@metadata.activationEvents)
|
||||
atom.rootView.command(event, @handleActivationEvent) for event in @metadata.activationEvents
|
||||
atom.workspaceView.command(event, @handleActivationEvent) for event in @metadata.activationEvents
|
||||
else if _.isString(@metadata.activationEvents)
|
||||
atom.rootView.command(@metadata.activationEvents, @handleActivationEvent)
|
||||
atom.workspaceView.command(@metadata.activationEvents, @handleActivationEvent)
|
||||
else
|
||||
atom.rootView.command(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
|
||||
atom.workspaceView.command(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
|
||||
|
||||
handleActivationEvent: (event) =>
|
||||
bubblePathEventHandlers = @disableEventHandlersOnBubblePath(event)
|
||||
@@ -234,11 +227,11 @@ class AtomPackage extends Package
|
||||
|
||||
unsubscribeFromActivationEvents: ->
|
||||
if _.isArray(@metadata.activationEvents)
|
||||
atom.rootView.off(event, @handleActivationEvent) for event in @metadata.activationEvents
|
||||
atom.workspaceView.off(event, @handleActivationEvent) for event in @metadata.activationEvents
|
||||
else if _.isString(@metadata.activationEvents)
|
||||
atom.rootView.off(@metadata.activationEvents, @handleActivationEvent)
|
||||
atom.workspaceView.off(@metadata.activationEvents, @handleActivationEvent)
|
||||
else
|
||||
atom.rootView.off(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
|
||||
atom.workspaceView.off(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
|
||||
|
||||
disableEventHandlersOnBubblePath: (event) ->
|
||||
bubblePathEventHandlers = []
|
||||
|
||||
+193
-159
@@ -1,21 +1,19 @@
|
||||
crypto = require 'crypto'
|
||||
ipc = require 'ipc'
|
||||
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'
|
||||
{Document} = require '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.
|
||||
@@ -28,28 +26,110 @@ WindowEventHandler = require './window-event-handler'
|
||||
# * `atom.contextMenu` - A {ContextMenuManager} instance
|
||||
# * `atom.keymap` - A {Keymap} instance
|
||||
# * `atom.menu` - A {MenuManager} instance
|
||||
# * `atom.rootView` - A {RootView} instance
|
||||
# * `atom.workspaceView` - A {WorkspaceView} instance
|
||||
# * `atom.packages` - A {PackageManager} instance
|
||||
# * `atom.pasteboard` - A {Pasteboard} instance
|
||||
# * `atom.project` - A {Project} instance
|
||||
# * `atom.syntax` - A {Syntax} instance
|
||||
# * `atom.themes` - A {ThemeManager} instance
|
||||
module.exports =
|
||||
class Atom
|
||||
Subscriber.includeInto(this)
|
||||
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: ->
|
||||
@rootViewParentSelector = '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()
|
||||
@loadTime = null
|
||||
|
||||
Config = require './config'
|
||||
Keymap = require './keymap'
|
||||
@@ -59,21 +139,33 @@ 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: ->
|
||||
@@ -81,12 +173,12 @@ class Atom
|
||||
|
||||
# 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()
|
||||
@@ -95,83 +187,76 @@ 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:
|
||||
deserializeRootView: ->
|
||||
RootView = require './root-view'
|
||||
state = @getWindowState()
|
||||
@rootView = @deserializers.deserialize(state.get('rootView'))
|
||||
unless @rootView?
|
||||
@rootView = new RootView()
|
||||
state.set('rootView', @rootView.getState())
|
||||
$(@rootViewParentSelector).append(@rootView)
|
||||
deserializeWorkspaceView: ->
|
||||
Workspace = require './workspace'
|
||||
WorkspaceView = require './workspace-view'
|
||||
@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: ->
|
||||
@deserializePackageStates()
|
||||
@deserializeProject()
|
||||
@deserializeRootView()
|
||||
@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('./root-view').configDefaults)
|
||||
@config.setDefaults('core', require('./workspace-view').configDefaults)
|
||||
@config.setDefaults('editor', require('./editor-view').configDefaults)
|
||||
@keymap.loadBundledKeymaps()
|
||||
@themes.loadBaseStylesheets()
|
||||
@@ -183,7 +268,7 @@ class Atom
|
||||
@menu.update()
|
||||
|
||||
$(window).on 'unload', =>
|
||||
$(document.body).hide()
|
||||
$(document.body).css('visibility', 'hidden')
|
||||
@unloadEditorWindow()
|
||||
false
|
||||
|
||||
@@ -191,29 +276,19 @@ class Atom
|
||||
|
||||
# Private:
|
||||
unloadEditorWindow: ->
|
||||
return if not @project and not @rootView
|
||||
return if not @project and not @workspaceView
|
||||
|
||||
windowState = @getWindowState()
|
||||
windowState.set('project', @project)
|
||||
windowState.set('syntax', @syntax.serialize())
|
||||
windowState.set('rootView', @rootView.serialize())
|
||||
@state.syntax = @syntax.serialize()
|
||||
@state.workspace = @workspace.serialize()
|
||||
@packages.deactivatePackages()
|
||||
windowState.set('packageStates', @packages.packageStates)
|
||||
@saveWindowState()
|
||||
@rootView.remove()
|
||||
@state.packageStates = @packages.packageStates
|
||||
@saveSync()
|
||||
@workspaceView.remove()
|
||||
@workspaceView = null
|
||||
@project.destroy()
|
||||
@windowEventHandler?.unsubscribe()
|
||||
|
||||
# Set up the default event handlers and menus for a non-editor window.
|
||||
#
|
||||
# This can be used by packages to have a minimum level of keybindings and
|
||||
# menus available when not using the standard editor window.
|
||||
#
|
||||
# This should only be called after setUpEnvironment() has been called.
|
||||
setUpDefaultEvents: ->
|
||||
@windowEventHandler = new WindowEventHandler
|
||||
@keymap.loadBundledKeymaps()
|
||||
@menu.update()
|
||||
@keymap.destroy()
|
||||
@windowState = null
|
||||
|
||||
# Private:
|
||||
loadThemes: ->
|
||||
@@ -319,7 +394,7 @@ class Atom
|
||||
setImmediate =>
|
||||
@show()
|
||||
@focus()
|
||||
@setFullScreen(true) if @rootView.getState().get('fullScreen')
|
||||
@setFullScreen(true) if @workspaceView.fullScreen
|
||||
|
||||
# Public: Close the current window.
|
||||
close: ->
|
||||
@@ -350,87 +425,46 @@ class Atom
|
||||
|
||||
# Public: Get the version of the Atom application.
|
||||
getVersion: ->
|
||||
app.getVersion()
|
||||
@constructor.getVersion()
|
||||
|
||||
# Public: Determine whether the current version is an official release.
|
||||
isReleasedVersion: ->
|
||||
@constructor.isReleasedVersion()
|
||||
|
||||
getGitHubAuthTokenName: ->
|
||||
'Atom GitHub API Token'
|
||||
|
||||
# Public: Set the the github token in the keychain
|
||||
setGitHubAuthToken: (token) ->
|
||||
keytar.replacePassword(@getGitHubAuthTokenName(), 'github', token)
|
||||
|
||||
# Public: Get the github token from the keychain
|
||||
getGitHubAuthToken: ->
|
||||
keytar.getPassword(@getGitHubAuthTokenName(), 'github')
|
||||
|
||||
# Public: Get the directory path to Atom's configuration area.
|
||||
#
|
||||
# Returns the absolute path to ~/.atom
|
||||
getConfigDirPath: ->
|
||||
@configDirPath ?= fs.absolute('~/.atom')
|
||||
@constructor.getConfigDirPath()
|
||||
|
||||
# Public: Get the directory path to Atom's storage area.
|
||||
# Private:
|
||||
saveSync: ->
|
||||
stateString = JSON.stringify(@state)
|
||||
if statePath = @constructor.getStatePath(@mode)
|
||||
fs.writeFileSync(statePath, stateString, 'utf8')
|
||||
else
|
||||
@getCurrentWindow().loadSettings.windowState = stateString
|
||||
|
||||
# Public: Get the time taken to completely load the current window.
|
||||
#
|
||||
# Returns the absoluste path to ~/.atom/storage
|
||||
getStorageDirPath: ->
|
||||
@storageDirPath ?= path.join(@getConfigDirPath(), 'storage')
|
||||
|
||||
# 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)
|
||||
else
|
||||
null
|
||||
|
||||
# Public: Set the window state of the given keypath to the value.
|
||||
setWindowState: (keyPath, value) ->
|
||||
windowState = @getWindowState()
|
||||
windowState.set(keyPath, value)
|
||||
windowState
|
||||
|
||||
# Private:
|
||||
loadWindowState: ->
|
||||
if windowStatePath = @getWindowStatePath()
|
||||
if fs.existsSync(windowStatePath)
|
||||
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
|
||||
|
||||
doc = Document.deserialize(documentState) if documentState?
|
||||
doc ?= Document.create()
|
||||
doc.registerModelClasses(require('./text-buffer'), require('./project'))
|
||||
# TODO: Remove this when everything is using telepath models
|
||||
if @site?
|
||||
@site.setRootDocument(doc)
|
||||
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
|
||||
|
||||
# Private: Returns a replicated copy of the current state.
|
||||
replicate: ->
|
||||
@getWindowState().replicate()
|
||||
# This time include things like loading and activating packages, creating
|
||||
# DOM elements for the editor, and reading the config.
|
||||
#
|
||||
# Returns the number of milliseconds taken to load the window or null
|
||||
# if the window hasn't finished loading yet.
|
||||
getWindowLoadTime: ->
|
||||
@loadTime
|
||||
|
||||
# Private:
|
||||
crashMainProcess: ->
|
||||
@@ -443,7 +477,7 @@ class Atom
|
||||
# Public: Visually and audibly trigger a beep.
|
||||
beep: ->
|
||||
shell.beep() if @config.get('core.audioBeep')
|
||||
@rootView.trigger 'beep'
|
||||
@workspaceView.trigger 'beep'
|
||||
|
||||
# Private:
|
||||
requireUserInitScript: ->
|
||||
|
||||
@@ -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: ->
|
||||
@@ -138,7 +144,7 @@ class AtomApplication
|
||||
@on 'application:zoom', -> Menu.sendActionToFirstResponder('zoom:')
|
||||
@on 'application:bring-all-windows-to-front', -> Menu.sendActionToFirstResponder('arrangeInFront:')
|
||||
@on 'application:inspect', ({x,y}) -> @focusedWindow().browserWindow.inspectElement(x, y)
|
||||
@on 'application:open-documentation', -> shell.openExternal('https://github-atom-io.herokuapp.com/docs/latest/?app=true')
|
||||
@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()
|
||||
@@ -245,6 +251,14 @@ class AtomApplication
|
||||
# + initialSize:
|
||||
# Object with height and width keys.
|
||||
openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, initialSize}={}) ->
|
||||
if devMode and not fs.existsSync(global.devResourcePath)
|
||||
dialog.showMessageBox
|
||||
type: 'warning'
|
||||
buttons: ['OK']
|
||||
message: 'Atom source directory not found.'
|
||||
detail: 'To run a window in dev mode you need to have the atom/atom repo cloned to ~/github/atom'
|
||||
return
|
||||
|
||||
if pathToOpen
|
||||
[basename, initialLine] = path.basename(pathToOpen).split(':')
|
||||
if initialLine
|
||||
@@ -318,7 +332,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
|
||||
|
||||
@@ -329,7 +343,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
|
||||
@@ -349,8 +363,8 @@ class AtomApplication
|
||||
# A Boolean which controls whether any newly opened windows should be in
|
||||
# dev mode or not.
|
||||
promptForPath: ({devMode}={}) ->
|
||||
pathsToOpen = dialog.showOpenDialog title: 'Open', properties: ['openFile', 'openDirectory', 'multiSelections', 'createDirectory']
|
||||
@openPaths({pathsToOpen, devMode})
|
||||
dialog.showOpenDialog title: 'Open', properties: ['openFile', 'openDirectory', 'multiSelections', 'createDirectory'], (pathsToOpen) =>
|
||||
@openPaths({pathsToOpen, devMode})
|
||||
|
||||
# Public: If an update is available, it returns the new version string
|
||||
# otherwise it returns null.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -10,14 +10,15 @@ _ = require 'underscore-plus'
|
||||
# Private:
|
||||
module.exports =
|
||||
class AtomWindow
|
||||
@iconPath: path.resolve(__dirname, '..', '..', 'atom.png')
|
||||
@iconPath: path.resolve(__dirname, '..', '..', 'resources', 'atom.png')
|
||||
@includeShellLoadTime: true
|
||||
|
||||
browserWindow: null
|
||||
loaded: null
|
||||
isSpec: null
|
||||
|
||||
constructor: (settings={}) ->
|
||||
{@resourcePath, pathToOpen, initialLine, @isSpec} = settings
|
||||
{@resourcePath, pathToOpen, initialLine, @isSpec, @exitWhenDone} = settings
|
||||
global.atomApplication.addWindow(this)
|
||||
|
||||
@setupNodePath(@resourcePath)
|
||||
@@ -29,7 +30,13 @@ 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
|
||||
@constructor.includeShellLoadTime = false
|
||||
loadSettings.shellLoadTime ?= Date.now() - global.shellStartTime
|
||||
|
||||
loadSettings.initialPath = pathToOpen
|
||||
if fs.statSyncNoException(pathToOpen).isFile?()
|
||||
loadSettings.initialPath = path.dirname(pathToOpen)
|
||||
@@ -70,11 +77,13 @@ class AtomWindow
|
||||
chosen = dialog.showMessageBox @browserWindow,
|
||||
type: 'warning'
|
||||
buttons: ['Close', 'Keep Waiting']
|
||||
message: 'Editor is not responsing'
|
||||
message: 'Editor is not responding'
|
||||
detail: 'The editor is not responding. Would you like to force close it or just keep waiting?'
|
||||
@browserWindow.destroy() if chosen is 0
|
||||
|
||||
@browserWindow.on 'crashed', =>
|
||||
global.atomApplication.exit(100) if @exitWhenDone
|
||||
|
||||
chosen = dialog.showMessageBox @browserWindow,
|
||||
type: 'warning'
|
||||
buttons: ['Close Window', 'Reload', 'Keep It Open']
|
||||
|
||||
+11
-10
@@ -1,8 +1,7 @@
|
||||
startTime = Date.now()
|
||||
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) ->
|
||||
@@ -62,14 +61,12 @@ delegate.browserMainParts.preMainMessageLoopRun = ->
|
||||
AtomApplication = require './atom-application'
|
||||
|
||||
AtomApplication.open(args)
|
||||
console.log("App load time: #{Date.now() - startTime}ms") unless args.test
|
||||
console.log("App load time: #{Date.now() - global.shellStartTime}ms") unless args.test
|
||||
|
||||
global.devResourcePath = path.join(app.getHomeDir(), 'github', 'atom')
|
||||
|
||||
setupCrashReporter = ->
|
||||
crashReporter.setCompanyName 'GitHub'
|
||||
crashReporter.setSubmissionUrl 'https://speakeasy.githubapp.com/submit_crash_log'
|
||||
crashReporter.setAutoSubmit true
|
||||
crashReporter.start(productName: 'Atom', companyName: 'GitHub')
|
||||
|
||||
setupAutoUpdater = ->
|
||||
autoUpdater.setFeedUrl 'https://speakeasy.githubapp.com/apps/27/appcast.xml'
|
||||
@@ -85,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.')
|
||||
@@ -92,11 +90,11 @@ parseCommandLine = ->
|
||||
options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.')
|
||||
args = options.argv
|
||||
|
||||
if args.h
|
||||
if args.help
|
||||
process.stdout.write(options.help())
|
||||
process.exit(0)
|
||||
|
||||
if args.v
|
||||
if args.version
|
||||
process.stdout.write("#{version}\n")
|
||||
process.exit(0)
|
||||
|
||||
@@ -108,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
|
||||
@@ -121,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()
|
||||
|
||||
@@ -32,10 +32,10 @@ module.exports =
|
||||
callback = commandName
|
||||
commandName = path.basename(commandPath, path.extname(commandPath))
|
||||
|
||||
installCallback = (error) ->
|
||||
installCallback = (error, sourcePath, destinationPath) ->
|
||||
if error?
|
||||
console.warn "Failed to install `#{commandName}` binary", error
|
||||
callback?(error)
|
||||
callback?(error, sourcePath, destinationPath)
|
||||
|
||||
@findInstallDirectory (directory) ->
|
||||
if directory?
|
||||
@@ -44,16 +44,25 @@ module.exports =
|
||||
if error?
|
||||
installCallback(error)
|
||||
else
|
||||
symlinkCommand(commandPath, destinationPath, installCallback)
|
||||
symlinkCommand commandPath, destinationPath, (error) ->
|
||||
installCallback(error, commandPath, destinationPath)
|
||||
else
|
||||
installCallback(new Error("No destination directory exists to install"))
|
||||
|
||||
installAtomCommand: (callback) ->
|
||||
{resourcePath} = atom.getLoadSettings()
|
||||
installAtomCommand: (resourcePath, callback) ->
|
||||
if _.isFunction(resourcePath)
|
||||
callback = resourcePath
|
||||
resourcePath = null
|
||||
|
||||
resourcePath ?= atom.getLoadSettings().resourcePath
|
||||
commandPath = path.join(resourcePath, 'atom.sh')
|
||||
@install(commandPath, callback)
|
||||
|
||||
installApmCommand: (callback) ->
|
||||
{resourcePath} = atom.getLoadSettings()
|
||||
commandPath = path.join(resourcePath, 'node_modules', '.bin', 'apm')
|
||||
installApmCommand: (resourcePath, callback) ->
|
||||
if _.isFunction(resourcePath)
|
||||
callback = resourcePath
|
||||
resourcePath = null
|
||||
|
||||
resourcePath ?= atom.getLoadSettings().resourcePath
|
||||
commandPath = path.join(resourcePath, 'apm', 'node_modules', '.bin', 'apm')
|
||||
@install(commandPath, callback)
|
||||
|
||||
+2
-2
@@ -125,11 +125,11 @@ class Config
|
||||
#
|
||||
# keyPath - The {String} name of the key to retrieve
|
||||
# defaultValue - The integer {Number} to fall back to if the value isn't
|
||||
# positive
|
||||
# positive, defaults to 0.
|
||||
#
|
||||
# Returns the value from Atom's default settings, the user's configuration file,
|
||||
# or `defaultValue` if the key value isn't greater than zero.
|
||||
getPositiveInt: (keyPath, defaultValue) ->
|
||||
getPositiveInt: (keyPath, defaultValue=0) ->
|
||||
Math.max(@getInt(keyPath), 0) or defaultValue
|
||||
|
||||
# Public: Sets the value for a configuration setting.
|
||||
|
||||
@@ -14,7 +14,7 @@ class ContextMenuManager
|
||||
@devModeDefinitions = {}
|
||||
@activeElement = null
|
||||
|
||||
@devModeDefinitions['#root-view'] = [
|
||||
@devModeDefinitions['.workspace'] = [
|
||||
label: 'Inspect Element'
|
||||
command: 'application:inspect'
|
||||
executeAtBuild: (e) ->
|
||||
|
||||
@@ -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: ->
|
||||
|
||||
+15
-9
@@ -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)
|
||||
@@ -28,6 +28,7 @@ class Cursor
|
||||
return if oldHeadScreenPosition.isEqual(newHeadScreenPosition)
|
||||
|
||||
@needsAutoscroll ?= @isLastCursor() and !textChanged
|
||||
@goalColumn = null
|
||||
|
||||
movedEvent =
|
||||
oldBufferPosition: oldHeadBufferPosition
|
||||
@@ -50,7 +51,6 @@ class Cursor
|
||||
|
||||
# Private:
|
||||
changePosition: (options, fn) ->
|
||||
@goalColumn = null
|
||||
@clearSelection()
|
||||
@needsAutoscroll = options.autoscroll ? @isLastCursor()
|
||||
unless fn()
|
||||
@@ -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} = 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 = {}
|
||||
|
||||
@@ -28,8 +26,6 @@ class DeserializerManager
|
||||
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
|
||||
|
||||
+48
-17
@@ -1,8 +1,11 @@
|
||||
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
|
||||
module.exports =
|
||||
@@ -41,7 +44,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 +59,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 +73,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 +100,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 +139,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,
|
||||
|
||||
+45
-58
@@ -1,8 +1,9 @@
|
||||
_ = require 'underscore-plus'
|
||||
{Emitter, Subscriber} = require 'emissary'
|
||||
guid = require 'guid'
|
||||
telepath = require 'telepath'
|
||||
{Point, Range} = telepath
|
||||
Serializable = require 'serializable'
|
||||
{Model} = require 'theorist'
|
||||
{Point, Range} = require 'text-buffer'
|
||||
TokenizedBuffer = require './tokenized-buffer'
|
||||
RowMap = require './row-map'
|
||||
Fold = require './fold'
|
||||
@@ -12,35 +13,19 @@ ConfigObserver = require './config-observer'
|
||||
|
||||
# Private:
|
||||
module.exports =
|
||||
class DisplayBuffer
|
||||
Emitter.includeInto(this)
|
||||
Subscriber.includeInto(this)
|
||||
class DisplayBuffer extends Model
|
||||
Serializable.includeInto(this)
|
||||
_.extend @prototype, ConfigObserver
|
||||
|
||||
@acceptsDocuments: true
|
||||
atom.deserializers.add(this)
|
||||
@version: 2
|
||||
|
||||
@deserialize: (state) -> new this(state)
|
||||
|
||||
constructor: (optionsOrState) ->
|
||||
if optionsOrState instanceof telepath.Document
|
||||
@state = optionsOrState
|
||||
@id = @state.get('id')
|
||||
@tokenizedBuffer = atom.deserializers.deserialize(@state.get('tokenizedBuffer'))
|
||||
@buffer = @tokenizedBuffer.buffer
|
||||
else
|
||||
{@buffer, softWrap, editorWidthInChars} = optionsOrState
|
||||
@id = guid.create().toString()
|
||||
@tokenizedBuffer = new TokenizedBuffer(optionsOrState)
|
||||
@state = atom.site.createDocument
|
||||
deserializer: @constructor.name
|
||||
version: @constructor.version
|
||||
id: @id
|
||||
tokenizedBuffer: @tokenizedBuffer.getState()
|
||||
softWrap: softWrap ? atom.config.get('editor.softWrap') ? false
|
||||
editorWidthInChars: editorWidthInChars
|
||||
@properties
|
||||
softWrap: null
|
||||
editorWidthInChars: null
|
||||
|
||||
constructor: ({tabLength, @editorWidthInChars, @tokenizedBuffer, buffer}={}) ->
|
||||
super
|
||||
@softWrap ?= atom.config.get('editor.softWrap') ? false
|
||||
@tokenizedBuffer ?= new TokenizedBuffer({tabLength, buffer})
|
||||
@buffer = @tokenizedBuffer.buffer
|
||||
@markers = {}
|
||||
@foldsByMarkerId = {}
|
||||
@updateAllScreenLines()
|
||||
@@ -50,19 +35,25 @@ class DisplayBuffer
|
||||
@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 @getSoftWrap() and atom.config.get('editor.softWrapAtPreferredLineLength')
|
||||
@updateWrappedScreenLines() if @softWrap and atom.config.get('editor.softWrapAtPreferredLineLength')
|
||||
|
||||
@observeConfig 'editor.softWrapAtPreferredLineLength', callNow: false, =>
|
||||
@updateWrappedScreenLines() if @getSoftWrap()
|
||||
@updateWrappedScreenLines() if @softWrap
|
||||
|
||||
serialize: -> @state.clone()
|
||||
getState: -> @state
|
||||
serializeParams: ->
|
||||
id: @id
|
||||
softWrap: @softWrap
|
||||
editorWidthInChars: @editorWidthInChars
|
||||
tokenizedBuffer: @tokenizedBuffer.serialize()
|
||||
|
||||
deserializeParams: (params) ->
|
||||
params.tokenizedBuffer = TokenizedBuffer.deserialize(params.tokenizedBuffer)
|
||||
params
|
||||
|
||||
copy: ->
|
||||
newDisplayBuffer = new DisplayBuffer({@buffer, tabLength: @getTabLength()})
|
||||
@@ -98,25 +89,26 @@ class DisplayBuffer
|
||||
# visible - A {Boolean} indicating of the tokenized buffer is shown
|
||||
setVisible: (visible) -> @tokenizedBuffer.setVisible(visible)
|
||||
|
||||
setSoftWrap: (softWrap) -> @state.set('softWrap', softWrap)
|
||||
# Deprecated: Use the softWrap property directly
|
||||
setSoftWrap: (@softWrap) -> @softWrap
|
||||
|
||||
getSoftWrap: -> @state.get('softWrap')
|
||||
# Deprecated: Use the softWrap property directly
|
||||
getSoftWrap: -> @softWrap
|
||||
|
||||
# Set the number of characters that fit horizontally in the editor.
|
||||
#
|
||||
# editorWidthInChars - A {Number} of characters.
|
||||
setEditorWidthInChars: (editorWidthInChars) ->
|
||||
previousWidthInChars = @state.get('editorWidthInChars')
|
||||
@state.set('editorWidthInChars', editorWidthInChars)
|
||||
if editorWidthInChars isnt previousWidthInChars and @getSoftWrap()
|
||||
previousWidthInChars = @editorWidthInChars
|
||||
@editorWidthInChars = editorWidthInChars
|
||||
if editorWidthInChars isnt previousWidthInChars and @softWrap
|
||||
@updateWrappedScreenLines()
|
||||
|
||||
getSoftWrapColumn: ->
|
||||
editorWidthInChars = @state.get('editorWidthInChars')
|
||||
if atom.config.get('editor.softWrapAtPreferredLineLength')
|
||||
Math.min(editorWidthInChars, atom.config.getPositiveInt('editor.preferredLineLength', editorWidthInChars))
|
||||
Math.min(@editorWidthInChars, atom.config.getPositiveInt('editor.preferredLineLength', @editorWidthInChars))
|
||||
else
|
||||
editorWidthInChars
|
||||
@editorWidthInChars
|
||||
|
||||
# Gets the screen line for the given screen row.
|
||||
#
|
||||
@@ -367,7 +359,7 @@ class DisplayBuffer
|
||||
|
||||
# Get the grammar for this buffer.
|
||||
#
|
||||
# Returns the current {TextMateGrammar} or the {NullGrammar}.
|
||||
# Returns the current {Grammar} or the {NullGrammar}.
|
||||
getGrammar: ->
|
||||
@tokenizedBuffer.grammar
|
||||
|
||||
@@ -433,7 +425,7 @@ class DisplayBuffer
|
||||
# Returns a {Number} representing the `line` position where the wrap would take place.
|
||||
# Returns `null` if a wrap wouldn't occur.
|
||||
findWrapColumn: (line, softWrapColumn=@getSoftWrapColumn()) ->
|
||||
return unless @getSoftWrap()
|
||||
return unless @softWrap
|
||||
return unless line.length > softWrapColumn
|
||||
|
||||
if /\s/.test(line[softWrapColumn])
|
||||
@@ -477,7 +469,7 @@ class DisplayBuffer
|
||||
# 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...) ->
|
||||
@@ -487,7 +479,7 @@ class DisplayBuffer
|
||||
# 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...) ->
|
||||
@@ -496,7 +488,7 @@ class DisplayBuffer
|
||||
# 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) ->
|
||||
@@ -505,7 +497,7 @@ class DisplayBuffer
|
||||
# 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) ->
|
||||
@@ -535,10 +527,10 @@ class DisplayBuffer
|
||||
#
|
||||
# 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
|
||||
@@ -584,12 +576,6 @@ class DisplayBuffer
|
||||
line = @lineForRow(row).text
|
||||
console.log row, line, line.length
|
||||
|
||||
getDebugSnapshot: ->
|
||||
lines = ["Display Buffer:"]
|
||||
for screenLine, row in @linesForRows(0, @getLastRow())
|
||||
lines.push "#{row}: #{screenLine.text}"
|
||||
lines.join('\n')
|
||||
|
||||
### Internal ###
|
||||
|
||||
handleTokenizedBufferChange: (tokenizedBufferChange) =>
|
||||
@@ -660,6 +646,7 @@ class DisplayBuffer
|
||||
softWraps = 0
|
||||
while wrapScreenColumn = @findWrapColumn(tokenizedLine.text)
|
||||
[wrappedLine, tokenizedLine] = tokenizedLine.softWrapAt(wrapScreenColumn)
|
||||
|
||||
newScreenLines.push(wrappedLine)
|
||||
softWraps++
|
||||
newScreenLines.push(tokenizedLine)
|
||||
|
||||
+258
-606
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
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