Comparar commits
866 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| d79881063a | |||
| 49b80d306b | |||
| 91adc01e4e | |||
| 4cc553daf8 | |||
| 538b648042 | |||
| 78b28b6ee6 | |||
| fe725ec909 | |||
| 35d4eadd45 | |||
| 47bccc142e | |||
| 8fb10e35a9 | |||
| 2687fafca4 | |||
| 40860a59f5 | |||
| 989fffba6a | |||
| 9f1edd3189 | |||
| e03544bab6 | |||
| fd9f3d6543 | |||
| 19b5f3e202 | |||
| f80ad91bec | |||
| 1e21665b69 | |||
| 57132f57bd | |||
| de5b2c5cf1 | |||
| d12eed3f68 | |||
| 951b3253da | |||
| e40d837b40 | |||
| 3e626f28c4 | |||
| 156e9f9b24 | |||
| e0ccfc4788 | |||
| 8307ae8938 | |||
| 464785ec3b | |||
| 64e47aeea8 | |||
| 2bd4386090 | |||
| 40b6183937 | |||
| 33dc008b10 | |||
| 9fe9e0dc41 | |||
| c8b7040144 | |||
| 8e4c9076f1 | |||
| 1381ffdbf5 | |||
| b8c906d00e | |||
| d9c62a76bf | |||
| c27103db8a | |||
| 2f46539026 | |||
| 6261124464 | |||
| 29f1f5cbe5 | |||
| 2921ac856c | |||
| 4474212b3e | |||
| b02ac8696e | |||
| bd96814576 | |||
| 5091efab8e | |||
| 14522f6769 | |||
| 38422150b0 | |||
| e28ef3a76c | |||
| 6baaf404af | |||
| f42bd3f34b | |||
| 2bca1abb2a | |||
| a44662c885 | |||
| 0f014463c1 | |||
| 7c55348fde | |||
| ae59078c5d | |||
| 4cb1f6c509 | |||
| 16ab3e5538 | |||
| 696a54392e | |||
| 9521358c55 | |||
| a5e563c04c | |||
| 631995996b | |||
| f239cdf307 | |||
| d3e482ed6f | |||
| 75af26ea3e | |||
| 2d547e3b9e | |||
| e84a6b4869 | |||
| c1c73a822a | |||
| e774b956e0 | |||
| 68affd9b08 | |||
| dd1a04aec0 | |||
| ec37ec1e49 | |||
| 3d1a27875d | |||
| ad69522bd1 | |||
| 0043b9de77 | |||
| 0abbe8affb | |||
| 1c39b851e4 | |||
| fdf250871d | |||
| 0e8ec74662 | |||
| 8e2777ccc0 | |||
| d5c2878d53 | |||
| f47fdd0ae1 | |||
| 2f4cca445b | |||
| b67496a4f2 | |||
| a604e51f1c | |||
| 61e5e6ea28 | |||
| 3808f81886 | |||
| 5a2b2b18c6 | |||
| 437cb0ccef | |||
| decaa3dfcf | |||
| 03573b4e06 | |||
| 70162d39f7 | |||
| f6f93342bc | |||
| b22e45b8a9 | |||
| 419be22b7b | |||
| c5535bde81 | |||
| 5640f57eae | |||
| 325d534d91 | |||
| eaf80a0194 | |||
| 3f848606ee | |||
| c2b3c2e3aa | |||
| 08b80c15ad | |||
| 052a489751 | |||
| 51192bb3e9 | |||
| 32396a2a8b | |||
| a45b93614e | |||
| 8f193e4fb5 | |||
| cbd4fab8f2 | |||
| 8b3b033b09 | |||
| e675fe73c8 | |||
| 0b7262aa62 | |||
| 5001c6e84e | |||
| a4411ab2ca | |||
| 5ea8c6c27b | |||
| da1e5f5c10 | |||
| c33f7cde2a | |||
| a39e136c43 | |||
| 275ee62e8d | |||
| 62a19aa6fa | |||
| 78b9a99f86 | |||
| ba632751a0 | |||
| 96a16494ff | |||
| a39d1c274e | |||
| de8dab5939 | |||
| f32a289193 | |||
| 92a80208d3 | |||
| 2793ebb5c4 | |||
| 44d90615ec | |||
| 7bf350251e | |||
| e4b5cfd600 | |||
| 20a669e94f | |||
| 25be233c5f | |||
| 13f35bc6e3 | |||
| 2efc39a265 | |||
| 1a31e3418d | |||
| 090d350802 | |||
| 4f547b8c21 | |||
| 76992dfaeb | |||
| 070a72e76c | |||
| 95a23cdada | |||
| 6fa0c82fac | |||
| a0db412140 | |||
| 2f4a57e5e4 | |||
| 0da647acdf | |||
| 147e75ad95 | |||
| 2f4db45320 | |||
| c18810ca67 | |||
| 204de3ac24 | |||
| 986a9ce0a0 | |||
| 25b7d356a1 | |||
| e833649111 | |||
| 0d74753d3e | |||
| 9306c9b262 | |||
| 283cbcc9ab | |||
| 935df2f4f1 | |||
| 07bcd8c1ef | |||
| 6ad088f6d5 | |||
| b181a77130 | |||
| d83adc3a27 | |||
| 1ad971f424 | |||
| 0b8800c818 | |||
| bb7d8812da | |||
| f0677e43fa | |||
| 159f521104 | |||
| 46d713169f | |||
| 95535d93c6 | |||
| 7173467b1a | |||
| 03712392c9 | |||
| 0e49582616 | |||
| 1dad3ae7ca | |||
| c2123fce1f | |||
| 2190a009d8 | |||
| e199000985 | |||
| ba70c15328 | |||
| 0192c57f46 | |||
| b60b21cf3a | |||
| 116c56c236 | |||
| 6fb8dcbaa5 | |||
| 6e2fd18f62 | |||
| 90da89d31c | |||
| 0999dc6d44 | |||
| 72cedf9027 | |||
| c38eccf39d | |||
| 3be3543ad9 | |||
| 588efa2e11 | |||
| 2fee0f1083 | |||
| 55ee11e16a | |||
| b22e4d67ec | |||
| 85626383ee | |||
| fc60ba682c | |||
| 0e7cb3ff78 | |||
| 03ef5a72c4 | |||
| 2cd91eda98 | |||
| 5d27d56056 | |||
| 0c9a1fdc80 | |||
| 79dd4320da | |||
| 37c7c8abee | |||
| 77bb829a3e | |||
| dba0998687 | |||
| 0c542d2317 | |||
| 477ac1768b | |||
| 39ec96021e | |||
| ab397b306c | |||
| d3575bd45e | |||
| f6ad85488c | |||
| b8406368c6 | |||
| 6278364ed2 | |||
| 068e656627 | |||
| 6d50d05336 | |||
| 60d804fb72 | |||
| fb2439f193 | |||
| 4d8b2672ff | |||
| e057d35048 | |||
| 6e3517dd4a | |||
| 37c17075ea | |||
| 1074642e8f | |||
| e360379ced | |||
| efb70e0562 | |||
| 9a156c8629 | |||
| afd1a7419d | |||
| b3582b2632 | |||
| e68bc47dc5 | |||
| b5f4b8d254 | |||
| 8803bab816 | |||
| b94d22794b | |||
| 27cee3e19c | |||
| b10a01ddc2 | |||
| 14b5308bb3 | |||
| 4c0ffe122d | |||
| 7ca2779e56 | |||
| 5579551521 | |||
| d89e95dc54 | |||
| 7ace46d81f | |||
| e13eebdcd2 | |||
| 3cb3c888ba | |||
| 0947e1cffa | |||
| 044bcd4053 | |||
| 805c17d20c | |||
| 77ed5324b1 | |||
| dc8362cabf | |||
| 87b859b947 | |||
| f92e61a683 | |||
| 3bd04f8da8 | |||
| d08aa4b9d9 | |||
| 38a3adae89 | |||
| 2f8c9ffa66 | |||
| a7a1244599 | |||
| 6ec8b6e26f | |||
| 76332c76bd | |||
| 45c11e6fd4 | |||
| b6785d78e8 | |||
| e31cd35ce8 | |||
| c80373b11e | |||
| cf65945d5c | |||
| f76108fdfd | |||
| 8ea8671430 | |||
| f02d05811c | |||
| 0af8c9362b | |||
| d01cde3358 | |||
| 128543ec7e | |||
| 5de52c59ee | |||
| ea019870fb | |||
| a98b2fd37f | |||
| c2aefa7616 | |||
| 2d12f08375 | |||
| 66162902be | |||
| 595f6a975f | |||
| 15085384b9 | |||
| 0eff8deabc | |||
| 72db35d663 | |||
| 51ec4d3285 | |||
| 8b149f63cb | |||
| 15a48147b1 | |||
| 3bc6023a66 | |||
| b89e58e551 | |||
| 6cd003f68e | |||
| 8730638ade | |||
| c3850e4d44 | |||
| 0a8ebca67a | |||
| 347801b074 | |||
| fe010eccc7 | |||
| 2959b51afb | |||
| 24e0d8fc03 | |||
| 42a34c01c9 | |||
| cfe5bb1b02 | |||
| f23f23d032 | |||
| 65330a00e3 | |||
| 7f9b057c52 | |||
| 647b5881a8 | |||
| 9c76214fe9 | |||
| 3d5516fad9 | |||
| 338b59dc5f | |||
| 09c8b2a3a8 | |||
| 1ca7663be4 | |||
| 0e2d6caf94 | |||
| fe2baa18ca | |||
| a147dc01d7 | |||
| 2f4555a16f | |||
| 4c121bb084 | |||
| d3130845d1 | |||
| 13a622a853 | |||
| 1254ceeb6d | |||
| eea38a696f | |||
| 27928a7145 | |||
| 67755ccdc3 | |||
| 6178c64c68 | |||
| b305a09be3 | |||
| 5313f6cf66 | |||
| f00bb58932 | |||
| ad36b2c6a6 | |||
| b3fe63b1b4 | |||
| c6d1409151 | |||
| 5235114eed | |||
| c61d45805d | |||
| c294c94c02 | |||
| b7a8e22d82 | |||
| bd6dda6f0f | |||
| 468034f261 | |||
| c6a0e385f7 | |||
| 4781636889 | |||
| fa6ea01bfc | |||
| f021ce3657 | |||
| 71b238fafc | |||
| 63df6c8dcd | |||
| 7fc8cea167 | |||
| 726a60b348 | |||
| 0429ed09ff | |||
| 14df854ea8 | |||
| c8727873c7 | |||
| 3b7e1400c0 | |||
| 2ddd77122e | |||
| 41268c67d7 | |||
| 4777c4c42c | |||
| ced95e7ad9 | |||
| 68ddeeef01 | |||
| 184e6a14c7 | |||
| 25378e9905 | |||
| 80a6999af4 | |||
| 257722f82e | |||
| 58a938b3f1 | |||
| 875f187915 | |||
| 229e380e28 | |||
| 52e8e3aaf3 | |||
| dc775f93ff | |||
| 63b1546212 | |||
| b9585d1cf2 | |||
| 189b9051a9 | |||
| f3ef0b77a8 | |||
| bdefff6abe | |||
| 235078be8e | |||
| 02d7668c73 | |||
| 7bb41f57b0 | |||
| c1c50a0b4c | |||
| 7bc135d82e | |||
| a1847b5355 | |||
| 3686a5b0ba | |||
| db82f6bb8b | |||
| d5b5c76485 | |||
| 6430aefaa8 | |||
| f41ffd6cbf | |||
| 8a44da49d1 | |||
| 9e50c829a0 | |||
| d2cf652a0b | |||
| 01fee754f7 | |||
| 24c304481e | |||
| d02d42f40e | |||
| c747e1c686 | |||
| 3c166edd26 | |||
| 2d46a98ea2 | |||
| 51a847c820 | |||
| 4f7c7568e9 | |||
| 326ee2e5fa | |||
| 32a0faf068 | |||
| 66da13a6ff | |||
| ebaa6c349f | |||
| 063d48c4ab | |||
| 0a53e2f7bb | |||
| 12fbd89e79 | |||
| 6a16ef0cf8 | |||
| 8d180dd767 | |||
| 475fefa2fd | |||
| 86d0fca621 | |||
| 3056efd351 | |||
| 7ba57df6f7 | |||
| 5c2f4d939a | |||
| 6c9c7b6d92 | |||
| 210dd61d2a | |||
| aa302cb935 | |||
| 994e856ccd | |||
| 808f7e6ed3 | |||
| 22f7bc8f26 | |||
| ab0b2cf12e | |||
| 599a2ad021 | |||
| db649798ef | |||
| 4f8f8c43a4 | |||
| 4491a9302b | |||
| ce138b56c0 | |||
| 8f8c0cbf45 | |||
| 7e04d85e1e | |||
| 2597837ba7 | |||
| 136836e615 | |||
| 82882624ce | |||
| c03e849089 | |||
| 25f98e223f | |||
| c2290ab8b4 | |||
| 9c39587d6e | |||
| 972aa75f3e | |||
| 1d1e4b33d4 | |||
| 79a61d47b2 | |||
| 26c9e7cfa9 | |||
| 28f9f22614 | |||
| 69caf6c98f | |||
| 51f1bb896c | |||
| 0fd834004e | |||
| d2654cb70c | |||
| 268553cda9 | |||
| 64a61d86d4 | |||
| 216a9e11c0 | |||
| 04373d2d9d | |||
| 9a2e768882 | |||
| d13d0543b9 | |||
| 6178294537 | |||
| 4871a7a06d | |||
| fe23d82e23 | |||
| d6e94b1105 | |||
| f0a2b17914 | |||
| ab2252f80f | |||
| f1cf8711a8 | |||
| 0f14dd9420 | |||
| d7cfb757d6 | |||
| ea2113e753 | |||
| 05f4604005 | |||
| 71b2400dbc | |||
| 1de46330d7 | |||
| de4e24582d | |||
| dffb0b35e7 | |||
| d8ec4573ec | |||
| 09a6e9a42e | |||
| 57fe9c6903 | |||
| cf3e1a9b6d | |||
| 4629905b65 | |||
| d4e8de60ce | |||
| 3237636206 | |||
| 934fe1c6d0 | |||
| 9aff1476d4 | |||
| 084d310366 | |||
| d122d18a34 | |||
| 6fb3dea9cd | |||
| ceb2a3857d | |||
| ab80da4363 | |||
| 19545e1113 | |||
| 88603e2771 | |||
| 5f323cc67c | |||
| 39d15d6087 | |||
| eaba8ef016 | |||
| 6cc4cf87b9 | |||
| c6a914a6e1 | |||
| 32af574206 | |||
| 552ab150cb | |||
| 2698925d10 | |||
| 13b1632dfc | |||
| de204b0321 | |||
| def07344e9 | |||
| 4ef3f39a7b | |||
| 2bf26e8db5 | |||
| 24b3b664dc | |||
| 2352829b28 | |||
| 3ca34dad40 | |||
| 15dc6442d0 | |||
| d775038f8d | |||
| 3829970a09 | |||
| 633306e6bf | |||
| 7d58008ed3 | |||
| bc9e32a3cf | |||
| a98791ebd0 | |||
| a72e1813e9 | |||
| fe9ffbeb12 | |||
| e61992c1f6 | |||
| 612332cd9f | |||
| 40d81ca22c | |||
| faf02460f5 | |||
| f010e8a888 | |||
| 712e9c7203 | |||
| 128a030880 | |||
| 907c3245a9 | |||
| da7cbeddc7 | |||
| 8b55c4981d | |||
| b7a59748c4 | |||
| b49de61402 | |||
| c38edbfe0b | |||
| 557ada7c81 | |||
| 236ec6fc3b | |||
| 35890588ca | |||
| c6c0ecbb37 | |||
| 1fdbf4612c | |||
| 948fa3a91c | |||
| a624d52f42 | |||
| 2b50f2409e | |||
| 0b11ce64a2 | |||
| f70c6928cd | |||
| cbb6d58d1b | |||
| 4dc2e48e8c | |||
| 7385bd97c6 | |||
| 063078cd34 | |||
| 789b8a2987 | |||
| 53ea135799 | |||
| 32242687bd | |||
| 3aca556827 | |||
| 372a64bd5d | |||
| 3f83d42afc | |||
| 8bf877e400 | |||
| 29f45b787b | |||
| 68491a512d | |||
| bd72f245b1 | |||
| 4ef5082887 | |||
| 27822fb37c | |||
| be803bf122 | |||
| db29a1b6a2 | |||
| 25d4bb8d6c | |||
| 8cc47a2069 | |||
| c825e20f62 | |||
| 69eace6292 | |||
| 2284435551 | |||
| 1252b94fcb | |||
| 8d0afc087f | |||
| f789c7e2df | |||
| 80bbd035db | |||
| 0397df2f4d | |||
| cd8796b561 | |||
| 68103601e6 | |||
| c278ead451 | |||
| c44e9b6308 | |||
| 1c37537d19 | |||
| 684dec4b24 | |||
| 30f72c1519 | |||
| 69733a8f7a | |||
| c22bbf98b8 | |||
| 555611aa1f | |||
| 579cfb4a4a | |||
| 9753a63227 | |||
| 292815c014 | |||
| b38a6f2235 | |||
| 0b6f38118e | |||
| 50932bc9e2 | |||
| 99ff064072 | |||
| 51899e602c | |||
| bca15a7589 | |||
| c723dd5406 | |||
| 891f032def | |||
| 433435743e | |||
| 2a010cb00a | |||
| d13cc7d96f | |||
| 624ca77173 | |||
| ac8c95d9a6 | |||
| 492fc46fb8 | |||
| 86d1990894 | |||
| 121ee10f5a | |||
| c1e6aeece8 | |||
| 450caa124a | |||
| 85e90f2c09 | |||
| c6293afc3b | |||
| 87183f52ad | |||
| c49e092082 | |||
| 100110430a | |||
| 9d2756a8ae | |||
| 163015517e | |||
| 5b267f4a3f | |||
| e07f92c4ee | |||
| a7b496f8c1 | |||
| f562a84bb5 | |||
| e84065fd9d | |||
| c6737caae6 | |||
| a0b7b4dd96 | |||
| 4fe90900cc | |||
| e088daa15b | |||
| cc9902958e | |||
| b87b4c1089 | |||
| c2ec172617 | |||
| a60320df3a | |||
| 32f75040de | |||
| c7321a795b | |||
| 8493ef092e | |||
| a9edaaba51 | |||
| 0e89db68a3 | |||
| 4f6f26735c | |||
| b9261a33be | |||
| b4401cf10c | |||
| b55d4364ff | |||
| 4c65332e55 | |||
| 20f983874a | |||
| f859c1e32a | |||
| eb40100b0f | |||
| ad946ea482 | |||
| f547af2896 | |||
| 05caa45939 | |||
| b73f020555 | |||
| b4ee425711 | |||
| ffb4387aab | |||
| ba644ea9f1 | |||
| 602fecefa6 | |||
| d49d3156eb | |||
| 47ee4337ce | |||
| a8cdf2036d | |||
| 0ee9aecbd1 | |||
| 34b6c71e8a | |||
| ff73ffcb16 | |||
| cff8833b4c | |||
| 9f8a6598e3 | |||
| 0771270c0d | |||
| 185f97e3b2 | |||
| 3685cb3b52 | |||
| bb3cacf2cd | |||
| 241e787d0f | |||
| 4c49b69613 | |||
| 954a130f41 | |||
| 721bc67389 | |||
| 1755702e1a | |||
| a4e1d84ebb | |||
| b9e23b96eb | |||
| a22587d09b | |||
| 50c1fb2e3c | |||
| 1f00254759 | |||
| de403a68c3 | |||
| 6b19cbad44 | |||
| 1fc6509a51 | |||
| e1a78d1f0f | |||
| c7de0ba3bc | |||
| 1bfb10bf2b | |||
| 16cce1efb1 | |||
| b3025a4dfc | |||
| 11a1a27dbd | |||
| e76fe439ba | |||
| cc8513afdf | |||
| c17f5bd41e | |||
| 2d9ef9aad6 | |||
| f7bb8aab9e | |||
| c5c1980c9a | |||
| f234b8fa27 | |||
| 778e3bbd67 | |||
| 5d8d5da602 | |||
| fe449e31e3 | |||
| 617f0ae79a | |||
| cf3de0e8dc | |||
| 173c47f780 | |||
| 77e12e3658 | |||
| 8db9551700 | |||
| ece0c76158 | |||
| 8881847c61 | |||
| 4254efd49e | |||
| 6968075d38 | |||
| 3cb29e1d5c | |||
| e632b091f9 | |||
| afa92e51f6 | |||
| d746c7566c | |||
| 0a075c6934 | |||
| cef3b7d634 | |||
| c31ba651cf | |||
| d4d3426b4e | |||
| 89e3d2efec | |||
| c2326d096b | |||
| 18146346f6 | |||
| 519c514092 | |||
| 4187615fa7 | |||
| d98ea0180c | |||
| 226a4bf391 | |||
| a689a5906e | |||
| 71cbcf9c4a | |||
| 40d76d2db9 | |||
| 79fab6602f | |||
| 672d0051cc | |||
| 865ab16fdf | |||
| ce2a613858 | |||
| 087c4c60a1 | |||
| 77acaf0a1a | |||
| 12ffff9dde | |||
| 81147c4bc6 | |||
| 0e3940373d | |||
| 07dea75562 | |||
| 1bd9a6bef3 | |||
| 1bf6307480 | |||
| b8b9653fc0 | |||
| 636f81703c | |||
| 18370bb663 | |||
| 1465736974 | |||
| 3907344fef | |||
| 7c414d47cb | |||
| 5161a71bae | |||
| 9c67aa105b | |||
| adbdb1492f | |||
| 4fff0b9792 | |||
| 4c912f4538 | |||
| a305e15746 | |||
| 202492cc4b | |||
| bc74be531d | |||
| f3a932bbde | |||
| 7aa2084147 | |||
| 019a0f2b84 | |||
| 501a6b7d11 | |||
| 0370eb9236 | |||
| 4cba8ab48f | |||
| f4e7693e70 | |||
| 73adc40c4e | |||
| 844469a9a7 | |||
| faa8a8d9fc | |||
| fa59aafffb | |||
| d051cbe0f6 | |||
| 1f18c0ba02 | |||
| 034aaa2927 | |||
| 7b57c12f59 | |||
| 592b0886b2 | |||
| 642df0924d | |||
| 1685d62cae | |||
| e705e3e045 | |||
| 56a2c79fe1 | |||
| 0bb7d3ba09 | |||
| 8e92499ebb | |||
| 62e0274ff6 | |||
| fe4c4e9751 | |||
| baa03f5587 | |||
| d85560e886 | |||
| 25d594e717 | |||
| 383ba80d1e | |||
| 561e666091 | |||
| 071f8a6a84 | |||
| da95d26c58 | |||
| 9c4478302e | |||
| 28a0bdaf95 | |||
| 50ade54c3c | |||
| d43f459ad1 | |||
| 383985d331 | |||
| 5246d4cd72 | |||
| 97c4b9a83a | |||
| ff9acb50ff | |||
| ca0832e58e | |||
| 503629fdcb | |||
| eeeb453cf9 | |||
| e1f795a352 | |||
| 2b7a294cdd | |||
| c85932d46b | |||
| 75cf1acce1 | |||
| 7f5d71dada | |||
| 1f8fd3c16c | |||
| 7ccfd6c94b | |||
| 1911ef2d80 | |||
| 74347ea874 | |||
| 214d73d812 | |||
| 8c6fe7a540 | |||
| 164d5591a0 | |||
| 85cb7c88fa | |||
| 4ccbd03daf | |||
| 3df33a7367 | |||
| 13b592d1f3 | |||
| ab8df8dcde | |||
| 2b25d433c7 | |||
| 1ba8218743 | |||
| de1b69dbc9 | |||
| 8b53b4d749 | |||
| 17cf2bd1c8 | |||
| c54bb792b2 | |||
| 1746bc8797 | |||
| 635f09b741 | |||
| ee0443e40c | |||
| d02e9172da | |||
| 804df73e8d | |||
| bc55ee6bd2 | |||
| 76be95bd6c | |||
| e43e8d156e | |||
| 2aca31988f | |||
| c937827582 | |||
| c52cf836cd | |||
| 0924ad586f | |||
| abdce2df04 | |||
| 3f8c120ad0 | |||
| 365e5e8413 | |||
| 90e53ad3e2 | |||
| a724ef3b40 | |||
| ab8c0bbf04 | |||
| 96f434f276 | |||
| ba3fa50c6e | |||
| e8d4bbe5e7 | |||
| bb695ec53f | |||
| 6079dd4ba3 | |||
| 47bfac22c2 | |||
| 461e331f58 | |||
| f32e1fc643 | |||
| baeae2d8d5 | |||
| 4a67bfcf15 | |||
| 2410ea0d5b | |||
| 6482e26526 | |||
| 0a4e3cec94 | |||
| c8accea5dc | |||
| 438b8f6a14 | |||
| 522768e6c0 | |||
| 5bb45d4684 | |||
| 3d6fb85152 | |||
| 9ccf9365c3 | |||
| a9710e7a63 | |||
| f3bb826e8d | |||
| 96b91ef36b | |||
| ae9ffbb526 | |||
| 89dba4603c | |||
| 8812b6c31d | |||
| 33f538ebf4 | |||
| 56b333e7fb | |||
| 72d76e511e | |||
| 601efa53e6 | |||
| af80327995 | |||
| 3ce520d9de | |||
| 1836257f0b | |||
| 98765c7d5c | |||
| f3ca26e2c9 | |||
| b1ca43ac0f | |||
| 5ce0cf65c4 | |||
| 460a09f9eb | |||
| 40d500949b | |||
| be078b2b41 | |||
| 0fd44994ee | |||
| 09e73b16fe | |||
| b55e62f2ab | |||
| aab4d7a78b | |||
| 533f91e7ac | |||
| aed7d3ed70 | |||
| 0fdb15f9a6 | |||
| 05748cd7dc | |||
| b127492c9f | |||
| 4fde8f0753 | |||
| e7ba9e1c9d | |||
| d1812d74d6 | |||
| 17cecda23e | |||
| abc20b3a05 | |||
| 9d733a2da9 | |||
| 6bbcc58542 | |||
| 1a76e3dc9d | |||
| ff70ae633d | |||
| d1f372e439 | |||
| 041f52aaaa | |||
| 270d17814e | |||
| 16095c8086 | |||
| 94e2dbbc2c | |||
| 12580bce83 | |||
| 753b11cf15 | |||
| 010fa219aa | |||
| cdbbb114f6 | |||
| b8bd9b6ec6 | |||
| 5a26fa838f | |||
| bffe361151 | |||
| bf45beedbe | |||
| e82170efcb | |||
| 9b22ca4825 | |||
| 85cc81851f | |||
| 30273c6a66 | |||
| e01ac96b66 | |||
| 2ff282011b | |||
| f766bbbb38 | |||
| e5ef23dc5a | |||
| fddcbae4eb | |||
| 2d313e07ff | |||
| 1efa480b4e | |||
| 05a3f35512 | |||
| 72f9af4d00 | |||
| dd0f7a032f | |||
| 010c3435da | |||
| da3a89fc27 | |||
| 30e4531a8f |
+1
-1
@@ -7,4 +7,4 @@ node_modules
|
||||
npm-debug.log
|
||||
/tags
|
||||
/atom-shell/
|
||||
docs/api
|
||||
docs/output
|
||||
|
||||
+2
-5
@@ -1,9 +1,6 @@
|
||||
[submodule "vendor/bootstrap"]
|
||||
path = vendor/bootstrap
|
||||
url = https://github.com/twitter/bootstrap
|
||||
url = https://github.com/twbs/bootstrap
|
||||
[submodule "vendor/apm"]
|
||||
path = vendor/apm
|
||||
url = https://github.com/github/apm.git
|
||||
[submodule "vendor/telepath"]
|
||||
path = vendor/telepath
|
||||
url = https://github.com/github/telepath.git
|
||||
url = https://github.com/atom/apm.git
|
||||
|
||||
@@ -7,6 +7,10 @@ pairs:
|
||||
bl: Brian Lopez; brian
|
||||
jp: Justin Palmer; justin
|
||||
gt: Garen Torikian; garen
|
||||
mc: Matt Colyer; mcolyer
|
||||
bo: Ben Ogle; benogle
|
||||
jr: Jason Rudolph; jasonrudolph
|
||||
jl: Jessica Lord; jlord
|
||||
email:
|
||||
domain: github.com
|
||||
#global: true
|
||||
|
||||
@@ -1,3 +1,34 @@
|
||||
* Added: Soft wrap and tab length can now be set in the settings view
|
||||
* Fixed: Python import statements not syntax highlighting correctly
|
||||
|
||||
* Added: Terminal package now bundled by default, open with ctrl-`
|
||||
* Fixed: Fuzzy finder not showing results for files at a certain depth
|
||||
* Fixed: Atom > Preferences... menu not opening settings in focused window
|
||||
|
||||
* Fixed: Atom failing to launch if the theme being used was not found
|
||||
|
||||
* Improved: Theme changes now immediately take effect
|
||||
* Fixed: Wrap in quotes/parens now works in split panes
|
||||
* Improved: Autocomplete now includes CSS property names and values
|
||||
* Improved: Settings GUI is now a pane item
|
||||
* Added: Support package filtering in Settings GUI
|
||||
* Added: Dynamically load all config options in the Settings GUI
|
||||
* Added: Ability to bookmark lines and navigate bookmarks
|
||||
* Fixed: Error when inserting newlines in CSS
|
||||
* Fixed: Folding all will fold comments as well
|
||||
* Added: Ability to fold all code at a given indentation level
|
||||
|
||||
* Improved: cmd-n now opens a new tab and cmd-shift-n now opens a new window.
|
||||
* Added: Inspect Element context menu
|
||||
* Fixed: Save As dialog now defaults to directory path of current editor
|
||||
* Fixed: Using toggle comment shortcut respects indentation level
|
||||
|
||||
* Fixed: Search never completing in the command panel
|
||||
|
||||
* Fixed: cmd-n now works when no windows are open
|
||||
|
||||
* Fixed: Error selecting a grammar for an untitled editor
|
||||
|
||||
* Added: j/k now can be used to navigate the tree view and archive editor
|
||||
|
||||
* Fixed: Atom can now be launched when ~/.atom/config.cson doesn't exist
|
||||
|
||||
+36
-1
@@ -1,5 +1,6 @@
|
||||
# :rotating_light: Contributing to Atom :rotating_light:
|
||||
|
||||
|
||||
## Issues
|
||||
* Include screenshots and animated GIFs whenever possible, they are immensely
|
||||
helpful
|
||||
@@ -17,12 +18,46 @@
|
||||
specs
|
||||
* Style new elements in both the light and dark default themes when
|
||||
appropriate
|
||||
* New packages go in `src/packages/`
|
||||
* 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:
|
||||
* Files end with a newline
|
||||
* 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
|
||||
|
||||
## Philosophy
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
+103
-39
@@ -1,58 +1,80 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
|
||||
fm = require 'json-front-matter'
|
||||
_ = require 'underscore'
|
||||
|
||||
packageJson = require './package.json'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
appName = 'Atom.app'
|
||||
[major, minor, patch] = packageJson.version.split('.')
|
||||
buildDir = grunt.option('build-dir') ? '/tmp/atom-build'
|
||||
shellAppDir = path.join(buildDir, appName)
|
||||
contentsDir = path.join(shellAppDir, 'Contents')
|
||||
appDir = path.join(contentsDir, 'Resources', 'app')
|
||||
installDir = path.join('/Applications', appName)
|
||||
|
||||
coffeeConfig =
|
||||
options:
|
||||
sourceMap: true
|
||||
glob_to_multiple:
|
||||
expand: true
|
||||
src: [
|
||||
'src/**/*.coffee'
|
||||
'static/**/*.coffee'
|
||||
]
|
||||
dest: appDir
|
||||
ext: '.js'
|
||||
|
||||
lessConfig =
|
||||
options:
|
||||
paths: [
|
||||
'static'
|
||||
'vendor'
|
||||
]
|
||||
glob_to_multiple:
|
||||
expand: true
|
||||
src: [
|
||||
'src/**/*.less'
|
||||
'static/**/*.less'
|
||||
'themes/**/*.less'
|
||||
]
|
||||
dest: appDir
|
||||
ext: '.css'
|
||||
|
||||
csonConfig =
|
||||
options:
|
||||
rootObject: true
|
||||
glob_to_multiple:
|
||||
expand: true
|
||||
src: [
|
||||
'keymaps/*.cson'
|
||||
'src/**/*.cson'
|
||||
'static/**/*.cson'
|
||||
'themes/**/*.cson'
|
||||
]
|
||||
dest: appDir
|
||||
ext: '.json'
|
||||
|
||||
for child in fs.readdirSync('node_modules') when child isnt '.bin'
|
||||
directory = path.join('node_modules', child)
|
||||
{engines} = grunt.file.readJSON(path.join(directory, 'package.json'))
|
||||
if engines?.atom?
|
||||
coffeeConfig.glob_to_multiple.src.push("#{directory}/**/*.coffee")
|
||||
lessConfig.glob_to_multiple.src.push("#{directory}/**/*.less")
|
||||
csonConfig.glob_to_multiple.src.push("#{directory}/**/*.cson")
|
||||
|
||||
grunt.initConfig
|
||||
pkg: grunt.file.readJSON('package.json')
|
||||
|
||||
atom: {appDir, appName, buildDir, contentsDir, installDir, shellAppDir}
|
||||
|
||||
coffee:
|
||||
options:
|
||||
sourceMap: true
|
||||
glob_to_multiple:
|
||||
expand: true
|
||||
src: [
|
||||
'src/**/*.coffee'
|
||||
'static/**/*.coffee'
|
||||
]
|
||||
dest: appDir
|
||||
ext: '.js'
|
||||
coffee: coffeeConfig
|
||||
|
||||
less:
|
||||
options:
|
||||
paths: [
|
||||
'static'
|
||||
'vendor'
|
||||
]
|
||||
glob_to_multiple:
|
||||
expand: true
|
||||
src: [
|
||||
'src/**/*.less'
|
||||
'static/**/*.less'
|
||||
'themes/**/*.less'
|
||||
]
|
||||
dest: appDir
|
||||
ext: '.css'
|
||||
less: lessConfig
|
||||
|
||||
cson:
|
||||
options:
|
||||
rootObject: true
|
||||
glob_to_multiple:
|
||||
expand: true
|
||||
src: [
|
||||
'src/**/*.cson'
|
||||
'static/**/*.cson'
|
||||
'themes/**/*.cson'
|
||||
]
|
||||
dest: appDir
|
||||
ext: '.json'
|
||||
cson: csonConfig
|
||||
|
||||
coffeelint:
|
||||
options:
|
||||
@@ -77,6 +99,7 @@ module.exports = (grunt) ->
|
||||
'box-sizing': false
|
||||
'bulletproof-font-face': false
|
||||
'compatible-vendor-prefixes': false
|
||||
'display-property-grouping': false
|
||||
'fallback-colors': false
|
||||
'font-sizes': false
|
||||
'gradients': false
|
||||
@@ -102,16 +125,57 @@ module.exports = (grunt) ->
|
||||
'themes/**/*.less'
|
||||
]
|
||||
|
||||
markdown:
|
||||
guides:
|
||||
files: [
|
||||
expand: true
|
||||
cwd: 'docs'
|
||||
src: '**/*.md'
|
||||
dest: 'docs/output/'
|
||||
ext: '.html'
|
||||
]
|
||||
options:
|
||||
template: 'docs/template.jst'
|
||||
templateContext:
|
||||
tag: "v#{major}.#{minor}"
|
||||
markdownOptions:
|
||||
gfm: true
|
||||
preCompile: (src, context) ->
|
||||
parsed = fm.parse(src)
|
||||
_.extend(context, parsed.attributes)
|
||||
parsed.body
|
||||
|
||||
shell:
|
||||
'kill-atom':
|
||||
command: 'pkill Atom'
|
||||
options:
|
||||
stdout: false
|
||||
stderr: false
|
||||
failOnError: false
|
||||
|
||||
test:
|
||||
command: "#{path.join(contentsDir, 'MacOS', 'Atom')} --test --resource-path=#{__dirname}"
|
||||
options:
|
||||
stdout: true
|
||||
stderr: true
|
||||
callback: (error, stdout, stderr, callback) ->
|
||||
grunt.warn('Specs failed') if error?
|
||||
callback()
|
||||
|
||||
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', 'less', 'cson'])
|
||||
grunt.registerTask('lint', ['coffeelint', 'csslint', 'lesslint'])
|
||||
grunt.registerTask('ci', ['lint', 'partial-clean', 'update-atom-shell', 'build', 'test'])
|
||||
grunt.registerTask('test', ['shell:kill-atom', 'shell:test'])
|
||||
grunt.registerTask('ci', ['lint', 'partial-clean', 'update-atom-shell', 'build', 'set-development-version', 'test'])
|
||||
grunt.registerTask('deploy', ['partial-clean', 'update-atom-shell', 'build', 'codesign'])
|
||||
grunt.registerTask('docs', ['markdown:guides', 'build-docs'])
|
||||
grunt.registerTask('default', ['update-atom-shell', 'build', 'set-development-version', 'install'])
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||

|
||||
|
||||
Check out our [documentation on the docs tab](https://github.com/github/atom/docs).
|
||||
Check out our [guides](https://atom-docs.githubapp.com/v20.0/index.html) and [API documentation](https://atom-docs.githubapp.com/v20.0/api/index.html).
|
||||
|
||||
## Installing
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
rule "" do |t|
|
||||
puts <<-HELP
|
||||
Atom now uses grunt instead of Rake.
|
||||
See https://github.com/github/atom/pull/596 for more info.
|
||||
See https://github.com/atom/atom/pull/596 for more info.
|
||||
|
||||
tl;dr
|
||||
-----
|
||||
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
coffee -c -o /Applications/Atom.app/Contents/Resources/app/src/ src/main.coffee src/atom-application.coffee src/atom-window.coffee &&
|
||||
/Applications/Atom.app/Contents/MacOS/Atom --resource-path=$(pwd) --executed-from=$(pwd) $@
|
||||
+27
-11
@@ -1,5 +1,6 @@
|
||||
#!/bin/sh
|
||||
ATOM_PATH=/Applications/Atom.app
|
||||
ATOM_BINARY=$ATOM_PATH/Contents/MacOS/Atom
|
||||
|
||||
if [ ! -d $ATOM_PATH ]; then sleep 5; fi # Wait for Atom to reappear, Sparkle may be replacing it.
|
||||
|
||||
@@ -8,7 +9,32 @@ if [ ! -d $ATOM_PATH ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
open -a $ATOM_PATH -n --args --executed-from="$(pwd)" --pid=$$ $@
|
||||
while getopts ":whvft-:" opt; do
|
||||
case "$opt" in
|
||||
-)
|
||||
case "${OPTARG}" in
|
||||
wait)
|
||||
WAIT=1
|
||||
;;
|
||||
help|version|foreground|test)
|
||||
EXPECT_OUTPUT=1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
w)
|
||||
WAIT=1
|
||||
;;
|
||||
h|v|f|t)
|
||||
EXPECT_OUTPUT=1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ $EXPECT_OUTPUT ]; then
|
||||
$ATOM_BINARY --executed-from="$(pwd)" --pid=$$ $@
|
||||
else
|
||||
open -a $ATOM_PATH -n --args --executed-from="$(pwd)" --pid=$$ $@
|
||||
fi
|
||||
|
||||
# Used to exit process when atom is used as $EDITOR
|
||||
on_die() {
|
||||
@@ -16,16 +42,6 @@ on_die() {
|
||||
}
|
||||
trap 'on_die' SIGQUIT SIGTERM
|
||||
|
||||
# Don't exit process if we were told to wait.
|
||||
while [ "$#" -gt "0" ]; do
|
||||
case $1 in
|
||||
-W|--wait)
|
||||
WAIT=1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ $WAIT ]; then
|
||||
while true; do
|
||||
sleep 1
|
||||
|
||||
@@ -1,30 +1,11 @@
|
||||
nakedLoad 'jasmine-jquery'
|
||||
require '../spec/spec-helper'
|
||||
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
Keymap = require 'keymap'
|
||||
Point = require 'point'
|
||||
Config = require 'config'
|
||||
{Point} = require 'telepath'
|
||||
Project = require 'project'
|
||||
|
||||
require 'window'
|
||||
requireStylesheet "jasmine.less"
|
||||
|
||||
# Load TextMate bundles, which specs rely on (but not other packages)
|
||||
atom.loadTextMatePackages()
|
||||
|
||||
beforeEach ->
|
||||
# reset config after each benchmark; don't load or save from/to `config.json`
|
||||
window.config = new Config()
|
||||
spyOn(config, 'load')
|
||||
spyOn(config, 'save')
|
||||
|
||||
keymap = new Keymap
|
||||
keymap.bindDefaultKeys()
|
||||
$(window).on 'keydown', (e) -> keymap.handleKeyEvent(e)
|
||||
keymap.bindKeys '*',
|
||||
'meta-w': 'close'
|
||||
'alt-meta-i': 'show-console'
|
||||
$(document).on 'close', -> window.close()
|
||||
fsUtils = require 'fs-utils'
|
||||
TokenizedBuffer = require 'tokenized-buffer'
|
||||
|
||||
defaultCount = 100
|
||||
window.pbenchmark = (args...) -> window.benchmark(args..., profile: true)
|
||||
@@ -32,7 +13,13 @@ window.fbenchmark = (args...) -> window.benchmark(args..., focused: true)
|
||||
window.fpbenchmark = (args...) -> window.benchmark(args..., profile: true, focused: true)
|
||||
window.pfbenchmark = window.fpbenchmark
|
||||
|
||||
window.benchmarkFixturesProject = new Project(require.resolve 'benchmark/fixtures')
|
||||
window.benchmarkFixturesProject = new Project(fsUtils.resolveOnLoadPath('benchmark/fixtures'))
|
||||
|
||||
beforeEach ->
|
||||
window.project = window.benchmarkFixturesProject
|
||||
jasmine.unspy(window, 'setTimeout')
|
||||
jasmine.unspy(window, 'clearTimeout')
|
||||
jasmine.unspy(TokenizedBuffer::, 'tokenizeInBackground')
|
||||
|
||||
window.benchmark = (args...) ->
|
||||
description = args.shift()
|
||||
@@ -43,7 +30,6 @@ window.benchmark = (args...) ->
|
||||
[fn, options] = args
|
||||
{ profile, focused } = (options ? {})
|
||||
|
||||
atom.showDevTools() if profile
|
||||
method = if focused then fit else it
|
||||
method description, ->
|
||||
total = measure ->
|
||||
|
||||
@@ -1,31 +1,28 @@
|
||||
require 'benchmark-helper'
|
||||
require './benchmark-helper'
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
TokenizedBuffer = require 'tokenized-buffer'
|
||||
RootView = require 'root-view'
|
||||
|
||||
describe "editor.", ->
|
||||
editor = null
|
||||
|
||||
beforeEach ->
|
||||
window.rootViewParentSelector = '#jasmine-content'
|
||||
window.attachRootView(require.resolve('benchmark/fixtures'))
|
||||
window.rootView = new RootView
|
||||
window.rootView.attachToDom()
|
||||
|
||||
|
||||
rootView.width(1024)
|
||||
rootView.height(768)
|
||||
rootView.open() # open blank editor
|
||||
editor = rootView.getActiveEditor()
|
||||
editor = rootView.getActiveView()
|
||||
|
||||
afterEach ->
|
||||
if editor.pendingDisplayUpdate
|
||||
waitsFor "editor to finish rendering", (done) ->
|
||||
editor.on 'editor:display-updated', done
|
||||
|
||||
runs ->
|
||||
projectPath = project.getPath()
|
||||
$(window).off 'beforeunload'
|
||||
window.shutdown()
|
||||
atom.setRootViewStateForPath(projectPath, null)
|
||||
|
||||
describe "keymap.", ->
|
||||
event = null
|
||||
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
## Command Panel
|
||||
|
||||
The command panel contains a partial implementation of the [Sam command language](http://man.cat-v.org/plan_9/1/sam).
|
||||
In addition, packages are free to design and define any scoped command.
|
||||
|
||||
Pop open the command line by hitting .
|
||||
You can get a list of commands available to Atom (including any keybindings) by hitting `meta-p`.
|
||||
|
||||
## Examples
|
||||
|
||||
`,` selects the entire file
|
||||
|
||||
`1,4` selects lines 1-4 in the current file
|
||||
|
||||
`/pattern` selects the first match after the cursor/selection
|
||||
|
||||
`s/pattern/replacement` replaces the first text matching pattern in current selection
|
||||
|
||||
`s/pattern/replacement/g` replaces all text matching pattern in current selection
|
||||
|
||||
`,s/pattern/replacement/g` replaces all text matching pattern in file
|
||||
|
||||
`1,4s/pattern/replacement` replaces all text matching pattern in lines 1-4
|
||||
|
||||
`x/pattern` selects all matches in the current selections
|
||||
|
||||
`,x/pattern` selects all matches in the file
|
||||
|
||||
`,x/pattern1/ x/pattern2` "structural regex" - selects all matches of pattern2 inside matches of pattern1
|
||||
@@ -1,3 +0,0 @@
|
||||
# Built-In Packages
|
||||
|
||||
Atom ships with several optional built-in packages.
|
||||
@@ -1,7 +0,0 @@
|
||||
## Markdown Preview
|
||||
|
||||
The `markdown-preview` extension displays the rendered HTML for the markdown
|
||||
in the current editor.
|
||||
|
||||
It can be activated from the editor using the `ctrl-m` key-binding and is
|
||||
currently enabled for `.markdown`, `.md`, `.mkd`, `.mkdown`, and `.ron` files.
|
||||
@@ -1,33 +0,0 @@
|
||||
## Wrap Guide
|
||||
|
||||
The `wrap-guide` extension places a vertical line in each editor at a certain
|
||||
column to guide your formatting, so lines do not exceed a certain width.
|
||||
|
||||
By default, the wrap-guide is placed at the 80th column.
|
||||
|
||||
### Configuration
|
||||
|
||||
You can customize where the column is placed using the `wrapGuide.columns`
|
||||
config option:
|
||||
|
||||
```coffeescript
|
||||
"wrap-guide":
|
||||
columns: [
|
||||
{ pattern: "\.mm$", column: 200 },
|
||||
{ pattern: "\.cc$", column: 120 }
|
||||
]
|
||||
```
|
||||
|
||||
The above config example would place the guide at the 200th column for paths
|
||||
that end with `.mm` and place the guide at the 120th column for paths that end
|
||||
with `.cc`.
|
||||
|
||||
You can configure the color and/or width of the line by adding the following
|
||||
CSS to a custom stylesheet:
|
||||
|
||||
```css
|
||||
.wrap-guide {
|
||||
width: 10px;
|
||||
background-color: red;
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,492 @@
|
||||
{{{
|
||||
"title": "Creating a Package"
|
||||
}}}
|
||||
|
||||
# Authoring Packages
|
||||
|
||||
Packages are at the core of Atom. Nearly everything outside of the main editor manipulation
|
||||
is handled by a package. That includes "core" pieces like the command panel, status bar,
|
||||
file tree, and more.
|
||||
|
||||
A package can contain a variety of different resource types to change Atom's
|
||||
behavior. The basic package layout is as follows (though not every package will
|
||||
have all of these directories):
|
||||
|
||||
```text
|
||||
my-package/
|
||||
lib/
|
||||
stylesheets/
|
||||
keymaps/
|
||||
snippets/
|
||||
grammars/
|
||||
spec/
|
||||
package.json
|
||||
index.coffee
|
||||
```
|
||||
|
||||
## package.json
|
||||
|
||||
Similar to [npm packages][npm], Atom packages
|
||||
can contain a _package.json_ file in their top-level directory. This file contains metadata
|
||||
about the package, such as the path to its "main" module, library dependencies,
|
||||
and manifests specifying the order in which its resources should be loaded.
|
||||
|
||||
In addition to the regular [npm package.json keys](https://npmjs.org/doc/json.html)
|
||||
available, Atom package.json files have their own additions.
|
||||
|
||||
- `main` (**Required**): the path to the CoffeeScript file that's the entry point
|
||||
to your package
|
||||
- `stylesheets` (**Optional**): an Array of Strings identifying the order of the
|
||||
stylesheets your package needs to load. If not specified, stylesheets in the _stylesheets_
|
||||
directory are added alphabetically.
|
||||
- `keymaps`(**Optional**): an Array of Strings identifying the order of the
|
||||
key mappings your package needs to load. If not specified, mappings in the _keymaps_
|
||||
directory are added alphabetically.
|
||||
- `snippets` (**Optional**): an Array of Strings identifying the order of the
|
||||
snippets your package needs to load. If not specified, snippets in the _snippets_
|
||||
directory are added alphabetically.
|
||||
- `activationEvents` (**Optional**): an Array of Strings identifying events that
|
||||
trigger your package's activation. You can delay the loading of your package until
|
||||
one of these events is trigged.
|
||||
|
||||
## Source Code
|
||||
|
||||
If you want to extend Atom's behavior, your package should contain a single
|
||||
top-level module, which you export from _index.coffee_ (or whichever file is
|
||||
indicated by the `main` key in your _package.json_ file). The remainder of your
|
||||
code should be placed in the `lib` directory, and required from your top-level
|
||||
file.
|
||||
|
||||
Your package's top-level module is a singleton object that manages the lifecycle
|
||||
of your extensions to Atom. Even if your package creates ten different views and
|
||||
appends them to different parts of the DOM, it's all managed from your top-level
|
||||
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).
|
||||
|
||||
- `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
|
||||
the window is later restored, the data you returned is passed to your
|
||||
module's `activate` method so you can restore your view to where the user left
|
||||
off.
|
||||
|
||||
- `deactivate()`: This **optional** method is called when the window is shutting
|
||||
down. If your package is watching any files or holding external resources in any
|
||||
other way, release them here. If you're just subscribing to things on window,
|
||||
you don't need to worry because that's getting torn down anyway.
|
||||
|
||||
### Simple Package Code
|
||||
|
||||
```text
|
||||
my-package/
|
||||
package.json # optional
|
||||
index.coffee
|
||||
lib/
|
||||
my-package.coffee
|
||||
```
|
||||
|
||||
`index.coffee`:
|
||||
```coffeescript
|
||||
module.exports = require "./lib/my-package"
|
||||
```
|
||||
|
||||
`my-package/my-package.coffee`:
|
||||
```coffeescript
|
||||
module.exports =
|
||||
activate: (rootView, state) -> # ...
|
||||
deactivate: -> # ...
|
||||
serialize: -> # ...
|
||||
```
|
||||
|
||||
Beyond this simple contract, your package has full access to Atom's internal
|
||||
API. Anything we call internally, you can call as well. Be aware that since we
|
||||
are early in development, APIs are subject to change and we have not yet
|
||||
established clear boundaries between what is public and what is private. Also,
|
||||
please collaborate with us if you need an API that doesn't exist. Our goal is
|
||||
to build out Atom's API organically based on the needs of package authors like
|
||||
you.
|
||||
|
||||
See [Atom's built-in packages](https://github.com/atom/atom/)
|
||||
for examples of Atom's API in action.
|
||||
|
||||
## Stylesheets
|
||||
|
||||
Stylesheets for your package should be placed in the _stylesheets_ directory.
|
||||
Any stylesheets in this directory will be loaded and attached to the DOM when
|
||||
your package is activated. Stylesheets can be written as CSS or LESS.
|
||||
|
||||
An optional `stylesheets` array in your _package.json_ can list the stylesheets by
|
||||
name to specify a loading order; otherwise, stylesheets are loaded alphabetically.
|
||||
|
||||
## Keymaps
|
||||
|
||||
Keymaps are placed in the _keymaps_ subdirectory. It's a good idea to provide
|
||||
default keymaps for your extension, especially if you're also adding a new command.
|
||||
|
||||
By default, all keymaps are loaded in alphabetical order. An optional `keymaps`
|
||||
array in your _package.json_ can specify which keymaps to load and in what order.
|
||||
|
||||
See the [main keymaps documentation](../internals/keymaps.md) for more information on
|
||||
how keymaps work.
|
||||
|
||||
## Snippets
|
||||
|
||||
An extension can supply language snippets in the _snippets_ directory. These can
|
||||
be `.cson` or `.json` files. Here's an example:
|
||||
|
||||
```coffeescript
|
||||
".source.coffee .specs":
|
||||
"Expect":
|
||||
prefix: "ex"
|
||||
body: "expect($1).to$2"
|
||||
"Describe":
|
||||
prefix: "de"
|
||||
body: """
|
||||
describe "${1:description}", ->
|
||||
${2:body}
|
||||
"""
|
||||
```
|
||||
|
||||
A snippets file contains scope selectors at its top level (`.source.coffee .spec`).
|
||||
Each scope selector contains a hash of snippets keyed by their name (`Expect`, `Describe`).
|
||||
Each snippet also specifies a `prefix` and a `body` key. The `prefix` represents
|
||||
the first few letters to type before hitting the `tab` key to autocomplete. The
|
||||
`body` defines the autofilled text. You can use placeholders like `$1`, `$2`, to indicate
|
||||
regions in the body the user can navigate to every time they hit `tab`.
|
||||
|
||||
All files in the directory are automatically loaded, unless the
|
||||
_package.json_ supplies a `snippets` key. As with all scoped
|
||||
items, snippets loaded later take precedence over earlier snippets when two
|
||||
snippets match a scope with the same specificity.
|
||||
|
||||
## Language Grammars
|
||||
|
||||
If you're developing a new language grammar, you'll want to place your file in
|
||||
the _grammars_ directory. Each grammar is a pairing of two keys, `match` and
|
||||
`captures`. `match` is a regular expression identifying the pattern to highlight,
|
||||
while `captures` is a JSON representing what to do with each matching group.
|
||||
For example:
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
'match': '(?:^|\\s)(__[^_]+__)'
|
||||
'captures':
|
||||
'1': 'name': 'markup.bold.gfm'
|
||||
}
|
||||
```
|
||||
|
||||
This indicates that the first matching capture (`(__[^_]+__)`) should have the
|
||||
`markup.bold.gfm` token applied to it.
|
||||
|
||||
To capture a single group, simply use the `name` key instead:
|
||||
|
||||
```json
|
||||
{
|
||||
'match': '^#{1,6}\\s+.+$'
|
||||
'name': 'markup.heading.gfm'
|
||||
}
|
||||
```
|
||||
|
||||
This indicates that Markdown header lines (`#`, `##`, `###`) should be applied with
|
||||
the `markup.heading.gfm` token.
|
||||
|
||||
More information about the significance of these tokens can be found in
|
||||
[section 12.4 of the TextMate Manual](http://manual.macromates.com/en/language_grammars.html).
|
||||
|
||||
Your grammar should also include a `filetypes` array, which is a list of file extensions
|
||||
your grammar supports:
|
||||
|
||||
```
|
||||
'fileTypes': [
|
||||
'markdown'
|
||||
'md'
|
||||
'mkd'
|
||||
'mkdown'
|
||||
'ron'
|
||||
]
|
||||
```
|
||||
|
||||
## Writing Tests
|
||||
|
||||
Your package **should** have tests, and if they're placed in the _spec_ directory,
|
||||
they can be run by Atom.
|
||||
|
||||
Under the hood, [Jasmine](https://github.com/pivotal/jasmine) is being used to run
|
||||
to execute the tests, so you can assume that any DSL available there is available
|
||||
to your package as well.
|
||||
|
||||
# Full Example
|
||||
|
||||
Let's take a look at creating our first package.
|
||||
|
||||
Atom has a command you can enter that'll create a package for you:
|
||||
`package-generator:generate`. Otherwise, you can hit `cmd-p`, and start typing
|
||||
"Package Generator." Once you activate this package, it'll ask you for a name for
|
||||
your new package. Let's call ours _changer_.
|
||||
|
||||
Now, _changer_ is going to have a default set of folders and files created for us.
|
||||
Hit `cmd-r` to reload Atom, then hit `cmd-p` and start typing "Changer." You'll
|
||||
see a new `Changer:Toggle` command which, if selected, pops up a new message. So
|
||||
far, so good!
|
||||
|
||||
In order to demonstrate the capabilities of Atom and its API, our Changer plugin
|
||||
is going to do two things:
|
||||
|
||||
1. It'll show only modified files in the file tree
|
||||
2. It'll append a new pane to the editor with some information about the modified
|
||||
files
|
||||
|
||||
Let's get started!
|
||||
|
||||
## Changing Keybindings and Commands
|
||||
|
||||
Since Changer is primarily concerned with the file tree, let's write a keybinding
|
||||
that works only when the tree is focused. Instead of using the default `toggle`,
|
||||
our keybinding executes a new command called `magic`.
|
||||
|
||||
_keymaps/changer.cson_ can easily become this:
|
||||
|
||||
```cson
|
||||
'.tree-view-scroller':
|
||||
'ctrl-V': 'changer:magic'
|
||||
```
|
||||
|
||||
Notice that the keybinding is called `ctrl-V`--that's actually `ctrl-shift-v`.
|
||||
You can use capital letters to denote using `shift` for your binding.
|
||||
|
||||
`.tree-view-scroller` represents the parent container for the tree view. Keybindings
|
||||
only work within the context of where they're entered. For example, hitting `ctrl-V`
|
||||
anywhere other than tree won't do anything. 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 use the `rootView.command` method. This
|
||||
takes a command name and executes a function in the code. For example:
|
||||
|
||||
```coffeescript
|
||||
rootView.command "changer:magic", => @magic()
|
||||
```
|
||||
|
||||
It's common practice to namespace your commands with your package name, and separate
|
||||
it with a colon (`:`). Rename the existing `toggle` method to `magic` to get the
|
||||
binding to work.
|
||||
|
||||
Reload the editor, click on the tree, hit your keybinding, and...nothing happens! What the heck?!
|
||||
|
||||
Open up the _package.json_ file, and notice the key that says `activationEvents`.
|
||||
Basically, this tells Atom to not load a package until it hears a certain event.
|
||||
Let's change the event to `changer:magic` and reload the editor.
|
||||
|
||||
Hitting the key binding on the tree now works!
|
||||
|
||||
## Working with styles
|
||||
|
||||
The next step is to hide elements in the tree that aren't modified. To do that,
|
||||
we'll first try and get a list of files that have not changed.
|
||||
|
||||
All packages are able to use jQuery in their code. In fact, we have [a list of
|
||||
some of the bundled libraries Atom provides by default](#included-libraries).
|
||||
|
||||
Let's bring in jQuery:
|
||||
|
||||
```coffeescript
|
||||
$ = require 'jquery'
|
||||
```
|
||||
|
||||
Now, we can query the tree to get us a list of every file that _wasn't_ modified:
|
||||
|
||||
```coffeescript
|
||||
magic: ->
|
||||
$('ol.entries li').each (i, el) ->
|
||||
if !$(el).hasClass("modified")
|
||||
console.log el
|
||||
```
|
||||
|
||||
You can access the dev console by hitting `alt-cmd-i`. When we execute the
|
||||
`changer:magic` command, the browser console lists the items that are not being
|
||||
modified. Let's add a class to each of these elements called `hide-me`:
|
||||
|
||||
```coffeescript
|
||||
magic: ->
|
||||
$('ol.entries li').each (i, el) ->
|
||||
if !$(el).hasClass("modified")
|
||||
$(el).addClass("hide-me")
|
||||
```
|
||||
|
||||
With our newly added class, we can manipulate the visibility of the elements
|
||||
with a simple stylesheet. Open up _changer.css_ in the _stylesheets_ directory,
|
||||
and add a single entry:
|
||||
|
||||
```css
|
||||
ol.entries .hide-me {
|
||||
display: none;
|
||||
}
|
||||
```
|
||||
|
||||
Refresh atom, and run the `changer` command. You'll see all the non-changed files
|
||||
disappear from the tree. There are a number of ways you can get the list back;
|
||||
let's just naively iterate over the same elements and remove the class:
|
||||
|
||||
```coffeescript
|
||||
magic: ->
|
||||
$('ol.entries li').each (i, el) ->
|
||||
if !$(el).hasClass("modified")
|
||||
if !$(el).hasClass("hide-me")
|
||||
$(el).addClass("hide-me")
|
||||
else
|
||||
$(el).removeClass("hide-me")
|
||||
```
|
||||
|
||||
## Creating a New Pane
|
||||
|
||||
The next goal of this package is to append a pane to the Atom editor that lists
|
||||
some information about the modified files.
|
||||
|
||||
To do that, we're going to first create a new class method called `content`. Every
|
||||
package that extends from the `View` class can provide an optional class method
|
||||
called `content`. The `content` method constructs the DOM that your package uses
|
||||
as its UI. The principals of `content` are built entirely on [SpacePen](https://github.com/nathansobo/space-pen),
|
||||
which we'll touch upon only briefly here.
|
||||
|
||||
Our display will simply be an unordered list of the file names, and their
|
||||
modified times. Let's start by carving out a `div` to hold the filenames:
|
||||
|
||||
```coffeescript
|
||||
@content: ->
|
||||
@div class: 'modified-files-container', =>
|
||||
@ul class: 'modified-files-list', outlet: 'modifiedFilesList', =>
|
||||
@li 'Test'
|
||||
@li 'Test2'
|
||||
```
|
||||
|
||||
You can add any HTML5 attribute you like. `outlet` names the variable
|
||||
your package can uses to manipulate the element directly. The fat pipe (`=>`) indicates
|
||||
that the next set are nested children.
|
||||
|
||||
We'll add one more line to `magic` to make this pane appear:
|
||||
|
||||
```coffeescript
|
||||
rootView.vertical.append(this)
|
||||
```
|
||||
|
||||
If you hit the key command, you'll see a box appear right underneath the editor.
|
||||
Success!
|
||||
|
||||
Before we populate this, let's apply some logic to toggle the pane off and on, just
|
||||
like we did with the tree view:
|
||||
|
||||
```coffeescript
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
rootView.vertical.children().last().remove()
|
||||
else
|
||||
rootView.vertical.append(this)
|
||||
```
|
||||
|
||||
There are about a hundred different ways to toggle a pane on and off, and this
|
||||
might not be the most efficient one. If you know your package needs to be toggled
|
||||
on and off more freely, it might be better to draw the UI during the initialization,
|
||||
then immediately call `hide()` on the element to remove it from the view. You can
|
||||
then swap between `show()` and `hide()`, and instead of forcing Atom to add and remove
|
||||
the element as we're doing here, it'll just set a CSS property to control your package's
|
||||
visibility.
|
||||
|
||||
You might have noticed that our two `li` elements aren't showing up. Let's set
|
||||
a color on them so that they pop. Open up `changer.css` and add this CSS:
|
||||
|
||||
```css
|
||||
ul.modified-files-list {
|
||||
color: white;
|
||||
}
|
||||
```
|
||||
|
||||
Refresh Atom, hit the key combo, and see your brilliantly white test list.
|
||||
|
||||
## Calling Node.js Code
|
||||
|
||||
Since Atom is built on top of Node.js, you can call any of its libraries, including
|
||||
other modules that your package requires.
|
||||
|
||||
We'll iterate through our resulting tree, and construct the path to our modified
|
||||
file based on its depth in the tree:
|
||||
|
||||
```coffeescript
|
||||
path = require 'path'
|
||||
|
||||
# ...
|
||||
|
||||
modifiedFiles = []
|
||||
# for each single entry...
|
||||
$('ol.entries li.file.modified span.name').each (i, el) ->
|
||||
filePath = []
|
||||
# ...grab its name...
|
||||
filePath.unshift($(el).text())
|
||||
|
||||
# ... then find its parent directories, and grab their names
|
||||
parents = $(el).parents('.directory.modified')
|
||||
parents.each (i, el) ->
|
||||
filePath.unshift($(el).find('div.header span.name').eq(0).text())
|
||||
|
||||
modifiedFilePath = path.join(project.rootDirectory.path, filePath.join(path.sep))
|
||||
modifiedFiles.push modifiedFilePath
|
||||
```
|
||||
|
||||
`modifiedFiles` is an array containing a list of our modified files. We're also using
|
||||
the node.js [`path` library](http://nodejs.org/docs/latest/api/path.html) to get
|
||||
the proper directory separator for our system.
|
||||
|
||||
Let's remove the two `@li` elements we added in `@content`, so that we can populate
|
||||
our `modifiedFilesList` with real information. We'll do that by iterating over
|
||||
`modifiedFiles`, accessing a file's last modified time, and appending it to
|
||||
`modifiedFilesList`:
|
||||
|
||||
```coffeescript
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
rootView.vertical.children().last().remove()
|
||||
else
|
||||
for file in modifiedFiles
|
||||
stat = fs.lstatSync(file)
|
||||
mtime = stat.mtime
|
||||
@modifiedFilesList.append("<li>#{file} - Modified at #{mtime}")
|
||||
rootView.vertical.append(this)
|
||||
```
|
||||
|
||||
When you toggle the modified files list, your pane is now populated with the filenames
|
||||
and modified times of files in your project. You might notice that subsequent calls
|
||||
to this command reduplicate information. We could provide an elegant way of rechecking
|
||||
files already in the list, but for this demonstration, we'll just clear the `modifiedFilesList`
|
||||
each time it's closed:
|
||||
|
||||
```coffeescript
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
@modifiedFilesList.empty()
|
||||
rootView.vertical.children().last().remove()
|
||||
else
|
||||
for file in modifiedFiles
|
||||
stat = fs.lstatSync(file)
|
||||
mtime = stat.mtime
|
||||
@modifiedFilesList.append("<li>#{file} - Modified at #{mtime}")
|
||||
rootView.vertical.append(this)
|
||||
```
|
||||
|
||||
# Included Libraries
|
||||
|
||||
In addition to core node.js modules, all packages can `require` the following popular
|
||||
libraries into their packages:
|
||||
|
||||
* [SpacePen](https://github.com/nathansobo/space-pen) (as `require 'space-pen'`)
|
||||
* [jQuery](http://jquery.com/) (as `require 'jquery'`)
|
||||
* [Underscore](http://underscorejs.org/) (as `require 'underscore'`)
|
||||
|
||||
Additional libraries can be found by browsing Atom's _node_modules_ folder.
|
||||
|
||||
|
||||
[npm]: http://en.wikipedia.org/wiki/Npm_(software)
|
||||
@@ -0,0 +1,5 @@
|
||||
{{{
|
||||
"title": "Creating a Theme"
|
||||
}}}
|
||||
|
||||
# Authoring Themes (Not Yet Implemented)
|
||||
@@ -1,9 +1,13 @@
|
||||
{{{
|
||||
"title": "Customizing Atom"
|
||||
}}}
|
||||
|
||||
# Configuration Settings
|
||||
|
||||
## Your .atom Directory
|
||||
|
||||
When you install Atom, an _.atom_ directory is created in your home directory.
|
||||
If you press `meta-,`, that directory is opened in a new window. For the
|
||||
If you press `cmd-,`, that directory is opened in a new window. For the
|
||||
time being, this serves as the primary interface for adjusting configuration
|
||||
settings, adding and changing key bindings, tweaking styles, etc.
|
||||
|
||||
@@ -81,36 +85,17 @@ keymaps or third-party packages.
|
||||
Atom comes bundled with two themes `atom-dark-*` and `atom-light-*`.
|
||||
|
||||
Because Atom themes are based on CSS, it's possible to have multiple themes
|
||||
active at the same time. For example, you'll usually select a theme for the UI
|
||||
and another theme for syntax highlighting. You can select themes by specifying
|
||||
them in the `core.themes` array in your `config.cson`:
|
||||
active at the same time.
|
||||
|
||||
```coffee-script
|
||||
core:
|
||||
themes: ["atom-light-ui", "atom-light-syntax"]
|
||||
# or, if the sun is going down:
|
||||
# themes: ["atom-dark-ui", "atom-dark-syntax"]
|
||||
```
|
||||
For example, you'll usually select a theme for the UI and another theme for
|
||||
syntax highlighting. You can change themes from the preferences pane.
|
||||
|
||||
You install new themes by placing them in the _~/.atom/themes_ directory. A
|
||||
theme can be a CSS file, a directory containing multiple CSS files, or a
|
||||
TextMate theme (either _.tmTheme_ or _.plist_).
|
||||
theme can be a single LESS file or a directory containing multiple LESS files.
|
||||
|
||||
## Installing Packages
|
||||
|
||||
## Installing Packages (Partially Implemented)
|
||||
|
||||
To install a package, clone it into the _~/.atom/packages_ directory. Atom will
|
||||
also load grammars and snippets from TextMate bundles. If you want to disable a
|
||||
package without removing it from the packages directory, insert its name into
|
||||
_config.core.disabledPackages_:
|
||||
|
||||
```coffeescript
|
||||
core:
|
||||
disabledPackages: [
|
||||
"fuzzy-finder",
|
||||
"tree-view"
|
||||
]
|
||||
```
|
||||
FIXME: Rewrite for the new dialog.
|
||||
|
||||
## Quick Personal Hacks
|
||||
|
||||
@@ -122,17 +107,19 @@ make customizations. You have full access to Atom's API from code in this file.
|
||||
Please refer to the [Atom Internals Guide](./internals/intro,md) for more information. If your
|
||||
customizations become extensive, consider [creating a package](./packages/creating_packages.md).
|
||||
|
||||
### user.css
|
||||
### user.less
|
||||
|
||||
If you want to apply quick-and-dirty personal styling changes without creating
|
||||
an entire theme that you intend to distribute, you can add styles to
|
||||
_user.css_ in your _~/.atom_ directory.
|
||||
_user.less_ in your _~/.atom_ directory.
|
||||
|
||||
For example, to change the color of the highlighted line number for the line that
|
||||
contains the cursor, you could add the following style to _user.css_:
|
||||
contains the cursor, you could add the following style to _user.less_:
|
||||
|
||||
```less
|
||||
@highlight-color: pink;
|
||||
|
||||
```css
|
||||
.editor .line-number.cursor-line {
|
||||
color: pink;
|
||||
color: @highlight-color;
|
||||
}
|
||||
```
|
||||
+47
-213
@@ -1,63 +1,46 @@
|
||||
{{{
|
||||
"title": "Getting Started"
|
||||
}}}
|
||||
|
||||
# Getting Started
|
||||
|
||||
Welcome to Atom. This documentation provides a basic introduction to being
|
||||
productive with this editor. We'll then delve into more details about configuring,
|
||||
theming, and extending Atom.
|
||||
Welcome to Atom! This guide provides a quick introduction so you can be
|
||||
productive as quickly as possible. There are also guides which cover
|
||||
[configuring][configuring], [theming][theming], and [extending][extending] Atom.
|
||||
|
||||
## The Command Palette
|
||||
|
||||
If there's one key-command you must remember in Atom, it should be `meta-p` (`meta` is
|
||||
synonymous with the ⌘ key). You can always hit `meta-p` to bring up a list of
|
||||
commands that are relevant to the currently focused UI 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](#customizing-key-bindings) section.
|
||||
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 UI 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](#customizing-key-bindings)
|
||||
section below.
|
||||
|
||||

|
||||
|
||||
## Basic Key Bindings
|
||||
|
||||
You can always use `meta-p` to explore available commands and their
|
||||
bindings, but here's a list of a few useful commands.
|
||||
|
||||
- `meta-o` : open a file or directory
|
||||
- `meta-n` : open new window
|
||||
- `meta-r` : reload the current window
|
||||
- `meta-alt-ctrl-s` : run test specs
|
||||
- `meta-t` : open file finder to navigate files in your project
|
||||
- `meta-;` : open command prompt
|
||||
- `meta-f` : open command prompt with `/` for a local file search
|
||||
- `meta-g` : repeat the last local search
|
||||
- `meta-shift-f` : open command prompt with `Xx/` for a project-wide search
|
||||
- `meta-\` : focus/open tree view, or close it when it is focused
|
||||
- `meta-|` : open tree view with the current file selected
|
||||
- `ctrl-w v`, `ctrl-|` : split screen vertically
|
||||
- `ctrl-w s`, `ctrl--` : split screen horizontally
|
||||
- `meta-l` : go to line
|
||||
|
||||
## Usage Basics
|
||||
|
||||
### If You See A Rendering Bug
|
||||
|
||||
Things are pretty stable, but we think we have a couple rendering bugs lurking
|
||||
that are hard to reproduce. If you see one, please hit `meta-p` and type
|
||||
"save debug snapshot". Run that command to save a snapshot of the misbehaving
|
||||
editor and send it to us, along with a screenshot and your best description of
|
||||
how you produced the bug. Refreshing with `meta-r` should usually resolve the
|
||||
issue so you can keep working.
|
||||
## The Basics
|
||||
|
||||
### 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.
|
||||
|
||||
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.
|
||||
|
||||
#### Finding Files
|
||||
|
||||
The fastest way to find a file in your project is to use the fuzzy finder. Just
|
||||
hit `meta-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 `meta-b` to bring
|
||||
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.
|
||||
|
||||
You can also use the tree view to navigate to a file. To open or move focus to
|
||||
the tree view, hit `meta-\`. You can then navigate to a file and select it with
|
||||
the tree view, hit `cmd-\`. You can then navigate to a file and select it with
|
||||
`return`.
|
||||
|
||||
#### Adding, Moving, Deleting Files
|
||||
@@ -70,49 +53,21 @@ 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`.
|
||||
|
||||
### Searching For Stuff
|
||||
### Searching
|
||||
|
||||
#### Using the Command Line
|
||||
#### Find and Replace
|
||||
|
||||
Atom has a command line similar to old-school editors such as emacs and vim. Nearly
|
||||
every command has a key binding which you can discover with `meta-p`.
|
||||
|
||||
The command line is also (currently) the only place you can perform a search. Hitting
|
||||
`meta-f` opens the command line and prepopulates it with the `/` command. This finds
|
||||
text in the current buffer, starting at the location of the cursor. Pressing `meta-g`
|
||||
repeats the search. Hitting `meta-shift-f` opens the command line and prepopulates
|
||||
it with `Xx/`, which is a composite command that performs a global search. The results
|
||||
of the search appear in the operation preview list, which you can focus
|
||||
with `meta-:`.
|
||||
|
||||
Atom's command language is still under construction, and is loosely based on
|
||||
the [Sam editor](http://doc.cat-v.org/bell_labs/sam_lang_tutorial/) from the
|
||||
Plan 9 operating system. It's similar to Ex mode in vim, but is selection-based
|
||||
rather than line-based. It allows you to compose commands together in
|
||||
interesting ways.
|
||||
FIXME: Describe https://github.com/atom/find-and-replace
|
||||
|
||||
#### Navigating By Symbols
|
||||
|
||||
If you want to jump to a method, you can use the ctags-based symbols package.
|
||||
The `meta-j` binding opens a list of all symbols in the current file. The
|
||||
`meta-shift-j` binding opens a list of all symbols for the current project
|
||||
based on a tags file. `meta-.` jumps to the tag for the word currently
|
||||
under the cursor.
|
||||
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.
|
||||
|
||||
Make sure you have a tags file generated for the project for
|
||||
the latter of these two bindings to work. 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](https://github.com/kevinsawicki/dotfiles/blob/master/.ctags).
|
||||
|
||||
### Replacing Stuff
|
||||
|
||||
To perform a replacement, open up the command line with `meta-;` and use the `s`
|
||||
command, as follows: `s/foo/bar/g`. Note that if you have a selection, the
|
||||
replacement will only occur inside the selected text. An empty selection will
|
||||
cause the replacement to occur across the whole buffer. If you want to run the
|
||||
command on the whole buffer even if you have a selection, precede your
|
||||
substitution with the `,` address; this indicates that the following command should
|
||||
run on the whole buffer.
|
||||
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](https://github.com/kevinsawicki/dotfiles/blob/master/.ctags).
|
||||
|
||||
### Split Panes
|
||||
|
||||
@@ -130,145 +85,24 @@ planning to improve it soon.
|
||||
### Soft-Wrap
|
||||
|
||||
If you want to toggle soft wrap, trigger the command from the command palette.
|
||||
Hit `meta-p` to open the palette, then type "wrap" to find the correct
|
||||
Hit `cmd-p` to open the palette, then type "wrap" to find the correct
|
||||
command.
|
||||
|
||||
## Your .atom Directory
|
||||
## Configuration
|
||||
|
||||
When you install Atom, an `.atom` directory is created in your home directory.
|
||||
If you press `meta-,`, that directory will be opened in a new window. For the
|
||||
time being, this will serve as the primary interface for adjusting configuration
|
||||
If you press `cmd-,`, a configuration panel will appear in the currently focused
|
||||
pane. This will serve as the primary interface for adjusting configuration
|
||||
settings, adding and changing key bindings, tweaking styles, etc.
|
||||
|
||||
## Configuration Settings
|
||||
For more advanced configuration see the [customization guide][customization].
|
||||
|
||||
Atom loads configuration settings from the `config.cson` file in your `~/.atom`
|
||||
directory, which contains CoffeeScript-style JSON:
|
||||
## Installing Packages
|
||||
|
||||
```coffeescript
|
||||
core:
|
||||
hideGitIgnoredFiles: true
|
||||
editor:
|
||||
fontSize: 18
|
||||
```
|
||||
To install a package, open the configuration panel and select the packages tab.
|
||||
|
||||
Configuration is broken into namespaces, which are defined by the config hash's
|
||||
top-level keys. In addition to Atom's core components, each package may define
|
||||
its own namespace.
|
||||
FIXME: Needs more details.
|
||||
|
||||
### Glossary of Config Keys
|
||||
|
||||
- core
|
||||
- disabledPackages: An array of package names to disable
|
||||
- hideGitIgnoredFiles: Whether files in the .gitignore should be hidden
|
||||
- ignoredNames: File names to ignore across all of atom (not fully implemented)
|
||||
- themes: An array of theme names to load, in cascading order
|
||||
- autosave: Save a resource when its view loses focus
|
||||
- editor
|
||||
- autoIndent: Enable/disable basic auto-indent (defaults to true)
|
||||
- autoIndentOnPaste: Enable/disable auto-indented pasted text (defaults to false)
|
||||
- nonWordCharacters: A string of non-word characters to define word boundaries
|
||||
- fontSize
|
||||
- fontFamily
|
||||
- invisibles: Specify characters that Atom renders for invisibles in this hash
|
||||
- tab: Hard tab characters
|
||||
- cr: Carriage return (For Microsoft-style line endings)
|
||||
- eol: `\n` characters
|
||||
- space: Leading and trailing space characters
|
||||
- preferredLineLength: Packages such as autoflow use this (defaults to 80)
|
||||
- showInvisibles: Whether to render placeholders for invisible characters (defaults to false)
|
||||
- fuzzyFinder
|
||||
- ignoredNames: Files to ignore *only* in the fuzzy-finder
|
||||
- whitespace
|
||||
- ensureSingleTrailingNewline: Whether to reduce multiple newlines to one at the end of files
|
||||
- wrapGuide
|
||||
- columns: Array of hashes with a `pattern` and `column` key to match the
|
||||
the path of the current editor to a column position.
|
||||
|
||||
## Customizing Key Bindings
|
||||
|
||||
Atom keymaps work similarly to stylesheets. Just as stylesheets use selectors
|
||||
to apply styles to elements, Atom keymaps use selectors to associate keystrokes
|
||||
with events in specific contexts. Here's a small example, excerpted from Atom's
|
||||
built-in keymaps:
|
||||
|
||||
```coffee-script
|
||||
'.editor':
|
||||
'enter': 'editor:newline'
|
||||
|
||||
".select-list .editor.mini":
|
||||
'enter': 'core:confirm',
|
||||
```
|
||||
|
||||
This keymap defines the meaning of `enter` in two different contexts. In a
|
||||
normal editor, pressing `enter` emits the `editor:newline` event, which causes
|
||||
the editor to insert a newline. But if the same keystroke occurs inside of a
|
||||
select list's mini-editor, it instead emits the `core:confirm` event based on
|
||||
the binding in the more-specific selector.
|
||||
|
||||
By default, any keymap files in your `~/.atom/keymaps` directory will be loaded
|
||||
in alphabetical order when Atom is started. They will always be loaded last,
|
||||
giving you the chance to override bindings that are defined by Atom's core
|
||||
keymaps or third-party packages.
|
||||
|
||||
## Changing The Theme
|
||||
|
||||
Atom comes bundles with two themes `atom-dark-*` and `atom-light-*`.
|
||||
|
||||
Because Atom themes are based on CSS, it's possible to have multiple themes
|
||||
active at the same time. For example, you'll usually select a theme for the UI
|
||||
and another theme for syntax highlighting. You can select themes by specifying
|
||||
them in the `core.themes` array in your `config.cson`:
|
||||
|
||||
```coffee-script
|
||||
core:
|
||||
themes: ["atom-light-ui", "atom-light-syntax"]
|
||||
# or, if the sun is going down:
|
||||
# themes: ["atom-dark-ui", "atom-dark-syntax"]
|
||||
```
|
||||
|
||||
You install new themes by placing them in the `~/.atom/themes` directory. A
|
||||
theme can be a CSS file, a directory containing multiple CSS files, or a
|
||||
TextMate theme (either `.tmTheme` or `.plist`).
|
||||
|
||||
|
||||
## Installing Packages (Partially Implemented)
|
||||
|
||||
To install a package, clone it into the `~/.atom/packages` directory. Atom will
|
||||
also load grammars and snippets from TextMate bundles. If you want to disable a
|
||||
package without removing it from the packages directory, insert its name into
|
||||
`config.core.disabledPackages`:
|
||||
|
||||
config.cson:
|
||||
```coffeescript
|
||||
core:
|
||||
disabledPackages: [
|
||||
"fuzzy-finder",
|
||||
"tree-view"
|
||||
]
|
||||
```
|
||||
|
||||
## Quick Personal Hacks
|
||||
|
||||
### user.coffee
|
||||
|
||||
When Atom finishes loading, it will evaluate `user.coffee` in your `~/.atom`
|
||||
directory, giving you a chance to run arbitrary personal CoffeeScript code to
|
||||
make customizations. You have full access to Atom's API from code in this file.
|
||||
Please refer to the Atom Internals Guide for more information. If your
|
||||
customizations become extensive, consider creating a package.
|
||||
|
||||
### user.css
|
||||
|
||||
If you want to apply quick-and-dirty personal styling changes without creating
|
||||
an entire theme that you intend to distribute, you can add styles to
|
||||
`user.css` in your `~/.atom` directory.
|
||||
|
||||
For example to change the color of the highlighted line number for the line that
|
||||
contains the cursor, you could add the following style to `user.css`:
|
||||
|
||||
```css
|
||||
.editor .line-number.cursor-line {
|
||||
color: pink;
|
||||
}
|
||||
```
|
||||
[configuring]: customizing-atom.html
|
||||
[theming]: creating-a-theme.html
|
||||
[extending]: creating-a-package.html
|
||||
[customization]: customizing-atom.html
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{{{
|
||||
"title": "Guides"
|
||||
}}}
|
||||
|
||||
## Guides
|
||||
|
||||
* [Getting Started](getting-started.html)
|
||||
* [Customizing Atom](customizing-atom.html)
|
||||
* [Creating a Package](creating-a-package.html)
|
||||
* [Creating a Theme](creating-a-theme.html)
|
||||
|
||||
### Advanced Topics
|
||||
* [Configuration](internals/configuration.html)
|
||||
* [Keymaps](internals/keymaps.html)
|
||||
* [Serialization](internals/serialization.html)
|
||||
* [View System](internals/view-system.html)
|
||||
@@ -1,6 +0,0 @@
|
||||
# Atom Internals
|
||||
|
||||
To extend Atom effectively, you'll need to understand how it works internally.
|
||||
This system is evolving fast and it's unlikely that these docs will cover all
|
||||
of it any time soon, so view this as a jumping-off point. To really understand
|
||||
how things work, use the web inspector or dive into the code.
|
||||
@@ -15,7 +15,7 @@ Note that the last example describes multiple keystrokes in succession:
|
||||
- `p`
|
||||
- `2`
|
||||
- `ctrl-p`
|
||||
- `ctrl-alt-meta-p`
|
||||
- `ctrl-alt-cmd-p`
|
||||
- `tab`
|
||||
- `escape`
|
||||
- `enter`
|
||||
@@ -23,12 +23,12 @@ 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 `meta-p`), to get a list of relevant events and their bindings
|
||||
palette (bound to `cmd-p`), to get a list of relevant events and their bindings
|
||||
in any focused context in Atom.
|
||||
|
||||
### Rules for Mapping A Keydown Event to A Semantic Event
|
||||
|
||||
A keymap's job is to translate a physical keystroke event (like `meta-D`) into a
|
||||
A keymap's job is to translate a physical keystroke event (like `cmd-D`) into a
|
||||
semantic event (like `editor:duplicate-line`). Whenever a keydown event occurs
|
||||
on a focused element, it bubbles up the DOM as usual. As soon as an element on
|
||||
the bubble path matches a key binding for the keystroke, the binding's semantic
|
||||
|
||||
@@ -33,10 +33,10 @@ for more details.
|
||||
|
||||
### RootView
|
||||
|
||||
The root of Atom's view hiererchy is a global called `rootView`, which is a
|
||||
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
|
||||
window, and contains every other view. If you open Atom's inspector with
|
||||
`alt-meta-i`, you can see the internal structure of `RootView`:
|
||||
`alt-cmd-i`, you can see the internal structure of `RootView`:
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
getting-started.md
|
||||
configuring-atom.md
|
||||
built-in-packages/intro.md
|
||||
built-in-packages/command-panel.md
|
||||
built-in-packages/markdown-preview.md
|
||||
built-in-packages/wrap-guide.md
|
||||
packages/authoring-packages.md
|
||||
packages/creating_a_package.md
|
||||
packages/included_libraries.md
|
||||
packages/package_json.md
|
||||
themes/authoring-themes.md
|
||||
internals/intro.md
|
||||
internals/configuration.md
|
||||
internals/keymaps.md
|
||||
internals/view-system.md
|
||||
@@ -1,211 +0,0 @@
|
||||
# Authoring Packages
|
||||
|
||||
Packages are at the core of Atom. Nearly everything outside of the main editor manipulation
|
||||
is handled by a package. That includes "core" pieces like the command panel, status bar,
|
||||
file tree, and more.
|
||||
|
||||
A package can contain a variety of different resource types to change Atom's
|
||||
behavior. The basic package layout is as follows (though not every package will
|
||||
have all of these directories):
|
||||
|
||||
```text
|
||||
my-package/
|
||||
lib/
|
||||
stylesheets/
|
||||
keymaps/
|
||||
snippets/
|
||||
grammars/
|
||||
spec/
|
||||
package.json
|
||||
index.coffee
|
||||
```
|
||||
|
||||
**NOTE:** NPM behavior is partially implemented until we get a working Node.js
|
||||
API built into Atom. The goal is to make Atom packages be a superset of NPM
|
||||
packages.
|
||||
|
||||
Below, we'll break down each directory. There's also [a tutorial](./creating_a_package.md)
|
||||
on creating your first package.
|
||||
|
||||
## package.json
|
||||
|
||||
Similar to [npm packages](http://en.wikipedia.org/wiki/Npm_(software)), Atom packages
|
||||
can contain a _package.json_ file in their top-level directory. This file contains metadata
|
||||
about the package, such as the path to its "main" module, library dependencies,
|
||||
and manifests specifying the order in which its resources should be loaded.
|
||||
|
||||
In addition to the regular [npm package.json keys](https://npmjs.org/doc/json.html)
|
||||
available, Atom package.json files [have their own additions](./package_json.md).
|
||||
|
||||
## Source Code
|
||||
|
||||
If you want to extend Atom's behavior, your package should contain a single
|
||||
top-level module, which you export from _index.coffee_ (or whichever file is
|
||||
indicated by the `main` key in your _package.json_ file). The remainder of your
|
||||
code should be placed in the `lib` directory, and required from your top-level
|
||||
file.
|
||||
|
||||
Your package's top-level module is a singleton object that manages the lifecycle
|
||||
of your extensions to Atom. Even if your package creates ten different views and
|
||||
appends them to different parts of the DOM, it's all managed from your top-level
|
||||
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).
|
||||
|
||||
- `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
|
||||
the window is later restored, the data you returned is passed to your
|
||||
module's `activate` method so you can restore your view to where the user left
|
||||
off.
|
||||
|
||||
- `deactivate()`: This **optional** method is called when the window is shutting
|
||||
down. If your package is watching any files or holding external resources in any
|
||||
other way, release them here. If you're just subscribing to things on window,
|
||||
you don't need to worry because that's getting torn down anyway.
|
||||
|
||||
### Simple Package Code
|
||||
|
||||
```text
|
||||
my-package/
|
||||
package.json # optional
|
||||
index.coffee
|
||||
lib/
|
||||
my-package.coffee
|
||||
```
|
||||
|
||||
`index.coffee`:
|
||||
```coffeescript
|
||||
module.exports = require "./lib/my-package"
|
||||
```
|
||||
|
||||
`my-package/my-package.coffee`:
|
||||
```coffeescript
|
||||
module.exports =
|
||||
activate: (rootView, state) -> # ...
|
||||
deactivate: -> # ...
|
||||
serialize: -> # ...
|
||||
```
|
||||
|
||||
Beyond this simple contract, your package has full access to Atom's internal
|
||||
API. Anything we call internally, you can call as well. Be aware that since we
|
||||
are early in development, APIs are subject to change and we have not yet
|
||||
established clear boundaries between what is public and what is private. Also,
|
||||
please collaborate with us if you need an API that doesn't exist. Our goal is
|
||||
to build out Atom's API organically based on the needs of package authors like
|
||||
you.
|
||||
|
||||
See [Atom's built-in packages](https://github.com/github/atom/tree/master/src/packages)
|
||||
for examples of Atom's API in action.
|
||||
|
||||
## Stylesheets
|
||||
|
||||
Stylesheets for your package should be placed in the _stylesheets_ directory.
|
||||
Any stylesheets in this directory will be loaded and attached to the DOM when
|
||||
your package is activated. Stylesheets can be written as CSS or LESS.
|
||||
|
||||
An optional `stylesheets` array in your _package.json_ can list the stylesheets by
|
||||
name to specify a loading order; otherwise, stylesheets are loaded alphabetically.
|
||||
|
||||
## Keymaps
|
||||
|
||||
Keymaps are placed in the _keymaps_ subdirectory. It's a good idea to provide
|
||||
default keymaps for your extension, especially if you're also adding a new command.
|
||||
|
||||
By default, all keymaps are loaded in alphabetical order. An optional `keymaps`
|
||||
array in your _package.json_ can specify which keymaps to load and in what order.
|
||||
|
||||
See the [main keymaps documentation](../internals/keymaps.md) for more information on
|
||||
how keymaps work.
|
||||
|
||||
## Snippets
|
||||
|
||||
An extension can supply language snippets in the _snippets_ directory. These can
|
||||
be `.cson` or `.json` files. Here's an example:
|
||||
|
||||
```coffeescript
|
||||
".source.coffee .specs":
|
||||
"Expect":
|
||||
prefix: "ex"
|
||||
body: "expect($1).to$2"
|
||||
"Describe":
|
||||
prefix: "de"
|
||||
body: """
|
||||
describe "${1:description}", ->
|
||||
${2:body}
|
||||
"""
|
||||
```
|
||||
|
||||
A snippets file contains scope selectors at its top level (`.source.coffee .spec`).
|
||||
Each scope selector contains a hash of snippets keyed by their name (`Expect`, `Describe`).
|
||||
Each snippet also specifies a `prefix` and a `body` key. The `prefix` represents
|
||||
the first few letters to type before hitting the `tab` key to autocomplete. The
|
||||
`body` defines the autofilled text. You can use placeholders like `$1`, `$2`, to indicate
|
||||
regions in the body the user can navigate to every time they hit `tab`.
|
||||
|
||||
All files in the directory are automatically loaded, unless the
|
||||
_package.json_ supplies a `snippets` key. As with all scoped
|
||||
items, snippets loaded later take precedence over earlier snippets when two
|
||||
snippets match a scope with the same specificity.
|
||||
|
||||
## Language Grammars
|
||||
|
||||
If you're developing a new language grammar, you'll want to place your file in
|
||||
the _grammars_ directory. Each grammar is a pairing of two keys, `match` and
|
||||
`captures`. `match` is a regular expression identifying the pattern to highlight,
|
||||
while `captures` is a JSON representing what to do with each matching group.
|
||||
For example:
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
'match': '(?:^|\\s)(__[^_]+__)'
|
||||
'captures':
|
||||
'1': 'name': 'markup.bold.gfm'
|
||||
}
|
||||
```
|
||||
|
||||
This indicates that the first matching capture (`(__[^_]+__)`) should have the
|
||||
`markup.bold.gfm` token applied to it.
|
||||
|
||||
To capture a single group, simply use the `name` key instead:
|
||||
|
||||
```json
|
||||
{
|
||||
'match': '^#{1,6}\\s+.+$'
|
||||
'name': 'markup.heading.gfm'
|
||||
}
|
||||
```
|
||||
|
||||
This indicates that Markdown header lines (`#`, `##`, `###`) should be applied with
|
||||
the `markup.heading.gfm` token.
|
||||
|
||||
More information about the significance of these tokens can be found in
|
||||
[section 12.4 of the TextMate Manual](http://manual.macromates.com/en/language_grammars.html).
|
||||
|
||||
Your grammar should also include a `filetypes` array, which is a list of file extensions
|
||||
your grammar supports:
|
||||
|
||||
```
|
||||
'fileTypes': [
|
||||
'markdown'
|
||||
'md'
|
||||
'mkd'
|
||||
'mkdown'
|
||||
'ron'
|
||||
]
|
||||
```
|
||||
|
||||
## Writing Tests
|
||||
|
||||
Your package **should** have tests, and if they're placed in the _spec_ directory,
|
||||
they can be run by Atom.
|
||||
|
||||
Under the hood, [Jasmine](https://github.com/pivotal/jasmine) is being used to run
|
||||
to execute the tests, so you can assume that any DSL available there is available
|
||||
to your package as well.
|
||||
@@ -1,254 +0,0 @@
|
||||
# Creating Packages
|
||||
|
||||
Let's take a look at creating our first package.
|
||||
|
||||
Atom has a command you can enter that'll create a package for you:
|
||||
`package-generator:generate`. Otherwise, you can hit `meta-p`, and start typing
|
||||
"Package Generator." Once you activate this package, it'll ask you for a name for
|
||||
your new package. Let's call ours _changer_.
|
||||
|
||||
Now, _changer_ is going to have a default set of folders and files created for us.
|
||||
Hit `meta-R` to reload Atom, then hit `meta-p` and start typing "Changer." You'll
|
||||
see a new `Changer:Toggle` command which, if selected, pops up a new message. So
|
||||
far, so good!
|
||||
|
||||
In order to demonstrate the capabilities of Atom and its API, our Changer plugin
|
||||
is going to do two things:
|
||||
|
||||
1. It'll show only modified files in the file tree
|
||||
2. It'll append a new pane to the editor with some information about the modified
|
||||
files
|
||||
|
||||
Let's get started!
|
||||
|
||||
## Changing Keybindings and Commands
|
||||
|
||||
Since Changer is primarily concerned with the file tree, let's write a keybinding
|
||||
that works only when the tree is focused. Instead of using the default `toggle`,
|
||||
our keybinding executes a new command called `magic`.
|
||||
|
||||
_keymaps/changer.cson_ can easily become this:
|
||||
|
||||
```cson
|
||||
'.tree-view-scroller':
|
||||
'ctrl-V': 'changer:magic'
|
||||
```
|
||||
|
||||
Notice that the keybinding is called `ctrl-V`--that's actually `ctrl-shift-v`.
|
||||
You can use capital letters to denote using `shift` for your binding.
|
||||
|
||||
`.tree-view-scroller` represents the parent container for the tree view. Keybindings
|
||||
only work within the context of where they're entered. For example, hitting `ctrl-V`
|
||||
anywhere other than tree won't do anything. 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 use the `rootView.command` method. This
|
||||
takes a command name and executes a function in the code. For example:
|
||||
|
||||
```coffeescript
|
||||
rootView.command "changer:magic", => @magic()
|
||||
```
|
||||
|
||||
It's common practice to namespace your commands with your package name, and separate
|
||||
it with a colon (`:`). Rename the existing `toggle` method to `magic` to get the
|
||||
binding to work.
|
||||
|
||||
Reload the editor, click on the tree, hit your keybinding, and...nothing happens! What the heck?!
|
||||
|
||||
Open up the _package.json_ file, and notice the key that says `activationEvents`.
|
||||
Basically, this tells Atom to not load a package until it hears a certain event.
|
||||
Let's change the event to `changer:magic` and reload the editor.
|
||||
|
||||
Hitting the key binding on the tree now works!
|
||||
|
||||
## Working with styles
|
||||
|
||||
The next step is to hide elements in the tree that aren't modified. To do that,
|
||||
we'll first try and get a list of files that have not changed.
|
||||
|
||||
All packages are able to use jQuery in their code. In fact, we have [a list of
|
||||
some of the bundled libraries Atom provides by default](./included_libraries.md).
|
||||
|
||||
Let's bring in jQuery:
|
||||
|
||||
```coffeescript
|
||||
$ = require 'jquery'
|
||||
```
|
||||
|
||||
Now, we can query the tree to get us a list of every file that _wasn't_ modified:
|
||||
|
||||
```coffeescript
|
||||
magic: ->
|
||||
$('ol.entries li').each (i, el) ->
|
||||
if !$(el).hasClass("modified")
|
||||
console.log el
|
||||
```
|
||||
|
||||
You can access the dev console by hitting `alt-meta-i`. When we execute the
|
||||
`changer:magic` command, the browser console lists the items that are not being
|
||||
modified. Let's add a class to each of these elements called `hide-me`:
|
||||
|
||||
```coffeescript
|
||||
magic: ->
|
||||
$('ol.entries li').each (i, el) ->
|
||||
if !$(el).hasClass("modified")
|
||||
$(el).addClass("hide-me")
|
||||
```
|
||||
|
||||
With our newly added class, we can manipulate the visibility of the elements
|
||||
with a simple stylesheet. Open up _changer.css_ in the _stylesheets_ directory,
|
||||
and add a single entry:
|
||||
|
||||
```css
|
||||
ol.entries .hide-me {
|
||||
display: none;
|
||||
}
|
||||
```
|
||||
|
||||
Refresh atom, and run the `changer` command. You'll see all the non-changed files
|
||||
disappear from the tree. There are a number of ways you can get the list back;
|
||||
let's just naively iterate over the same elements and remove the class:
|
||||
|
||||
```coffeescript
|
||||
magic: ->
|
||||
$('ol.entries li').each (i, el) ->
|
||||
if !$(el).hasClass("modified")
|
||||
if !$(el).hasClass("hide-me")
|
||||
$(el).addClass("hide-me")
|
||||
else
|
||||
$(el).removeClass("hide-me")
|
||||
```
|
||||
|
||||
## Creating a New Pane
|
||||
|
||||
The next goal of this package is to append a pane to the Atom editor that lists
|
||||
some information about the modified files.
|
||||
|
||||
To do that, we're going to first create a new class method called `content`. Every
|
||||
package that extends from the `View` class can provide an optional class method
|
||||
called `content`. The `content` method constructs the DOM that your package uses
|
||||
as its UI. The principals of `content` are built entirely on [SpacePen](https://github.com/nathansobo/space-pen),
|
||||
which we'll touch upon only briefly here.
|
||||
|
||||
Our display will simply be an unordered list of the file names, and their
|
||||
modified times. Let's start by carving out a `div` to hold the filenames:
|
||||
|
||||
```coffeescript
|
||||
@content: ->
|
||||
@div class: 'modified-files-container', =>
|
||||
@ul class: 'modified-files-list', outlet: 'modifiedFilesList', =>
|
||||
@li 'Test'
|
||||
@li 'Test2'
|
||||
```
|
||||
|
||||
You can add any HTML5 attribute you like. `outlet` names the variable
|
||||
your package can uses to manipulate the element directly. The fat pipe (`=>`) indicates
|
||||
that the next set are nested children.
|
||||
|
||||
We'll add one more line to `magic` to make this pane appear:
|
||||
|
||||
```coffeescript
|
||||
rootView.vertical.append(this)
|
||||
```
|
||||
|
||||
If you hit the key command, you'll see a box appear right underneath the editor.
|
||||
Success!
|
||||
|
||||
Before we populate this, let's apply some logic to toggle the pane off and on, just
|
||||
like we did with the tree view:
|
||||
|
||||
```coffeescript
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
rootView.vertical.children().last().remove()
|
||||
else
|
||||
rootView.vertical.append(this)
|
||||
```
|
||||
|
||||
There are about a hundred different ways to toggle a pane on and off, and this
|
||||
might not be the most efficient one. If you know your package needs to be toggled
|
||||
on and off more freely, it might be better to draw the UI during the initialization,
|
||||
then immediately call `hide()` on the element to remove it from the view. You can
|
||||
then swap between `show()` and `hide()`, and instead of forcing Atom to add and remove
|
||||
the element as we're doing here, it'll just set a CSS property to control your package's
|
||||
visibility.
|
||||
|
||||
You might have noticed that our two `li` elements aren't showing up. Let's set
|
||||
a color on them so that they pop. Open up `changer.css` and add this CSS:
|
||||
|
||||
```css
|
||||
ul.modified-files-list {
|
||||
color: white;
|
||||
}
|
||||
```
|
||||
|
||||
Refresh Atom, hit the key combo, and see your brilliantly white test list.
|
||||
|
||||
## Calling Node.js Code
|
||||
|
||||
Since Atom is built on top of Node.js, you can call any of its libraries, including
|
||||
other modules that your package requires.
|
||||
|
||||
We'll iterate through our resulting tree, and construct the path to our modified
|
||||
file based on its depth in the tree:
|
||||
|
||||
```coffeescript
|
||||
path = require 'path'
|
||||
|
||||
# ...
|
||||
|
||||
modifiedFiles = []
|
||||
# for each single entry...
|
||||
$('ol.entries li.file.modified span.name').each (i, el) ->
|
||||
filePath = []
|
||||
# ...grab its name...
|
||||
filePath.unshift($(el).text())
|
||||
|
||||
# ... then find its parent directories, and grab their names
|
||||
parents = $(el).parents('.directory.modified')
|
||||
parents.each (i, el) ->
|
||||
filePath.unshift($(el).find('div.header span.name').eq(0).text())
|
||||
|
||||
modifiedFilePath = path.join(project.rootDirectory.path, filePath.join(path.sep))
|
||||
modifiedFiles.push modifiedFilePath
|
||||
```
|
||||
|
||||
`modifiedFiles` is an array containing a list of our modified files. We're also using
|
||||
the node.js [`path` library](http://nodejs.org/docs/latest/api/path.html) to get
|
||||
the proper directory separator for our system.
|
||||
|
||||
Let's remove the two `@li` elements we added in `@content`, so that we can populate
|
||||
our `modifiedFilesList` with real information. We'll do that by iterating over
|
||||
`modifiedFiles`, accessing a file's last modified time, and appending it to
|
||||
`modifiedFilesList`:
|
||||
|
||||
```coffeescript
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
rootView.vertical.children().last().remove()
|
||||
else
|
||||
for file in modifiedFiles
|
||||
stat = fs.lstatSync(file)
|
||||
mtime = stat.mtime
|
||||
@modifiedFilesList.append("<li>#{file} - Modified at #{mtime}")
|
||||
rootView.vertical.append(this)
|
||||
```
|
||||
|
||||
When you toggle the modified files list, your pane is now populated with the filenames
|
||||
and modified times of files in your project. You might notice that subsequent calls
|
||||
to this command reduplicate information. We could provide an elegant way of rechecking
|
||||
files already in the list, but for this demonstration, we'll just clear the `modifiedFilesList`
|
||||
each time it's closed:
|
||||
|
||||
```coffeescript
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
@modifiedFilesList.empty()
|
||||
rootView.vertical.children().last().remove()
|
||||
else
|
||||
for file in modifiedFiles
|
||||
stat = fs.lstatSync(file)
|
||||
mtime = stat.mtime
|
||||
@modifiedFilesList.append("<li>#{file} - Modified at #{mtime}")
|
||||
rootView.vertical.append(this)
|
||||
```
|
||||
@@ -1,10 +0,0 @@
|
||||
# Included Libraries
|
||||
|
||||
In addition to core node.js modules, all packages can `require` the following popular
|
||||
libraries into their packages:
|
||||
|
||||
* [SpacePen](https://github.com/nathansobo/space-pen) (as `require 'space-pen'`)
|
||||
* [jQuery](http://jquery.com/) (as `require 'jquery'`)
|
||||
* [Underscore](http://underscorejs.org/) (as `require 'underscore'`)
|
||||
|
||||
Additional libraries can be found by browsing Atom's _node_modules_ folder.
|
||||
@@ -1,18 +0,0 @@
|
||||
# package.json format
|
||||
|
||||
The following keys are available to your package's _package.json_ manifest file:
|
||||
|
||||
- `main` (**Required**): the path to the CoffeeScript file that's the entry point
|
||||
to your package
|
||||
- `stylesheets` (**Optional**): an Array of Strings identifying the order of the
|
||||
stylesheets your package needs to load. If not specified, stylesheets in the _stylesheets_
|
||||
directory are added alphabetically.
|
||||
- `keymaps`(**Optional**): an Array of Strings identifying the order of the
|
||||
key mappings your package needs to load. If not specified, mappings in the _keymaps_
|
||||
directory are added alphabetically.
|
||||
- `snippets` (**Optional**): an Array of Strings identifying the order of the
|
||||
snippets your package needs to load. If not specified, snippets in the _snippets_
|
||||
directory are added alphabetically.
|
||||
- `activationEvents` (**Optional**): an Array of Strings identifying events that
|
||||
trigger your package's activation. You can delay the loading of your package until
|
||||
one of these events is trigged.
|
||||
@@ -0,0 +1,70 @@
|
||||
**Polish the user experience**
|
||||
|
||||
First and foremost, Atom is a **product**. Atom needs to feel familiar and
|
||||
inviting. This includes a solid introductory experience and parity with the most
|
||||
important features of Sublime Text.
|
||||
|
||||
* First launch UI and flow (actions below should be easily discoverable)
|
||||
* Create a new file
|
||||
* Open a project and edit an existing file
|
||||
* Install a package
|
||||
* Change settings (adjust theme, change key bindings, set config options)
|
||||
* How to use command P
|
||||
* Use collaboration internally
|
||||
* How and where to edit keyBinding should be obvious to new users
|
||||
* Finish find and replace in buffer/project
|
||||
* Atom should start < 300ms
|
||||
* Match Sublime's multiple selection functionality (#523)
|
||||
* Fix softwrap bugs
|
||||
* Menus & Context menus
|
||||
* Track usage/engagement of our users (make this opted in?)
|
||||
* Windows support
|
||||
* Reliably and securely auto-update and list what's new
|
||||
* Secure access to the keychain (don't give every package access to the keychain)
|
||||
* Secure access to GitHub (each package can ask to have it's own oauth token)
|
||||
* Don't crash when opening/editing large (> 10Mb) files
|
||||
* Send js and native crash reports to a remote server
|
||||
|
||||
**Lay solid groundwork for a package and theme ecosystem**
|
||||
|
||||
Extensibility is one of Atom's key value propositions, so a smooth experience
|
||||
for creating and maintaining packages is just as important as the user
|
||||
experience. The package development, dependency and publishing workflow needs to
|
||||
be solid. We also want to have a mechanism for clearly communicating with
|
||||
package authors about breaking API changes.
|
||||
|
||||
* Finish APM backend (integrate with GitHub Releases)
|
||||
* Streamline Dev workflow
|
||||
* `apm create` - create package scaffolding
|
||||
* `apm test` - so users can run focused package tests
|
||||
* `apm publish` - should integrate release best practices (ie npm version)
|
||||
* Determine which classes and methods should be included in the public API
|
||||
* Users can find/install/update/fork existing packages and themes
|
||||
|
||||
**Tighten up the view layer**
|
||||
Our current approach to the view layer need some improvement. We want to
|
||||
actively promote the use of the M-V-VM design pattern, provide some declarative
|
||||
event binding mechanisms in the view layer, and improve the performance of the
|
||||
typical package specs. We don't want the current approach to be used as an
|
||||
example in a bunch of new packages, so it's important to improve it now.
|
||||
|
||||
* Add marker view API
|
||||
|
||||
**Get atom.io online with some exciting articles and documentation**
|
||||
We'd love to send our private alpha candidates to a nice site with information
|
||||
about what Atom is, the philosophies and technologies behind it, and guidance
|
||||
for how to get started.
|
||||
|
||||
* Design and create www.atom.io
|
||||
* Guides
|
||||
* Theme & Package creation guide
|
||||
* Full API per release tag
|
||||
* Changelog per release
|
||||
* Explanation of features
|
||||
* Explain Semver and general plans for the future (reassure developers we care about them)
|
||||
* General Values/Goals
|
||||
* Make docs accessible from Atom
|
||||
* Community/contribution guidelines
|
||||
* Is all communication to be done through issues?
|
||||
* When should you publish a plugin?
|
||||
* Do we need to vet plugins from a security perspective?
|
||||
@@ -0,0 +1,16 @@
|
||||
## Proposed Timeline
|
||||
|
||||
1. **October 30st** - Internal launch - persuade as many githubers to switch as
|
||||
possible.
|
||||
|
||||
1. Triage bugs and identify what needs to be fixed before private alpha. Maybe
|
||||
talk to @chrissiebrodigan about doing a UX study.
|
||||
|
||||
1. **November 22st** - Private alpha launch
|
||||
|
||||
1. Trickle out invites as people ask/we need more testers.
|
||||
|
||||
1. If our usage metrics/engagement metrics decrease, stop, identify the issue
|
||||
and fix it before continuing.
|
||||
|
||||
1. Launch
|
||||
@@ -0,0 +1,151 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-theme.min.css">
|
||||
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="../../assets/js/html5shiv.js"></script>
|
||||
<script src="../../assets/js/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<title>Atom - <%= title %></title>
|
||||
<style>
|
||||
/*github.com style (c) Vasily Polovnyov <vast@whiteants.net>*/
|
||||
pre code {
|
||||
display: block; padding: 0.5em;
|
||||
color: #333;
|
||||
background: #f8f8ff
|
||||
}
|
||||
pre .comment,
|
||||
pre .template_comment,
|
||||
pre .diff .header,
|
||||
pre .javadoc {
|
||||
color: #998;
|
||||
font-style: italic
|
||||
}
|
||||
pre .keyword,
|
||||
pre .css .rule .keyword,
|
||||
pre .winutils,
|
||||
pre .javascript .title,
|
||||
pre .nginx .title,
|
||||
pre .subst,
|
||||
pre .request,
|
||||
pre .status {
|
||||
color: #333;
|
||||
font-weight: bold
|
||||
}
|
||||
pre .number,
|
||||
pre .hexcolor,
|
||||
pre .ruby .constant {
|
||||
color: #099;
|
||||
}
|
||||
pre .string,
|
||||
pre .tag .value,
|
||||
pre .phpdoc,
|
||||
pre .tex .formula {
|
||||
color: #d14
|
||||
}
|
||||
pre .title,
|
||||
pre .id {
|
||||
color: #900;
|
||||
font-weight: bold
|
||||
}
|
||||
pre .javascript .title,
|
||||
pre .lisp .title,
|
||||
pre .clojure .title,
|
||||
pre .subst {
|
||||
font-weight: normal
|
||||
}
|
||||
pre .class .title,
|
||||
pre .haskell .type,
|
||||
pre .vhdl .literal,
|
||||
pre .tex .command {
|
||||
color: #458;
|
||||
font-weight: bold
|
||||
}
|
||||
pre .tag,
|
||||
pre .tag .title,
|
||||
pre .rules .property,
|
||||
pre .django .tag .keyword {
|
||||
color: #000080;
|
||||
font-weight: normal
|
||||
}
|
||||
pre .attribute,
|
||||
pre .variable,
|
||||
pre .lisp .body {
|
||||
color: #008080
|
||||
}
|
||||
pre .regexp {
|
||||
color: #009926
|
||||
}
|
||||
pre .class {
|
||||
color: #458;
|
||||
font-weight: bold
|
||||
}
|
||||
pre .symbol,
|
||||
pre .ruby .symbol .string,
|
||||
pre .lisp .keyword,
|
||||
pre .tex .special,
|
||||
pre .prompt {
|
||||
color: #990073
|
||||
}
|
||||
pre .built_in,
|
||||
pre .lisp .title,
|
||||
pre .clojure .built_in {
|
||||
color: #0086b3
|
||||
}
|
||||
pre .preprocessor,
|
||||
pre .pi,
|
||||
pre .doctype,
|
||||
pre .shebang,
|
||||
pre .cdata {
|
||||
color: #999;
|
||||
font-weight: bold
|
||||
}
|
||||
pre .deletion {
|
||||
background: #fdd
|
||||
}
|
||||
pre .addition {
|
||||
background: #dfd
|
||||
}
|
||||
pre .diff .change {
|
||||
background: #0086b3
|
||||
}
|
||||
pre .chunk {
|
||||
color: #aaa
|
||||
}
|
||||
|
||||
body {
|
||||
padding-top: 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="/<%= tag %>/index.html">Atom Documentation</a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="/<%= tag %>/index.html">Guides</a></li>
|
||||
<li><a href="/<%= tag %>/api/index.html">API</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<%= content %>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
externo
-55
@@ -1,55 +0,0 @@
|
||||
# Authoring Themes
|
||||
|
||||
If you understand CSS, you can write an Atom theme easily. Your theme can style
|
||||
Atom's user interface, specify the appearance of syntax-highlighted code, or
|
||||
both. For making a syntax highlighting theme, refer to
|
||||
[section 12.4 of the TextMate Manual](http://manual.macromates.com/en/language_grammars.html)
|
||||
for a list of the common scopes used by TextMate grammars. You'll just need to
|
||||
translate scope names to CSS classes. To theme Atom's user interface, take a
|
||||
look at the existing light and dark themes for an example. Pressing `alt-meta-i`
|
||||
and inspecting the Atom's markup directly can also be helpful.
|
||||
|
||||
The most basic theme is just a _.css_ file. More complex themes occupy their own
|
||||
folder, which can contain multiple stylesheets along with an optional
|
||||
_package.cson_ file containing a manifest to control their load-order:
|
||||
|
||||
```text
|
||||
~/.atom/themes/
|
||||
rockstar.css
|
||||
rainbow/
|
||||
package.json
|
||||
core.css
|
||||
editor.css
|
||||
tree-view.css
|
||||
```
|
||||
|
||||
package.cson:
|
||||
```coffee-script
|
||||
stylesheets: ["core.css", "editor.less", "tree-view.css"]
|
||||
```
|
||||
|
||||
The `package.cson` file specifies which stylesheets to load and in what order
|
||||
with the `stylesheets` key. If no manifest is specified, all stylesheets are
|
||||
loaded in alphabetical order when the user selects the theme.
|
||||
|
||||
|
||||
## Theme Extensions (Not Yet Implemented)
|
||||
|
||||
A theme may need to be extended to cover DOM elements that are introduced by a
|
||||
third-party Atom package. When a package is loaded, stylesheets with the same
|
||||
name as the package will automatically be loaded from the `packages` directory
|
||||
of active themes:
|
||||
|
||||
```text
|
||||
~/.atom/themes/
|
||||
midnight/midnight.less
|
||||
midnight/packages/terminal.less
|
||||
midnight/packages/tree-view.less
|
||||
```
|
||||
|
||||
In the example above, if the `midnight` theme is active, its `terminal` and
|
||||
`tree-view` stylesheets will be loaded automatically if and when those packages
|
||||
are activated. If you author an extension to a theme consider sending its author
|
||||
a pull request to have it included in the theme by default. Package-specific
|
||||
theme stylesheets need not be listed in the theme's `package.json` because they
|
||||
will be loaded automatically when the package is loaded.
|
||||
@@ -17,3 +17,5 @@
|
||||
'meta-backspace': 'editor:backspace-to-beginning-of-line'
|
||||
'alt-delete': 'editor:delete-to-end-of-word'
|
||||
'ctrl-t': 'editor:transpose'
|
||||
'ctrl-A': 'editor:select-to-first-character-of-line'
|
||||
'ctrl-E': 'editor:select-to-end-of-line'
|
||||
@@ -1,4 +1,16 @@
|
||||
'body':
|
||||
'meta-q': 'application:quit'
|
||||
'meta-h': 'application:hide'
|
||||
'meta-H': 'application:hide-other-applications'
|
||||
'meta-n': 'application:new-file'
|
||||
'meta-N': 'application:new-window'
|
||||
'meta-o': 'application:open'
|
||||
'meta-O': 'application:open-dev'
|
||||
'meta-m': 'application:minimize'
|
||||
'meta-,': 'application:show-settings'
|
||||
'alt-meta-ctrl-m': 'application:zoom'
|
||||
'meta-alt-ctrl-s': 'application:run-all-specs'
|
||||
|
||||
'meta-s': 'core:save'
|
||||
'meta-S': 'core:save-as'
|
||||
'enter': 'core:confirm'
|
||||
@@ -26,12 +38,14 @@
|
||||
|
||||
'meta-alt-s': 'window:save-all'
|
||||
'meta-W': 'window:close'
|
||||
'meta-r': 'window:reload'
|
||||
'meta-+': 'window:increase-font-size'
|
||||
'meta--': 'window:decrease-font-size'
|
||||
'ctrl-w w': 'window:focus-next-pane'
|
||||
'ctrl-tab': 'window:focus-next-pane'
|
||||
'ctrl-meta-f': 'window:toggle-full-screen'
|
||||
'meta-r': 'window:reload'
|
||||
'alt-meta-i': 'window:toggle-dev-tools'
|
||||
'meta-alt-ctrl-p': 'window:run-package-specs'
|
||||
|
||||
'ctrl-|': 'pane:split-right'
|
||||
'ctrl-w v': 'pane:split-right'
|
||||
@@ -55,14 +69,6 @@
|
||||
'alt-meta-w': 'pane:close-other-items'
|
||||
'meta-P': 'pane:close'
|
||||
|
||||
'meta-n': 'new-window'
|
||||
'meta-N': 'new-editor'
|
||||
'meta-,': 'open-user-configuration'
|
||||
'meta-o': 'open'
|
||||
'meta-O': 'open-dev'
|
||||
'meta-w': 'core:close'
|
||||
'alt-meta-i': 'toggle-dev-tools'
|
||||
|
||||
'.tool-panel':
|
||||
'meta-escape': 'tool-panel:unfocus'
|
||||
'escape': 'core:close'
|
||||
@@ -5,10 +5,21 @@
|
||||
'tab': 'editor:indent'
|
||||
'meta-=': 'editor:auto-indent'
|
||||
'meta-d': 'editor:delete-line'
|
||||
|
||||
'ctrl-[': 'editor:fold-current-row'
|
||||
'ctrl-]': 'editor:unfold-current-row'
|
||||
'ctrl-{': 'editor:fold-all'
|
||||
'ctrl-}': 'editor:unfold-all'
|
||||
'ctrl-meta-1': 'editor:fold-at-indent-level-1'
|
||||
'ctrl-meta-2': 'editor:fold-at-indent-level-2'
|
||||
'ctrl-meta-3': 'editor:fold-at-indent-level-3'
|
||||
'ctrl-meta-4': 'editor:fold-at-indent-level-4'
|
||||
'ctrl-meta-5': 'editor:fold-at-indent-level-5'
|
||||
'ctrl-meta-6': 'editor:fold-at-indent-level-6'
|
||||
'ctrl-meta-7': 'editor:fold-at-indent-level-7'
|
||||
'ctrl-meta-8': 'editor:fold-at-indent-level-8'
|
||||
'ctrl-meta-9': 'editor:fold-at-indent-level-9'
|
||||
|
||||
'alt-shift-down': 'editor:add-selection-below'
|
||||
'alt-shift-up': 'editor:add-selection-above'
|
||||
'alt-meta-ctrl-f': 'editor:fold-selection'
|
||||
@@ -0,0 +1,4 @@
|
||||
'.select-list .mini.editor':
|
||||
'enter': 'core:confirm'
|
||||
'escape': 'core:cancel'
|
||||
'meta-w': 'core:cancel'
|
||||
+76
-37
@@ -1,43 +1,76 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"version": "0.0.0",
|
||||
"version": "21.0.0",
|
||||
"main": "./src/main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/github/atom.git"
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/atom/atom/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"coffee-script": "1.6.2",
|
||||
"ctags": "0.5.0",
|
||||
"oniguruma": "0.16.0",
|
||||
"mkdirp": "0.3.5",
|
||||
"git-utils": "0.19.0",
|
||||
"underscore": "1.4.4",
|
||||
"d3": "3.0.8",
|
||||
"coffee-cache": "0.1.0",
|
||||
"pegjs": "0.7.0",
|
||||
"async": "0.2.6",
|
||||
"nak": "0.2.16",
|
||||
"spellchecker": "0.6.0",
|
||||
"pathwatcher": "0.5.0",
|
||||
"keytar": "0.9.0",
|
||||
"ls-archive": "0.9.0",
|
||||
"temp": "0.5.0",
|
||||
"rimraf": "2.1.4",
|
||||
"plist": "git://github.com/nathansobo/node-plist.git",
|
||||
"space-pen": "1.0.0",
|
||||
"less": "git://github.com/nathansobo/less.js.git",
|
||||
"roaster": "0.0.5",
|
||||
"jqueryui-browser": "1.10.2-1",
|
||||
"optimist": "0.4.0",
|
||||
"season": "0.10.0",
|
||||
"humanize-plus": "1.1.0",
|
||||
"semver": "1.1.4",
|
||||
"guid": "0.0.10",
|
||||
"tantamount": "0.3.0",
|
||||
"coffee-cache": "0.1.0",
|
||||
"coffee-script": "1.6.2",
|
||||
"coffeestack": "0.4.0",
|
||||
"first-mate": "0.1.0",
|
||||
"git-utils": "0.24.0",
|
||||
"guid": "0.0.10",
|
||||
"mkdirp": "0.3.5",
|
||||
"less": "git://github.com/nathansobo/less.js.git",
|
||||
"nak": "0.2.16",
|
||||
"nslog": "0.1.0",
|
||||
"oniguruma": "0.16.0",
|
||||
"optimist": "0.4.0",
|
||||
"pathwatcher": "0.5.0",
|
||||
"patrick": "0.4.0",
|
||||
"pegjs": "0.7.0",
|
||||
"plist": "git://github.com/nathansobo/node-plist.git",
|
||||
"rimraf": "2.1.4",
|
||||
"season": "0.10.0",
|
||||
"semver": "1.1.4",
|
||||
"space-pen": "1.2.0",
|
||||
"tantamount": "0.3.0",
|
||||
"telepath": "0.1.2",
|
||||
"temp": "0.5.0",
|
||||
"underscore": "1.4.4",
|
||||
|
||||
"archive-view": "0.3.0",
|
||||
"autocomplete": "0.4.0",
|
||||
"autoflow": "0.1.0",
|
||||
"bookmarks": "0.2.0",
|
||||
"bracket-matcher": "0.3.0",
|
||||
"collaboration": "0.8.0",
|
||||
"command-logger": "0.2.0",
|
||||
"command-panel": "0.2.0",
|
||||
"command-palette": "0.1.0",
|
||||
"fuzzy-finder": "0.3.0",
|
||||
"editor-stats": "0.1.0",
|
||||
"gfm": "0.2.0",
|
||||
"git-diff": "0.2.0",
|
||||
"gists": "0.1.0",
|
||||
"github-sign-in": "0.1.0",
|
||||
"go-to-line": "0.2.0",
|
||||
"grammar-selector": "0.2.0",
|
||||
"image-view": "0.3.0",
|
||||
"link": "0.1.0",
|
||||
"markdown-preview": "0.1.0",
|
||||
"package-generator": "0.2.0",
|
||||
"settings-view": "0.8.0",
|
||||
"snippets": "0.2.0",
|
||||
"spell-check": "0.3.0",
|
||||
"status-bar": "0.3.0",
|
||||
"symbols-view": "0.4.0",
|
||||
"tabs": "0.1.0",
|
||||
"terminal": "0.6.0",
|
||||
"toml": "0.1.0",
|
||||
"tree-view": "0.2.0",
|
||||
"whitespace": "0.1.0",
|
||||
"wrap-guide": "0.1.0",
|
||||
|
||||
"c-tmbundle": "1.0.0",
|
||||
"coffee-script-tmbundle": "2.0.0",
|
||||
"coffee-script-tmbundle": "4.0.0",
|
||||
"css-tmbundle": "1.0.0",
|
||||
"git-tmbundle": "1.0.0",
|
||||
"go-tmbundle": "1.0.0",
|
||||
@@ -66,11 +99,10 @@
|
||||
"textmate-clojure": "1.0.0",
|
||||
"todo-tmbundle": "1.0.0",
|
||||
"xml-tmbundle": "1.0.0",
|
||||
"yaml-tmbundle": "1.0.0",
|
||||
"nslog": "0.1.0"
|
||||
"yaml-tmbundle": "1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"biscotto": "0.0.12",
|
||||
"biscotto": "0.0.14",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-cli": "~0.1.9",
|
||||
"grunt-coffeelint": "0.0.6",
|
||||
@@ -78,12 +110,19 @@
|
||||
"grunt-cson": "0.5.0",
|
||||
"grunt-contrib-csslint": "~0.1.2",
|
||||
"grunt-contrib-coffee": "~0.7.0",
|
||||
"grunt-contrib-less": "~0.5.2",
|
||||
"jasmine-focused": "~0.7.0",
|
||||
"walkdir": "0.0.7"
|
||||
"grunt-contrib-less": "~0.6.4",
|
||||
"jasmine-focused": "~0.12.0",
|
||||
"walkdir": "0.0.7",
|
||||
"ws": "0.4.27",
|
||||
"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"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"preinstall": "true"
|
||||
"preinstall": "true",
|
||||
"test": "grunt test"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,17 @@
|
||||
<string>speakeasy.pem</string>
|
||||
<key>SUScheduledCheckInterval</key>
|
||||
<string>3600</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>atom</string>
|
||||
</array>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>Atom Shared Session Protocol</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
||||
@@ -18,4 +18,5 @@ git submodule --quiet update --recursive --init
|
||||
(cd vendor/apm && npm install .)
|
||||
|
||||
npm install --silent vendor/apm
|
||||
echo ""
|
||||
./node_modules/.bin/apm install --silent
|
||||
|
||||
Arquivo executável
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
coffee -c -o /Applications/Atom.app/Contents/Resources/app/src/ src/main.coffee
|
||||
Arquivo executável
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env coffee
|
||||
|
||||
usage = """
|
||||
Usage:
|
||||
update-octicons PATH-TO-OCTICONS
|
||||
"""
|
||||
|
||||
path = require 'path'
|
||||
fs = require 'fs'
|
||||
YAML = require 'js-yaml'
|
||||
|
||||
scriptPath = process.argv[1]
|
||||
pathToOcticons = process.argv[2] ? path.join(process.env.HOME, 'github', 'octicons')
|
||||
atomDir = path.resolve(scriptPath, "../../..")
|
||||
|
||||
unless fs.existsSync(pathToOcticons)
|
||||
console.error(usage)
|
||||
process.exit(1)
|
||||
|
||||
# Copy font-file
|
||||
fontSrc = path.join(pathToOcticons, 'octicons', 'octicons.woff')
|
||||
fontDest = path.join(atomDir, 'static', 'octicons.woff')
|
||||
fs.createReadStream(fontSrc).pipe(fs.createWriteStream(fontDest))
|
||||
|
||||
# Update Octicon UTF codes
|
||||
glyphsSrc = path.join(pathToOcticons, 'data', 'glyphs.yml')
|
||||
octiconUtfDest = path.join atomDir, 'static', 'octicon-utf-codes.less'
|
||||
output = []
|
||||
for {css, code} in YAML.load(fs.readFileSync(glyphsSrc).toString())
|
||||
output.push "@#{css}: \"\\#{code}\";"
|
||||
|
||||
fs.writeFileSync octiconUtfDest, "#{output.join('\n')}\n"
|
||||
@@ -1,138 +0,0 @@
|
||||
ConfigPanel = require 'config-panel'
|
||||
Editor = require 'editor'
|
||||
|
||||
describe "ConfigPanel", ->
|
||||
it "automatically binds named input fields to their corresponding config keys", ->
|
||||
class TestPanel extends ConfigPanel
|
||||
@content: ->
|
||||
@div =>
|
||||
@input outlet: 'intInput', id: 'foo.int', type: 'int'
|
||||
@input outlet: 'floatInput', id: 'foo.float', type: 'float'
|
||||
@input outlet: 'stringInput', id: 'foo.string', type: 'string'
|
||||
@input outlet: 'booleanInput', id: 'foo.boolean', type: 'checkbox'
|
||||
|
||||
config.set('foo.int', 22)
|
||||
config.set('foo.boolean', true)
|
||||
|
||||
panel = new TestPanel
|
||||
expect(panel.intInput.val()).toBe '22'
|
||||
expect(panel.floatInput.val()).toBe ''
|
||||
expect(panel.stringInput.val()).toBe ''
|
||||
expect(panel.booleanInput.attr('checked')).toBeTruthy()
|
||||
|
||||
config.set('foo.int', 10)
|
||||
expect(panel.intInput.val()).toBe '10'
|
||||
expect(panel.floatInput.val()).toBe ''
|
||||
expect(panel.stringInput.val()).toBe ''
|
||||
|
||||
config.set('foo.string', 'hey')
|
||||
expect(panel.intInput.val()).toBe '10'
|
||||
expect(panel.floatInput.val()).toBe ''
|
||||
expect(panel.stringInput.val()).toBe 'hey'
|
||||
|
||||
config.set('foo.boolean', false)
|
||||
expect(panel.booleanInput.attr('checked')).toBeFalsy()
|
||||
|
||||
panel.intInput.val('90.2').change()
|
||||
expect(config.get('foo.int')).toBe 90
|
||||
|
||||
panel.floatInput.val('90.2').change()
|
||||
expect(config.get('foo.float')).toBe 90.2
|
||||
|
||||
panel.intInput.val('0').change()
|
||||
expect(config.get('foo.int')).toBe 0
|
||||
|
||||
panel.floatInput.val('0').change()
|
||||
expect(config.get('foo.float')).toBe 0
|
||||
|
||||
panel.stringInput.val('moo').change()
|
||||
expect(config.get('foo.string')).toBe 'moo'
|
||||
|
||||
panel.intInput.val('abcd').change()
|
||||
expect(config.get('foo.int')).toBe 'abcd'
|
||||
|
||||
panel.floatInput.val('defg').change()
|
||||
expect(config.get('foo.float')).toBe 'defg'
|
||||
|
||||
panel.intInput.val('').change()
|
||||
expect(config.get('foo.int')).toBe undefined
|
||||
panel.floatInput.val('').change()
|
||||
expect(config.get('foo.float')).toBe undefined
|
||||
panel.stringInput.val('').change()
|
||||
expect(config.get('foo.string')).toBe undefined
|
||||
|
||||
it "automatically binds named editors to their corresponding config keys", ->
|
||||
class TestPanel extends ConfigPanel
|
||||
@content: ->
|
||||
@div =>
|
||||
@subview 'intEditor', new Editor(mini: true, attributes: { id: 'foo.int', type: 'int' })
|
||||
@subview 'floatEditor', new Editor(mini: true, attributes: { id: 'foo.float', type: 'float' })
|
||||
@subview 'stringEditor', new Editor(mini: true, attributes: { id: 'foo.string', type: 'string' })
|
||||
|
||||
config.set('foo.int', 1)
|
||||
config.set('foo.float', 1.1)
|
||||
config.set('foo.string', 'I think therefore I am.')
|
||||
panel = new TestPanel
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(panel.intEditor.getText()).toBe '1'
|
||||
expect(panel.floatEditor.getText()).toBe '1.1'
|
||||
expect(panel.stringEditor.getText()).toBe 'I think therefore I am.'
|
||||
|
||||
config.set('foo.int', 2)
|
||||
config.set('foo.float', 2.2)
|
||||
config.set('foo.string', 'We are what we think.')
|
||||
expect(panel.intEditor.getText()).toBe '2'
|
||||
expect(panel.floatEditor.getText()).toBe '2.2'
|
||||
expect(panel.stringEditor.getText()).toBe 'We are what we think.'
|
||||
|
||||
panel.intEditor.setText('3')
|
||||
panel.floatEditor.setText('3.3')
|
||||
panel.stringEditor.setText('All limitations are self imposed.')
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(config.get('foo.int')).toBe 3
|
||||
expect(config.get('foo.float')).toBe 3.3
|
||||
expect(config.get('foo.string')).toBe 'All limitations are self imposed.'
|
||||
|
||||
|
||||
panel.intEditor.setText('not an int')
|
||||
panel.floatEditor.setText('not a float')
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(config.get('foo.int')).toBe 'not an int'
|
||||
expect(config.get('foo.float')).toBe 'not a float'
|
||||
|
||||
panel.intEditor.setText('')
|
||||
panel.floatEditor.setText('')
|
||||
panel.stringEditor.setText('')
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(config.get('foo.int')).toBe undefined
|
||||
expect(config.get('foo.float')).toBe undefined
|
||||
expect(config.get('foo.string')).toBe undefined
|
||||
|
||||
panel.intEditor.setText('0')
|
||||
panel.floatEditor.setText('0')
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(config.get('foo.int')).toBe 0
|
||||
expect(config.get('foo.float')).toBe 0
|
||||
|
||||
it "does not save the config value until it has been changed to a new value", ->
|
||||
class TestPanel extends ConfigPanel
|
||||
@content: ->
|
||||
@div =>
|
||||
@subview "fooInt", new Editor(mini: true, attributes: {id: 'foo.int', type: 'int'})
|
||||
|
||||
config.set('foo.int', 1)
|
||||
observeHandler = jasmine.createSpy("observeHandler")
|
||||
config.observe "foo.int", observeHandler
|
||||
observeHandler.reset()
|
||||
|
||||
testPanel = new TestPanel
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
|
||||
testPanel.fooInt.setText("1")
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
|
||||
testPanel.fooInt.setText("2")
|
||||
window.advanceClock(10000) # wait for contents-modified to be triggered
|
||||
expect(observeHandler).toHaveBeenCalled()
|
||||
@@ -1,46 +0,0 @@
|
||||
ConfigView = require 'config-view'
|
||||
{$$} = require 'space-pen'
|
||||
|
||||
describe "ConfigView", ->
|
||||
configView = null
|
||||
|
||||
beforeEach ->
|
||||
configView = new ConfigView
|
||||
|
||||
describe "serialization", ->
|
||||
it "remembers which panel was visible", ->
|
||||
configView.showPanel('Editor')
|
||||
newConfigView = deserialize(configView.serialize())
|
||||
configView.remove()
|
||||
newConfigView.attachToDom()
|
||||
expect(newConfigView.activePanelName).toBe 'Editor'
|
||||
|
||||
it "shows the previously active panel if it is added after deserialization", ->
|
||||
configView.addPanel('Panel 1', $$ -> @div id: 'panel-1')
|
||||
configView.showPanel('Panel 1')
|
||||
newConfigView = deserialize(configView.serialize())
|
||||
configView.remove()
|
||||
newConfigView.attachToDom()
|
||||
newConfigView.addPanel('Panel 1', $$ -> @div id: 'panel-1')
|
||||
expect(newConfigView.activePanelName).toBe 'Panel 1'
|
||||
|
||||
describe ".addPanel(name, view)", ->
|
||||
it "adds a menu entry to the left and a panel that can be activated by clicking it", ->
|
||||
configView.addPanel('Panel 1', $$ -> @div id: 'panel-1')
|
||||
configView.addPanel('Panel 2', $$ -> @div id: 'panel-2')
|
||||
|
||||
expect(configView.panelMenu.find('li a:contains(Panel 1)')).toExist()
|
||||
expect(configView.panelMenu.find('li a:contains(Panel 2)')).toExist()
|
||||
expect(configView.panelMenu.children(':first')).toHaveClass 'active'
|
||||
|
||||
configView.attachToDom()
|
||||
configView.panelMenu.find('li a:contains(Panel 1)').click()
|
||||
expect(configView.panelMenu.children('.active').length).toBe 1
|
||||
expect(configView.panelMenu.find('li:contains(Panel 1)')).toHaveClass('active')
|
||||
expect(configView.panels.find('#panel-1')).toBeVisible()
|
||||
expect(configView.panels.find('#panel-2')).toBeHidden()
|
||||
configView.panelMenu.find('li a:contains(Panel 2)').click()
|
||||
expect(configView.panelMenu.children('.active').length).toBe 1
|
||||
expect(configView.panelMenu.find('li:contains(Panel 2)')).toHaveClass('active')
|
||||
expect(configView.panels.find('#panel-1')).toBeHidden()
|
||||
expect(configView.panels.find('#panel-2')).toBeVisible()
|
||||
@@ -1,26 +0,0 @@
|
||||
{createSite} = require 'telepath'
|
||||
Editor = require 'editor'
|
||||
|
||||
describe "EditSession replication", ->
|
||||
[editSession1, editSession2] = []
|
||||
beforeEach ->
|
||||
editSession1 = project.open('sample.js')
|
||||
doc1 = editSession1.getState()
|
||||
doc2 = doc1.clone(createSite(2))
|
||||
doc1.connect(doc2)
|
||||
editSession2 = deserialize(doc2)
|
||||
|
||||
it "replicates the scroll position", ->
|
||||
editor1 = new Editor(editSession1)
|
||||
editor2 = new Editor(editSession2)
|
||||
|
||||
editor1.attachToDom().width(50).height(50)
|
||||
editor2.attachToDom().width(50).height(50)
|
||||
|
||||
editor1.scrollTop(10)
|
||||
expect(editor1.scrollTop()).toBe 10
|
||||
expect(editor2.scrollTop()).toBe 10
|
||||
|
||||
editor2.scrollLeft(20)
|
||||
expect(editor2.scrollLeft()).toBe 20
|
||||
expect(editor1.scrollLeft()).toBe 20
|
||||
@@ -1,141 +0,0 @@
|
||||
Project = require 'project'
|
||||
Buffer = require 'text-buffer'
|
||||
EditSession = require 'edit-session'
|
||||
|
||||
describe "LanguageMode", ->
|
||||
[editSession, buffer, languageMode] = []
|
||||
|
||||
afterEach ->
|
||||
editSession.destroy()
|
||||
|
||||
describe "javascript", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('javascript-tmbundle', sync: true)
|
||||
editSession = project.open('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
|
||||
describe ".toggleLineCommentsForBufferRows(start, end)", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 7)
|
||||
expect(buffer.lineForRow(4)).toBe "// while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe "// current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe "// current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe "// }"
|
||||
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 5)
|
||||
expect(buffer.lineForRow(4)).toBe " while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe " current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe "// current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe "// }"
|
||||
|
||||
describe "fold suggestion", ->
|
||||
describe ".doesBufferRowStartFold(bufferRow)", ->
|
||||
it "returns true only when the buffer row starts a foldable region", ->
|
||||
expect(languageMode.doesBufferRowStartFold(0)).toBeTruthy()
|
||||
expect(languageMode.doesBufferRowStartFold(1)).toBeTruthy()
|
||||
expect(languageMode.doesBufferRowStartFold(2)).toBeFalsy()
|
||||
expect(languageMode.doesBufferRowStartFold(3)).toBeFalsy()
|
||||
|
||||
describe ".rowRangeForFoldAtBufferRow(bufferRow)", ->
|
||||
it "returns the start/end rows of the foldable region starting at the given row", ->
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(0)).toEqual [0, 12]
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(1)).toEqual [1, 9]
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(2)).toBeNull()
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(4)).toEqual [4, 7]
|
||||
|
||||
describe "suggestedIndentForBufferRow", ->
|
||||
it "returns the suggested indentation based on auto-indent/outdent rules", ->
|
||||
expect(languageMode.suggestedIndentForBufferRow(0)).toBe 0
|
||||
expect(languageMode.suggestedIndentForBufferRow(1)).toBe 1
|
||||
expect(languageMode.suggestedIndentForBufferRow(2)).toBe 2
|
||||
expect(languageMode.suggestedIndentForBufferRow(9)).toBe 1
|
||||
|
||||
describe "coffeescript", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('coffee-script-tmbundle', sync: true)
|
||||
editSession = project.open('coffee.coffee', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
|
||||
describe ".toggleLineCommentsForBufferRows(start, end)", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 7)
|
||||
expect(buffer.lineForRow(4)).toBe "# pivot = items.shift()"
|
||||
expect(buffer.lineForRow(5)).toBe "# left = []"
|
||||
expect(buffer.lineForRow(6)).toBe "# right = []"
|
||||
expect(buffer.lineForRow(7)).toBe "# "
|
||||
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 5)
|
||||
expect(buffer.lineForRow(4)).toBe " pivot = items.shift()"
|
||||
expect(buffer.lineForRow(5)).toBe " left = []"
|
||||
expect(buffer.lineForRow(6)).toBe "# right = []"
|
||||
expect(buffer.lineForRow(7)).toBe "# "
|
||||
|
||||
describe "fold suggestion", ->
|
||||
describe ".doesBufferRowStartFold(bufferRow)", ->
|
||||
it "returns true only when the buffer row starts a foldable region", ->
|
||||
expect(languageMode.doesBufferRowStartFold(0)).toBeTruthy()
|
||||
expect(languageMode.doesBufferRowStartFold(1)).toBeTruthy()
|
||||
expect(languageMode.doesBufferRowStartFold(2)).toBeFalsy()
|
||||
expect(languageMode.doesBufferRowStartFold(3)).toBeFalsy()
|
||||
expect(languageMode.doesBufferRowStartFold(19)).toBeTruthy()
|
||||
|
||||
describe ".rowRangeForFoldAtBufferRow(bufferRow)", ->
|
||||
it "returns the start/end rows of the foldable region starting at the given row", ->
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(0)).toEqual [0, 20]
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(1)).toEqual [1, 17]
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(2)).toBeNull()
|
||||
expect(languageMode.rowRangeForFoldAtBufferRow(19)).toEqual [19, 20]
|
||||
|
||||
describe "css", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('css-tmbundle', sync: true)
|
||||
editSession = project.open('css.css', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
|
||||
describe ".toggleLineCommentsForBufferRows(start, end)", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 1)
|
||||
expect(buffer.lineForRow(0)).toBe "/*body {"
|
||||
expect(buffer.lineForRow(1)).toBe " font-size: 1234px;*/"
|
||||
expect(buffer.lineForRow(2)).toBe " width: 110%;"
|
||||
expect(buffer.lineForRow(3)).toBe " font-weight: bold !important;"
|
||||
|
||||
languageMode.toggleLineCommentsForBufferRows(2, 2)
|
||||
expect(buffer.lineForRow(0)).toBe "/*body {"
|
||||
expect(buffer.lineForRow(1)).toBe " font-size: 1234px;*/"
|
||||
expect(buffer.lineForRow(2)).toBe "/* width: 110%;*/"
|
||||
expect(buffer.lineForRow(3)).toBe " font-weight: bold !important;"
|
||||
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 1)
|
||||
expect(buffer.lineForRow(0)).toBe "body {"
|
||||
expect(buffer.lineForRow(1)).toBe " font-size: 1234px;"
|
||||
expect(buffer.lineForRow(2)).toBe "/* width: 110%;*/"
|
||||
expect(buffer.lineForRow(3)).toBe " font-weight: bold !important;"
|
||||
|
||||
it "uncomments lines with leading whitespace", ->
|
||||
buffer.change([[2, 0], [2, Infinity]], " /*width: 110%;*/")
|
||||
languageMode.toggleLineCommentsForBufferRows(2, 2)
|
||||
expect(buffer.lineForRow(2)).toBe " width: 110%;"
|
||||
|
||||
it "uncomments lines with trailing whitespace", ->
|
||||
buffer.change([[2, 0], [2, Infinity]], "/*width: 110%;*/ ")
|
||||
languageMode.toggleLineCommentsForBufferRows(2, 2)
|
||||
expect(buffer.lineForRow(2)).toBe "width: 110%; "
|
||||
|
||||
it "uncomments lines with leading and trailing whitespace", ->
|
||||
buffer.change([[2, 0], [2, Infinity]], " /*width: 110%;*/ ")
|
||||
languageMode.toggleLineCommentsForBufferRows(2, 2)
|
||||
expect(buffer.lineForRow(2)).toBe " width: 110%; "
|
||||
|
||||
describe "less", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('less-tmbundle', sync: true)
|
||||
atom.activatePackage('css-tmbundle', sync: true)
|
||||
editSession = project.open('sample.less', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
|
||||
describe "when commenting lines", ->
|
||||
it "only uses the `commentEnd` pattern if it comes from the same grammar as the `commentStart`", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 0)
|
||||
expect(buffer.lineForRow(0)).toBe "// @color: #4D926F;"
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
PackageConfigPanel = require 'package-config-panel'
|
||||
packageManager = require 'package-manager'
|
||||
_ = require 'underscore'
|
||||
|
||||
describe "PackageConfigPanel", ->
|
||||
[panel, configObserver] = []
|
||||
|
||||
beforeEach ->
|
||||
installedPackages = [
|
||||
{
|
||||
name: 'p1'
|
||||
version: '3.2.1'
|
||||
}
|
||||
{
|
||||
name: 'p2'
|
||||
version: '1.2.3'
|
||||
}
|
||||
{
|
||||
name: 'p3'
|
||||
version: '5.8.5'
|
||||
}
|
||||
]
|
||||
|
||||
availablePackages = [
|
||||
{
|
||||
name: 'p4'
|
||||
version: '3.2.1'
|
||||
homepage: 'http://p4.io'
|
||||
}
|
||||
{
|
||||
name: 'p5'
|
||||
version: '1.2.3'
|
||||
repository: url: 'http://github.com/atom/p5.git'
|
||||
bugs: url: 'http://github.com/atom/p5/issues'
|
||||
}
|
||||
{
|
||||
name: 'p6'
|
||||
version: '5.8.5'
|
||||
}
|
||||
]
|
||||
|
||||
spyOn(packageManager, 'getAvailable').andCallFake (callback) ->
|
||||
callback(null, availablePackages)
|
||||
spyOn(packageManager, 'uninstall').andCallFake (pack, callback) ->
|
||||
_.remove(installedPackages, pack)
|
||||
callback()
|
||||
spyOn(packageManager, 'install').andCallFake (pack, callback) ->
|
||||
installedPackages.push(pack)
|
||||
callback()
|
||||
|
||||
spyOn(atom, 'getAvailablePackageMetadata').andReturn(installedPackages)
|
||||
spyOn(atom, 'resolvePackagePath').andCallFake (name) ->
|
||||
if _.contains(_.pluck(installedPackages, 'name'), name)
|
||||
"/tmp/atom-packages/#{name}"
|
||||
|
||||
configObserver = jasmine.createSpy("configObserver")
|
||||
observeSubscription = config.observe('core.disabledPackages', configObserver)
|
||||
config.set('core.disabledPackages', ['p1', 'p3'])
|
||||
configObserver.reset()
|
||||
jasmine.unspy(window, "setTimeout")
|
||||
panel = new PackageConfigPanel
|
||||
|
||||
installedCallback = jasmine.createSpy("installed packages callback")
|
||||
panel.packageEventEmitter.on("installed-packages-loaded", installedCallback)
|
||||
waitsFor -> installedCallback.callCount > 0
|
||||
|
||||
describe 'Installed tab', ->
|
||||
it "lists all installed packages with a link to enable or disable the package", ->
|
||||
p1View = panel.installed.find("[name='p1']").view()
|
||||
expect(p1View).toExist()
|
||||
expect(p1View.enableToggle.find('a').text()).toBe 'Enable'
|
||||
|
||||
p2View = panel.installed.find("[name='p2']").view()
|
||||
expect(p2View).toExist()
|
||||
expect(p2View.enableToggle.find('a').text()).toBe 'Disable'
|
||||
|
||||
p3View = panel.installed.find("[name='p3']").view()
|
||||
expect(p3View).toExist()
|
||||
expect(p3View.enableToggle.find('a').text()).toBe 'Enable'
|
||||
|
||||
describe "when the core.disabledPackages array changes", ->
|
||||
it "updates the checkboxes for newly disabled / enabled packages", ->
|
||||
config.set('core.disabledPackages', ['p2'])
|
||||
p1View = panel.installed.find("[name='p1']").view()
|
||||
expect(p1View.enableToggle.find('a').text()).toBe 'Disable'
|
||||
|
||||
p2View = panel.installed.find("[name='p2']").view()
|
||||
expect(p2View.enableToggle.find('a').text()).toBe 'Enable'
|
||||
|
||||
p3View = panel.installed.find("[name='p3']").view()
|
||||
expect(p3View.enableToggle.find('a').text()).toBe 'Disable'
|
||||
|
||||
describe "when the disable link is clicked", ->
|
||||
it "adds the package name to the disabled packages array", ->
|
||||
p2View = panel.installed.find("[name='p2']").view()
|
||||
p2View.enableToggle.find('a').click()
|
||||
expect(configObserver).toHaveBeenCalledWith(['p1', 'p3', 'p2'])
|
||||
|
||||
describe "when the enable link is clicked", ->
|
||||
it "removes the package name from the disabled packages array", ->
|
||||
p3View = panel.installed.find("[name='p3']").view()
|
||||
p3View.enableToggle.find('a').click()
|
||||
expect(configObserver).toHaveBeenCalledWith(['p1'])
|
||||
|
||||
describe "when Uninstall is clicked", ->
|
||||
it "removes the package from the tab", ->
|
||||
expect(panel.installed.find("[name='p1']")).toExist()
|
||||
p1View = panel.installed.find("[name='p1']").view()
|
||||
expect(p1View.defaultAction.text()).toBe 'Uninstall'
|
||||
p1View.defaultAction.click()
|
||||
expect(panel.installed.find("[name='p1']")).not.toExist()
|
||||
|
||||
describe 'Available tab', ->
|
||||
it 'lists all available packages', ->
|
||||
panel.availableLink.click()
|
||||
panel.attachToDom()
|
||||
|
||||
expect(panel.available.packagesArea.children('.panel').length).toBe 3
|
||||
p4View = panel.available.packagesArea.children('.panel:eq(0)').view()
|
||||
p5View = panel.available.packagesArea.children('.panel:eq(1)').view()
|
||||
p6View = panel.available.packagesArea.children('.panel:eq(2)').view()
|
||||
|
||||
expect(p4View.name.text()).toBe 'p4'
|
||||
expect(p5View.name.text()).toBe 'p5'
|
||||
expect(p6View.name.text()).toBe 'p6'
|
||||
|
||||
expect(p4View.version.text()).toBe '3.2.1'
|
||||
expect(p5View.version.text()).toBe '1.2.3'
|
||||
expect(p6View.version.text()).toBe '5.8.5'
|
||||
|
||||
p4View.dropdownButton.click()
|
||||
expect(p4View.homepage).toBeVisible()
|
||||
expect(p4View.homepage.find('a').attr('href')).toBe 'http://p4.io'
|
||||
expect(p4View.issues).toBeHidden()
|
||||
|
||||
p5View.dropdownButton.click()
|
||||
expect(p5View.homepage).toBeVisible()
|
||||
expect(p5View.homepage.find('a').attr('href')).toBe 'http://github.com/atom/p5'
|
||||
expect(p5View.issues).toBeVisible()
|
||||
expect(p5View.issues.find('a').attr('href')).toBe 'http://github.com/atom/p5/issues'
|
||||
|
||||
p6View.dropdownButton.click()
|
||||
expect(p6View.homepage).toBeHidden()
|
||||
expect(p6View.issues).toBeHidden()
|
||||
|
||||
describe "when Install is clicked", ->
|
||||
it "adds the package to the Installed tab", ->
|
||||
expect(panel.installed.find("[name='p4']")).not.toExist()
|
||||
expect(panel.available.find("[name='p4']")).toExist()
|
||||
p4View = panel.available.find("[name='p4']").view()
|
||||
expect(p4View.defaultAction.text()).toBe 'Install'
|
||||
p4View.defaultAction.click()
|
||||
expect(panel.installed.find("[name='p4']")).toExist()
|
||||
expect(p4View.defaultAction.text()).toBe 'Uninstall'
|
||||
@@ -1,33 +0,0 @@
|
||||
Point = require 'point'
|
||||
|
||||
describe "Point", ->
|
||||
describe ".isEqual(value)", ->
|
||||
describe "when given value is a Point", ->
|
||||
it "returns true when the rows and columns match", ->
|
||||
expect(new Point(1,2)).toEqual new Point(1,2)
|
||||
expect(new Point(2,1)).not.toEqual new Point(1,2)
|
||||
|
||||
describe "when given value is an Array", ->
|
||||
it "returns true only when index zero matches row and index one matches column", ->
|
||||
expect(new Point(1,2)).toEqual [1,2]
|
||||
expect(new Point(2,1)).not.toEqual [1,2]
|
||||
|
||||
describe "when one of the points has a row or column that is NaN", ->
|
||||
it "returns false", ->
|
||||
expect(new Point(1, 3)).not.toEqual new Point(NaN, 3)
|
||||
expect(new Point(1, 3)).not.toEqual new Point(1, NaN)
|
||||
|
||||
describe "compare", ->
|
||||
it "returns 1, 0, or -1 based on whether the given point precedes, equals, or follows the receivers location in the buffer", ->
|
||||
expect(new Point(5, 0).compare(new Point(5, 0))).toBe 0
|
||||
expect(new Point(5, 0).compare(new Point(6, 0))).toBe -1
|
||||
expect(new Point(5, 0).compare(new Point(5, 1))).toBe -1
|
||||
expect(new Point(5, 0).compare(new Point(6, 1))).toBe -1
|
||||
expect(new Point(5, 5).compare(new Point(4, 1))).toBe 1
|
||||
expect(new Point(5, 5).compare(new Point(5, 3))).toBe 1
|
||||
|
||||
describe ".translate(other)", ->
|
||||
it "returns a translated point", ->
|
||||
expect(new Point(1,2).translate([2,4])).toEqual [3,6]
|
||||
expect(new Point(1,2).translate([-1])).toEqual [0,2]
|
||||
expect(new Point(1,2).translate([0,-2])).toEqual [1,0]
|
||||
@@ -1,47 +0,0 @@
|
||||
Range = require 'range'
|
||||
Point = require 'point'
|
||||
|
||||
describe "Range", ->
|
||||
describe "constructor", ->
|
||||
it "ensures that @start <= @end", ->
|
||||
range1 = new Range(new Point(0, 1), new Point(0, 4))
|
||||
expect(range1.start).toEqual(row: 0, column: 1)
|
||||
|
||||
range2 = new Range(new Point(1, 4), new Point(0, 1))
|
||||
expect(range2.start).toEqual(row: 0, column: 1)
|
||||
|
||||
describe ".isEmpty()", ->
|
||||
it "returns true if @start equals @end", ->
|
||||
expect(new Range(new Point(1, 1), new Point(1, 1)).isEmpty()).toBeTruthy()
|
||||
expect(new Range(new Point(1, 1), new Point(1, 2)).isEmpty()).toBeFalsy()
|
||||
|
||||
describe ".intersectsWith(otherRange)", ->
|
||||
it "returns true if the ranges intersect or share an endpoint", ->
|
||||
expect(new Range([1, 1], [2, 10]).intersectsWith(new Range([2, 1], [3, 10]))).toBeTruthy()
|
||||
expect(new Range([2, 1], [3, 10]).intersectsWith(new Range([1, 1], [2, 10]))).toBeTruthy()
|
||||
expect(new Range([2, 1], [3, 10]).intersectsWith(new Range([2, 5], [3, 1]))).toBeTruthy()
|
||||
expect(new Range([2, 5], [3, 1]).intersectsWith(new Range([2, 1], [3, 10]))).toBeTruthy()
|
||||
expect(new Range([2, 5], [3, 1]).intersectsWith(new Range([3, 1], [3, 10]))).toBeTruthy()
|
||||
expect(new Range([3, 1], [3, 10]).intersectsWith(new Range([2, 5], [3, 1]))).toBeTruthy()
|
||||
expect(new Range([2, 5], [3, 1]).intersectsWith(new Range([3, 2], [3, 10]))).toBeFalsy()
|
||||
expect(new Range([3, 2], [3, 10]).intersectsWith(new Range([2, 5], [3, 1]))).toBeFalsy()
|
||||
|
||||
describe ".union(otherRange)", ->
|
||||
it "returns the union of the two ranges", ->
|
||||
expect(new Range([1, 1], [2, 10]).union(new Range([2, 1], [3, 10]))).toEqual [[1, 1], [3, 10]]
|
||||
expect(new Range([2, 1], [3, 10]).union(new Range([1, 1], [2, 10]))).toEqual [[1, 1], [3, 10]]
|
||||
expect(new Range([2, 1], [3, 10]).union(new Range([2, 5], [3, 1]))).toEqual [[2, 1], [3, 10]]
|
||||
expect(new Range([2, 5], [3, 1]).union(new Range([2, 1], [3, 10]))).toEqual [[2, 1], [3, 10]]
|
||||
|
||||
describe ".compare(otherRange)", ->
|
||||
it "sorts earlier ranges first, and larger ranges first if both ranges start at the same place", ->
|
||||
expect(new Range([1, 1], [2, 10]).compare(new Range([2, 1], [3, 10]))).toBe -1
|
||||
expect(new Range([2, 1], [3, 10]).compare(new Range([1, 1], [2, 10]))).toBe 1
|
||||
expect(new Range([1, 1], [3, 10]).compare(new Range([1, 1], [2, 10]))).toBe -1
|
||||
expect(new Range([1, 1], [2, 10]).compare(new Range([1, 1], [3, 10]))).toBe 1
|
||||
expect(new Range([1, 1], [3, 10]).compare(new Range([1, 1], [3, 10]))).toBe 0
|
||||
|
||||
describe ".translate(startPoint, endPoint)", ->
|
||||
it "returns a range translates by the specified start and end points", ->
|
||||
expect(new Range([1, 1], [2, 10]).translate([1])).toEqual [[2, 1], [3, 10]]
|
||||
expect(new Range([1, 1], [2, 10]).translate([1,2], [3,4])).toEqual [[2, 3], [5, 14]]
|
||||
@@ -1,46 +0,0 @@
|
||||
TextMateScopeSelector = require 'text-mate-scope-selector'
|
||||
|
||||
describe "TextMateScopeSelector", ->
|
||||
it "matches the asterix", ->
|
||||
expect(new TextMateScopeSelector('*').matches(['a'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('*').matches(['b', 'c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a.*.c').matches(['a.b.c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a.*.c').matches(['a.b.c.d'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a.*.c').matches(['a.b.d.c'])).toBeFalsy()
|
||||
|
||||
it "matches prefixes", ->
|
||||
expect(new TextMateScopeSelector('a').matches(['a'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a').matches(['a.b'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a').matches(['abc'])).toBeFalsy()
|
||||
|
||||
it "matches disjunction", ->
|
||||
expect(new TextMateScopeSelector('a | b').matches(['b'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a|b|c').matches(['c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a|b|c').matches(['d'])).toBeFalsy()
|
||||
|
||||
it "matches negation", ->
|
||||
expect(new TextMateScopeSelector('a - c').matches(['a', 'b'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a-b').matches(['a', 'b'])).toBeFalsy()
|
||||
|
||||
it "matches conjunction", ->
|
||||
expect(new TextMateScopeSelector('a & b').matches(['b', 'a'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a&b&c').matches(['c'])).toBeFalsy()
|
||||
expect(new TextMateScopeSelector('a&b&c').matches(['a', 'b', 'd'])).toBeFalsy()
|
||||
|
||||
it "matches composites", ->
|
||||
expect(new TextMateScopeSelector('a,b,c').matches(['b', 'c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a, b, c').matches(['d', 'e'])).toBeFalsy()
|
||||
expect(new TextMateScopeSelector('a, b, c').matches(['d', 'c.e'])).toBeTruthy()
|
||||
|
||||
it "matches groups", ->
|
||||
expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['a'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['b'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['c'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['d'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('(a,b) | (c, d)').matches(['e'])).toBeFalsy()
|
||||
|
||||
it "matches paths", ->
|
||||
expect(new TextMateScopeSelector('a b').matches(['a', 'b'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a b').matches(['b', 'a'])).toBeFalsy()
|
||||
expect(new TextMateScopeSelector('a c').matches(['a', 'b', 'c', 'd', 'e'])).toBeTruthy()
|
||||
expect(new TextMateScopeSelector('a b e').matches(['a', 'b', 'c', 'd', 'e'])).toBeTruthy()
|
||||
@@ -1,54 +0,0 @@
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
plist = require 'plist'
|
||||
TextMateTheme = require 'text-mate-theme'
|
||||
Theme = require 'theme'
|
||||
|
||||
describe "TextMateTheme", ->
|
||||
[theme, themePath] = []
|
||||
|
||||
beforeEach ->
|
||||
themePath = fsUtils.resolveOnLoadPath(path.join('fixtures', 'test.tmTheme'))
|
||||
theme = Theme.load(themePath)
|
||||
|
||||
afterEach ->
|
||||
theme.deactivate()
|
||||
|
||||
describe ".getRulesets()", ->
|
||||
rulesets = null
|
||||
|
||||
beforeEach ->
|
||||
rulesets = theme.getRulesets()
|
||||
|
||||
it "returns rulesets representing the theme's global style settings", ->
|
||||
expect(rulesets[0]).toEqual
|
||||
selector: '.editor, .editor .gutter'
|
||||
properties:
|
||||
'background-color': '#141414'
|
||||
'color': '#F8F8F8'
|
||||
|
||||
expect(rulesets[1]).toEqual
|
||||
selector: '.editor.is-focused .cursor'
|
||||
properties:
|
||||
'border-color': '#A7A7A7'
|
||||
|
||||
expect(rulesets[2]).toEqual
|
||||
selector: '.editor.is-focused .selection .region'
|
||||
properties:
|
||||
'background-color': "rgba(221, 240, 255, 0.2)"
|
||||
|
||||
it "returns an array of objects representing the theme's scope selectors", ->
|
||||
expect(rulesets[12]).toEqual
|
||||
comment: "Invalid – Deprecated"
|
||||
selector: ".invalid.deprecated"
|
||||
properties:
|
||||
'color': "#D2A8A1"
|
||||
'font-style': 'italic'
|
||||
'text-decoration': 'underline'
|
||||
|
||||
expect(rulesets[13]).toEqual
|
||||
comment: "Invalid – Illegal"
|
||||
selector: ".invalid.illegal"
|
||||
properties:
|
||||
'color': "#F8F8F8"
|
||||
'background-color': 'rgba(86, 45, 86, 0.75)'
|
||||
@@ -1,49 +0,0 @@
|
||||
$ = require 'jquery'
|
||||
ThemeConfigPanel = require 'theme-config-panel'
|
||||
|
||||
describe "ThemeConfigPanel", ->
|
||||
panel = null
|
||||
|
||||
beforeEach ->
|
||||
config.set('core.themes', ['atom-dark-ui', 'atom-dark-syntax'])
|
||||
panel = new ThemeConfigPanel
|
||||
|
||||
describe "when an enabled theme is reloced in the themes list", ->
|
||||
it "updates the 'core.themes' config key to reflect the new order", ->
|
||||
li = panel.enabledThemes.children(':first').detach()
|
||||
panel.enabledThemes.append(li)
|
||||
panel.enabledThemes.sortable('option', 'update')()
|
||||
expect(config.get('core.themes')).toEqual ['atom-dark-syntax', 'atom-dark-ui']
|
||||
|
||||
describe "when a theme is dragged into the enabled themes list", ->
|
||||
it "updates the 'core.themes' config key to reflect the themes in the enabled list", ->
|
||||
dragHelper = panel.availableThemes.find("li[name='atom-light-ui']").clone()
|
||||
panel.enabledThemes.prepend(dragHelper)
|
||||
panel.enabledThemes.sortable('option', 'receive')(null, helper: dragHelper[0])
|
||||
panel.enabledThemes.sortable('option', 'update')()
|
||||
expect(config.get('core.themes')).toEqual ['atom-light-ui', 'atom-dark-ui', 'atom-dark-syntax']
|
||||
|
||||
describe "when the theme is already present in the enabled list", ->
|
||||
it "removes the previous instance of the theme, updating the order based on the location of drag", ->
|
||||
dragHelper = panel.availableThemes.find("li[name='atom-dark-ui']").clone()
|
||||
panel.enabledThemes.append(dragHelper)
|
||||
panel.enabledThemes.sortable('option', 'receive')(null, helper: dragHelper[0])
|
||||
panel.enabledThemes.sortable('option', 'update')()
|
||||
expect(config.get('core.themes')).toEqual ['atom-dark-syntax', 'atom-dark-ui']
|
||||
|
||||
dragHelper = panel.availableThemes.find("li[name='atom-dark-ui']").clone()
|
||||
panel.enabledThemes.prepend(dragHelper)
|
||||
panel.enabledThemes.sortable('option', 'receive')(null, helper: dragHelper[0])
|
||||
panel.enabledThemes.sortable('option', 'update')()
|
||||
expect(config.get('core.themes')).toEqual ['atom-dark-ui', 'atom-dark-syntax']
|
||||
|
||||
describe "when the disable icon is clicked on a theme li", ->
|
||||
it "removes the theme from the list and the 'core.themes' array", ->
|
||||
panel.enabledThemes.find('li:first .disable-theme').click()
|
||||
expect(panel.getEnabledThemeNames()).toEqual ['atom-dark-syntax']
|
||||
expect(config.get('core.themes')).toEqual ['atom-dark-syntax']
|
||||
|
||||
describe "when the 'core.config' key is updated", ->
|
||||
it "refreshes the enabled themes list", ->
|
||||
config.set('core.themes', ['atom-light-ui', 'atom-light-syntax'])
|
||||
expect(panel.getEnabledThemeNames()).toEqual ['atom-light-ui', 'atom-light-syntax']
|
||||
@@ -1,59 +0,0 @@
|
||||
$ = require 'jquery'
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
Theme = require 'theme'
|
||||
|
||||
describe "@load(name)", ->
|
||||
theme = null
|
||||
|
||||
beforeEach ->
|
||||
$("#jasmine-content").append $("<div class='editor'></div>")
|
||||
|
||||
afterEach ->
|
||||
theme.deactivate()
|
||||
|
||||
describe "TextMateTheme", ->
|
||||
it "applies the theme's stylesheet to the current window", ->
|
||||
expect($(".editor").css("background-color")).not.toBe("rgb(20, 20, 20)")
|
||||
|
||||
themePath = fsUtils.resolveOnLoadPath(path.join('fixtures', 'test.tmTheme'))
|
||||
theme = Theme.load(themePath)
|
||||
expect($(".editor").css("background-color")).toBe("rgb(20, 20, 20)")
|
||||
|
||||
describe "AtomTheme", ->
|
||||
describe "when the theme is a file", ->
|
||||
it "loads and applies css", ->
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "1234px"
|
||||
themePath = project.resolve('themes/theme-stylesheet.css')
|
||||
theme = Theme.load(themePath)
|
||||
expect($(".editor").css("padding-top")).toBe "1234px"
|
||||
|
||||
it "parses, loads and applies less", ->
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "1234px"
|
||||
themePath = project.resolve('themes/theme-stylesheet.less')
|
||||
theme = Theme.load(themePath)
|
||||
expect($(".editor").css("padding-top")).toBe "4321px"
|
||||
|
||||
describe "when the theme contains a package.json file", ->
|
||||
it "loads and applies stylesheets from package.json in the correct order", ->
|
||||
expect($(".editor").css("padding-top")).not.toBe("101px")
|
||||
expect($(".editor").css("padding-right")).not.toBe("102px")
|
||||
expect($(".editor").css("padding-bottom")).not.toBe("103px")
|
||||
|
||||
themePath = project.resolve('themes/theme-with-package-file')
|
||||
theme = Theme.load(themePath)
|
||||
expect($(".editor").css("padding-top")).toBe("101px")
|
||||
expect($(".editor").css("padding-right")).toBe("102px")
|
||||
expect($(".editor").css("padding-bottom")).toBe("103px")
|
||||
|
||||
describe "when the theme does not contain a package.json file and is a directory", ->
|
||||
it "loads all stylesheet files in the directory", ->
|
||||
expect($(".editor").css("padding-top")).not.toBe "10px"
|
||||
expect($(".editor").css("padding-right")).not.toBe "20px"
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "30px"
|
||||
|
||||
themePath = project.resolve('themes/theme-without-package-file')
|
||||
theme = Theme.load(themePath)
|
||||
expect($(".editor").css("padding-top")).toBe "10px"
|
||||
expect($(".editor").css("padding-right")).toBe "20px"
|
||||
expect($(".editor").css("padding-bottom")).toBe "30px"
|
||||
@@ -1,163 +0,0 @@
|
||||
UndoManager = require 'undo-manager'
|
||||
Buffer = require 'text-buffer'
|
||||
Range = require 'range'
|
||||
|
||||
describe "UndoManager", ->
|
||||
[buffer, undoManager] = []
|
||||
|
||||
beforeEach ->
|
||||
buffer = new Buffer(require.resolve('fixtures/sample.js'))
|
||||
undoManager = buffer.undoManager
|
||||
|
||||
afterEach ->
|
||||
buffer.destroy()
|
||||
|
||||
describe ".undo()", ->
|
||||
it "undoes the last change", ->
|
||||
buffer.change(new Range([0, 5], [0, 9]), '')
|
||||
buffer.insert([0, 6], 'h')
|
||||
buffer.insert([0, 10], 'y')
|
||||
expect(buffer.lineForRow(0)).toContain 'qshorty'
|
||||
|
||||
undoManager.undo()
|
||||
expect(buffer.lineForRow(0)).toContain 'qshort'
|
||||
expect(buffer.lineForRow(0)).not.toContain 'qshorty'
|
||||
|
||||
undoManager.undo()
|
||||
expect(buffer.lineForRow(0)).toContain 'qsort'
|
||||
|
||||
undoManager.undo()
|
||||
expect(buffer.lineForRow(0)).toContain 'quicksort'
|
||||
|
||||
it "does not throw an exception when there is nothing to undo", ->
|
||||
undoManager.undo()
|
||||
|
||||
describe ".redo()", ->
|
||||
beforeEach ->
|
||||
buffer.change(new Range([0, 5], [0, 9]), '')
|
||||
buffer.insert([0, 6], 'h')
|
||||
buffer.insert([0, 10], 'y')
|
||||
undoManager.undo()
|
||||
undoManager.undo()
|
||||
expect(buffer.lineForRow(0)).toContain 'qsort'
|
||||
|
||||
it "redoes the last undone change", ->
|
||||
undoManager.redo()
|
||||
expect(buffer.lineForRow(0)).toContain 'qshort'
|
||||
|
||||
undoManager.redo()
|
||||
expect(buffer.lineForRow(0)).toContain 'qshorty'
|
||||
|
||||
undoManager.undo()
|
||||
expect(buffer.lineForRow(0)).toContain 'qshort'
|
||||
|
||||
it "does not throw an exception when there is nothing to redo", ->
|
||||
undoManager.redo()
|
||||
undoManager.redo()
|
||||
undoManager.redo()
|
||||
|
||||
it "discards the redo history when there is a new change following an undo", ->
|
||||
buffer.insert([0, 6], 'p')
|
||||
expect(buffer.getText()).toContain 'qsport'
|
||||
|
||||
undoManager.redo()
|
||||
expect(buffer.getText()).toContain 'qsport'
|
||||
|
||||
describe "transaction methods", ->
|
||||
describe "transact()", ->
|
||||
beforeEach ->
|
||||
buffer.setText('')
|
||||
|
||||
it "starts a transaction that can be committed later", ->
|
||||
buffer.append('1')
|
||||
undoManager.transact()
|
||||
buffer.append('2')
|
||||
buffer.append('3')
|
||||
undoManager.commit()
|
||||
buffer.append('4')
|
||||
|
||||
expect(buffer.getText()).toBe '1234'
|
||||
undoManager.undo()
|
||||
expect(buffer.getText()).toBe '123'
|
||||
undoManager.undo()
|
||||
expect(buffer.getText()).toBe '1'
|
||||
undoManager.redo()
|
||||
expect(buffer.getText()).toBe '123'
|
||||
|
||||
it "starts a transaction that can be aborted later", ->
|
||||
buffer.append('1')
|
||||
buffer.append('2')
|
||||
|
||||
undoManager.transact()
|
||||
|
||||
buffer.append('3')
|
||||
buffer.append('4')
|
||||
expect(buffer.getText()).toBe '1234'
|
||||
|
||||
undoManager.abort()
|
||||
expect(buffer.getText()).toBe '12'
|
||||
|
||||
undoManager.undo()
|
||||
expect(buffer.getText()).toBe '1'
|
||||
|
||||
undoManager.redo()
|
||||
expect(buffer.getText()).toBe '12'
|
||||
|
||||
undoManager.redo()
|
||||
expect(buffer.getText()).toBe '12'
|
||||
|
||||
describe "commit", ->
|
||||
it "throws an exception if there is no current transaction", ->
|
||||
expect(-> buffer.commit()).toThrow()
|
||||
|
||||
it "does not record empty transactions", ->
|
||||
buffer.insert([0,0], "foo")
|
||||
undoManager.transact()
|
||||
undoManager.commit()
|
||||
undoManager.undo()
|
||||
expect(buffer.lineForRow(0)).not.toContain("foo")
|
||||
|
||||
describe "abort", ->
|
||||
it "does not affect the undo stack when the current transaction is empty", ->
|
||||
buffer.setText('')
|
||||
buffer.append('1')
|
||||
buffer.transact()
|
||||
buffer.abort()
|
||||
expect(buffer.getText()).toBe '1'
|
||||
buffer.undo()
|
||||
expect(buffer.getText()).toBe ''
|
||||
|
||||
it "throws an exception if there is no current transaction", ->
|
||||
expect(-> buffer.abort()).toThrow()
|
||||
|
||||
describe "exception handling", ->
|
||||
describe "when a `do` operation throws an exception", ->
|
||||
it "clears the stack", ->
|
||||
spyOn(console, 'error')
|
||||
buffer.setText("word")
|
||||
buffer.insert([0,0], "1")
|
||||
expect(->
|
||||
undoManager.pushOperation(do: -> throw new Error("I'm a bad do operation"))
|
||||
).toThrow("I'm a bad do operation")
|
||||
|
||||
undoManager.undo()
|
||||
expect(buffer.lineForRow(0)).toBe "1word"
|
||||
|
||||
describe "when an `undo` operation throws an exception", ->
|
||||
it "clears the stack", ->
|
||||
spyOn(console, 'error')
|
||||
buffer.setText("word")
|
||||
buffer.insert([0,0], "1")
|
||||
undoManager.pushOperation(undo: -> throw new Error("I'm a bad undo operation"))
|
||||
expect(-> undoManager.undo()).toThrow("I'm a bad undo operation")
|
||||
expect(buffer.lineForRow(0)).toBe "1word"
|
||||
|
||||
describe "when an `redo` operation throws an exception", ->
|
||||
it "clears the stack", ->
|
||||
spyOn(console, 'error')
|
||||
buffer.setText("word")
|
||||
buffer.insert([0,0], "1")
|
||||
undoManager.pushOperation(redo: -> throw new Error("I'm a bad redo operation"))
|
||||
undoManager.undo()
|
||||
expect(-> undoManager.redo()).toThrow("I'm a bad redo operation")
|
||||
expect(buffer.lineForRow(0)).toBe "1word"
|
||||
@@ -21,7 +21,15 @@ class AtomReporter extends View
|
||||
@div id: 'HTMLReporter', class: 'jasmine_reporter', =>
|
||||
@div outlet: 'specPopup', class: "spec-popup"
|
||||
@div outlet: "suites"
|
||||
@ul outlet: "symbolSummary", class: 'symbolSummary list-unstyled'
|
||||
@div outlet: 'coreArea', =>
|
||||
@div outlet: 'coreHeader', class: 'symbolHeader'
|
||||
@ul outlet: 'coreSummary', class: 'symbolSummary list-unstyled'
|
||||
@div outlet: 'bundledArea', =>
|
||||
@div outlet: 'bundledHeader', class: 'symbolHeader'
|
||||
@ul outlet: 'bundledSummary', class: 'symbolSummary list-unstyled'
|
||||
@div outlet: 'userArea', =>
|
||||
@div outlet: 'userHeader', class: 'symbolHeader'
|
||||
@ul outlet: 'userSummary', class: 'symbolSummary list-unstyled'
|
||||
@div outlet: "status", class: 'status', =>
|
||||
@div outlet: "time", class: 'time'
|
||||
@div outlet: "specCount", class: 'spec-count'
|
||||
@@ -118,15 +126,40 @@ class AtomReporter extends View
|
||||
@time.text "#{time[0...-2]}.#{time[-2..]}s"
|
||||
|
||||
addSpecs: (specs) ->
|
||||
coreSpecs = 0
|
||||
bundledPackageSpecs = 0
|
||||
userPackageSpecs = 0
|
||||
for spec in specs
|
||||
symbol = $$ -> @li class: "spec-summary pending spec-summary-#{spec.id}"
|
||||
@symbolSummary.append symbol
|
||||
symbol = $$ -> @li id: "spec-summary-#{spec.id}", class: "spec-summary pending"
|
||||
switch spec.specType
|
||||
when 'core'
|
||||
coreSpecs++
|
||||
@coreSummary.append symbol
|
||||
when 'bundled'
|
||||
bundledPackageSpecs++
|
||||
@bundledSummary.append symbol
|
||||
when 'user'
|
||||
userPackageSpecs++
|
||||
@userSummary.append symbol
|
||||
|
||||
if coreSpecs > 0
|
||||
@coreHeader.text("Core Specs (#{coreSpecs}):")
|
||||
else
|
||||
@coreArea.hide()
|
||||
if bundledPackageSpecs > 0
|
||||
@bundledHeader.text("Bundled Package Specs (#{bundledPackageSpecs}):")
|
||||
else
|
||||
@bundledArea.hide()
|
||||
if userPackageSpecs > 0
|
||||
@userHeader.text("User Package Specs (#{userPackageSpecs}):")
|
||||
else
|
||||
@userArea.hide()
|
||||
|
||||
specStarted: (spec) ->
|
||||
@runningSpecCount++
|
||||
|
||||
specComplete: (spec) ->
|
||||
specSummaryElement = $(".spec-summary-#{spec.id}")
|
||||
specSummaryElement = $("#spec-summary-#{spec.id}")
|
||||
specSummaryElement.removeClass('pending')
|
||||
specSummaryElement.data("description", spec.getFullName())
|
||||
|
||||
@@ -152,7 +185,7 @@ class SuiteResultView extends View
|
||||
suite: null
|
||||
|
||||
initialize: (@suite) ->
|
||||
@addClass("suite-view-#{@suite.id}")
|
||||
@attr('id', "suite-view-#{@suite.id}")
|
||||
@description.html @suite.description
|
||||
|
||||
attach: ->
|
||||
@@ -161,7 +194,7 @@ class SuiteResultView extends View
|
||||
parentSuiteView: ->
|
||||
return unless @suite.parentSuite
|
||||
|
||||
if not suiteView = $(".suite-view-#{@suite.parentSuite.id}").view()
|
||||
if not suiteView = $("#suite-view-#{@suite.parentSuite.id}").view()
|
||||
suiteView = new SuiteResultView(@suite.parentSuite)
|
||||
suiteView.attach()
|
||||
|
||||
@@ -189,7 +222,7 @@ class SpecResultView extends View
|
||||
@parentSuiteView().append this
|
||||
|
||||
parentSuiteView: ->
|
||||
if not suiteView = $(".suite-view-#{@spec.suite.id}").view()
|
||||
if not suiteView = $("#suite-view-#{@spec.suite.id}").view()
|
||||
suiteView = new SuiteResultView(@spec.suite)
|
||||
suiteView.attach()
|
||||
|
||||
|
||||
@@ -217,21 +217,9 @@ describe "the `atom` global", ->
|
||||
runs ->
|
||||
expect(syntax.getProperty(['.source.pref'], 'editor.increaseIndentPattern')).toBe '^abc$'
|
||||
|
||||
describe ".activatePackageConfig(id)", ->
|
||||
it "calls the optional .activateConfigMenu method on the package's main module", ->
|
||||
pack = atom.activatePackageConfig('package-with-activate-config')
|
||||
expect(pack.mainModule.activateCalled).toBeFalsy()
|
||||
expect(pack.mainModule.activateConfigCalled).toBeTruthy()
|
||||
|
||||
it "loads the package's config defaults", ->
|
||||
expect(config.get('package-with-config-defaults.numbers.one')).toBeUndefined()
|
||||
atom.activatePackageConfig('package-with-config-defaults')
|
||||
expect(config.get('package-with-config-defaults.numbers.one')).toBe 1
|
||||
expect(config.get('package-with-config-defaults.numbers.two')).toBe 2
|
||||
|
||||
describe ".deactivatePackage(id)", ->
|
||||
describe "atom packages", ->
|
||||
it "calls `deactivate` on the package's main module", ->
|
||||
it "calls `deactivate` on the package's main module if activate was successful", ->
|
||||
pack = atom.activatePackage("package-with-deactivate")
|
||||
expect(atom.isPackageActive("package-with-deactivate")).toBeTruthy()
|
||||
spyOn(pack.mainModule, 'deactivate').andCallThrough()
|
||||
@@ -240,6 +228,23 @@ describe "the `atom` global", ->
|
||||
expect(pack.mainModule.deactivate).toHaveBeenCalled()
|
||||
expect(atom.isPackageActive("package-with-module")).toBeFalsy()
|
||||
|
||||
spyOn(console, 'warn')
|
||||
badPack = atom.activatePackage("package-that-throws-on-activate")
|
||||
expect(atom.isPackageActive("package-that-throws-on-activate")).toBeTruthy()
|
||||
spyOn(badPack.mainModule, 'deactivate').andCallThrough()
|
||||
|
||||
atom.deactivatePackage("package-that-throws-on-activate")
|
||||
expect(badPack.mainModule.deactivate).not.toHaveBeenCalled()
|
||||
expect(atom.isPackageActive("package-that-throws-on-activate")).toBeFalsy()
|
||||
|
||||
it "does not serialize packages that have not been activated called on their main module", ->
|
||||
spyOn(console, 'warn')
|
||||
badPack = atom.activatePackage("package-that-throws-on-activate")
|
||||
spyOn(badPack.mainModule, 'serialize').andCallThrough()
|
||||
|
||||
atom.deactivatePackage("package-that-throws-on-activate")
|
||||
expect(badPack.mainModule.serialize).not.toHaveBeenCalled()
|
||||
|
||||
it "absorbs exceptions that are thrown by the package module's serialize methods", ->
|
||||
spyOn(console, 'error')
|
||||
atom.activatePackage('package-with-serialize-error', immediate: true)
|
||||
@@ -277,7 +282,7 @@ describe "the `atom` global", ->
|
||||
atom.deactivatePackage("package-with-scoped-properties")
|
||||
expect(syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBeUndefined()
|
||||
|
||||
describe "texmate packages", ->
|
||||
describe "textmate packages", ->
|
||||
it "removes the package's grammars", ->
|
||||
expect(syntax.selectGrammar("file.rb").name).toBe "Null Grammar"
|
||||
atom.activatePackage('ruby-tmbundle', sync: true)
|
||||
@@ -289,7 +294,3 @@ describe "the `atom` global", ->
|
||||
atom.activatePackage('ruby-tmbundle', sync: true)
|
||||
atom.deactivatePackage('ruby-tmbundle')
|
||||
expect(syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined()
|
||||
|
||||
describe ".getVersion", ->
|
||||
it "returns the current version number", ->
|
||||
expect(typeof atom.getVersion()).toBe 'string'
|
||||
@@ -8,13 +8,49 @@ describe "DisplayBuffer", ->
|
||||
tabLength = 2
|
||||
atom.activatePackage('javascript-tmbundle', sync: true)
|
||||
buffer = project.bufferForPath('sample.js')
|
||||
displayBuffer = new DisplayBuffer(buffer, { tabLength })
|
||||
displayBuffer.on 'changed', changeHandler = jasmine.createSpy 'changeHandler'
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength})
|
||||
changeHandler = jasmine.createSpy 'changeHandler'
|
||||
displayBuffer.on 'changed', changeHandler
|
||||
|
||||
afterEach ->
|
||||
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 = 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)
|
||||
marker2 = displayBuffer.markBufferRange([[2, 3], [4, 5]], isReversed: true, id: 2)
|
||||
marker3 = displayBuffer.markBufferPosition([5, 6], id: 3)
|
||||
displayBuffer.createFold(3, 5)
|
||||
|
||||
displayBuffer2 = displayBuffer.copy()
|
||||
expect(displayBuffer2.id).not.toBe displayBuffer.id
|
||||
expect(displayBuffer2.buffer).toBe displayBuffer.buffer
|
||||
expect(displayBuffer2.getTabLength()).toBe displayBuffer.getTabLength()
|
||||
|
||||
expect(displayBuffer2.getMarkerCount()).toEqual displayBuffer.getMarkerCount()
|
||||
expect(displayBuffer2.findMarker(id: 1)).toEqual marker1
|
||||
expect(displayBuffer2.findMarker(id: 2)).toEqual marker2
|
||||
expect(displayBuffer2.findMarker(id: 3)).toEqual marker3
|
||||
expect(displayBuffer2.isFoldedAtBufferRow(3)).toBeTruthy()
|
||||
|
||||
# can diverge from origin
|
||||
displayBuffer2.destroyFoldsContainingBufferRow(3)
|
||||
expect(displayBuffer2.isFoldedAtBufferRow(3)).not.toBe displayBuffer.isFoldedAtBufferRow(3)
|
||||
|
||||
describe "when the buffer changes", ->
|
||||
it "renders line numbers correctly", ->
|
||||
originalLineCount = displayBuffer.getLineCount()
|
||||
@@ -24,10 +60,23 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe "soft wrapping", ->
|
||||
beforeEach ->
|
||||
displayBuffer.setSoftWrapColumn(50)
|
||||
displayBuffer.setSoftWrap(true)
|
||||
displayBuffer.setEditorWidthInChars(50)
|
||||
changeHandler.reset()
|
||||
|
||||
describe "rendering of soft-wrapped lines", ->
|
||||
describe "when editor.softWrapAtPreferredLineLength is set", ->
|
||||
it "uses the preferred line length as the soft wrap column when it is less than the configured soft wrap column", ->
|
||||
config.set('editor.preferredLineLength', 100)
|
||||
config.set('editor.softWrapAtPreferredLineLength', true)
|
||||
expect(displayBuffer.lineForRow(10).text).toBe ' return '
|
||||
|
||||
config.set('editor.preferredLineLength', 5)
|
||||
expect(displayBuffer.lineForRow(10).text).toBe 'funct'
|
||||
|
||||
config.set('editor.softWrapAtPreferredLineLength', false)
|
||||
expect(displayBuffer.lineForRow(10).text).toBe ' return '
|
||||
|
||||
describe "when the line is shorter than the max line length", ->
|
||||
it "renders the line unchanged", ->
|
||||
expect(displayBuffer.lineForRow(0).text).toBe buffer.lineForRow(0)
|
||||
@@ -45,7 +94,7 @@ describe "DisplayBuffer", ->
|
||||
describe "when there is no whitespace before the boundary", ->
|
||||
it "wraps the line exactly at the boundary since there's no more graceful place to wrap it", ->
|
||||
buffer.change([[0, 0], [1, 0]], 'abcdefghijklmnopqrstuvwxyz\n')
|
||||
displayBuffer.setSoftWrapColumn(10)
|
||||
displayBuffer.setEditorWidthInChars(10)
|
||||
expect(displayBuffer.lineForRow(0).text).toBe 'abcdefghij'
|
||||
expect(displayBuffer.lineForRow(1).text).toBe 'klmnopqrst'
|
||||
expect(displayBuffer.lineForRow(2).text).toBe 'uvwxyz'
|
||||
@@ -104,6 +153,20 @@ describe "DisplayBuffer", ->
|
||||
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 3, end: 9, screenDelta: -6, bufferDelta: -4)
|
||||
|
||||
describe "when a newline is inserted, deleted, and re-inserted at the end of a wrapped line (regression)", ->
|
||||
it "correctly renders the original wrapped line", ->
|
||||
buffer = project.buildBuffer(null, '')
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength, editorWidthInChars: 30, softWrap: true})
|
||||
|
||||
buffer.insert([0, 0], "the quick brown fox jumps over the lazy dog.")
|
||||
buffer.insert([0, Infinity], '\n')
|
||||
buffer.delete([[0, Infinity], [1, 0]])
|
||||
buffer.insert([0, Infinity], '\n')
|
||||
|
||||
expect(displayBuffer.lineForRow(0).text).toBe "the quick brown fox jumps over "
|
||||
expect(displayBuffer.lineForRow(1).text).toBe "the lazy dog."
|
||||
expect(displayBuffer.lineForRow(2).text).toBe ""
|
||||
|
||||
describe "position translation", ->
|
||||
it "translates positions accounting for wrapped lines", ->
|
||||
# before any wrapped lines
|
||||
@@ -132,9 +195,9 @@ describe "DisplayBuffer", ->
|
||||
expect(displayBuffer.bufferPositionForScreenPosition([3, -5])).toEqual([3, 0])
|
||||
expect(displayBuffer.bufferPositionForScreenPosition([3, Infinity])).toEqual([3, 50])
|
||||
|
||||
describe ".setSoftWrapColumn(length)", ->
|
||||
describe ".setEditorWidthInChars(length)", ->
|
||||
it "changes the length at which lines are wrapped and emits a change event for all screen lines", ->
|
||||
displayBuffer.setSoftWrapColumn(40)
|
||||
displayBuffer.setEditorWidthInChars(40)
|
||||
expect(tokensText displayBuffer.lineForRow(4).tokens).toBe 'left = [], right = [];'
|
||||
expect(tokensText displayBuffer.lineForRow(5).tokens).toBe ' while(items.length > 0) {'
|
||||
expect(tokensText displayBuffer.lineForRow(12).tokens).toBe 'sort(left).concat(pivot).concat(sort(rig'
|
||||
@@ -145,13 +208,14 @@ describe "DisplayBuffer", ->
|
||||
displayBuffer.destroy()
|
||||
buffer.release()
|
||||
buffer = project.bufferForPath('two-hundred.txt')
|
||||
displayBuffer = new DisplayBuffer(buffer, { tabLength })
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength})
|
||||
displayBuffer.on 'changed', changeHandler
|
||||
|
||||
describe "when folds are created and destroyed", ->
|
||||
describe "when a fold spans multiple lines", ->
|
||||
it "replaces the lines spanned by the fold with a placeholder that references the fold object", ->
|
||||
fold = displayBuffer.createFold(4, 7)
|
||||
expect(fold).toBeDefined()
|
||||
|
||||
[line4, line5] = displayBuffer.linesForRows(4, 5)
|
||||
expect(line4.fold).toBe fold
|
||||
@@ -250,7 +314,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe "when there is another display buffer pointing to the same buffer", ->
|
||||
it "does not create folds in the other display buffer", ->
|
||||
otherDisplayBuffer = new DisplayBuffer(buffer, { tabLength })
|
||||
otherDisplayBuffer = new DisplayBuffer({buffer, tabLength})
|
||||
displayBuffer.createFold(2, 4)
|
||||
expect(otherDisplayBuffer.foldsStartingAtBufferRow(2).length).toBe 0
|
||||
|
||||
@@ -274,7 +338,7 @@ describe "DisplayBuffer", ->
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 1, end: 3, screenDelta: -2, bufferDelta: -4)
|
||||
|
||||
describe "when the changes is subsequently undone", ->
|
||||
it "restores destroyed folds", ->
|
||||
xit "restores destroyed folds", ->
|
||||
buffer.undo()
|
||||
expect(displayBuffer.lineForRow(2).text).toBe '2'
|
||||
expect(displayBuffer.lineForRow(2).fold).toBe fold1
|
||||
@@ -454,7 +518,8 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe ".clipScreenPosition(screenPosition, wrapBeyondNewlines: false, wrapAtSoftNewlines: false, skipAtomicTokens: false)", ->
|
||||
beforeEach ->
|
||||
displayBuffer.setSoftWrapColumn(50)
|
||||
displayBuffer.setSoftWrap(true)
|
||||
displayBuffer.setEditorWidthInChars(50)
|
||||
|
||||
it "allows valid positions", ->
|
||||
expect(displayBuffer.clipScreenPosition([4, 5])).toEqual [4, 5]
|
||||
@@ -601,8 +666,8 @@ describe "DisplayBuffer", ->
|
||||
oldTailBufferPosition: [8, 4]
|
||||
newTailScreenPosition: [5, 4]
|
||||
newTailBufferPosition: [8, 4]
|
||||
bufferChanged: false
|
||||
valid: true
|
||||
textChanged: false
|
||||
isValid: true
|
||||
}
|
||||
markerChangedHandler.reset()
|
||||
|
||||
@@ -617,8 +682,8 @@ describe "DisplayBuffer", ->
|
||||
oldTailBufferPosition: [8, 4]
|
||||
newTailScreenPosition: [5, 4]
|
||||
newTailBufferPosition: [8, 4]
|
||||
bufferChanged: true
|
||||
valid: true
|
||||
textChanged: true
|
||||
isValid: true
|
||||
}
|
||||
markerChangedHandler.reset()
|
||||
|
||||
@@ -633,8 +698,8 @@ describe "DisplayBuffer", ->
|
||||
oldTailBufferPosition: [8, 4]
|
||||
newTailScreenPosition: [8, 4]
|
||||
newTailBufferPosition: [8, 4]
|
||||
bufferChanged: false
|
||||
valid: true
|
||||
textChanged: false
|
||||
isValid: true
|
||||
}
|
||||
markerChangedHandler.reset()
|
||||
|
||||
@@ -649,8 +714,8 @@ describe "DisplayBuffer", ->
|
||||
oldTailBufferPosition: [8, 4]
|
||||
newTailScreenPosition: [5, 4]
|
||||
newTailBufferPosition: [8, 4]
|
||||
bufferChanged: false
|
||||
valid: true
|
||||
textChanged: false
|
||||
isValid: true
|
||||
}
|
||||
|
||||
it "triggers the 'changed' event whenever the marker tail's position changes in the buffer or on screen", ->
|
||||
@@ -665,8 +730,8 @@ describe "DisplayBuffer", ->
|
||||
oldTailBufferPosition: [8, 4]
|
||||
newTailScreenPosition: [8, 20]
|
||||
newTailBufferPosition: [11, 20]
|
||||
bufferChanged: false
|
||||
valid: true
|
||||
textChanged: false
|
||||
isValid: true
|
||||
}
|
||||
markerChangedHandler.reset()
|
||||
|
||||
@@ -681,24 +746,24 @@ describe "DisplayBuffer", ->
|
||||
oldTailBufferPosition: [11, 20]
|
||||
newTailScreenPosition: [8, 23]
|
||||
newTailBufferPosition: [11, 23]
|
||||
bufferChanged: true
|
||||
valid: true
|
||||
textChanged: true
|
||||
isValid: true
|
||||
}
|
||||
|
||||
it "triggers the 'changed' event whenever the marker is invalidated or revalidated", ->
|
||||
xit "triggers the 'changed' event whenever the marker is invalidated or revalidated", ->
|
||||
buffer.deleteRow(8)
|
||||
expect(markerChangedHandler).toHaveBeenCalled()
|
||||
expect(markerChangedHandler.argsForCall[0][0]).toEqual {
|
||||
oldHeadScreenPosition: [5, 10]
|
||||
oldHeadBufferPosition: [8, 10]
|
||||
newHeadScreenPosition: [5, 10]
|
||||
newHeadBufferPosition: [8, 10]
|
||||
newHeadBufferPosition: [8, 0]
|
||||
oldTailScreenPosition: [5, 4]
|
||||
oldTailBufferPosition: [8, 4]
|
||||
newTailScreenPosition: [5, 4]
|
||||
newTailBufferPosition: [8, 4]
|
||||
bufferChanged: true
|
||||
valid: false
|
||||
newTailBufferPosition: [8, 0]
|
||||
textChanged: true
|
||||
isValid: false
|
||||
}
|
||||
|
||||
markerChangedHandler.reset()
|
||||
@@ -714,15 +779,15 @@ describe "DisplayBuffer", ->
|
||||
oldTailBufferPosition: [8, 4]
|
||||
newTailScreenPosition: [5, 4]
|
||||
newTailBufferPosition: [8, 4]
|
||||
bufferChanged: true
|
||||
valid: true
|
||||
textChanged: true
|
||||
isValid: true
|
||||
}
|
||||
|
||||
it "does not call the callback for screen changes that don't change the position of the marker", ->
|
||||
displayBuffer.createFold(10, 11)
|
||||
expect(markerChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "updates markers before emitting buffer change events, but does not notify their observers until the change event", ->
|
||||
xit "updates markers before emitting buffer change events, but does not notify their observers until the change event", ->
|
||||
marker2 = displayBuffer.markBufferRange([[8, 1], [8, 1]])
|
||||
marker2.on 'changed', marker2ChangedHandler = jasmine.createSpy("marker2ChangedHandler")
|
||||
displayBuffer.on 'changed', changeHandler = jasmine.createSpy("changeHandler").andCallFake -> onDisplayBufferChange()
|
||||
@@ -839,3 +904,15 @@ describe "DisplayBuffer", ->
|
||||
marker2.on 'destroyed', destroyedHandler
|
||||
buffer.getMarker(marker2.id).destroy()
|
||||
expect(destroyedHandler).toHaveBeenCalled()
|
||||
|
||||
describe "DisplayBufferMarker.copy(attributes)", ->
|
||||
it "creates a copy of the marker with the given attributes merged in", ->
|
||||
initialMarkerCount = displayBuffer.getMarkerCount()
|
||||
marker1 = displayBuffer.markScreenRange([[5, 4], [5, 10]], a: 1, b: 2)
|
||||
expect(displayBuffer.getMarkerCount()).toBe initialMarkerCount + 1
|
||||
|
||||
marker2 = marker1.copy(b: 3)
|
||||
expect(marker2.getBufferRange()).toEqual marker1.getBufferRange()
|
||||
expect(displayBuffer.getMarkerCount()).toBe initialMarkerCount + 2
|
||||
expect(marker1.getAttributes()).toEqual a: 1, b: 2
|
||||
expect(marker2.getAttributes()).toEqual a: 1, b: 3
|
||||
@@ -0,0 +1,62 @@
|
||||
{Site} = require 'telepath'
|
||||
Environment = require 'environment'
|
||||
|
||||
describe "EditSession replication", ->
|
||||
[env1, env2, editSession1, editSession2] = []
|
||||
beforeEach ->
|
||||
env1 = new Environment(siteId: 1)
|
||||
env2 = env1.clone(siteId: 2)
|
||||
envConnection = env1.connect(env2)
|
||||
doc2 = null
|
||||
|
||||
env1.run ->
|
||||
editSession1 = project.open('sample.js')
|
||||
editSession1.setScrollTop(5)
|
||||
editSession1.setScrollLeft(5)
|
||||
editSession1.setCursorScreenPosition([0, 5])
|
||||
editSession1.addSelectionForBufferRange([[1, 2], [3, 4]])
|
||||
doc1 = editSession1.getState()
|
||||
doc2 = doc1.clone(env2.site)
|
||||
envConnection.connect(doc1, doc2)
|
||||
|
||||
env2.run ->
|
||||
editSession2 = deserialize(doc2)
|
||||
|
||||
afterEach ->
|
||||
env1.destroy()
|
||||
env2.destroy()
|
||||
|
||||
it "replicates the selections of existing replicas", ->
|
||||
expect(editSession2.getRemoteSelectedBufferRanges()).toEqual editSession1.getSelectedBufferRanges()
|
||||
|
||||
editSession1.getLastSelection().setBufferRange([[2, 3], [4, 5]])
|
||||
expect(editSession2.getRemoteSelectedBufferRanges()).toEqual editSession1.getSelectedBufferRanges()
|
||||
|
||||
editSession1.addCursorAtBufferPosition([5, 6])
|
||||
expect(editSession2.getRemoteSelectedBufferRanges()).toEqual editSession1.getSelectedBufferRanges()
|
||||
|
||||
editSession1.consolidateSelections()
|
||||
expect(editSession2.getRemoteSelectedBufferRanges()).toEqual editSession1.getSelectedBufferRanges()
|
||||
|
||||
it "introduces a local cursor for a new replica at the position of the last remote cursor", ->
|
||||
expect(editSession2.getCursors().length).toBe 1
|
||||
expect(editSession2.getSelections().length).toBe 1
|
||||
expect(editSession2.getCursorBufferPosition()).toEqual [3, 4]
|
||||
expect(editSession2.getSelectedBufferRanges()).toEqual [[[3, 4], [3, 4]]]
|
||||
|
||||
expect(editSession1.getRemoteCursors().length).toBe 1
|
||||
expect(editSession1.getRemoteSelections().length).toBe 1
|
||||
[cursor] = editSession1.getRemoteCursors()
|
||||
[selection] = editSession1.getRemoteSelections()
|
||||
expect(cursor.getBufferPosition()).toEqual [3, 4]
|
||||
expect(selection.getBufferRange()).toEqual [[3, 4], [3, 4]]
|
||||
|
||||
it "replicates the scroll position", ->
|
||||
expect(editSession2.getScrollTop()).toBe editSession1.getScrollTop()
|
||||
expect(editSession2.getScrollLeft()).toBe editSession1.getScrollLeft()
|
||||
|
||||
editSession1.setScrollTop(10)
|
||||
expect(editSession2.getScrollTop()).toBe 10
|
||||
|
||||
editSession2.setScrollLeft(20)
|
||||
expect(editSession1.getScrollLeft()).toBe 20
|
||||
@@ -15,6 +15,58 @@ describe "EditSession", ->
|
||||
buffer = editSession.buffer
|
||||
lineLengths = buffer.getLines().map (line) -> line.length
|
||||
|
||||
describe "@deserialize(state)", ->
|
||||
it "restores selections and folds based on markers in the buffer", ->
|
||||
editSession.setSelectedBufferRange([[1, 2], [3, 4]])
|
||||
editSession.addSelectionForBufferRange([[5, 6], [7, 5]], isReversed: true)
|
||||
editSession.foldBufferRow(4)
|
||||
expect(editSession.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
|
||||
editSession2 = deserialize(editSession.serialize())
|
||||
|
||||
expect(editSession2.id).toBe editSession.id
|
||||
expect(editSession2.getBuffer().getPath()).toBe editSession.getBuffer().getPath()
|
||||
expect(editSession2.getSelectedBufferRanges()).toEqual [[[1, 2], [3, 4]], [[5, 6], [7, 5]]]
|
||||
expect(editSession2.getSelection(1).isReversed()).toBeTruthy()
|
||||
expect(editSession2.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
|
||||
describe ".copy()", ->
|
||||
it "returns a different edit session with the same initial state", ->
|
||||
editSession.setSelectedBufferRange([[1, 2], [3, 4]])
|
||||
editSession.addSelectionForBufferRange([[5, 6], [7, 8]], isReversed: true)
|
||||
editSession.foldBufferRow(4)
|
||||
expect(editSession.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
|
||||
editSession2 = editSession.copy()
|
||||
expect(editSession2.id).not.toBe editSession.id
|
||||
expect(editSession2.getSelectedBufferRanges()).toEqual editSession.getSelectedBufferRanges()
|
||||
expect(editSession2.getSelection(1).isReversed()).toBeTruthy()
|
||||
expect(editSession2.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
|
||||
# editSession2 can now diverge from its origin edit session
|
||||
editSession2.getSelection().setBufferRange([[2, 1], [4, 3]])
|
||||
expect(editSession2.getSelectedBufferRanges()).not.toEqual editSession.getSelectedBufferRanges()
|
||||
editSession2.unfoldBufferRow(4)
|
||||
expect(editSession2.isFoldedAtBufferRow(4)).not.toBe editSession.isFoldedAtBufferRow(4)
|
||||
|
||||
describe "config defaults", ->
|
||||
it "uses the `editor.tabLength`, `editor.softWrap`, and `editor.softTabs` config values", ->
|
||||
config.set('editor.tabLength', 4)
|
||||
config.set('editor.softWrap', true)
|
||||
config.set('editor.softTabs', false)
|
||||
editSession1 = project.open('a')
|
||||
expect(editSession1.getTabLength()).toBe 4
|
||||
expect(editSession1.getSoftWrap()).toBe true
|
||||
expect(editSession1.getSoftTabs()).toBe false
|
||||
|
||||
config.set('editor.tabLength', 100)
|
||||
config.set('editor.softWrap', false)
|
||||
config.set('editor.softTabs', true)
|
||||
editSession2 = project.open('b')
|
||||
expect(editSession2.getTabLength()).toBe 100
|
||||
expect(editSession2.getSoftWrap()).toBe false
|
||||
expect(editSession2.getSoftTabs()).toBe true
|
||||
|
||||
describe "title", ->
|
||||
describe ".getTitle()", ->
|
||||
it "uses the basename of the buffer's path as its title, or 'untitled' if the path is undefined", ->
|
||||
@@ -68,7 +120,8 @@ describe "EditSession", ->
|
||||
|
||||
describe "when soft-wrap is enabled and code is folded", ->
|
||||
beforeEach ->
|
||||
editSession.setSoftWrapColumn(50)
|
||||
editSession.setSoftWrap(true)
|
||||
editSession.setEditorWidthInChars(50)
|
||||
editSession.createFold(2, 3)
|
||||
|
||||
it "positions the cursor at the buffer position that corresponds to the given screen position", ->
|
||||
@@ -103,6 +156,15 @@ describe "EditSession", ->
|
||||
editSession.moveCursorDown()
|
||||
expect(editSession.getCursorScreenPosition()).toEqual([1, 4])
|
||||
|
||||
describe "when there is a selection", ->
|
||||
beforeEach ->
|
||||
editSession.setSelectedBufferRange([[4, 9],[5, 10]])
|
||||
|
||||
it "moves above the selection", ->
|
||||
cursor = editSession.getCursor()
|
||||
editSession.moveCursorUp()
|
||||
expect(cursor.getBufferPosition()).toEqual [3, 9]
|
||||
|
||||
it "merges cursors when they overlap", ->
|
||||
editSession.addCursorAtScreenPosition([1, 0])
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
@@ -152,6 +214,15 @@ describe "EditSession", ->
|
||||
editSession.moveCursorUp()
|
||||
expect(editSession.getCursorScreenPosition().column).toBe 0
|
||||
|
||||
describe "when there is a selection", ->
|
||||
beforeEach ->
|
||||
editSession.setSelectedBufferRange([[4, 9],[5, 10]])
|
||||
|
||||
it "moves below the selection", ->
|
||||
cursor = editSession.getCursor()
|
||||
editSession.moveCursorDown()
|
||||
expect(cursor.getBufferPosition()).toEqual [6, 10]
|
||||
|
||||
it "merges cursors when they overlap", ->
|
||||
editSession.setCursorScreenPosition([12, 2])
|
||||
editSession.addCursorAtScreenPosition([11, 2])
|
||||
@@ -187,6 +258,18 @@ describe "EditSession", ->
|
||||
editSession.moveCursorLeft()
|
||||
expect(editSession.getCursorBufferPosition()).toEqual [5, 4]
|
||||
|
||||
describe "when there is a selection", ->
|
||||
beforeEach ->
|
||||
editSession.setSelectedBufferRange([[5, 22],[5, 27]])
|
||||
|
||||
it "moves to the left of the selection", ->
|
||||
cursor = editSession.getCursor()
|
||||
editSession.moveCursorLeft()
|
||||
expect(cursor.getBufferPosition()).toEqual [5, 22]
|
||||
|
||||
editSession.moveCursorLeft()
|
||||
expect(cursor.getBufferPosition()).toEqual [5, 21]
|
||||
|
||||
it "merges cursors when they overlap", ->
|
||||
editSession.setCursorScreenPosition([0, 0])
|
||||
editSession.addCursorAtScreenPosition([0, 1])
|
||||
@@ -221,6 +304,18 @@ describe "EditSession", ->
|
||||
|
||||
expect(editSession.getCursorScreenPosition()).toEqual(lastPosition)
|
||||
|
||||
describe "when there is a selection", ->
|
||||
beforeEach ->
|
||||
editSession.setSelectedBufferRange([[5, 22],[5, 27]])
|
||||
|
||||
it "moves to the left of the selection", ->
|
||||
cursor = editSession.getCursor()
|
||||
editSession.moveCursorRight()
|
||||
expect(cursor.getBufferPosition()).toEqual [5, 27]
|
||||
|
||||
editSession.moveCursorRight()
|
||||
expect(cursor.getBufferPosition()).toEqual [5, 28]
|
||||
|
||||
it "merges cursors when they overlap", ->
|
||||
editSession.setCursorScreenPosition([12, 2])
|
||||
editSession.addCursorAtScreenPosition([12, 1])
|
||||
@@ -247,44 +342,75 @@ describe "EditSession", ->
|
||||
expect(editSession.getCursorBufferPosition()).toEqual [12,2]
|
||||
|
||||
describe ".moveCursorToBeginningOfLine()", ->
|
||||
it "moves cursor to the beginning of line", ->
|
||||
editSession.setCursorScreenPosition [0,5]
|
||||
editSession.addCursorAtScreenPosition [1,7]
|
||||
editSession.moveCursorToBeginningOfLine()
|
||||
expect(editSession.getCursors().length).toBe 2
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,0]
|
||||
describe "when soft wrap is on", ->
|
||||
it "moves cursor to the beginning of the screen line", ->
|
||||
editSession.setSoftWrap(true)
|
||||
editSession.setEditorWidthInChars(10)
|
||||
editSession.setCursorScreenPosition([1, 2])
|
||||
editSession.moveCursorToBeginningOfLine()
|
||||
cursor = editSession.getCursor()
|
||||
expect(cursor.getScreenPosition()).toEqual [1, 0]
|
||||
|
||||
describe "when soft wrap is off", ->
|
||||
it "moves cursor to the beginning of then line", ->
|
||||
editSession.setCursorScreenPosition [0,5]
|
||||
editSession.addCursorAtScreenPosition [1,7]
|
||||
editSession.moveCursorToBeginningOfLine()
|
||||
expect(editSession.getCursors().length).toBe 2
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,0]
|
||||
|
||||
describe ".moveCursorToEndOfLine()", ->
|
||||
it "moves cursor to the end of line", ->
|
||||
editSession.setCursorScreenPosition [0,0]
|
||||
editSession.addCursorAtScreenPosition [1,0]
|
||||
editSession.moveCursorToEndOfLine()
|
||||
expect(editSession.getCursors().length).toBe 2
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,29]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,30]
|
||||
describe "when soft wrap is on", ->
|
||||
it "moves cursor to the beginning of the screen line", ->
|
||||
editSession.setSoftWrap(true)
|
||||
editSession.setEditorWidthInChars(10)
|
||||
editSession.setCursorScreenPosition([1, 2])
|
||||
editSession.moveCursorToEndOfLine()
|
||||
cursor = editSession.getCursor()
|
||||
expect(cursor.getScreenPosition()).toEqual [1, 9]
|
||||
|
||||
describe "when soft wrap is off", ->
|
||||
it "moves cursor to the end of line", ->
|
||||
editSession.setCursorScreenPosition [0,0]
|
||||
editSession.addCursorAtScreenPosition [1,0]
|
||||
editSession.moveCursorToEndOfLine()
|
||||
expect(editSession.getCursors().length).toBe 2
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,29]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,30]
|
||||
|
||||
describe ".moveCursorToFirstCharacterOfLine()", ->
|
||||
it "moves to the first character of the current line or the beginning of the line if it's already on the first character", ->
|
||||
editSession.setCursorScreenPosition [0,5]
|
||||
editSession.addCursorAtScreenPosition [1,7]
|
||||
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", ->
|
||||
editSession.setSoftWrap(true)
|
||||
editSession.setEditorWidthInChars(10)
|
||||
editSession.setCursorScreenPosition [2,5]
|
||||
editSession.addCursorAtScreenPosition [8,7]
|
||||
|
||||
editSession.moveCursorToFirstCharacterOfLine()
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,2]
|
||||
|
||||
editSession.moveCursorToFirstCharacterOfLine()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,0]
|
||||
|
||||
describe "when triggered ", ->
|
||||
it "does not move the cursor", ->
|
||||
editSession.setCursorBufferPosition([10, 0])
|
||||
editSession.moveCursorToFirstCharacterOfLine()
|
||||
expect(editSession.getCursorBufferPosition()).toEqual [10, 0]
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
expect(cursor1.getScreenPosition()).toEqual [2,0]
|
||||
expect(cursor2.getScreenPosition()).toEqual [8,4]
|
||||
|
||||
editSession.moveCursorToFirstCharacterOfLine()
|
||||
expect(cursor1.getScreenPosition()).toEqual [2,0]
|
||||
expect(cursor2.getScreenPosition()).toEqual [8,0]
|
||||
|
||||
describe "when soft wrap is off", ->
|
||||
it "moves to the first character of the current line or the beginning of the line if it's already on the first character", ->
|
||||
editSession.setCursorScreenPosition [0,5]
|
||||
editSession.addCursorAtScreenPosition [1,7]
|
||||
|
||||
editSession.moveCursorToFirstCharacterOfLine()
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,2]
|
||||
|
||||
editSession.moveCursorToFirstCharacterOfLine()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,0]
|
||||
|
||||
describe ".moveCursorToBeginningOfWord()", ->
|
||||
it "moves the cursor to the beginning of the word", ->
|
||||
@@ -313,6 +439,36 @@ describe "EditSession", ->
|
||||
editSession.moveCursorToBeginningOfWord()
|
||||
expect(editSession.getCursorBufferPosition()).toEqual [9, 2]
|
||||
|
||||
describe ".moveCursorToPreviousWordBoundary()", ->
|
||||
it "moves the cursor to the previous word boundary", ->
|
||||
editSession.setCursorBufferPosition [0, 8]
|
||||
editSession.addCursorAtBufferPosition [2, 0]
|
||||
editSession.addCursorAtBufferPosition [2, 4]
|
||||
editSession.addCursorAtBufferPosition [3, 14]
|
||||
[cursor1, cursor2, cursor3, cursor4] = editSession.getCursors()
|
||||
|
||||
editSession.moveCursorToPreviousWordBoundary()
|
||||
|
||||
expect(cursor1.getBufferPosition()).toEqual [0, 4]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1, 30]
|
||||
expect(cursor3.getBufferPosition()).toEqual [2, 0]
|
||||
expect(cursor4.getBufferPosition()).toEqual [3, 13]
|
||||
|
||||
describe ".moveCursorToNextWordBoundary()", ->
|
||||
it "moves the cursor to the previous word boundary", ->
|
||||
editSession.setCursorBufferPosition [0, 8]
|
||||
editSession.addCursorAtBufferPosition [2, 40]
|
||||
editSession.addCursorAtBufferPosition [3, 0]
|
||||
editSession.addCursorAtBufferPosition [3, 30]
|
||||
[cursor1, cursor2, cursor3, cursor4] = editSession.getCursors()
|
||||
|
||||
editSession.moveCursorToNextWordBoundary()
|
||||
|
||||
expect(cursor1.getBufferPosition()).toEqual [0, 13]
|
||||
expect(cursor2.getBufferPosition()).toEqual [3, 0]
|
||||
expect(cursor3.getBufferPosition()).toEqual [3, 4]
|
||||
expect(cursor4.getBufferPosition()).toEqual [3, 31]
|
||||
|
||||
describe ".moveCursorToEndOfWord()", ->
|
||||
it "moves the cursor to the end of the word", ->
|
||||
editSession.setCursorBufferPosition [0, 6]
|
||||
@@ -355,6 +511,14 @@ describe "EditSession", ->
|
||||
expect(cursor2.getBufferPosition()).toEqual [1, 13]
|
||||
expect(cursor3.getBufferPosition()).toEqual [2, 4]
|
||||
|
||||
# When the cursor is on whitespace
|
||||
editSession.setText("ab cde- ")
|
||||
editSession.setCursorBufferPosition [0,2]
|
||||
cursor = editSession.getCursor()
|
||||
editSession.moveCursorToBeginningOfNextWord()
|
||||
|
||||
expect(cursor.getBufferPosition()).toEqual [0, 3]
|
||||
|
||||
it "does not blow up when there is no next word", ->
|
||||
editSession.setCursorBufferPosition [Infinity, Infinity]
|
||||
endPosition = editSession.getCursorBufferPosition()
|
||||
@@ -418,7 +582,7 @@ describe "EditSession", ->
|
||||
oldScreenPosition: [6, 0]
|
||||
newBufferPosition: [9, 3]
|
||||
newScreenPosition: [6, 3]
|
||||
bufferChanged: true
|
||||
textChanged: true
|
||||
)
|
||||
|
||||
describe "when the position of the associated selection's tail changes, but not the cursor's position", ->
|
||||
@@ -478,16 +642,18 @@ describe "EditSession", ->
|
||||
expect(selection1.isReversed()).toBeFalsy()
|
||||
|
||||
it "merges selections when they intersect when moving up", ->
|
||||
editSession.setSelectedBufferRanges([[[0,9], [0,13]], [[1,10], [1,20]]], reverse: true)
|
||||
editSession.setSelectedBufferRanges([[[0,9], [0,13]], [[1,10], [1,20]]], isReversed: true)
|
||||
[selection1, selection2] = editSession.getSelections()
|
||||
|
||||
editSession.selectUp()
|
||||
|
||||
expect(editSession.getSelections().length).toBe 1
|
||||
expect(editSession.getSelections()).toEqual [selection1]
|
||||
expect(selection1.getScreenRange()).toEqual([[0, 0], [1, 20]])
|
||||
expect(selection1.isReversed()).toBeTruthy()
|
||||
|
||||
it "merges selections when they intersect when moving left", ->
|
||||
editSession.setSelectedBufferRanges([[[0,9], [0,13]], [[0,14], [1,20]]], reverse: true)
|
||||
editSession.setSelectedBufferRanges([[[0,9], [0,13]], [[0,14], [1,20]]], isReversed: true)
|
||||
[selection1, selection2] = editSession.getSelections()
|
||||
|
||||
editSession.selectLeft()
|
||||
@@ -668,6 +834,46 @@ describe "EditSession", ->
|
||||
expect(selection2.getBufferRange()).toEqual [[3,48], [3,51]]
|
||||
expect(selection2.isReversed()).toBeFalsy()
|
||||
|
||||
describe ".selectToPreviousWordBoundary()", ->
|
||||
it "select to the previous word boundary", ->
|
||||
editSession.setCursorBufferPosition [0, 8]
|
||||
editSession.addCursorAtBufferPosition [2, 0]
|
||||
editSession.addCursorAtBufferPosition [3, 4]
|
||||
editSession.addCursorAtBufferPosition [3, 14]
|
||||
|
||||
editSession.selectToPreviousWordBoundary()
|
||||
|
||||
expect(editSession.getSelections().length).toBe 4
|
||||
[selection1, selection2, selection3, selection4] = editSession.getSelections()
|
||||
expect(selection1.getBufferRange()).toEqual [[0,8], [0,4]]
|
||||
expect(selection1.isReversed()).toBeTruthy()
|
||||
expect(selection2.getBufferRange()).toEqual [[2,0], [1,30]]
|
||||
expect(selection2.isReversed()).toBeTruthy()
|
||||
expect(selection3.getBufferRange()).toEqual [[3,4], [3,0]]
|
||||
expect(selection3.isReversed()).toBeTruthy()
|
||||
expect(selection4.getBufferRange()).toEqual [[3,14], [3,13]]
|
||||
expect(selection4.isReversed()).toBeTruthy()
|
||||
|
||||
describe ".selectToNextWordBoundary()", ->
|
||||
it "select to the next word boundary", ->
|
||||
editSession.setCursorBufferPosition [0, 8]
|
||||
editSession.addCursorAtBufferPosition [2, 40]
|
||||
editSession.addCursorAtBufferPosition [4, 0]
|
||||
editSession.addCursorAtBufferPosition [3, 30]
|
||||
|
||||
editSession.selectToNextWordBoundary()
|
||||
|
||||
expect(editSession.getSelections().length).toBe 4
|
||||
[selection1, selection2, selection3, selection4] = editSession.getSelections()
|
||||
expect(selection1.getBufferRange()).toEqual [[0,8], [0,13]]
|
||||
expect(selection1.isReversed()).toBeFalsy()
|
||||
expect(selection2.getBufferRange()).toEqual [[2,40], [3,0]]
|
||||
expect(selection2.isReversed()).toBeFalsy()
|
||||
expect(selection3.getBufferRange()).toEqual [[4,0], [4,4]]
|
||||
expect(selection3.isReversed()).toBeFalsy()
|
||||
expect(selection4.getBufferRange()).toEqual [[3,30], [3,31]]
|
||||
expect(selection4.isReversed()).toBeFalsy()
|
||||
|
||||
describe ".selectWord()", ->
|
||||
describe "when the cursor is inside a word", ->
|
||||
it "selects the entire word", ->
|
||||
@@ -703,6 +909,31 @@ describe "EditSession", ->
|
||||
editSession.selectWord()
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[12, 2], [12, 6]]
|
||||
|
||||
describe ".selectToFirstCharacterOfLine()", ->
|
||||
it "moves to the first character of the current line or the beginning of the line if it's already on the first character", ->
|
||||
editSession.setCursorScreenPosition [0,5]
|
||||
editSession.addCursorAtScreenPosition [1,7]
|
||||
|
||||
editSession.selectToFirstCharacterOfLine()
|
||||
|
||||
[cursor1, cursor2] = editSession.getCursors()
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor2.getBufferPosition()).toEqual [1,2]
|
||||
|
||||
expect(editSession.getSelections().length).toBe 2
|
||||
[selection1, selection2] = editSession.getSelections()
|
||||
expect(selection1.getBufferRange()).toEqual [[0,0], [0,5]]
|
||||
expect(selection1.isReversed()).toBeTruthy()
|
||||
expect(selection2.getBufferRange()).toEqual [[1,2], [1,7]]
|
||||
expect(selection2.isReversed()).toBeTruthy()
|
||||
|
||||
editSession.selectToFirstCharacterOfLine()
|
||||
[selection1, selection2] = editSession.getSelections()
|
||||
expect(selection1.getBufferRange()).toEqual [[0,0], [0,5]]
|
||||
expect(selection1.isReversed()).toBeTruthy()
|
||||
expect(selection2.getBufferRange()).toEqual [[1,0], [1,7]]
|
||||
expect(selection2.isReversed()).toBeTruthy()
|
||||
|
||||
describe ".setSelectedBufferRanges(ranges)", ->
|
||||
it "clears existing selections and creates selections for each of the given ranges", ->
|
||||
editSession.setSelectedBufferRanges([[[2, 2], [3, 3]], [[4, 4], [5, 5]]])
|
||||
@@ -741,8 +972,10 @@ describe "EditSession", ->
|
||||
it "does not remove folds that contain the selections", ->
|
||||
editSession.setSelectedBufferRange([[0,0], [0,0]])
|
||||
editSession.createFold(1, 4)
|
||||
editSession.setSelectedBufferRanges([[[2, 2], [3, 3]]], preserveFolds: true)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeDefined()
|
||||
editSession.createFold(6, 8)
|
||||
editSession.setSelectedBufferRanges([[[2, 2], [3, 3]], [[6, 0], [6, 1]]], preserveFolds: true)
|
||||
expect(editSession.isFoldedAtBufferRow(1)).toBeTruthy()
|
||||
expect(editSession.isFoldedAtBufferRow(6)).toBeTruthy()
|
||||
|
||||
describe ".selectMarker(marker)", ->
|
||||
describe "if the marker is valid", ->
|
||||
@@ -944,6 +1177,12 @@ describe "EditSession", ->
|
||||
editSession.setCursorScreenPosition([3, 3])
|
||||
expect(selection.isEmpty()).toBeTruthy()
|
||||
|
||||
it "does not share selections between different edit sessions for the same buffer", ->
|
||||
editSession2 = project.open('sample.js')
|
||||
editSession.setSelectedBufferRanges([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
|
||||
editSession2.setSelectedBufferRanges([[[8, 7], [6, 5]], [[4, 3], [2, 1]]])
|
||||
expect(editSession2.getSelectedBufferRanges()).not.toEqual editSession.getSelectedBufferRanges()
|
||||
|
||||
describe "buffer manipulation", ->
|
||||
describe ".insertText(text)", ->
|
||||
describe "when there are multiple empty selections", ->
|
||||
@@ -1081,7 +1320,7 @@ describe "EditSession", ->
|
||||
expect(cursor2.getBufferPosition()).toEqual [8,0]
|
||||
|
||||
describe ".insertNewlineBelow()", ->
|
||||
describe "when the operation is undone", ->
|
||||
xdescribe "when the operation is undone", ->
|
||||
it "places the cursor back at the previous location", ->
|
||||
editSession.setCursorBufferPosition([0,2])
|
||||
editSession.insertNewlineBelow()
|
||||
@@ -1478,7 +1717,7 @@ describe "EditSession", ->
|
||||
|
||||
describe "if 'softTabs' is false", ->
|
||||
it "insert a \t into the buffer", ->
|
||||
editSession.softTabs = false
|
||||
editSession.setSoftTabs(false)
|
||||
expect(buffer.lineForRow(0)).not.toMatch(/^\t/)
|
||||
editSession.indent()
|
||||
expect(buffer.lineForRow(0)).toMatch(/^\t/)
|
||||
@@ -1497,7 +1736,7 @@ describe "EditSession", ->
|
||||
describe "when 'softTabs' is false", ->
|
||||
it "moves the cursor to the end of the leading whitespace and inserts enough tabs to bring the line to the suggested level of indentaion", ->
|
||||
convertToHardTabs(buffer)
|
||||
editSession.softTabs = false
|
||||
editSession.setSoftTabs(false)
|
||||
buffer.insert([5, 0], "\t\n")
|
||||
editSession.setCursorBufferPosition [5, 0]
|
||||
editSession.indent(autoIndent: true)
|
||||
@@ -1517,7 +1756,7 @@ describe "EditSession", ->
|
||||
describe "when 'softTabs' is false", ->
|
||||
it "moves the cursor to the end of the leading whitespace and inserts \t into the buffer", ->
|
||||
convertToHardTabs(buffer)
|
||||
editSession.softTabs = false
|
||||
editSession.setSoftTabs(false)
|
||||
buffer.insert([7, 0], "\t\t\t\n")
|
||||
editSession.setCursorBufferPosition [7, 1]
|
||||
editSession.indent(autoIndent: true)
|
||||
@@ -1559,24 +1798,33 @@ describe "EditSession", ->
|
||||
expect(clipboard.readText()).toBe 'quicksort\nsort'
|
||||
|
||||
describe ".cutToEndOfLine()", ->
|
||||
describe "when nothing is selected", ->
|
||||
describe "when soft wrap is on", ->
|
||||
it "cuts up to the end of the line", ->
|
||||
editSession.setCursorBufferPosition([2, 20])
|
||||
editSession.addCursorAtBufferPosition([3, 20])
|
||||
editSession.setSoftWrap(true)
|
||||
editSession.setEditorWidthInChars(10)
|
||||
editSession.setCursorScreenPosition([2, 2])
|
||||
editSession.cutToEndOfLine()
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.length'
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
|
||||
expect(pasteboard.read()[0]).toBe ' <= 1) return items;\ns.shift(), current, left = [], right = [];'
|
||||
expect(editSession.lineForScreenRow(2).text).toBe '= () {'
|
||||
|
||||
describe "when text is selected", ->
|
||||
it "only cuts the selected text, not to the end of the line", ->
|
||||
editSession.setSelectedBufferRanges([[[2,20], [2, 30]], [[3, 20], [3, 20]]])
|
||||
describe "when soft wrap is off", ->
|
||||
describe "when nothing is selected", ->
|
||||
it "cuts up to the end of the line", ->
|
||||
editSession.setCursorBufferPosition([2, 20])
|
||||
editSession.addCursorAtBufferPosition([3, 20])
|
||||
editSession.cutToEndOfLine()
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.length'
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
|
||||
expect(pasteboard.read()[0]).toBe ' <= 1) return items;\ns.shift(), current, left = [], right = [];'
|
||||
|
||||
editSession.cutToEndOfLine()
|
||||
describe "when text is selected", ->
|
||||
it "only cuts the selected text, not to the end of the line", ->
|
||||
editSession.setSelectedBufferRanges([[[2,20], [2, 30]], [[3, 20], [3, 20]]])
|
||||
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.lengthurn items;'
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
|
||||
expect(pasteboard.read()[0]).toBe ' <= 1) ret\ns.shift(), current, left = [], right = [];'
|
||||
editSession.cutToEndOfLine()
|
||||
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.lengthurn items;'
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
|
||||
expect(pasteboard.read()[0]).toBe ' <= 1) ret\ns.shift(), current, left = [], right = [];'
|
||||
|
||||
describe ".copySelectedText()", ->
|
||||
it "copies selected text onto the clipboard", ->
|
||||
@@ -1604,7 +1852,7 @@ describe "EditSession", ->
|
||||
describe "when softTabs is disabled", ->
|
||||
it "indents line and retains selection", ->
|
||||
convertToHardTabs(buffer)
|
||||
editSession.softTabs = false
|
||||
editSession.setSoftTabs(false)
|
||||
editSession.setSelectedBufferRange([[0,3], [0,3]])
|
||||
editSession.indentSelectedRows()
|
||||
expect(buffer.lineForRow(0)).toBe "\tvar quicksort = function () {"
|
||||
@@ -1621,7 +1869,7 @@ describe "EditSession", ->
|
||||
describe "when softTabs is disabled", ->
|
||||
it "indents line and retains selection", ->
|
||||
convertToHardTabs(buffer)
|
||||
editSession.softTabs = false
|
||||
editSession.setSoftTabs(false)
|
||||
editSession.setSelectedBufferRange([[0,4], [0,14]])
|
||||
editSession.indentSelectedRows()
|
||||
expect(buffer.lineForRow(0)).toBe "\tvar quicksort = function () {"
|
||||
@@ -1648,7 +1896,7 @@ describe "EditSession", ->
|
||||
describe "when softTabs is disabled", ->
|
||||
it "indents selected lines (that are not empty) and retains selection", ->
|
||||
convertToHardTabs(buffer)
|
||||
editSession.softTabs = false
|
||||
editSession.setSoftTabs(false)
|
||||
editSession.setSelectedBufferRange([[9,1], [11,15]])
|
||||
editSession.indentSelectedRows()
|
||||
expect(buffer.lineForRow(9)).toBe "\t\t};"
|
||||
@@ -1717,10 +1965,10 @@ describe "EditSession", ->
|
||||
editSession.setSelectedBufferRange([[4, 5], [7, 5]])
|
||||
editSession.toggleLineCommentsInSelection()
|
||||
|
||||
expect(buffer.lineForRow(4)).toBe "// while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe "// current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe "// current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe "// }"
|
||||
expect(buffer.lineForRow(4)).toBe " // while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe " // current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe " // current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe " // }"
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[4, 8], [7, 8]]
|
||||
|
||||
editSession.toggleLineCommentsInSelection()
|
||||
@@ -1732,9 +1980,9 @@ describe "EditSession", ->
|
||||
it "does not comment the last line of a non-empty selection if it ends at column 0", ->
|
||||
editSession.setSelectedBufferRange([[4, 5], [7, 0]])
|
||||
editSession.toggleLineCommentsInSelection()
|
||||
expect(buffer.lineForRow(4)).toBe "// while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe "// current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe "// current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(4)).toBe " // while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe " // current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe " // current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe " }"
|
||||
|
||||
it "uncomments lines if all lines match the comment regex", ->
|
||||
@@ -1808,7 +2056,7 @@ describe "EditSession", ->
|
||||
editSession.toggleLineCommentsInSelection()
|
||||
expect(buffer.lineForRow(10)).toBe " "
|
||||
|
||||
describe ".undo() and .redo()", ->
|
||||
xdescribe ".undo() and .redo()", ->
|
||||
it "undoes/redoes the last change", ->
|
||||
editSession.insertText("foo")
|
||||
editSession.undo()
|
||||
@@ -1879,7 +2127,7 @@ describe "EditSession", ->
|
||||
expect(editSession.isFoldedAtBufferRow(1)).toBeFalsy()
|
||||
expect(editSession.isFoldedAtBufferRow(2)).toBeTruthy()
|
||||
|
||||
describe ".transact([fn])", ->
|
||||
xdescribe ".transact([fn])", ->
|
||||
describe "when called without a function", ->
|
||||
it "restores the selection when the transaction is undone/redone", ->
|
||||
buffer.setText('1234')
|
||||
@@ -1942,91 +2190,6 @@ describe "EditSession", ->
|
||||
expect(cursor1.getBufferPosition()).toEqual [0,0]
|
||||
expect(cursor3.getBufferPosition()).toEqual [1,2]
|
||||
|
||||
describe "folding", ->
|
||||
describe ".unfoldAll()", ->
|
||||
it "unfolds every folded line", ->
|
||||
initialScreenLineCount = editSession.getScreenLineCount()
|
||||
editSession.foldBufferRow(0)
|
||||
editSession.foldBufferRow(1)
|
||||
expect(editSession.getScreenLineCount()).toBeLessThan initialScreenLineCount
|
||||
editSession.unfoldAll()
|
||||
expect(editSession.getScreenLineCount()).toBe initialScreenLineCount
|
||||
|
||||
describe ".foldAll()", ->
|
||||
it "folds every foldable line", ->
|
||||
editSession.foldAll()
|
||||
|
||||
fold1 = editSession.lineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 12]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(1).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 9]
|
||||
fold2.destroy()
|
||||
|
||||
fold3 = editSession.lineForScreenRow(4).fold
|
||||
expect([fold3.getStartRow(), fold3.getEndRow()]).toEqual [4, 7]
|
||||
|
||||
describe ".foldBufferRow(bufferRow)", ->
|
||||
describe "when bufferRow can be folded", ->
|
||||
it "creates a fold based on the syntactic region starting at the given row", ->
|
||||
editSession.foldBufferRow(1)
|
||||
fold = editSession.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 9
|
||||
|
||||
describe "when bufferRow can't be folded", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the given buffer row (and folds it)", ->
|
||||
editSession.foldBufferRow(8)
|
||||
fold = editSession.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 9
|
||||
|
||||
describe "when the bufferRow is already folded", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", ->
|
||||
editSession.foldBufferRow(2)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeDefined()
|
||||
expect(editSession.lineForScreenRow(0).fold).not.toBeDefined()
|
||||
|
||||
editSession.foldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(0).fold).toBeDefined()
|
||||
|
||||
describe "when the bufferRow is in a multi-line comment", ->
|
||||
it "searches upward and downward for surrounding comment lines and folds them as a single fold", ->
|
||||
buffer.insert([1,0], " //this is a comment\n // and\n //more docs\n\n//second comment")
|
||||
editSession.foldBufferRow(1)
|
||||
fold = editSession.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 3
|
||||
|
||||
describe "when the bufferRow is a single-line comment", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", ->
|
||||
buffer.insert([1,0], " //this is a single line comment\n")
|
||||
editSession.foldBufferRow(1)
|
||||
fold = editSession.lineForScreenRow(0).fold
|
||||
expect(fold.getStartRow()).toBe 0
|
||||
expect(fold.getEndRow()).toBe 13
|
||||
|
||||
describe ".unfoldBufferRow(bufferRow)", ->
|
||||
describe "when bufferRow can be unfolded", ->
|
||||
it "destroys a fold based on the syntactic region starting at the given row", ->
|
||||
editSession.foldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeDefined()
|
||||
|
||||
editSession.unfoldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
|
||||
describe "when bufferRow can't be unfolded", ->
|
||||
it "does not throw an error", ->
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
editSession.unfoldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
|
||||
it "maintains cursor buffer position when a folding/unfolding", ->
|
||||
editSession.setCursorBufferPosition([5,5])
|
||||
editSession.foldAll()
|
||||
expect(editSession.getCursorBufferPosition()).toEqual([5,5])
|
||||
|
||||
describe ".deleteLine()", ->
|
||||
it "deletes the first line when the cursor is there", ->
|
||||
editSession.getCursor().moveToTop()
|
||||
@@ -2094,7 +2257,8 @@ describe "EditSession", ->
|
||||
|
||||
describe "when soft wrap is enabled", ->
|
||||
it "deletes the entire line that the cursor is on", ->
|
||||
editSession.setSoftWrapColumn(10)
|
||||
editSession.setSoftWrap(true)
|
||||
editSession.setEditorWidthInChars(10)
|
||||
editSession.setCursorBufferPosition([6])
|
||||
|
||||
line7 = buffer.lineForRow(7)
|
||||
@@ -2104,7 +2268,7 @@ describe "EditSession", ->
|
||||
expect(buffer.lineForRow(6)).toBe(line7)
|
||||
expect(buffer.getLineCount()).toBe(count - 1)
|
||||
|
||||
describe "when the line being deleted preceeds a fold, and the command is undone", ->
|
||||
xdescribe "when the line being deleted preceeds a fold, and the command is undone", ->
|
||||
it "restores the line and preserves the fold", ->
|
||||
editSession.setCursorBufferPosition([4])
|
||||
editSession.foldCurrentRow()
|
||||
@@ -2185,15 +2349,15 @@ describe "EditSession", ->
|
||||
expect(editSession.getSelectedBufferRange()).toEqual [[0, 0], [0, 2]]
|
||||
|
||||
describe "soft-tabs detection", ->
|
||||
it "assign soft / hard tabs based on the contents of the buffer, or uses the default if unknown", ->
|
||||
it "assigns soft / hard tabs based on the contents of the buffer, or uses the default if unknown", ->
|
||||
editSession = project.open('sample.js', softTabs: false)
|
||||
expect(editSession.softTabs).toBeTruthy()
|
||||
expect(editSession.getSoftTabs()).toBeTruthy()
|
||||
|
||||
editSession = project.open('sample-with-tabs.coffee', softTabs: true)
|
||||
expect(editSession.softTabs).toBeFalsy()
|
||||
expect(editSession.getSoftTabs()).toBeFalsy()
|
||||
|
||||
editSession = project.open(null, softTabs: false)
|
||||
expect(editSession.softTabs).toBeFalsy()
|
||||
expect(editSession.getSoftTabs()).toBeFalsy()
|
||||
|
||||
describe ".indentLevelForLine(line)", ->
|
||||
it "returns the indent level when the line has only leading whitespace", ->
|
||||
@@ -2334,55 +2498,10 @@ describe "EditSession", ->
|
||||
editSession.insertText('foo')
|
||||
expect(editSession.indentationForBufferRow(2)).toBe editSession.indentationForBufferRow(1) + 1
|
||||
|
||||
describe "editor.autoIndentOnPaste", ->
|
||||
describe "when the text contains multiple lines", ->
|
||||
beforeEach ->
|
||||
copyText("function() {\ninside=true\n}\n i=1\n")
|
||||
editSession.setCursorBufferPosition([2, 0])
|
||||
|
||||
it "does not auto-indent pasted text by default", ->
|
||||
editSession.pasteText()
|
||||
expect(editSession.lineForBufferRow(2)).toBe "function() {"
|
||||
expect(editSession.lineForBufferRow(3)).toBe "inside=true"
|
||||
expect(editSession.lineForBufferRow(4)).toBe "}"
|
||||
expect(editSession.lineForBufferRow(5)).toBe " i=1"
|
||||
|
||||
it "auto-indents pasted text when editor.autoIndentOnPaste is true", ->
|
||||
config.set("editor.autoIndentOnPaste", true)
|
||||
editSession.pasteText()
|
||||
expect(editSession.lineForBufferRow(2)).toBe " function() {"
|
||||
expect(editSession.lineForBufferRow(3)).toBe " inside=true"
|
||||
expect(editSession.lineForBufferRow(4)).toBe " }"
|
||||
expect(editSession.lineForBufferRow(5)).toBe " i=1"
|
||||
|
||||
describe "when the text contains no newlines", ->
|
||||
it "increaseses indent of pasted text when editor.autoIndentOnPaste is true", ->
|
||||
copyText("var number")
|
||||
editSession.setCursorBufferPosition([10, 0])
|
||||
config.set("editor.autoIndentOnPaste", true)
|
||||
editSession.pasteText()
|
||||
expect(editSession.lineForBufferRow(10)).toBe " var number"
|
||||
|
||||
it "decreaseses indent of pasted text when editor.autoIndentOnPaste is true", ->
|
||||
copyText(" var number")
|
||||
editSession.setCursorBufferPosition([10, 0])
|
||||
config.set("editor.autoIndentOnPaste", true)
|
||||
editSession.pasteText()
|
||||
expect(editSession.lineForBufferRow(10)).toBe " var number"
|
||||
|
||||
describe "editor.normalizeIndentOnPaste", ->
|
||||
beforeEach ->
|
||||
config.set('editor.normalizeIndentOnPaste', true)
|
||||
|
||||
it "does not normalize the indentation level of the text when editor.autoIndentOnPaste is true", ->
|
||||
copyText(" function() {\nvar cool = 1;\n }\n")
|
||||
config.set('editor.autoIndentOnPaste', true)
|
||||
editSession.setCursorBufferPosition([5, ])
|
||||
editSession.pasteText()
|
||||
expect(editSession.lineForBufferRow(5)).toBe " function() {"
|
||||
expect(editSession.lineForBufferRow(6)).toBe " var cool = 1;"
|
||||
expect(editSession.lineForBufferRow(7)).toBe " }"
|
||||
|
||||
it "does not normalize the indentation level of the text when editor.normalizeIndentOnPaste is false", ->
|
||||
copyText(" function() {\nvar cool = 1;\n }\n")
|
||||
config.set('editor.normalizeIndentOnPaste', false)
|
||||
@@ -0,0 +1,37 @@
|
||||
{Site} = require 'telepath'
|
||||
Editor = require 'editor'
|
||||
Environment = require 'environment'
|
||||
|
||||
describe "Editor replication", ->
|
||||
[env1, env2, editSession1, editSession2, editor1, editor2] = []
|
||||
|
||||
beforeEach ->
|
||||
env1 = new Environment(siteId: 1)
|
||||
env2 = env1.clone(siteId: 2)
|
||||
envConnection = env1.connect(env2)
|
||||
doc2 = null
|
||||
|
||||
env1.run ->
|
||||
editSession1 = project.open('sample.js')
|
||||
editSession1.setSelectedBufferRange([[1, 2], [3, 4]])
|
||||
doc1 = editSession1.getState()
|
||||
doc2 = doc1.clone(env2.site)
|
||||
envConnection.connect(doc1, doc2)
|
||||
editor1 = new Editor(editSession1)
|
||||
editor1.attachToDom()
|
||||
|
||||
env2.run ->
|
||||
editSession2 = deserialize(doc2)
|
||||
editor2 = new Editor(editSession2)
|
||||
editor2.attachToDom()
|
||||
|
||||
afterEach ->
|
||||
env1.destroy()
|
||||
env2.destroy()
|
||||
|
||||
it "displays the cursors and selections from all replicas", ->
|
||||
expect(editor1.getSelectionViews().length).toBe 2
|
||||
expect(editor2.getSelectionViews().length).toBe 2
|
||||
|
||||
expect(editor1.getCursorViews().length).toBe 2
|
||||
expect(editor2.getCursorViews().length).toBe 2
|
||||
@@ -2,7 +2,7 @@ RootView = require 'root-view'
|
||||
EditSession = require 'edit-session'
|
||||
Buffer = require 'text-buffer'
|
||||
Editor = require 'editor'
|
||||
Range = require 'range'
|
||||
{Range} = require 'telepath'
|
||||
Project = require 'project'
|
||||
$ = require 'jquery'
|
||||
{$$} = require 'space-pen'
|
||||
@@ -1013,7 +1013,7 @@ describe "Editor", ->
|
||||
|
||||
describe "when soft-wrap is enabled", ->
|
||||
beforeEach ->
|
||||
editor.setSoftWrap(true)
|
||||
editSession.setSoftWrap(true)
|
||||
|
||||
it "does not scroll the buffer horizontally", ->
|
||||
editor.width(charWidth * 30)
|
||||
@@ -1479,10 +1479,13 @@ describe "Editor", ->
|
||||
expect(editor.renderedLines.find('.line:first').text()).toBe "a line that ends with a carriage return#{cr}#{eol}"
|
||||
|
||||
describe "when wrapping is on", ->
|
||||
beforeEach ->
|
||||
editSession.setSoftWrap(true)
|
||||
|
||||
it "doesn't show the end of line invisible at the end of lines broken due to wrapping", ->
|
||||
editor.setSoftWrapColumn(6)
|
||||
editor.setText "a line that wraps"
|
||||
editor.attachToDom()
|
||||
editor.setWidthInChars(6)
|
||||
config.set "editor.showInvisibles", true
|
||||
space = editor.invisibles?.space
|
||||
expect(space).toBeTruthy()
|
||||
@@ -1492,9 +1495,9 @@ describe "Editor", ->
|
||||
expect(editor.renderedLines.find('.line:last').text()).toBe "wraps#{eol}"
|
||||
|
||||
it "displays trailing carriage return using a visible non-empty value", ->
|
||||
editor.setSoftWrapColumn(6)
|
||||
editor.setText "a line that\r\n"
|
||||
editor.attachToDom()
|
||||
editor.setWidthInChars(6)
|
||||
config.set "editor.showInvisibles", true
|
||||
space = editor.invisibles?.space
|
||||
expect(space).toBeTruthy()
|
||||
@@ -1628,11 +1631,11 @@ describe "Editor", ->
|
||||
|
||||
describe "when soft-wrap is enabled", ->
|
||||
beforeEach ->
|
||||
editSession.setSoftWrap(true)
|
||||
editor.attachToDom()
|
||||
setEditorHeightInLines(editor, 20)
|
||||
setEditorWidthInChars(editor, 50)
|
||||
editor.setSoftWrap(true)
|
||||
expect(editor.activeEditSession.softWrapColumn).toBe 50
|
||||
expect(editor.activeEditSession.getSoftWrapColumn()).toBe 50
|
||||
|
||||
it "wraps lines that are too long to fit within the editor's width, adjusting cursor positioning accordingly", ->
|
||||
expect(editor.renderedLines.find('.line').length).toBe 16
|
||||
@@ -1670,14 +1673,10 @@ describe "Editor", ->
|
||||
editor.edit(otherEditSession)
|
||||
expect(editor.renderedLines.find('.line').length).toBe(1)
|
||||
|
||||
it "unwraps lines and cancels window resize listener when softwrap is disabled", ->
|
||||
it "unwraps lines when softwrap is disabled", ->
|
||||
editor.toggleSoftWrap()
|
||||
expect(editor.renderedLines.find('.line:eq(3)').text()).toBe ' var pivot = items.shift(), current, left = [], right = [];'
|
||||
|
||||
spyOn(editor, 'setSoftWrapColumn')
|
||||
$(window).trigger 'resize'
|
||||
expect(editor.setSoftWrapColumn).not.toHaveBeenCalled()
|
||||
|
||||
it "allows the cursor to move down to the last line", ->
|
||||
_.times editor.getLastScreenRow(), -> editor.moveCursorDown()
|
||||
expect(editor.getCursorScreenPosition()).toEqual [editor.getLastScreenRow(), 0]
|
||||
@@ -1700,15 +1699,15 @@ describe "Editor", ->
|
||||
editor.moveCursorRight()
|
||||
expect(editor.getCursorScreenPosition()).toEqual [11, 0]
|
||||
|
||||
it "calls .setSoftWrapColumn() when the editor is attached because now its dimensions are available to calculate it", ->
|
||||
it "calls .setWidthInChars() when the editor is attached because now its dimensions are available to calculate it", ->
|
||||
otherEditor = new Editor(editSession: project.open('sample.js'))
|
||||
spyOn(otherEditor, 'setSoftWrapColumn')
|
||||
spyOn(otherEditor, 'setWidthInChars')
|
||||
|
||||
otherEditor.setSoftWrap(true)
|
||||
expect(otherEditor.setSoftWrapColumn).not.toHaveBeenCalled()
|
||||
otherEditor.activeEditSession.setSoftWrap(true)
|
||||
expect(otherEditor.setWidthInChars).not.toHaveBeenCalled()
|
||||
|
||||
otherEditor.simulateDomAttachment()
|
||||
expect(otherEditor.setSoftWrapColumn).toHaveBeenCalled()
|
||||
expect(otherEditor.setWidthInChars).toHaveBeenCalled()
|
||||
otherEditor.remove()
|
||||
|
||||
describe "gutter rendering", ->
|
||||
@@ -1744,7 +1743,8 @@ describe "Editor", ->
|
||||
|
||||
describe "when wrapping is on", ->
|
||||
it "renders a • instead of line number for wrapped portions of lines", ->
|
||||
editor.setSoftWrapColumn(50)
|
||||
editSession.setSoftWrap(true)
|
||||
editor.setWidthInChars(50)
|
||||
expect(editor.gutter.find('.line-number').length).toEqual(8)
|
||||
expect(editor.gutter.find('.line-number:eq(3)').intValue()).toBe 4
|
||||
expect(editor.gutter.find('.line-number:eq(4)').html()).toBe ' •'
|
||||
@@ -1883,7 +1883,7 @@ describe "Editor", ->
|
||||
describe "when there is wrapping", ->
|
||||
beforeEach ->
|
||||
editor.attachToDom(30)
|
||||
editor.setSoftWrap(true)
|
||||
editSession.setSoftWrap(true)
|
||||
setEditorWidthInChars(editor, 20)
|
||||
|
||||
it "highlights the line where the initial cursor position is", ->
|
||||
@@ -1946,7 +1946,7 @@ describe "Editor", ->
|
||||
|
||||
describe "when there is wrapping", ->
|
||||
beforeEach ->
|
||||
editor.setSoftWrap(true)
|
||||
editSession.setSoftWrap(true)
|
||||
setEditorWidthInChars(editor, 20)
|
||||
|
||||
it "highlights the line where the initial cursor position is", ->
|
||||
@@ -2024,7 +2024,7 @@ describe "Editor", ->
|
||||
it "adds/removes the 'selected' class to the fold's line element and hides the cursor if it is on the fold line", ->
|
||||
editor.createFold(2, 4)
|
||||
|
||||
editor.setSelectedBufferRange([[1, 0], [2, 0]], preserveFolds: true, reverse: true)
|
||||
editor.setSelectedBufferRange([[1, 0], [2, 0]], preserveFolds: true, isReversed: true)
|
||||
expect(editor.lineElementForScreenRow(2)).toMatchSelector('.fold.selected')
|
||||
|
||||
editor.setSelectedBufferRange([[1, 0], [1, 1]], preserveFolds: true)
|
||||
@@ -2670,3 +2670,12 @@ describe "Editor", ->
|
||||
|
||||
for rowNumber in [1..5]
|
||||
expect(editor.lineElementForScreenRow(rowNumber).text()).toBe buffer.lineForRow(rowNumber)
|
||||
|
||||
describe "when the window is resized", ->
|
||||
it "updates the active edit session with the current soft wrap column", ->
|
||||
editor.attachToDom()
|
||||
setEditorWidthInChars(editor, 50)
|
||||
expect(editor.activeEditSession.getSoftWrapColumn()).toBe 50
|
||||
setEditorWidthInChars(editor, 100)
|
||||
$(window).trigger 'resize'
|
||||
expect(editor.activeEditSession.getSoftWrapColumn()).toBe 100
|
||||
@@ -0,0 +1,58 @@
|
||||
{Site} = require 'telepath'
|
||||
fsUtils = require 'fs-utils'
|
||||
Project = require 'project'
|
||||
|
||||
module.exports =
|
||||
class Environment
|
||||
constructor: ({@site, @state, siteId, projectPath}={}) ->
|
||||
@site ?= new Site(siteId ? 1)
|
||||
if @state?
|
||||
@run => @project = deserialize(@state.get('project'))
|
||||
else
|
||||
@state = @site.createDocument({})
|
||||
@project = new Project(projectPath ? fsUtils.resolveOnLoadPath('fixtures'))
|
||||
@state.set(project: @project.getState())
|
||||
|
||||
clone: (params) ->
|
||||
site = new Site(params.siteId)
|
||||
new Environment(site: site, state: @state.clone(site))
|
||||
|
||||
destroy: ->
|
||||
@project.destroy()
|
||||
|
||||
getState: -> @state
|
||||
|
||||
run: (fn) ->
|
||||
uninstall = @install()
|
||||
fn()
|
||||
uninstall()
|
||||
|
||||
install: ->
|
||||
oldSite = window.site
|
||||
oldProject = window.project
|
||||
window.site = @site
|
||||
window.project = @project
|
||||
->
|
||||
window.site = oldSite
|
||||
window.project = oldProject
|
||||
|
||||
connect: (otherEnv) ->
|
||||
new EnvironmentConnection(this, otherEnv)
|
||||
|
||||
|
||||
connectDocuments: (docA, docB, envB) ->
|
||||
|
||||
class EnvironmentConnection
|
||||
constructor: (@envA, @envB) ->
|
||||
@envA.getState().connect(@envB.getState())
|
||||
|
||||
connect: (docA, docB) ->
|
||||
unless docA.site is @envA.site
|
||||
throw new Error("Document and environment sites do not match (doc: site #{docA.site.id}, env: site #{@envA.site.id})")
|
||||
unless docB.site is @envB.site
|
||||
throw new Error("Document and environment sites do not match (doc: site #{docB.site.id}, env: site #{@envB.site.id})")
|
||||
|
||||
connection = docA.connect(docB)
|
||||
connection.abFilter = (fn) => @envB.run(fn)
|
||||
connection.baFilter = (fn) => @envA.run(fn)
|
||||
connection
|
||||
@@ -0,0 +1,4 @@
|
||||
module.exports =
|
||||
activate: -> throw new Error('Top that')
|
||||
deactivate: ->
|
||||
serialize: ->
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports =
|
||||
activate: -> @activateCalled = true
|
||||
activateConfig: -> @activateConfigCalled = true
|
||||
@@ -0,0 +1 @@
|
||||
Hello World!
|
||||
@@ -0,0 +1 @@
|
||||
Goodbye World!
|
||||
@@ -0,0 +1 @@
|
||||
Hello World!
|
||||
@@ -0,0 +1 @@
|
||||
Goodbye World!
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
var quicksort = function () {
|
||||
/*
|
||||
this is a multiline comment
|
||||
it is, I promise
|
||||
*/
|
||||
var sort = function(items) {
|
||||
// This is a collection of
|
||||
// single line comments.
|
||||
// Wowza
|
||||
if (items.length <= 1) return items;
|
||||
var pivot = items.shift(), current, left = [], right = [];
|
||||
while(items.length > 0) {
|
||||
current = items.shift();
|
||||
current < pivot ? left.push(current) : right.push(current);
|
||||
}
|
||||
return sort(left).concat(pivot).concat(sort(right));
|
||||
};
|
||||
|
||||
return sort(Array.apply(this, arguments));
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
fsUtils = require 'fs-utils'
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
|
||||
describe "fsUtils", ->
|
||||
describe ".read(path)", ->
|
||||
@@ -82,6 +83,14 @@ describe "fsUtils", ->
|
||||
|
||||
expect(symlinkPaths).toEqual(paths)
|
||||
|
||||
it "ignores missing symlinks", ->
|
||||
directory = temp.mkdirSync('symlink-in-here')
|
||||
paths = []
|
||||
onPath = (childPath) -> paths.push(childPath)
|
||||
fs.symlinkSync(path.join(directory, 'source'), path.join(directory, 'destination'))
|
||||
fsUtils.traverseTreeSync(directory, onPath)
|
||||
expect(paths.length).toBe 0
|
||||
|
||||
describe ".md5ForPath(path)", ->
|
||||
it "returns the MD5 hash of the file at the given path", ->
|
||||
expect(fsUtils.md5ForPath(require.resolve('fixtures/sample.js'))).toBe 'dd38087d0d7e3e4802a6d3f9b9745f2b'
|
||||
@@ -11,27 +11,33 @@ window.nakedLoad = (file) ->
|
||||
module.exports.runSpecSuite = (specSuite, logErrors=true) ->
|
||||
{$$} = require 'space-pen'
|
||||
nakedLoad 'jasmine'
|
||||
nakedLoad 'jasmine-console-reporter'
|
||||
require 'jasmine-focused'
|
||||
|
||||
AtomReporter = require 'atom-reporter'
|
||||
|
||||
$ = require 'jquery'
|
||||
TimeReporter = require 'time-reporter'
|
||||
timeReporter = new TimeReporter()
|
||||
|
||||
reporter = if atom.getLoadSettings().exitWhenDone
|
||||
new jasmine.ConsoleReporter(document, logErrors)
|
||||
if atom.getLoadSettings().exitWhenDone
|
||||
{jasmineNode} = require 'jasmine-node/lib/jasmine-node/reporter'
|
||||
reporter = new jasmineNode.TerminalReporter
|
||||
print: (args...) ->
|
||||
process.stderr.write(args...)
|
||||
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")
|
||||
atom.exit(runner.results().failedCount > 0 ? 1 : 0)
|
||||
else
|
||||
new AtomReporter()
|
||||
AtomReporter = require 'atom-reporter'
|
||||
reporter = new AtomReporter()
|
||||
|
||||
require specSuite
|
||||
|
||||
jasmineEnv = jasmine.getEnv()
|
||||
jasmineEnv.addReporter(reporter)
|
||||
jasmineEnv.addReporter(timeReporter)
|
||||
|
||||
jasmineEnv.addReporter(new TimeReporter())
|
||||
jasmineEnv.specFilter = (spec) -> reporter.specFilter(spec)
|
||||
|
||||
$('body').append $$ ->
|
||||
@div id: 'jasmine-content'
|
||||
$('body').append $$ -> @div id: 'jasmine-content'
|
||||
|
||||
jasmineEnv.execute()
|
||||
|
||||
@@ -256,6 +256,24 @@ describe "Keymap", ->
|
||||
it "returns false to prevent the browser from transferring focus", ->
|
||||
expect(keymap.handleKeyEvent(keydownEvent('U+0009', target: fragment[0]))).toBe false
|
||||
|
||||
describe ".keystrokesByCommandForSelector(selector)", ->
|
||||
it "returns a hash of all commands and their keybindings", ->
|
||||
keymap.bindKeys 'body', 'a': 'letter'
|
||||
keymap.bindKeys '.editor', 'b': 'letter'
|
||||
keymap.bindKeys '.editor', '1': 'number'
|
||||
keymap.bindKeys '.editor', 'meta-alt-1': 'number-with-modifiers'
|
||||
|
||||
expect(keymap.keystrokesByCommandForSelector()).toEqual
|
||||
'letter': ['b', 'a']
|
||||
'number': ['1']
|
||||
'number-with-modifiers': ['alt-meta-1']
|
||||
|
||||
expect(keymap.keystrokesByCommandForSelector('.editor')).toEqual
|
||||
'letter': ['b']
|
||||
'number': ['1']
|
||||
'number-with-modifiers': ['alt-meta-1']
|
||||
|
||||
|
||||
describe ".bindKeys(selector, bindings)", ->
|
||||
it "normalizes the key patterns in the hash to put the modifiers in alphabetical order", ->
|
||||
fooHandler = jasmine.createSpy('fooHandler')
|
||||
@@ -0,0 +1,354 @@
|
||||
Project = require 'project'
|
||||
Buffer = require 'text-buffer'
|
||||
EditSession = require 'edit-session'
|
||||
|
||||
describe "LanguageMode", ->
|
||||
[editSession, buffer, languageMode] = []
|
||||
|
||||
afterEach ->
|
||||
editSession.destroy()
|
||||
|
||||
describe "javascript", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('javascript-tmbundle', sync: true)
|
||||
editSession = project.open('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
|
||||
describe ".minIndentLevelForRowRange(startRow, endRow)", ->
|
||||
it "returns the minimum indent level for the given row range", ->
|
||||
expect(languageMode.minIndentLevelForRowRange(4, 7)).toBe 2
|
||||
expect(languageMode.minIndentLevelForRowRange(5, 7)).toBe 2
|
||||
expect(languageMode.minIndentLevelForRowRange(5, 6)).toBe 3
|
||||
expect(languageMode.minIndentLevelForRowRange(9, 11)).toBe 1
|
||||
expect(languageMode.minIndentLevelForRowRange(10, 10)).toBe 0
|
||||
|
||||
describe ".toggleLineCommentsForBufferRows(start, end)", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 7)
|
||||
expect(buffer.lineForRow(4)).toBe " // while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe " // current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe " // current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe " // }"
|
||||
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 5)
|
||||
expect(buffer.lineForRow(4)).toBe " while(items.length > 0) {"
|
||||
expect(buffer.lineForRow(5)).toBe " current = items.shift();"
|
||||
expect(buffer.lineForRow(6)).toBe " // current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe " // }"
|
||||
|
||||
describe "fold suggestion", ->
|
||||
describe ".doesBufferRowStartFold(bufferRow)", ->
|
||||
it "returns true only when the buffer row starts a foldable region", ->
|
||||
expect(languageMode.doesBufferRowStartFold(0)).toBeTruthy()
|
||||
expect(languageMode.doesBufferRowStartFold(1)).toBeTruthy()
|
||||
expect(languageMode.doesBufferRowStartFold(2)).toBeFalsy()
|
||||
expect(languageMode.doesBufferRowStartFold(3)).toBeFalsy()
|
||||
|
||||
describe ".rowRangeForCodeFoldAtBufferRow(bufferRow)", ->
|
||||
it "returns the start/end rows of the foldable region starting at the given row", ->
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(0)).toEqual [0, 12]
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(1)).toEqual [1, 9]
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(2)).toBeNull()
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(4)).toEqual [4, 7]
|
||||
|
||||
describe "suggestedIndentForBufferRow", ->
|
||||
it "returns the suggested indentation based on auto-indent/outdent rules", ->
|
||||
expect(languageMode.suggestedIndentForBufferRow(0)).toBe 0
|
||||
expect(languageMode.suggestedIndentForBufferRow(1)).toBe 1
|
||||
expect(languageMode.suggestedIndentForBufferRow(2)).toBe 2
|
||||
expect(languageMode.suggestedIndentForBufferRow(9)).toBe 1
|
||||
|
||||
describe "rowRangeForParagraphAtBufferRow", ->
|
||||
describe "with code and comments", ->
|
||||
beforeEach ->
|
||||
buffer.setText '''
|
||||
var quicksort = function () {
|
||||
/* Single line comment block */
|
||||
var sort = function(items) {};
|
||||
|
||||
/*
|
||||
A multiline
|
||||
comment is here
|
||||
*/
|
||||
var sort = function(items) {};
|
||||
|
||||
// A comment
|
||||
//
|
||||
// Multiple comment
|
||||
// lines
|
||||
var sort = function(items) {};
|
||||
// comment line after fn
|
||||
};
|
||||
'''
|
||||
|
||||
it "will limit paragraph range to comments", ->
|
||||
range = languageMode.rowRangeForParagraphAtBufferRow(0)
|
||||
expect(range).toEqual [[0,0], [0,29]]
|
||||
|
||||
range = languageMode.rowRangeForParagraphAtBufferRow(10)
|
||||
expect(range).toEqual [[10,0], [10,14]]
|
||||
range = languageMode.rowRangeForParagraphAtBufferRow(11)
|
||||
expect(range).toBeFalsy()
|
||||
range = languageMode.rowRangeForParagraphAtBufferRow(12)
|
||||
expect(range).toEqual [[12,0], [13,10]]
|
||||
|
||||
range = languageMode.rowRangeForParagraphAtBufferRow(14)
|
||||
expect(range).toEqual [[14,0], [14,32]]
|
||||
|
||||
range = languageMode.rowRangeForParagraphAtBufferRow(15)
|
||||
expect(range).toEqual [[15,0], [15,26]]
|
||||
|
||||
|
||||
describe "coffeescript", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('coffee-script-tmbundle', sync: true)
|
||||
editSession = project.open('coffee.coffee', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
|
||||
describe ".toggleLineCommentsForBufferRows(start, end)", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 6)
|
||||
expect(buffer.lineForRow(4)).toBe " # pivot = items.shift()"
|
||||
expect(buffer.lineForRow(5)).toBe " # left = []"
|
||||
expect(buffer.lineForRow(6)).toBe " # right = []"
|
||||
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 5)
|
||||
expect(buffer.lineForRow(4)).toBe " pivot = items.shift()"
|
||||
expect(buffer.lineForRow(5)).toBe " left = []"
|
||||
expect(buffer.lineForRow(6)).toBe " # right = []"
|
||||
|
||||
it "comments/uncomments lines when empty line", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 7)
|
||||
expect(buffer.lineForRow(4)).toBe " # pivot = items.shift()"
|
||||
expect(buffer.lineForRow(5)).toBe " # left = []"
|
||||
expect(buffer.lineForRow(6)).toBe " # right = []"
|
||||
expect(buffer.lineForRow(7)).toBe " # "
|
||||
|
||||
languageMode.toggleLineCommentsForBufferRows(4, 5)
|
||||
expect(buffer.lineForRow(4)).toBe " pivot = items.shift()"
|
||||
expect(buffer.lineForRow(5)).toBe " left = []"
|
||||
expect(buffer.lineForRow(6)).toBe " # right = []"
|
||||
expect(buffer.lineForRow(7)).toBe " # "
|
||||
|
||||
describe "fold suggestion", ->
|
||||
describe ".doesBufferRowStartFold(bufferRow)", ->
|
||||
it "returns true only when the buffer row starts a foldable region", ->
|
||||
expect(languageMode.doesBufferRowStartFold(0)).toBeTruthy()
|
||||
expect(languageMode.doesBufferRowStartFold(1)).toBeTruthy()
|
||||
expect(languageMode.doesBufferRowStartFold(2)).toBeFalsy()
|
||||
expect(languageMode.doesBufferRowStartFold(3)).toBeFalsy()
|
||||
expect(languageMode.doesBufferRowStartFold(19)).toBeTruthy()
|
||||
|
||||
describe ".rowRangeForCodeFoldAtBufferRow(bufferRow)", ->
|
||||
it "returns the start/end rows of the foldable region starting at the given row", ->
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(0)).toEqual [0, 20]
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(1)).toEqual [1, 17]
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(2)).toBeNull()
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(19)).toEqual [19, 20]
|
||||
|
||||
describe "css", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('css-tmbundle', sync: true)
|
||||
editSession = project.open('css.css', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
|
||||
describe ".toggleLineCommentsForBufferRows(start, end)", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 1)
|
||||
expect(buffer.lineForRow(0)).toBe "/*body {"
|
||||
expect(buffer.lineForRow(1)).toBe " font-size: 1234px;*/"
|
||||
expect(buffer.lineForRow(2)).toBe " width: 110%;"
|
||||
expect(buffer.lineForRow(3)).toBe " font-weight: bold !important;"
|
||||
|
||||
languageMode.toggleLineCommentsForBufferRows(2, 2)
|
||||
expect(buffer.lineForRow(0)).toBe "/*body {"
|
||||
expect(buffer.lineForRow(1)).toBe " font-size: 1234px;*/"
|
||||
expect(buffer.lineForRow(2)).toBe "/* width: 110%;*/"
|
||||
expect(buffer.lineForRow(3)).toBe " font-weight: bold !important;"
|
||||
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 1)
|
||||
expect(buffer.lineForRow(0)).toBe "body {"
|
||||
expect(buffer.lineForRow(1)).toBe " font-size: 1234px;"
|
||||
expect(buffer.lineForRow(2)).toBe "/* width: 110%;*/"
|
||||
expect(buffer.lineForRow(3)).toBe " font-weight: bold !important;"
|
||||
|
||||
it "uncomments lines with leading whitespace", ->
|
||||
buffer.change([[2, 0], [2, Infinity]], " /*width: 110%;*/")
|
||||
languageMode.toggleLineCommentsForBufferRows(2, 2)
|
||||
expect(buffer.lineForRow(2)).toBe " width: 110%;"
|
||||
|
||||
it "uncomments lines with trailing whitespace", ->
|
||||
buffer.change([[2, 0], [2, Infinity]], "/*width: 110%;*/ ")
|
||||
languageMode.toggleLineCommentsForBufferRows(2, 2)
|
||||
expect(buffer.lineForRow(2)).toBe "width: 110%; "
|
||||
|
||||
it "uncomments lines with leading and trailing whitespace", ->
|
||||
buffer.change([[2, 0], [2, Infinity]], " /*width: 110%;*/ ")
|
||||
languageMode.toggleLineCommentsForBufferRows(2, 2)
|
||||
expect(buffer.lineForRow(2)).toBe " width: 110%; "
|
||||
|
||||
describe "less", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('less-tmbundle', sync: true)
|
||||
atom.activatePackage('css-tmbundle', sync: true)
|
||||
editSession = project.open('sample.less', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
|
||||
describe "when commenting lines", ->
|
||||
it "only uses the `commentEnd` pattern if it comes from the same grammar as the `commentStart`", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 0)
|
||||
expect(buffer.lineForRow(0)).toBe "// @color: #4D926F;"
|
||||
|
||||
describe "folding", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('javascript-tmbundle', sync: true)
|
||||
editSession = project.open('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
|
||||
it "maintains cursor buffer position when a folding/unfolding", ->
|
||||
editSession.setCursorBufferPosition([5,5])
|
||||
languageMode.foldAll()
|
||||
expect(editSession.getCursorBufferPosition()).toEqual([5,5])
|
||||
|
||||
describe ".unfoldAll()", ->
|
||||
it "unfolds every folded line", ->
|
||||
initialScreenLineCount = editSession.getScreenLineCount()
|
||||
languageMode.foldBufferRow(0)
|
||||
languageMode.foldBufferRow(1)
|
||||
expect(editSession.getScreenLineCount()).toBeLessThan initialScreenLineCount
|
||||
languageMode.unfoldAll()
|
||||
expect(editSession.getScreenLineCount()).toBe initialScreenLineCount
|
||||
|
||||
describe ".foldAll()", ->
|
||||
it "folds every foldable line", ->
|
||||
languageMode.foldAll()
|
||||
|
||||
fold1 = editSession.lineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 12]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(1).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 9]
|
||||
fold2.destroy()
|
||||
|
||||
fold3 = editSession.lineForScreenRow(4).fold
|
||||
expect([fold3.getStartRow(), fold3.getEndRow()]).toEqual [4, 7]
|
||||
|
||||
describe ".foldBufferRow(bufferRow)", ->
|
||||
describe "when bufferRow can be folded", ->
|
||||
it "creates a fold based on the syntactic region starting at the given row", ->
|
||||
languageMode.foldBufferRow(1)
|
||||
fold = editSession.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 9
|
||||
|
||||
describe "when bufferRow can't be folded", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the given buffer row (and folds it)", ->
|
||||
languageMode.foldBufferRow(8)
|
||||
fold = editSession.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 9
|
||||
|
||||
describe "when the bufferRow is already folded", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", ->
|
||||
languageMode.foldBufferRow(2)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeDefined()
|
||||
expect(editSession.lineForScreenRow(0).fold).not.toBeDefined()
|
||||
|
||||
languageMode.foldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(0).fold).toBeDefined()
|
||||
|
||||
describe "when the bufferRow is in a multi-line comment", ->
|
||||
it "searches upward and downward for surrounding comment lines and folds them as a single fold", ->
|
||||
buffer.insert([1,0], " //this is a comment\n // and\n //more docs\n\n//second comment")
|
||||
languageMode.foldBufferRow(1)
|
||||
fold = editSession.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 3
|
||||
|
||||
describe "when the bufferRow is a single-line comment", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", ->
|
||||
buffer.insert([1,0], " //this is a single line comment\n")
|
||||
languageMode.foldBufferRow(1)
|
||||
fold = editSession.lineForScreenRow(0).fold
|
||||
expect(fold.getStartRow()).toBe 0
|
||||
expect(fold.getEndRow()).toBe 13
|
||||
|
||||
describe ".unfoldBufferRow(bufferRow)", ->
|
||||
describe "when bufferRow can be unfolded", ->
|
||||
it "destroys a fold based on the syntactic region starting at the given row", ->
|
||||
languageMode.foldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeDefined()
|
||||
|
||||
languageMode.unfoldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
|
||||
describe "when bufferRow can't be unfolded", ->
|
||||
it "does not throw an error", ->
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
languageMode.unfoldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
|
||||
describe "folding with comments", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('javascript-tmbundle', sync: true)
|
||||
editSession = project.open('sample-with-comments.js', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
|
||||
describe ".unfoldAll()", ->
|
||||
it "unfolds every folded line", ->
|
||||
initialScreenLineCount = editSession.getScreenLineCount()
|
||||
languageMode.foldBufferRow(0)
|
||||
languageMode.foldBufferRow(5)
|
||||
expect(editSession.getScreenLineCount()).toBeLessThan initialScreenLineCount
|
||||
languageMode.unfoldAll()
|
||||
expect(editSession.getScreenLineCount()).toBe initialScreenLineCount
|
||||
|
||||
describe ".foldAll()", ->
|
||||
it "folds every foldable line", ->
|
||||
languageMode.foldAll()
|
||||
|
||||
fold1 = editSession.lineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(1).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 4]
|
||||
|
||||
fold3 = editSession.lineForScreenRow(2).fold.destroy()
|
||||
|
||||
fold4 = editSession.lineForScreenRow(3).fold
|
||||
expect([fold4.getStartRow(), fold4.getEndRow()]).toEqual [6, 8]
|
||||
|
||||
describe ".foldAllAtIndentLevel()", ->
|
||||
it "folds every foldable range at a given indentLevel", ->
|
||||
languageMode.foldAllAtIndentLevel(2)
|
||||
|
||||
fold1 = editSession.lineForScreenRow(6).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [6, 8]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(11).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [11, 14]
|
||||
fold2.destroy()
|
||||
|
||||
it "does not fold anything but the indentLevel", ->
|
||||
languageMode.foldAllAtIndentLevel(0)
|
||||
|
||||
fold1 = editSession.lineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(5).fold
|
||||
expect(fold2).toBeFalsy()
|
||||
|
||||
describe "css", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('source-tmbundle', sync: true)
|
||||
atom.activatePackage('css-tmbundle', sync: true)
|
||||
editSession = project.open('css.css', autoIndent: true)
|
||||
|
||||
describe "suggestedIndentForBufferRow", ->
|
||||
it "does not return negative values (regression)", ->
|
||||
editSession.setText('.test {\npadding: 0;\n}')
|
||||
expect(editSession.suggestedIndentForBufferRow(2)).toBe 0
|
||||
+37
-11
@@ -1,10 +1,11 @@
|
||||
{createSite} = require 'telepath'
|
||||
{Site} = require 'telepath'
|
||||
{View} = require 'space-pen'
|
||||
PaneContainer = require 'pane-container'
|
||||
Pane = require 'pane'
|
||||
Environment = require 'environment'
|
||||
|
||||
describe "PaneContainer replication", ->
|
||||
[container1, container2, pane1a, pane1b, pane1c] = []
|
||||
[env1, env2, envConnection, container1, container2, pane1a, pane1b, pane1c] = []
|
||||
|
||||
class TestView extends View
|
||||
@deserialize: ({name}) -> new TestView(name)
|
||||
@@ -17,18 +18,29 @@ describe "PaneContainer replication", ->
|
||||
|
||||
beforeEach ->
|
||||
registerDeserializer(TestView)
|
||||
container1 = new PaneContainer
|
||||
pane1a = new Pane(new TestView('A'))
|
||||
container1.setRoot(pane1a)
|
||||
pane1b = pane1a.splitRight(new TestView('B'))
|
||||
pane1c = pane1b.splitDown(new TestView('C'))
|
||||
|
||||
doc1 = container1.getState()
|
||||
doc2 = doc1.clone(createSite(2))
|
||||
doc1.connect(doc2)
|
||||
container2 = deserialize(doc2)
|
||||
env1 = new Environment(siteId: 1)
|
||||
env2 = env1.clone(siteId: 2)
|
||||
envConnection = env1.connect(env2)
|
||||
doc2 = null
|
||||
|
||||
env1.run ->
|
||||
container1 = new PaneContainer
|
||||
pane1a = new Pane(new TestView('A'))
|
||||
container1.setRoot(pane1a)
|
||||
pane1b = pane1a.splitRight(new TestView('B'))
|
||||
pane1c = pane1b.splitDown(new TestView('C'))
|
||||
|
||||
doc1 = container1.getState()
|
||||
doc2 = doc1.clone(env2.site)
|
||||
envConnection.connect(doc1, doc2)
|
||||
|
||||
env2.run ->
|
||||
container2 = deserialize(doc2)
|
||||
|
||||
afterEach ->
|
||||
env1.destroy()
|
||||
env2.destroy()
|
||||
unregisterDeserializer(TestView)
|
||||
|
||||
it "replicates the inital state of a pane container with splits", ->
|
||||
@@ -80,3 +92,17 @@ describe "PaneContainer replication", ->
|
||||
|
||||
expect(container1.children()).not.toExist()
|
||||
expect(container2.children()).not.toExist()
|
||||
|
||||
# FIXME: We need to get this passing again on master
|
||||
xit "replicates splitting of panes containing edit sessions", ->
|
||||
env1.run ->
|
||||
pane1a.showItem(project.open('dir/a'))
|
||||
pane1a.splitDown()
|
||||
|
||||
expect(project.getBuffers().length).toBe 1
|
||||
expect(container1.find('.row > :eq(0) > :eq(0)').view().activeItem.getRelativePath()).toBe 'dir/a'
|
||||
expect(container1.find('.row > :eq(0) > :eq(1)').view().activeItem.getRelativePath()).toBe 'dir/a'
|
||||
|
||||
env2.run ->
|
||||
expect(container2.find('.row > :eq(0) > :eq(0)').view().activeItem.getRelativePath()).toBe 'dir/a'
|
||||
expect(container2.find('.row > :eq(0) > :eq(1)').view().activeItem.getRelativePath()).toBe 'dir/a'
|
||||
@@ -109,6 +109,15 @@ describe "PaneContainer", ->
|
||||
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)", ->
|
||||
editSession = project.open('sample.js')
|
||||
pane3.showItem(editSession)
|
||||
pane3.destroyItem(editSession)
|
||||
expect(container.reopenItem()).toBeTruthy()
|
||||
expect(pane3.activeItem.getPath()).toBe editSession.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()
|
||||
@@ -140,6 +149,12 @@ describe "PaneContainer", ->
|
||||
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'))
|
||||
@@ -1,6 +1,6 @@
|
||||
PaneContainer = require 'pane-container'
|
||||
Pane = require 'pane'
|
||||
{createSite} = require 'telepath'
|
||||
{Site} = require 'telepath'
|
||||
|
||||
describe "Pane replication", ->
|
||||
[editSession1a, editSession1b, container1, pane1, doc1] = []
|
||||
@@ -14,7 +14,7 @@ describe "Pane replication", ->
|
||||
container1.setRoot(pane1)
|
||||
|
||||
doc1 = container1.getState()
|
||||
doc2 = doc1.clone(createSite(2))
|
||||
doc2 = doc1.clone(new Site(2))
|
||||
doc1.connect(doc2)
|
||||
|
||||
container2 = deserialize(doc2)
|
||||
@@ -2,6 +2,7 @@ PaneContainer = require 'pane-container'
|
||||
Pane = require 'pane'
|
||||
{View} = require 'space-pen'
|
||||
$ = require 'jquery'
|
||||
{dirname} = require 'path'
|
||||
|
||||
describe "Pane", ->
|
||||
[container, view1, view2, editSession1, editSession2, pane] = []
|
||||
@@ -373,7 +374,7 @@ describe "Pane", ->
|
||||
|
||||
pane.trigger 'core:save-as'
|
||||
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalledWith(dirname(editSession2.getPath()))
|
||||
expect(editSession2.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
|
||||
describe "when the current item does not have a saveAs method", ->
|
||||
@@ -516,6 +517,18 @@ describe "Pane", ->
|
||||
|
||||
expect(becameActiveHandler.callCount).toBe 1
|
||||
|
||||
it "triggers 'pane:became-inactive' when it was previously active", ->
|
||||
becameInactiveHandler = jasmine.createSpy("becameInactiveHandler")
|
||||
container.on 'pane:became-inactive', becameInactiveHandler
|
||||
|
||||
expect(pane.isActive()).toBeFalsy()
|
||||
pane.focusin()
|
||||
expect(pane.isActive()).toBeTruthy()
|
||||
pane.splitRight()
|
||||
expect(pane.isActive()).toBeFalsy()
|
||||
|
||||
expect(becameInactiveHandler.callCount).toBe 1
|
||||
|
||||
describe "split methods", ->
|
||||
[pane1, view3, view4] = []
|
||||
beforeEach ->
|
||||
@@ -718,27 +731,33 @@ describe "Pane", ->
|
||||
newPane = deserialize(pane.serialize())
|
||||
expect(newPane.activeItem).toEqual editSession2
|
||||
|
||||
xit "defaults to the first item on deserialization if the active item was not serializable", ->
|
||||
expect(view2.serialize?()).toBeFalsy()
|
||||
it "does not show items that cannot be deserialized", ->
|
||||
spyOn(console, 'warn')
|
||||
|
||||
pane.showItem(view2)
|
||||
paneState = pane.serialize()
|
||||
paneState.get('items').set(pane.items.indexOf(view2), {deserializer: 'Bogus'}) # nuke serialized state of active item
|
||||
|
||||
console.log pane.serialize().toObject()
|
||||
|
||||
newPane = deserialize(pane.serialize())
|
||||
expect(newPane.activeItem).toEqual editSession1
|
||||
newPane = deserialize(paneState)
|
||||
expect(newPane.activeItem).toEqual pane.items[0]
|
||||
expect(newPane.items.length).toBe pane.items.length - 1
|
||||
|
||||
it "focuses the pane after attach only if had focus when serialized", ->
|
||||
container.attachToDom()
|
||||
reloadContainer = ->
|
||||
projectState = project.serialize()
|
||||
containerState = container.serialize()
|
||||
container.remove()
|
||||
project.destroy()
|
||||
window.project = deserialize(projectState)
|
||||
container = deserialize(containerState)
|
||||
pane = container.getRoot()
|
||||
container.attachToDom()
|
||||
|
||||
container.attachToDom()
|
||||
pane.focus()
|
||||
state = pane.serialize()
|
||||
pane.remove()
|
||||
newPane = deserialize(state)
|
||||
container.setRoot(newPane)
|
||||
expect(newPane).toMatchSelector(':has(:focus)')
|
||||
reloadContainer()
|
||||
expect(pane).toMatchSelector(':has(:focus)')
|
||||
|
||||
$(document.activeElement).blur()
|
||||
state = newPane.serialize()
|
||||
newPane.remove()
|
||||
newerPane = deserialize(state)
|
||||
expect(newerPane).not.toMatchSelector(':has(:focus)')
|
||||
reloadContainer()
|
||||
expect(pane).not.toMatchSelector(':has(:focus)')
|
||||
@@ -0,0 +1,49 @@
|
||||
{Site} = require 'telepath'
|
||||
fsUtils = require 'fs-utils'
|
||||
Project = require 'project'
|
||||
Git = require 'git'
|
||||
|
||||
describe "Project replication", ->
|
||||
[doc1, doc2, project1, project2] = []
|
||||
|
||||
beforeEach ->
|
||||
# pretend that home-1/project and home-2/project map to the same git repository url
|
||||
spyOn(Git, 'open').andReturn
|
||||
getOriginUrl: -> 'git://server/project.git'
|
||||
destroy: ->
|
||||
|
||||
config.set('core.projectHome', fsUtils.resolveOnLoadPath('fixtures/replication/home-1'))
|
||||
project1 = new Project(fsUtils.resolveOnLoadPath('fixtures/replication/home-1/project'))
|
||||
project1.bufferForPath('file-1.txt')
|
||||
project1.bufferForPath('file-1.txt')
|
||||
expect(project1.getBuffers().length).toBe 1
|
||||
|
||||
doc1 = project1.getState()
|
||||
doc2 = doc1.clone(new Site(2))
|
||||
connection = doc1.connect(doc2)
|
||||
|
||||
# pretend we're bootstrapping a joining window
|
||||
config.set('core.projectHome', fsUtils.resolveOnLoadPath('fixtures/replication/home-2'))
|
||||
project2 = deserialize(doc2)
|
||||
|
||||
afterEach ->
|
||||
project1.destroy()
|
||||
project2.destroy()
|
||||
|
||||
it "replicates the initial path and open buffers of the project", ->
|
||||
expect(project2.getPath()).not.toBe project1.getPath()
|
||||
expect(project2.getBuffers().length).toBe 1
|
||||
expect(project2.getBuffers()[0].getRelativePath()).toBe project1.getBuffers()[0].getRelativePath()
|
||||
expect(project2.getBuffers()[0].getPath()).not.toBe project1.getBuffers()[0].getPath()
|
||||
|
||||
it "replicates insertion and removal of open buffers", ->
|
||||
project2.bufferForPath('file-2.txt')
|
||||
expect(project1.getBuffers().length).toBe 2
|
||||
expect(project2.getBuffers()[0].getRelativePath()).toBe project1.getBuffers()[0].getRelativePath()
|
||||
expect(project2.getBuffers()[1].getRelativePath()).toBe project1.getBuffers()[1].getRelativePath()
|
||||
expect(project2.getBuffers()[0].getRelativePath()).not.toBe project1.getBuffers()[0].getPath()
|
||||
expect(project2.getBuffers()[1].getRelativePath()).not.toBe project1.getBuffers()[1].getPath()
|
||||
|
||||
project1.removeBuffer(project1.bufferForPath('file-2.txt'))
|
||||
expect(project1.getBuffers().length).toBe 1
|
||||
expect(project2.getBuffers()[0].getRelativePath()).toBe project1.getBuffers()[0].getRelativePath()
|
||||
@@ -8,6 +8,19 @@ describe "Project", ->
|
||||
beforeEach ->
|
||||
project.setPath(project.resolve('dir'))
|
||||
|
||||
describe "serialization", ->
|
||||
deserializedProject = null
|
||||
|
||||
afterEach ->
|
||||
deserializedProject?.destroy()
|
||||
|
||||
it "destroys unretained buffers and does not include them in the serialized state", ->
|
||||
project.bufferForPath('a')
|
||||
expect(project.getBuffers().length).toBe 1
|
||||
deserializedProject = deserialize(project.serialize())
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
expect(project.getBuffers().length).toBe 0
|
||||
|
||||
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)", ->
|
||||
editSession = project.open("a")
|
||||
@@ -31,6 +44,22 @@ describe "Project", ->
|
||||
expect(project.getPath()).toBe '/tmp'
|
||||
fsUtils.remove('/tmp/atom-test-save-sets-project-path')
|
||||
|
||||
describe "when an edit session is deserialized", ->
|
||||
it "emits an 'edit-session-created' event and stores the edit session", ->
|
||||
handler = jasmine.createSpy('editSessionCreatedHandler')
|
||||
project.on 'edit-session-created', handler
|
||||
|
||||
editSession1 = project.open("a")
|
||||
expect(handler.callCount).toBe 1
|
||||
expect(project.getEditSessions().length).toBe 1
|
||||
expect(project.getEditSessions()[0]).toBe editSession1
|
||||
|
||||
editSession2 = deserialize(editSession1.serialize())
|
||||
expect(handler.callCount).toBe 2
|
||||
expect(project.getEditSessions().length).toBe 2
|
||||
expect(project.getEditSessions()[0]).toBe editSession1
|
||||
expect(project.getEditSessions()[1]).toBe editSession2
|
||||
|
||||
describe ".open(path)", ->
|
||||
[fooOpener, barOpener, absolutePath, newBufferHandler, newEditSessionHandler] = []
|
||||
beforeEach ->
|
||||
@@ -82,7 +111,8 @@ describe "Project", ->
|
||||
|
||||
describe "when passed a path that matches a custom opener", ->
|
||||
it "returns the resource returned by the custom opener", ->
|
||||
expect(project.open("a.foo", hey: "there")).toEqual { foo: "a.foo", options: {hey: "there"} }
|
||||
pathToOpen = project.resolve('a.foo')
|
||||
expect(project.open(pathToOpen, hey: "there")).toEqual { foo: pathToOpen, options: {hey: "there"} }
|
||||
expect(project.open("bar://baz")).toEqual { bar: "bar://baz" }
|
||||
|
||||
describe ".bufferForPath(path)", ->
|
||||
@@ -22,6 +22,15 @@ describe "RootView", ->
|
||||
describe "@deserialize()", ->
|
||||
viewState = null
|
||||
|
||||
refreshRootViewAndProject = ->
|
||||
rootViewState = rootView.serialize()
|
||||
projectState = project.serialize()
|
||||
rootView.remove()
|
||||
project.destroy()
|
||||
window.project = deserialize(projectState)
|
||||
window.rootView = deserialize(rootViewState)
|
||||
rootView.attachToDom()
|
||||
|
||||
describe "when the serialized RootView has an unsaved buffer", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
rootView.attachToDom()
|
||||
@@ -29,14 +38,12 @@ describe "RootView", ->
|
||||
editor1 = rootView.getActiveView()
|
||||
buffer = editor1.getBuffer()
|
||||
editor1.splitRight()
|
||||
viewState = rootView.serialize()
|
||||
rootView.remove()
|
||||
expect(rootView.getActiveView()).toBe rootView.getEditors()[1]
|
||||
|
||||
window.rootView = deserialize(viewState)
|
||||
rootView.attachToDom()
|
||||
refreshRootViewAndProject()
|
||||
|
||||
expect(rootView.getEditors().length).toBe 2
|
||||
expect(rootView.getActiveView().getText()).toBe buffer.getText()
|
||||
expect(rootView.getActiveView()).toBe rootView.getEditors()[1]
|
||||
expect(rootView.title).toBe "untitled - #{project.getPath()}"
|
||||
|
||||
describe "when there are open editors", ->
|
||||
@@ -53,10 +60,7 @@ describe "RootView", ->
|
||||
pane4.activeItem.setCursorScreenPosition([0, 2])
|
||||
pane2.focus()
|
||||
|
||||
viewState = rootView.serialize()
|
||||
rootView.remove()
|
||||
window.rootView = deserialize(viewState)
|
||||
rootView.attachToDom()
|
||||
refreshRootViewAndProject()
|
||||
|
||||
expect(rootView.getEditors().length).toBe 4
|
||||
editor1 = rootView.panes.find('.row > .pane .editor:eq(0)').view()
|
||||
@@ -89,12 +93,7 @@ describe "RootView", ->
|
||||
it "constructs the view with no open editors", ->
|
||||
rootView.getActivePane().remove()
|
||||
expect(rootView.getEditors().length).toBe 0
|
||||
|
||||
viewState = rootView.serialize()
|
||||
rootView.remove()
|
||||
window.rootView = deserialize(viewState)
|
||||
|
||||
rootView.attachToDom()
|
||||
refreshRootViewAndProject()
|
||||
expect(rootView.getEditors().length).toBe 0
|
||||
|
||||
describe "focus", ->
|
||||
@@ -366,11 +365,3 @@ describe "RootView", ->
|
||||
rootView.open(require.resolve('fixtures/sample.txt'))
|
||||
expect(count).toBe 1
|
||||
expect(callbackBuffer).toBe rootView.getActiveView().getBuffer()
|
||||
|
||||
describe "when a 'new-editor' event is triggered", ->
|
||||
it "opens a new untitled editor", ->
|
||||
itemCount = rootView.getActivePane().getItems().length
|
||||
rootView.trigger 'new-editor'
|
||||
expect(rootView.getActivePaneItem().getPath()).toBeUndefined()
|
||||
expect(rootView.getActivePaneItem().getBuffer().fileExists()).toBeFalsy()
|
||||
expect(rootView.getActivePane().getItems().length).toBe itemCount + 1
|
||||
@@ -172,6 +172,13 @@ describe "RowMap", ->
|
||||
expect(map.bufferRowRangeForScreenRow(7)).toEqual [21, 22]
|
||||
expect(map.bufferRowRangeForScreenRow(8)).toEqual [22, 27]
|
||||
|
||||
it "replaces regions that cover 0 buffer rows at the start or end of the buffer row range", ->
|
||||
map.mapBufferRowRange(0, 0, 1)
|
||||
map.mapBufferRowRange(0, 1, 1)
|
||||
map.mapBufferRowRange(1, 1, 1)
|
||||
map.mapBufferRowRange(0, 1, 3)
|
||||
expect(map.screenRowRangeForBufferRow(0)).toEqual [0, 3]
|
||||
|
||||
describe "when the row range straddles existing regions", ->
|
||||
it "splits the straddled regions and places the new region between them", ->
|
||||
# filler region 0
|
||||
@@ -1,12 +1,10 @@
|
||||
Buffer = require 'text-buffer'
|
||||
EditSession = require 'edit-session'
|
||||
Range = require 'range'
|
||||
|
||||
describe "Selection", ->
|
||||
[buffer, editSession, selection] = []
|
||||
|
||||
beforeEach ->
|
||||
buffer = new Buffer(require.resolve('fixtures/sample.js'))
|
||||
buffer = project.buildBuffer('sample.js')
|
||||
editSession = new EditSession(buffer: buffer, tabLength: 2)
|
||||
selection = editSession.getSelection()
|
||||
|
||||
@@ -16,18 +14,18 @@ describe "Selection", ->
|
||||
describe ".deleteSelectedText()", ->
|
||||
describe "when nothing is selected", ->
|
||||
it "deletes nothing", ->
|
||||
selection.setBufferRange new Range([0,3], [0,3])
|
||||
selection.setBufferRange [[0,3], [0,3]]
|
||||
selection.deleteSelectedText()
|
||||
expect(buffer.lineForRow(0)).toBe "var quicksort = function () {"
|
||||
|
||||
describe "when one line is selected", ->
|
||||
it "deletes selected text and clears the selection", ->
|
||||
selection.setBufferRange new Range([0,4], [0,14])
|
||||
selection.setBufferRange [[0,4], [0,14]]
|
||||
selection.deleteSelectedText()
|
||||
expect(buffer.lineForRow(0)).toBe "var = function () {"
|
||||
|
||||
endOfLine = buffer.lineForRow(0).length
|
||||
selection.setBufferRange new Range([0,0], [0, endOfLine])
|
||||
selection.setBufferRange [[0,0], [0, endOfLine]]
|
||||
selection.deleteSelectedText()
|
||||
expect(buffer.lineForRow(0)).toBe ""
|
||||
|
||||
@@ -35,7 +33,7 @@ describe "Selection", ->
|
||||
|
||||
describe "when multiple lines are selected", ->
|
||||
it "deletes selected text and clears the selection", ->
|
||||
selection.setBufferRange new Range([0,1], [2,39])
|
||||
selection.setBufferRange [[0,1], [2,39]]
|
||||
selection.deleteSelectedText()
|
||||
expect(buffer.lineForRow(0)).toBe "v;"
|
||||
expect(selection.isEmpty()).toBeTruthy()
|
||||
@@ -60,7 +58,7 @@ describe "Selection", ->
|
||||
|
||||
describe "when only the selection's tail is moved (regression)", ->
|
||||
it "emits the 'screen-range-changed' event", ->
|
||||
selection.setBufferRange([[2, 0], [2, 10]], reverse: true)
|
||||
selection.setBufferRange([[2, 0], [2, 10]], isReversed: true)
|
||||
changeScreenRangeHandler = jasmine.createSpy('changeScreenRangeHandler')
|
||||
selection.on 'screen-range-changed', changeScreenRangeHandler
|
||||
|
||||
@@ -7,7 +7,7 @@ $ = jQuery = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
Keymap = require 'keymap'
|
||||
Config = require 'config'
|
||||
Point = require 'point'
|
||||
{Point} = require 'telepath'
|
||||
Project = require 'project'
|
||||
Directory = require 'directory'
|
||||
File = require 'file'
|
||||
@@ -16,7 +16,6 @@ TokenizedBuffer = require 'tokenized-buffer'
|
||||
fsUtils = require 'fs-utils'
|
||||
pathwatcher = require 'pathwatcher'
|
||||
RootView = require 'root-view'
|
||||
Git = require 'git'
|
||||
clipboard = require 'clipboard'
|
||||
requireStylesheet "jasmine"
|
||||
fixturePackagesPath = fsUtils.resolveOnLoadPath('fixtures/packages')
|
||||
@@ -36,13 +35,8 @@ jasmine.getEnv().defaultTimeoutInterval = 5000
|
||||
beforeEach ->
|
||||
jQuery.fx.off = true
|
||||
window.project = new Project(fsUtils.resolveOnLoadPath('fixtures'))
|
||||
window.git = Git.open(project.getPath())
|
||||
window.project.on 'path-changed', ->
|
||||
window.git?.destroy()
|
||||
window.git = Git.open(window.project.getPath())
|
||||
|
||||
window.resetTimeouts()
|
||||
atom.windowMode = 'editor'
|
||||
atom.packageStates = {}
|
||||
spyOn(atom, 'saveWindowState')
|
||||
syntax.clearGrammarOverrides()
|
||||
@@ -89,9 +83,6 @@ afterEach ->
|
||||
if project?
|
||||
project.destroy()
|
||||
window.project = null
|
||||
if git?
|
||||
git.destroy()
|
||||
window.git = null
|
||||
$('#jasmine-content').empty() unless window.debugContent
|
||||
delete atom.windowState
|
||||
jasmine.unspy(atom, 'saveWindowState')
|
||||
|
||||
@@ -1,16 +1,47 @@
|
||||
require 'window'
|
||||
|
||||
measure 'spec suite require time', ->
|
||||
fs = require 'fs'
|
||||
fsUtils = require 'fs-utils'
|
||||
path = require 'path'
|
||||
_ = require 'underscore'
|
||||
require 'spec-helper'
|
||||
|
||||
# Run core specs
|
||||
for specPath in fsUtils.listTreeSync(fsUtils.resolveOnLoadPath("spec")) when /-spec\.coffee$/.test specPath
|
||||
require specPath
|
||||
requireSpecs = (directoryPath, specType) ->
|
||||
for specPath in fsUtils.listTreeSync(path.join(directoryPath, 'spec')) when /-spec\.coffee$/.test specPath
|
||||
require specPath
|
||||
|
||||
# Run extension specs
|
||||
for packageDirPath in config.packageDirPaths
|
||||
for packagePath in fsUtils.listSync(packageDirPath)
|
||||
for specPath in fsUtils.listTreeSync(path.join(packagePath, "spec")) when /-spec\.coffee$/.test specPath
|
||||
require specPath
|
||||
setSpecType = (specType) ->
|
||||
for spec in jasmine.getEnv().currentRunner().specs() when not spec.specType?
|
||||
spec.specType = specType
|
||||
|
||||
runAllSpecs = ->
|
||||
requireSpecs(window.resourcePath)
|
||||
setSpecType('core')
|
||||
|
||||
fixturesPackagesPath = fsUtils.resolveOnLoadPath('fixtures/packages')
|
||||
packagePaths = atom.getAvailablePackageNames().map (packageName) -> atom.resolvePackagePath(packageName)
|
||||
packagePaths = _.groupBy packagePaths, (packagePath) ->
|
||||
if packagePath.indexOf("#{fixturesPackagesPath}#{path.sep}") is 0
|
||||
'fixtures'
|
||||
else if packagePath.indexOf("#{window.resourcePath}#{path.sep}") is 0
|
||||
'bundled'
|
||||
else
|
||||
'user'
|
||||
|
||||
# Run bundled package specs
|
||||
requireSpecs(packagePath) for packagePath in packagePaths.bundled ? []
|
||||
setSpecType('bundled')
|
||||
|
||||
# Run user package specs
|
||||
requireSpecs(packagePath) for packagePath in packagePaths.user ? []
|
||||
setSpecType('user')
|
||||
|
||||
runSpecs = (specPath) ->
|
||||
requireSpecs(specPath)
|
||||
setSpecType("user")
|
||||
|
||||
if specPath = atom.getLoadSettings().specPath
|
||||
runSpecs(specPath)
|
||||
else
|
||||
runAllSpecs()
|
||||
|
||||
@@ -75,6 +75,12 @@ describe "the `syntax` global", ->
|
||||
|
||||
expect(syntax.selectGrammar('more.test', '')).toBe grammar1
|
||||
|
||||
describe "when there is no file path", ->
|
||||
it "does not throw an exception (regression)", ->
|
||||
expect(-> syntax.selectGrammar(null, '#!/usr/bin/ruby')).not.toThrow()
|
||||
expect(-> syntax.selectGrammar(null, '')).not.toThrow()
|
||||
expect(-> syntax.selectGrammar(null, null)).not.toThrow()
|
||||
|
||||
describe ".removeGrammar(grammar)", ->
|
||||
it "removes the grammar, so it won't be returned by selectGrammar", ->
|
||||
grammar = syntax.selectGrammar('foo.js')
|
||||
@@ -0,0 +1,41 @@
|
||||
{Site} = require 'telepath'
|
||||
|
||||
describe "TextBuffer replication", ->
|
||||
[buffer1, buffer2] = []
|
||||
|
||||
beforeEach ->
|
||||
buffer1 = project.buildBuffer('sample.js')
|
||||
buffer1.insert([0, 0], 'changed\n')
|
||||
doc1 = buffer1.getState()
|
||||
doc2 = doc1.clone(new Site(2))
|
||||
doc1.connect(doc2)
|
||||
buffer2 = deserialize(doc2, {project})
|
||||
|
||||
afterEach ->
|
||||
buffer1.destroy()
|
||||
buffer2.destroy()
|
||||
|
||||
it "replicates the initial path and text", ->
|
||||
expect(buffer2.getPath()).toBe buffer1.getPath()
|
||||
expect(buffer2.getText()).toBe buffer1.getText()
|
||||
|
||||
it "replicates changes to the text and emits 'change' events on all replicas", ->
|
||||
buffer1.on 'changed', handler1 = jasmine.createSpy("buffer1 change handler")
|
||||
buffer2.on 'changed', handler2 = jasmine.createSpy("buffer2 change handler")
|
||||
|
||||
buffer1.change([[1, 4], [1, 6]], 'h')
|
||||
expect(buffer1.lineForRow(1)).toBe 'var hicksort = function () {'
|
||||
expect(buffer2.lineForRow(1)).toBe 'var hicksort = function () {'
|
||||
|
||||
expect(buffer1.isModified()).toBeTruthy()
|
||||
expect(buffer2.isModified()).toBeTruthy()
|
||||
|
||||
expectedEvent =
|
||||
oldRange: [[1, 4], [1, 6]]
|
||||
oldText: "qu"
|
||||
newRange: [[1, 4], [1, 5]]
|
||||
newText: "h"
|
||||
expect(handler1).toHaveBeenCalledWith(expectedEvent)
|
||||
expect(handler2).toHaveBeenCalledWith(expectedEvent)
|
||||
expect(handler1.callCount).toBe 1
|
||||
expect(handler2.callCount).toBe 1
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário