Comparar commits
852 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 79c6badce8 | |||
| a48ef934eb | |||
| 85d5968318 | |||
| 9b4dd602be | |||
| 62d84e5d4b | |||
| 61ed5da1dc | |||
| 32e25d4bb2 | |||
| aae9614839 | |||
| 7fa4121227 | |||
| 1d783826a2 | |||
| bdb6e193de | |||
| 3f75512de2 | |||
| 5910b05344 | |||
| 7f039b3383 | |||
| ca35ced587 | |||
| 734ef19f48 | |||
| ef9ce1bf70 | |||
| c754b73b71 | |||
| 21565332a4 | |||
| 8871d9cd2d | |||
| 64f0fcc839 | |||
| 581d12b04f | |||
| 05a9ace3e6 | |||
| 03eac362f6 | |||
| 8a05b0f51d | |||
| 4069d23d86 | |||
| 78b48345ac | |||
| 139e2f6de0 | |||
| ed86b4a478 | |||
| f2352131cc | |||
| 56df435fb8 | |||
| 595ff19b5b | |||
| 33eb0bae8f | |||
| fde1560377 | |||
| a640d07599 | |||
| 45b65cd3aa | |||
| f217278001 | |||
| 3320176a0a | |||
| 754b5a6004 | |||
| 79b84b2433 | |||
| 17be036ff8 | |||
| 23deec833f | |||
| 6a8d0bef4a | |||
| d5ce1a0312 | |||
| 74decbd18a | |||
| 085806f97f | |||
| ca8be83903 | |||
| da713da311 | |||
| 7a6c75a83e | |||
| 3e7a517c25 | |||
| e623ef4232 | |||
| 18399aa264 | |||
| 2a5f393712 | |||
| 27f779ec03 | |||
| 5d717eb7bd | |||
| 0b457fd80a | |||
| dd9aa2d02f | |||
| 87b530140b | |||
| 2f46fee1ca | |||
| fbb48e7807 | |||
| 01c141eec6 | |||
| 7d9d0c715c | |||
| a9887b5007 | |||
| c4dec72dcd | |||
| d093d5cc06 | |||
| 27c9e54538 | |||
| 3bbe6ee98c | |||
| 524d8e8e21 | |||
| c31211dc21 | |||
| 6c1b63d352 | |||
| 6fa7da79eb | |||
| c2371f3054 | |||
| 2349627e3c | |||
| 90d92a4c92 | |||
| 66171e0301 | |||
| cdc7f70b22 | |||
| e225dbe93d | |||
| 7dc18765ad | |||
| c1d379fd6c | |||
| a754ac4da0 | |||
| 21560df2f0 | |||
| f0aa408b70 | |||
| 161c9a62b5 | |||
| 2c5bbcbc22 | |||
| eee72f7664 | |||
| 073d398e6f | |||
| 2310e263a7 | |||
| de7b212d99 | |||
| 75873ef6b3 | |||
| 73855a49fc | |||
| cae055fd3f | |||
| 2aad31c4dc | |||
| 93052ad611 | |||
| 67733b8b05 | |||
| 21543569ef | |||
| 7371ebbf20 | |||
| 73470cc294 | |||
| 1e60b5fa3b | |||
| 172ecbd897 | |||
| 289cc24b56 | |||
| a20483ccdb | |||
| b2ceaf3b8b | |||
| 1c3c508985 | |||
| 645f4ad907 | |||
| 05dd6f8f17 | |||
| b19390b519 | |||
| a3f339e0c3 | |||
| 42040e14b3 | |||
| 91640f0886 | |||
| 0377d64788 | |||
| cd37caae96 | |||
| aab63c26e7 | |||
| 6da6101a52 | |||
| 87b33648dc | |||
| d0b380e535 | |||
| 99480901e2 | |||
| 6852720408 | |||
| f9498732a5 | |||
| e4aa82fda1 | |||
| bc1a743b2f | |||
| 3fe22aa5c8 | |||
| fe035d4d7c | |||
| e8bfb7ca09 | |||
| 4bfd48b983 | |||
| 5a1fadf7ce | |||
| 819ac9ea68 | |||
| 6384841134 | |||
| a0fbec29c3 | |||
| 238fca2004 | |||
| 014beda455 | |||
| 777df644ce | |||
| f3ea3a3395 | |||
| b82fdace61 | |||
| 7195102a04 | |||
| 5ec6a4a189 | |||
| e330b1a2e8 | |||
| 5334433bc2 | |||
| 366a12903a | |||
| 063cb04fb5 | |||
| d9c2f07fbe | |||
| 5c1fa8e53d | |||
| a1f3540cb4 | |||
| 2eeb399cf1 | |||
| 71e8e865f2 | |||
| 9f080be6e1 | |||
| 56f66f8578 | |||
| 0b1dc704ea | |||
| 578d823118 | |||
| 0196f2a2eb | |||
| 1e68a7266f | |||
| 093143f7a9 | |||
| 4eeef9cfbd | |||
| ce3ec75c55 | |||
| bb09de9703 | |||
| e64ba18fe3 | |||
| 3908f81fc6 | |||
| fac46a295c | |||
| b01470a738 | |||
| f3be613662 | |||
| ede29d99c1 | |||
| d2369e94c8 | |||
| dbe3399016 | |||
| 3952423d99 | |||
| ffdcecc0f2 | |||
| 0069eb4d0d | |||
| 5fa55026d5 | |||
| 6d04d57e74 | |||
| 3f0dca5a40 | |||
| 8b14a66e2c | |||
| 1607411df1 | |||
| dade9f6309 | |||
| 568aa1d396 | |||
| 9febe179fa | |||
| c9e68ab044 | |||
| 285186567a | |||
| 1c136f16e3 | |||
| 534a2d4565 | |||
| 9e814de969 | |||
| 7202ba274a | |||
| 46e85fac87 | |||
| d1f1b494cf | |||
| f11803df60 | |||
| f094a86ae7 | |||
| a6c791ce39 | |||
| dfa870f514 | |||
| a98377b899 | |||
| c3de3d8eea | |||
| 2d15f5e49a | |||
| 56386cb06a | |||
| 986a7ad5c3 | |||
| 9a01b5a6bf | |||
| ccafda6f7f | |||
| 33538a5ed7 | |||
| aae85cd7c1 | |||
| ce098e587f | |||
| 233d819e04 | |||
| fbbf3d177a | |||
| c33bd34996 | |||
| 34cdb23d71 | |||
| fcedcd117d | |||
| 3d7de21d6c | |||
| 2278ee742a | |||
| 55e90f8ae1 | |||
| 2ca738453b | |||
| 31a9bb83cf | |||
| 1e4504e7f3 | |||
| bf05ddb958 | |||
| 756c2be64a | |||
| 6fdd4f775b | |||
| 625fcaffc8 | |||
| 955d379e0e | |||
| 0d71f20073 | |||
| 42c40e8c7a | |||
| 89212e599f | |||
| 95e4ac903c | |||
| 311155ac0d | |||
| aaa82e23da | |||
| 8e46bc5241 | |||
| 37a5a6f501 | |||
| 83f14c137c | |||
| b0b458b1f6 | |||
| f4de124aa6 | |||
| 4b0eaf05a5 | |||
| 03e8bc6f19 | |||
| 13186fcf7a | |||
| fea0f1f90a | |||
| b449bb4444 | |||
| fe6b40fc5d | |||
| 82e3935ae3 | |||
| 32a3b6302c | |||
| 6157fcaf73 | |||
| ab74d8be38 | |||
| 886a2aa867 | |||
| aeaa76a9e1 | |||
| 6a7bcb6f52 | |||
| ee1ec4670d | |||
| 7e0af4c575 | |||
| f55a200591 | |||
| 89d8eac091 | |||
| 5011b6e78f | |||
| d6e67c5b32 | |||
| de547e20c2 | |||
| 2b79b19330 | |||
| 2b234545b5 | |||
| a66543048b | |||
| ca96aa2804 | |||
| 316571308a | |||
| b323d9ce18 | |||
| 0e1c757cd0 | |||
| 084bbb1578 | |||
| 7c348ee478 | |||
| a0c6a94409 | |||
| 80cdf61fa4 | |||
| 29c3fadb6f | |||
| 1ae3806c69 | |||
| 98b509441c | |||
| 02f40688e2 | |||
| cb8e378af6 | |||
| babc4732b8 | |||
| dcccde8f3f | |||
| 8ed4923e58 | |||
| 529c829438 | |||
| 809a02ca10 | |||
| b9902cb6f2 | |||
| de4d3dbbe9 | |||
| 3fe88c4df1 | |||
| 20811a9f52 | |||
| b137f1a3e3 | |||
| 0b12f01206 | |||
| 1d7b4c5f9a | |||
| e2d4b58d5f | |||
| 1c8df2c0b5 | |||
| 9067c65a41 | |||
| 1d4f2fba8b | |||
| 12dd412439 | |||
| 19d680544b | |||
| a45dd3fe37 | |||
| f6c8a435ae | |||
| 93bfe0edf6 | |||
| d3fed57cb3 | |||
| 8372adb38a | |||
| e4b3d3a83c | |||
| 11f1ef9d8b | |||
| 7d87ae00ff | |||
| 3ec2378242 | |||
| 28943a35da | |||
| 7b43c8a860 | |||
| 8b17b7eca9 | |||
| 6432cda691 | |||
| cf0bdb9c94 | |||
| 34f1472653 | |||
| a475e27cd4 | |||
| abc1f23516 | |||
| dc7e7f9ed0 | |||
| 5fdec4dc7b | |||
| 618d281d6c | |||
| 055ec8cb9c | |||
| df3fe90c89 | |||
| 21e0e95a7a | |||
| 6caed6e918 | |||
| 7dd84636ba | |||
| b12954760d | |||
| 4bb21fd9ec | |||
| 11787e5a5d | |||
| 276e63611a | |||
| 957374eb40 | |||
| fe9f1373c1 | |||
| 6290c19264 | |||
| 913bb82d6e | |||
| ffc936ca4d | |||
| 1808e5f991 | |||
| 62feefd28d | |||
| ada992be4d | |||
| f8933cfeab | |||
| 0878d7ab6a | |||
| bd8e19bce7 | |||
| 4852ba6d95 | |||
| caffcafe2e | |||
| c7a1205ca6 | |||
| 2c4f94c319 | |||
| 941fc97e79 | |||
| 8788b2a51c | |||
| ca8ae9ad61 | |||
| 2fb00af255 | |||
| f6ce0f038f | |||
| 94a8d16664 | |||
| fc0a46d6b2 | |||
| fd443a8b68 | |||
| c43f277c5b | |||
| dd0938dca6 | |||
| e90f19da97 | |||
| c3aea1d149 | |||
| 569c3116a8 | |||
| 8bdc1d2418 | |||
| fa1600c53d | |||
| 6a1e83205f | |||
| 33891b51f2 | |||
| 1ef821f4e7 | |||
| ed030a54c3 | |||
| 6bb3a69410 | |||
| 53f5e9fbc6 | |||
| 1dac1f375c | |||
| cfab5c619d | |||
| b6afc415f8 | |||
| 5454e93168 | |||
| 389b2bd8d6 | |||
| 8ea011597c | |||
| 21060ae85a | |||
| fe0cc7d273 | |||
| f67e9b6e03 | |||
| 64f2cdb795 | |||
| a996597d49 | |||
| 52680bd63f | |||
| b8f0f1c683 | |||
| 0840de95dc | |||
| fe1b2c6d9d | |||
| 7b307a17ea | |||
| 097571a83d | |||
| 0673ce8e71 | |||
| 967b022fa6 | |||
| e330b8940a | |||
| 526e5311b9 | |||
| 3720f0fb8f | |||
| 46b108f1cc | |||
| aa59002922 | |||
| 6af125bd33 | |||
| d5d2ae63fe | |||
| 08f774e57a | |||
| 513a964732 | |||
| 8f9f5ed0ed | |||
| 001fe5931e | |||
| c7541f89da | |||
| cb0f3ce2ec | |||
| d71e58ec33 | |||
| 5aacccb03b | |||
| 8f63e40ba0 | |||
| 6c09a42545 | |||
| 1cb1387abd | |||
| fe426000d0 | |||
| 3617a61ea2 | |||
| c7b5753814 | |||
| 4ce23e4b51 | |||
| c8aeb8ec2d | |||
| 0d75bcae48 | |||
| 81c56ca4f1 | |||
| fb02917adf | |||
| 8c8f1bc048 | |||
| 94bc4ce737 | |||
| 9914085ead | |||
| b801c3ce56 | |||
| e31dbfd7d6 | |||
| d4b5303983 | |||
| ad0d0473b3 | |||
| 0db23c3e96 | |||
| 0b78450917 | |||
| 4b15b98bdd | |||
| 572157124e | |||
| d0e1d32ae9 | |||
| 33cb42a3f7 | |||
| 055109a708 | |||
| 3539288a1e | |||
| 88d80918a7 | |||
| 0897007662 | |||
| 1c4e38c867 | |||
| 78617e31bc | |||
| a377a49004 | |||
| 5756ec45ba | |||
| 23c8db09b7 | |||
| 9f8a8139e0 | |||
| d793d114d4 | |||
| 9b3edc89a6 | |||
| 58b2a4f98e | |||
| f356190b42 | |||
| 08a81b61a3 | |||
| bff47e10f4 | |||
| 6dbe86b063 | |||
| 21edc61384 | |||
| ef0022f4da | |||
| cbee5efee6 | |||
| 55da08c69e | |||
| df7c3d066a | |||
| ba627c005e | |||
| b3efe00757 | |||
| 5f8e757f57 | |||
| 5f6f09fc45 | |||
| f9e7d64131 | |||
| eefeb2ab2a | |||
| ee896846bb | |||
| ddd560b785 | |||
| 59d8beb935 | |||
| c4de03d5c4 | |||
| 53f7592815 | |||
| 86a1f43196 | |||
| 177f3b9d53 | |||
| affcb5ff4a | |||
| a168692eea | |||
| 80db09f47b | |||
| 1ef1f60556 | |||
| 7934043646 | |||
| ae6b4547e1 | |||
| aaa654de39 | |||
| 1d3f33e740 | |||
| 19e6c632ea | |||
| 0d014c6257 | |||
| 445421f961 | |||
| 778ed06272 | |||
| e16481d6e0 | |||
| f34af85ad3 | |||
| 9118a70592 | |||
| 43419fdef2 | |||
| 11f3686832 | |||
| 455321c4a8 | |||
| dfa5ac11a2 | |||
| 8754a0cbaf | |||
| 6485e4f540 | |||
| 4ca66a535c | |||
| 56c88820d6 | |||
| 5d56365c26 | |||
| 55c5ae926c | |||
| ef68fd24d5 | |||
| 4497ec9733 | |||
| 38a3c0dbd3 | |||
| b69bc8ce71 | |||
| fe6cf8ec01 | |||
| b4f8387053 | |||
| 6b63f5db9e | |||
| 291f4fbb90 | |||
| dba7687470 | |||
| e6a7678fd6 | |||
| 4b6867831a | |||
| 28d7160c3b | |||
| c4ab4364ba | |||
| 98db48eae6 | |||
| d6cb848503 | |||
| 3119ed9dfe | |||
| 3f0f3267c1 | |||
| 2cc32e1faf | |||
| a8fe8ab982 | |||
| 4bee197e01 | |||
| 01f41207d4 | |||
| ce2e123cc8 | |||
| 16b64b03f7 | |||
| 2adcc9709c | |||
| 0f1d701646 | |||
| 6e049dd173 | |||
| face4a3f76 | |||
| 015f4f7542 | |||
| 9f5e913804 | |||
| 383a724144 | |||
| 90c21906a4 | |||
| 0686efc429 | |||
| 2058d5a431 | |||
| 6c9f037cd9 | |||
| 5033fac8eb | |||
| 0aa0dc01d5 | |||
| 994756f8c1 | |||
| 47bab20363 | |||
| efb87237e2 | |||
| 2be5790311 | |||
| 60fa937ed7 | |||
| 867a6ecd95 | |||
| 36c9d68009 | |||
| 4dda2f93ae | |||
| fcdc341981 | |||
| 5b187e7a8c | |||
| 740085e561 | |||
| 5a2730c8c0 | |||
| 08ee9c08b7 | |||
| 103f602c9c | |||
| fb6b8029be | |||
| edc789173b | |||
| 7ba4782ceb | |||
| 9863386644 | |||
| 1fe6a2974d | |||
| 6bd7dd8841 | |||
| 3db786953c | |||
| b9b9b7df91 | |||
| da2d37408f | |||
| 8739dfe739 | |||
| cd2e78f04f | |||
| 8851120db2 | |||
| c59404b2b0 | |||
| 151a4290d4 | |||
| 1ef5809b24 | |||
| 8cca42a6b8 | |||
| aad23d4760 | |||
| d20bff473b | |||
| 287f1984db | |||
| 1cb5d16a60 | |||
| 105f8d0baf | |||
| 7e9a1d8ffe | |||
| ee280d87f9 | |||
| e312050163 | |||
| 717626fbaf | |||
| ec761d00ef | |||
| 3d99e580b9 | |||
| b0da17701b | |||
| 8443db6630 | |||
| 2333b53d05 | |||
| f05eb67125 | |||
| 4c5fdbce93 | |||
| 9482e9b397 | |||
| 0eb4704f5e | |||
| 13c374845f | |||
| 0c87f0edf8 | |||
| c9db0f14ed | |||
| 875e9c886c | |||
| 05867bc889 | |||
| 621058aa9e | |||
| 3b52a6a040 | |||
| e70f87dfc7 | |||
| 0695aafe80 | |||
| c9eebcf00a | |||
| f26110a81b | |||
| 651b40f360 | |||
| 456c27475b | |||
| bc0a268841 | |||
| 9d9ff97995 | |||
| a03a89d359 | |||
| 7e043f5bc6 | |||
| e8f8cd2dcd | |||
| 132c6f7414 | |||
| a458f7581d | |||
| 64565cc510 | |||
| 7de8b4c2f4 | |||
| 01e40bd866 | |||
| 6e47135ba7 | |||
| b041c0176c | |||
| b22d64745f | |||
| cbf55f38f9 | |||
| 9e8b1fb661 | |||
| 6d4285f180 | |||
| 7d3c272909 | |||
| 6677b2ccdf | |||
| bf48195752 | |||
| b8680be6c6 | |||
| 0264fd6be5 | |||
| 9c79e39401 | |||
| e06100fe25 | |||
| f7686a5485 | |||
| 01610a3fb8 | |||
| d8766b9aba | |||
| ebb7a2637a | |||
| a143ca551b | |||
| 3cfefc03ff | |||
| 1646cdc349 | |||
| bc289cc0cb | |||
| ef97dadea1 | |||
| 83dcf79bcf | |||
| 60563f46c9 | |||
| 70925263f7 | |||
| 166faf1af2 | |||
| aab2937a48 | |||
| 1b9f86d13a | |||
| 88802ef4c5 | |||
| e8e3e3224f | |||
| 1b6c34673e | |||
| aa6f2187d8 | |||
| 7d64cd179e | |||
| 56ff3b79e4 | |||
| ba654f15cb | |||
| 62ccf92a5d | |||
| ca073fb7c2 | |||
| 94b5d08b63 | |||
| 678dc82d50 | |||
| 5b5f0af6b5 | |||
| 695c8d97f2 | |||
| 5ac3a74bf0 | |||
| d150170e5b | |||
| d14e5a6992 | |||
| b419cdf193 | |||
| a57f1e4727 | |||
| 34499d87ab | |||
| 4e18e7a67c | |||
| aef9a12910 | |||
| b501758bf7 | |||
| 993cc75c15 | |||
| 64e02d007c | |||
| a39255fa77 | |||
| ddfd1842d1 | |||
| 3e71dcfcd0 | |||
| 2629345df2 | |||
| 8c3fbc3437 | |||
| c30f63fa0c | |||
| 5685eb9263 | |||
| e3fd999964 | |||
| e8109adeee | |||
| b150191263 | |||
| 4dc909de03 | |||
| c029f8eb95 | |||
| 31e75884be | |||
| 82e5545f6e | |||
| febd845876 | |||
| 61021b6a13 | |||
| b9274412c5 | |||
| d6457152f0 | |||
| 49e48a0895 | |||
| eae7a7065e | |||
| 696355f6dd | |||
| ab0c60bd45 | |||
| 83005deae6 | |||
| d486a55b4c | |||
| e6b3f400c0 | |||
| 86cba12457 | |||
| 1d6e3c73cf | |||
| 8fb06a5b11 | |||
| 8afb3fb6b9 | |||
| ff9356bc2c | |||
| b523decc7e | |||
| e7a14bf17f | |||
| a593d3b152 | |||
| 9781565df8 | |||
| a595378850 | |||
| b2e2a2cdda | |||
| eb8d9214b7 | |||
| 8ac8b3748f | |||
| b4a28d7418 | |||
| c6ee5699e7 | |||
| 19220aa9f3 | |||
| 0a10603eb3 | |||
| 0700732495 | |||
| c3b270dc80 | |||
| 2baa5ba678 | |||
| 0bd701b4a6 | |||
| 1be8d5e618 | |||
| a212a7c259 | |||
| 76a041cfe1 | |||
| 46eb6df61e | |||
| 93c7531cf9 | |||
| 8bf726d3e1 | |||
| 412cb602a4 | |||
| f10abfe630 | |||
| 14664625d5 | |||
| 9e12c4610d | |||
| c1d09f5229 | |||
| d486522cde | |||
| e3ff39bf4f | |||
| 74a48e58dc | |||
| e63bf75f96 | |||
| 8b9595f1bd | |||
| 53954c236c | |||
| 3de18ae9f6 | |||
| 9c7ce22abe | |||
| bfab56247f | |||
| e4400c648d | |||
| 271f23083e | |||
| e3f4a72ab3 | |||
| ee3b7cee76 | |||
| 5a357a54dc | |||
| d07bf338c8 | |||
| 516ddec854 | |||
| 0ab2b89c1a | |||
| 2c2c7e67d7 | |||
| e1f8b042ed | |||
| f7ce6a015d | |||
| 4775783ba3 | |||
| d0bf4c1d2e | |||
| 4f1d862bc0 | |||
| 059671c454 | |||
| 1f16cd3912 | |||
| 2d16fc33a3 | |||
| 6ad8062d45 | |||
| 8f0c0362e8 | |||
| 91436bec25 | |||
| e93d83dcdf | |||
| 62f44e64f4 | |||
| c571d6a9d4 | |||
| bdd4ac63f4 | |||
| 0c77712a77 | |||
| a00db81765 | |||
| c91bca44cb | |||
| dbf4ba099b | |||
| e67324dcc9 | |||
| 1528d6ea49 | |||
| a49fd569e3 | |||
| 44213aff96 | |||
| 22589bf2dd | |||
| 28a2b48688 | |||
| 668ef0b3a1 | |||
| 9a4db9d95d | |||
| e457600f38 | |||
| 2f62123a75 | |||
| e7e28b568b | |||
| b70df78041 | |||
| c4b24fe738 | |||
| b1f5d1e276 | |||
| cd9d6ca6ee | |||
| d6159a38d4 | |||
| a828f389f4 | |||
| 91c8371752 | |||
| 05c70aceec | |||
| 002fc570e7 | |||
| f81c64f068 | |||
| e9bcb4637e | |||
| e21f1c1394 | |||
| eae5a25872 | |||
| 6364589630 | |||
| 41f6d6b0dd | |||
| c581135029 | |||
| af92c96a73 | |||
| cdd4c9669a | |||
| 1368b60f16 | |||
| cc42db3bff | |||
| cc3a2ab2d5 | |||
| f9837aadaa | |||
| 20c7f298a9 | |||
| 86506752a7 | |||
| 83206ac3a7 | |||
| dc54ebf416 | |||
| d5b960f32b | |||
| ffaaa21b37 | |||
| ef2a322229 | |||
| 1931cb6dff | |||
| 6040221d26 | |||
| 7c70c43c1c | |||
| cde0fae1f3 | |||
| 95107052d0 | |||
| 650915724b | |||
| ad67abc2a2 | |||
| 50d2d5b1cd | |||
| 010905db0b | |||
| 232bc8be8e | |||
| 57577966e7 | |||
| 08d5231d28 | |||
| 05c3d89381 | |||
| 7a3dc2c3b8 | |||
| 8eecc56d08 | |||
| e009ff6c59 | |||
| f500e2e9fc | |||
| 2a98ccec8d | |||
| 8742f6c06c | |||
| 9605da9d56 | |||
| 1cdb87fdce | |||
| 330bb1df4b | |||
| 45ceb3f9f9 | |||
| 3fa7c8fb48 | |||
| 97c183d6df | |||
| 42362e6ce9 | |||
| 2266ea25e8 | |||
| 970d7654cb | |||
| 82cbfb3829 | |||
| c2c6b5f868 | |||
| 0dd0c39918 | |||
| 7002b4e5f6 | |||
| 78f1865bcf | |||
| bb281ce5af | |||
| b02d906cfb | |||
| b5b07c1052 | |||
| c77b44d5ef | |||
| eba1e474f8 | |||
| 6fa580f543 | |||
| feed72b68e | |||
| 5cee47c207 | |||
| 1d1a14b8e9 | |||
| 18421465c0 | |||
| ea9930db70 | |||
| 2d40cb09d1 | |||
| 8956881192 | |||
| 32bc8a6258 | |||
| 30c1124e5e | |||
| 63cb53b169 | |||
| 17ebb079eb | |||
| 9f4f3d26e5 | |||
| 8bc9c9aba3 | |||
| 2e20c7696e | |||
| 78bb8b473b | |||
| a0fb4333d5 | |||
| 197291e453 | |||
| bafea9b95b | |||
| 23b635db1e | |||
| 7aa57c44f8 | |||
| ece0ea2a54 | |||
| f22732b109 | |||
| 71b9188ece | |||
| a2017628bc | |||
| cf025639bd | |||
| df6ebddc69 | |||
| aa2734f3bf | |||
| b9e3a8bd40 | |||
| 2a3a38a5f8 | |||
| 0254812446 | |||
| 11186f627e | |||
| db44df040e | |||
| cdf439b556 | |||
| e4fc15deec | |||
| 53547b5a8d | |||
| 6460cbe288 | |||
| 29646cdff8 | |||
| feff67457d | |||
| 1f30231d5c | |||
| 3e584f29b1 | |||
| c424f7bafa | |||
| 48fbdc69fb | |||
| c1b0ba2773 | |||
| d4949c872b | |||
| a8a28c8635 | |||
| 6f47010a72 | |||
| 39fb929af6 | |||
| 1b8f8cd914 | |||
| b288a5c68f | |||
| b79068ea65 | |||
| afb2ec86b7 | |||
| f2f915c0d0 | |||
| 8503a53fec | |||
| d0ed2a87cd | |||
| eeb24e1d5c | |||
| d805b6f6ac | |||
| 58ef0f4176 | |||
| 6ed3bb48f2 | |||
| 814ab4e980 | |||
| ffbce2020e | |||
| 0fc2f70dc5 |
@@ -0,0 +1,8 @@
|
||||
# Specs depend on character counts, if we don't specify the line endings the
|
||||
# fixtures will vary depending on platform
|
||||
spec/fixtures/**/*.js text eol=lf
|
||||
spec/fixtures/**/*.coffee text eol=lf
|
||||
spec/fixtures/**/*.less text eol=lf
|
||||
spec/fixtures/**/*.css text eol=lf
|
||||
spec/fixtures/**/*.txt text eol=lf
|
||||
spec/fixtures/dir/**/* text eol=lf
|
||||
@@ -5,6 +5,7 @@
|
||||
.nvm-version
|
||||
node_modules
|
||||
npm-debug.log
|
||||
debug.log
|
||||
/tags
|
||||
/atom-shell/
|
||||
docs/output
|
||||
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
* Improved: Faster and better looking find and replace
|
||||
* Improved: Double-click selection behavior between word/non-word
|
||||
* Added: Solarized theme now bundled by default
|
||||
* Added: Base16 Tomorrow Dark theme now bundled by default
|
||||
|
||||
* Fixed: Make Atom's version the same as Speakeasy's version
|
||||
|
||||
* Fixed: Package generator package not opening window to generated package
|
||||
|
||||
* Fixed: Precompile bootstrap.less for faster startup
|
||||
|
||||
* Fixed: Save sometimes failing from an editor that was split
|
||||
* Fixed: Search results not appearing when set to exclude ignores
|
||||
|
||||
* Fixed: Status bar and gutter displaying incorrect Git status information
|
||||
* Fixed: Packages not installing from the Settings view
|
||||
* Fixed: Spec runner now works from a released build
|
||||
* Fixed: Literate CoffeeScript not syntax highlighting correctly
|
||||
|
||||
* 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
|
||||
* Added: Initial collaboration sessions
|
||||
* Fixed: Empty lines being deleted via uppercase/downcase command
|
||||
* Fixed: Keybindings not working when using non-English keyboard language
|
||||
* Fixed: cmd-shift-p and cmd-alt-w not doing anything when pressed
|
||||
|
||||
* Improved: Use grunt (instead of rake) for build system
|
||||
* Fixed: Java files not syntax highlighting correctly.
|
||||
* Fixed: LESS/CSS now indents properly after hitting enter.
|
||||
* Added: Support for browsing .tar.gz and .zip files in the editor
|
||||
* Added: TODO/FIXME/CHANGED are now highlighted in comments.
|
||||
* Fixed: Full screen state of windows is now persisted across restarts.
|
||||
* Added: Makefile syntax highlighting now included.
|
||||
* Added: Open fuzzy finder to specific line using colon suffix (i.e ':25')
|
||||
* Fixed: Issues deleting and moving over certain UTF-8 characters
|
||||
* Fixed: Tree view not properly highlighting or revealing for open images.
|
||||
* Added: Packages can now be installed from the configuration UI.
|
||||
* Fixed: .git folder now ignored by default when searching
|
||||
|
||||
* Fixed: Not being able to disable packages from configuration UI.
|
||||
* Fixed: Fuzzy finder showing poor results for entered text
|
||||
* Improved: App icon
|
||||
|
||||
* Fixed: Fuzzy finder being empty sometimes
|
||||
|
||||
* Improved: App icon
|
||||
* Fixed: End of line invisibles rendering incorrectly with the indent guide
|
||||
* Fixed: Updates not installing automatically on restart
|
||||
* Fixed: Wrap guide not displaying
|
||||
* Fixed: Error when saving with the markdown preview focused
|
||||
|
||||
* Fixed: Atom always running in dev mode
|
||||
* Fixed: Crash when running in dev mode without a path to the Atom source
|
||||
|
||||
* Fixed: Freeze when editing a RoR class
|
||||
* Added: meta-N to open a new untitled editor in the current window
|
||||
|
||||
* Fixed: Styling in command logger
|
||||
* Added: XML and Ruby syntax highlighting in Markdown files
|
||||
* Fixed: Error when editing files in a HEAD-less Git repository
|
||||
|
||||
* Fixed: Invisible characters not being visible when enabled
|
||||
* Added: Editor gutter now displays Git status for lines
|
||||
|
||||
* Improved: Startup time
|
||||
* Added: SQL bundle now included
|
||||
* Added: PEG.js bundle now included
|
||||
* Added: Hyperlinks can now be opened with ctrl-O
|
||||
* Fixed: PHP syntax highlighting
|
||||
+4
-6
@@ -10,10 +10,8 @@
|
||||
* Check the Dev tools (`alt-cmd-i`) for errors and stack traces to include
|
||||
|
||||
## Code
|
||||
* Follow the [JavaScript](https://github.com/styleguide/javascript),
|
||||
[CSS](https://github.com/styleguide/css),
|
||||
and [Objective-C](https://github.com/github/objective-c-conventions)
|
||||
styleguides
|
||||
* Follow the [JavaScript](https://github.com/styleguide/javascript) and
|
||||
[CSS](https://github.com/styleguide/css) styleguides
|
||||
* Include thoughtfully worded [Jasmine](http://pivotal.github.com/jasmine/)
|
||||
specs
|
||||
* Add 3rd-party packages as a `package.json` dependency
|
||||
@@ -28,11 +26,11 @@
|
||||
* Class methods (methods starting with a `@`)
|
||||
* Instance variables
|
||||
* Instance methods
|
||||
* Be ware of platform differences
|
||||
* Beware of platform differences
|
||||
* The home directory is `process.env.USERPROFILE` on Windows, while on OS X
|
||||
and Linux it's `process.env.HOME`
|
||||
* Path separator is `\` on Windows, and is `/` on OS X and Linux, so use
|
||||
`path.join` to concatenate filenames.
|
||||
`path.join` to concatenate filenames
|
||||
* Temporary directory is not `/tmp` on Windows, use `os.tmpdir()` when
|
||||
possible
|
||||
|
||||
|
||||
+21
-7
@@ -8,14 +8,28 @@ _ = require 'underscore-plus'
|
||||
packageJson = require './package.json'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
appName = 'Atom.app'
|
||||
if not grunt.option('verbose')
|
||||
grunt.log.writeln = (args...) -> grunt.log
|
||||
grunt.log.write = (args...) -> grunt.log
|
||||
|
||||
[major, minor, patch] = packageJson.version.split('.')
|
||||
tmpDir = if process.platform is 'win32' then os.tmpdir() else '/tmp'
|
||||
buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'atom-build')
|
||||
shellAppDir = path.join(buildDir, appName)
|
||||
contentsDir = path.join(shellAppDir, 'Contents')
|
||||
appDir = path.join(contentsDir, 'Resources', 'app')
|
||||
installDir = path.join('/Applications', appName)
|
||||
if process.platform is 'win32'
|
||||
appName = 'Atom'
|
||||
tmpDir = os.tmpdir()
|
||||
installRoot = process.env.ProgramFiles
|
||||
buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'atom-build')
|
||||
shellAppDir = path.join(buildDir, appName)
|
||||
appDir = path.join(shellAppDir, 'resources', 'app')
|
||||
else
|
||||
appName = 'Atom.app'
|
||||
tmpDir = '/tmp'
|
||||
installRoot = '/Applications'
|
||||
buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'atom-build')
|
||||
shellAppDir = path.join(buildDir, appName)
|
||||
contentsDir = path.join(shellAppDir, 'Contents')
|
||||
appDir = path.join(contentsDir, 'Resources', 'app')
|
||||
|
||||
installDir = path.join(installRoot, appName)
|
||||
|
||||
coffeeConfig =
|
||||
options:
|
||||
|
||||
+5
-1
@@ -2,7 +2,7 @@
|
||||
|
||||

|
||||
|
||||
Check out our [guides](https://atom-docs.githubapp.com/v26.0/index.html) and [API documentation](https://atom-docs.githubapp.com/v26.0/api/index.html).
|
||||
Check out our [guides](https://www.atom.io/docs/latest/) and [API documentation](https://www.atom.io/docs/api/v34.0.0/api/)
|
||||
|
||||
## Installing
|
||||
|
||||
@@ -15,6 +15,7 @@ It will automatically update when a new release is available.
|
||||
### Requirements
|
||||
|
||||
* Mountain Lion
|
||||
* Looking for Windows support? Read [here][building].
|
||||
* Boxen (Obviously Atom won't release with this requirement)
|
||||
|
||||
### Installation
|
||||
@@ -24,3 +25,6 @@ It will automatically update when a new release is available.
|
||||
2. `cd ~/github/atom`
|
||||
|
||||
3. `script/build`
|
||||
|
||||
|
||||
[building]: https://github.com/atom/atom/blob/master/docs/building-atom.md
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
require '../spec/spec-helper'
|
||||
|
||||
path = require 'path'
|
||||
{$, _, Point, fs} = require 'atom'
|
||||
Project = require '../src/project'
|
||||
fsUtils = require '../src/fs-utils'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
|
||||
defaultCount = 100
|
||||
@@ -73,8 +73,8 @@ window.clickEvent = (properties={}) ->
|
||||
|
||||
window.mouseEvent = (type, properties) ->
|
||||
if properties.point
|
||||
{point, editor} = properties
|
||||
{top, left} = @pagePixelPositionForPoint(editor, point)
|
||||
{point, editorView} = properties
|
||||
{top, left} = @pagePixelPositionForPoint(editorView, point)
|
||||
properties.pageX = left + 1
|
||||
properties.pageY = top + 1
|
||||
properties.originalEvent ?= {detail: 1}
|
||||
@@ -86,14 +86,14 @@ window.mousedownEvent = (properties={}) ->
|
||||
window.mousemoveEvent = (properties={}) ->
|
||||
window.mouseEvent('mousemove', properties)
|
||||
|
||||
window.pagePixelPositionForPoint = (editor, point) ->
|
||||
window.pagePixelPositionForPoint = (editorView, point) ->
|
||||
point = Point.fromObject point
|
||||
top = editor.lines.offset().top + point.row * editor.lineHeight
|
||||
left = editor.lines.offset().left + point.column * editor.charWidth - editor.lines.scrollLeft()
|
||||
top = editorView.lines.offset().top + point.row * editorView.lineHeight
|
||||
left = editorView.lines.offset().left + point.column * editorView.charWidth - editorView.lines.scrollLeft()
|
||||
{ top, left }
|
||||
|
||||
window.setEditorWidthInChars = (editor, widthInChars, charWidth=editor.charWidth) ->
|
||||
editor.width(charWidth * widthInChars + editor.lines.position().left)
|
||||
window.seteditorViewWidthInChars = (editorView, widthInChars, charWidth=editorView.charWidth) ->
|
||||
editorView.width(charWidth * widthInChars + editorView.lines.position().left)
|
||||
|
||||
$.fn.resultOfTrigger = (type) ->
|
||||
event = $.Event(type)
|
||||
|
||||
@@ -2,8 +2,8 @@ require './benchmark-helper'
|
||||
{$, _, RootView} = require 'atom'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
|
||||
describe "editor.", ->
|
||||
editor = null
|
||||
describe "editorView.", ->
|
||||
editorView = null
|
||||
|
||||
beforeEach ->
|
||||
window.rootViewParentSelector = '#jasmine-content'
|
||||
@@ -12,19 +12,19 @@ describe "editor.", ->
|
||||
|
||||
rootView.width(1024)
|
||||
rootView.height(768)
|
||||
rootView.openSync() # open blank editor
|
||||
editor = rootView.getActiveView()
|
||||
rootView.openSync()
|
||||
editorView = rootView.getActiveView()
|
||||
|
||||
afterEach ->
|
||||
if editor.pendingDisplayUpdate
|
||||
if editorView.pendingDisplayUpdate
|
||||
waitsFor "editor to finish rendering", (done) ->
|
||||
editor.on 'editor:display-updated', done
|
||||
editorView.on 'editor:display-updated', done
|
||||
|
||||
describe "keymap.", ->
|
||||
event = null
|
||||
|
||||
beforeEach ->
|
||||
event = keydownEvent('x', target: editor.hiddenInput[0])
|
||||
event = keydownEvent('x', target: editorView.hiddenInput[0])
|
||||
|
||||
benchmark "keydown-event-with-no-binding", 10, ->
|
||||
keymap.handleKeyEvent(event)
|
||||
@@ -35,8 +35,8 @@ describe "editor.", ->
|
||||
|
||||
describe "empty-file.", ->
|
||||
benchmark "insert-delete", ->
|
||||
editor.insertText('x')
|
||||
editor.backspace()
|
||||
editorView.insertText('x')
|
||||
editorView.backspace()
|
||||
|
||||
describe "300-line-file.", ->
|
||||
beforeEach ->
|
||||
@@ -44,81 +44,81 @@ describe "editor.", ->
|
||||
|
||||
describe "at-begining.", ->
|
||||
benchmark "insert-delete", ->
|
||||
editor.insertText('x')
|
||||
editor.backspace()
|
||||
editorView.insertText('x')
|
||||
editorView.backspace()
|
||||
|
||||
benchmark "insert-delete-rehighlight", ->
|
||||
editor.insertText('"')
|
||||
editor.backspace()
|
||||
editorView.insertText('"')
|
||||
editorView.backspace()
|
||||
|
||||
describe "at-end.", ->
|
||||
beforeEach ->
|
||||
editor.moveCursorToBottom()
|
||||
editorView.moveCursorToBottom()
|
||||
|
||||
benchmark "insert-delete", ->
|
||||
editor.insertText('"')
|
||||
editor.backspace()
|
||||
editorView.insertText('"')
|
||||
editorView.backspace()
|
||||
|
||||
describe "empty-vs-set-innerHTML.", ->
|
||||
[firstRow, lastRow] = []
|
||||
beforeEach ->
|
||||
firstRow = editor.getFirstVisibleScreenRow()
|
||||
lastRow = editor.getLastVisibleScreenRow()
|
||||
firstRow = editorView.getFirstVisibleScreenRow()
|
||||
lastRow = editorView.getLastVisibleScreenRow()
|
||||
|
||||
benchmark "build-gutter-html.", 1000, ->
|
||||
editor.gutter.renderLineNumbers(null, firstRow, lastRow)
|
||||
editorView.gutter.renderLineNumbers(null, firstRow, lastRow)
|
||||
|
||||
benchmark "set-innerHTML.", 1000, ->
|
||||
editor.gutter.renderLineNumbers(null, firstRow, lastRow)
|
||||
editor.gutter.lineNumbers[0].innerHtml = ''
|
||||
editorView.gutter.renderLineNumbers(null, firstRow, lastRow)
|
||||
editorView.gutter.lineNumbers[0].innerHtml = ''
|
||||
|
||||
benchmark "empty.", 1000, ->
|
||||
editor.gutter.renderLineNumbers(null, firstRow, lastRow)
|
||||
editor.gutter.lineNumbers.empty()
|
||||
editorView.gutter.renderLineNumbers(null, firstRow, lastRow)
|
||||
editorView.gutter.lineNumbers.empty()
|
||||
|
||||
describe "positionLeftForLineAndColumn.", ->
|
||||
line = null
|
||||
beforeEach ->
|
||||
editor.scrollTop(2000)
|
||||
editor.resetDisplay()
|
||||
line = editor.lineElementForScreenRow(106)[0]
|
||||
editorView.scrollTop(2000)
|
||||
editorView.resetDisplay()
|
||||
line = editorView.lineElementForScreenRow(106)[0]
|
||||
|
||||
describe "one-line.", ->
|
||||
beforeEach ->
|
||||
editor.clearCharacterWidthCache()
|
||||
editorView.clearCharacterWidthCache()
|
||||
|
||||
benchmark "uncached", 5000, ->
|
||||
editor.positionLeftForLineAndColumn(line, 106, 82)
|
||||
editor.clearCharacterWidthCache()
|
||||
editorView.positionLeftForLineAndColumn(line, 106, 82)
|
||||
editorView.clearCharacterWidthCache()
|
||||
|
||||
benchmark "cached", 5000, ->
|
||||
editor.positionLeftForLineAndColumn(line, 106, 82)
|
||||
editorView.positionLeftForLineAndColumn(line, 106, 82)
|
||||
|
||||
describe "multiple-lines.", ->
|
||||
[firstRow, lastRow] = []
|
||||
beforeEach ->
|
||||
firstRow = editor.getFirstVisibleScreenRow()
|
||||
lastRow = editor.getLastVisibleScreenRow()
|
||||
firstRow = editorView.getFirstVisibleScreenRow()
|
||||
lastRow = editorView.getLastVisibleScreenRow()
|
||||
|
||||
benchmark "cache-entire-visible-area", 100, ->
|
||||
for i in [firstRow..lastRow]
|
||||
line = editor.lineElementForScreenRow(i)[0]
|
||||
editor.positionLeftForLineAndColumn(line, i, Math.max(0, editor.lineLengthForBufferRow(i)))
|
||||
line = editorView.lineElementForScreenRow(i)[0]
|
||||
editorView.positionLeftForLineAndColumn(line, i, Math.max(0, editorView.lineLengthForBufferRow(i)))
|
||||
|
||||
describe "text-rendering.", ->
|
||||
beforeEach ->
|
||||
editor.scrollTop(2000)
|
||||
editorView.scrollTop(2000)
|
||||
|
||||
benchmark "resetDisplay", 50, ->
|
||||
editor.resetDisplay()
|
||||
editorView.resetDisplay()
|
||||
|
||||
benchmark "htmlForScreenRows", 1000, ->
|
||||
lastRow = editor.getLastScreenRow()
|
||||
editor.htmlForScreenRows(0, lastRow)
|
||||
lastRow = editorView.getLastScreenRow()
|
||||
editorView.htmlForScreenRows(0, lastRow)
|
||||
|
||||
benchmark "htmlForScreenRows.htmlParsing", 50, ->
|
||||
lastRow = editor.getLastScreenRow()
|
||||
html = editor.htmlForScreenRows(0, lastRow)
|
||||
lastRow = editorView.getLastScreenRow()
|
||||
html = editorView.htmlForScreenRows(0, lastRow)
|
||||
|
||||
div = document.createElement('div')
|
||||
div.innerHTML = html
|
||||
@@ -126,44 +126,44 @@ describe "editor.", ->
|
||||
describe "gutter-api.", ->
|
||||
describe "getLineNumberElementsForClass.", ->
|
||||
beforeEach ->
|
||||
editor.gutter.addClassToLine(20, 'omgwow')
|
||||
editor.gutter.addClassToLine(40, 'omgwow')
|
||||
editorView.gutter.addClassToLine(20, 'omgwow')
|
||||
editorView.gutter.addClassToLine(40, 'omgwow')
|
||||
|
||||
benchmark "DOM", 20000, ->
|
||||
editor.gutter.getLineNumberElementsForClass('omgwow')
|
||||
editorView.gutter.getLineNumberElementsForClass('omgwow')
|
||||
|
||||
benchmark "getLineNumberElement.DOM", 20000, ->
|
||||
editor.gutter.getLineNumberElement(12)
|
||||
editorView.gutter.getLineNumberElement(12)
|
||||
|
||||
benchmark "toggle-class", 2000, ->
|
||||
editor.gutter.addClassToLine(40, 'omgwow')
|
||||
editor.gutter.removeClassFromLine(40, 'omgwow')
|
||||
editorView.gutter.addClassToLine(40, 'omgwow')
|
||||
editorView.gutter.removeClassFromLine(40, 'omgwow')
|
||||
|
||||
describe "find-then-unset.", ->
|
||||
classes = ['one', 'two', 'three', 'four']
|
||||
|
||||
benchmark "single-class", 200, ->
|
||||
editor.gutter.addClassToLine(30, 'omgwow')
|
||||
editor.gutter.addClassToLine(40, 'omgwow')
|
||||
editor.gutter.removeClassFromAllLines('omgwow')
|
||||
editorView.gutter.addClassToLine(30, 'omgwow')
|
||||
editorView.gutter.addClassToLine(40, 'omgwow')
|
||||
editorView.gutter.removeClassFromAllLines('omgwow')
|
||||
|
||||
benchmark "multiple-class", 200, ->
|
||||
editor.gutter.addClassToLine(30, 'one')
|
||||
editor.gutter.addClassToLine(30, 'two')
|
||||
editorView.gutter.addClassToLine(30, 'one')
|
||||
editorView.gutter.addClassToLine(30, 'two')
|
||||
|
||||
editor.gutter.addClassToLine(40, 'two')
|
||||
editor.gutter.addClassToLine(40, 'three')
|
||||
editor.gutter.addClassToLine(40, 'four')
|
||||
editorView.gutter.addClassToLine(40, 'two')
|
||||
editorView.gutter.addClassToLine(40, 'three')
|
||||
editorView.gutter.addClassToLine(40, 'four')
|
||||
|
||||
for klass in classes
|
||||
editor.gutter.removeClassFromAllLines(klass)
|
||||
editorView.gutter.removeClassFromAllLines(klass)
|
||||
|
||||
describe "line-htmlification.", ->
|
||||
div = null
|
||||
html = null
|
||||
beforeEach ->
|
||||
lastRow = editor.getLastScreenRow()
|
||||
html = editor.htmlForScreenRows(0, lastRow)
|
||||
lastRow = editorView.getLastScreenRow()
|
||||
html = editorView.htmlForScreenRows(0, lastRow)
|
||||
div = document.createElement('div')
|
||||
|
||||
benchmark "setInnerHTML", 1, ->
|
||||
@@ -178,40 +178,40 @@ describe "editor.", ->
|
||||
rootView.openSync('huge.js')
|
||||
|
||||
benchmark "moving-to-eof.", 1, ->
|
||||
editor.moveCursorToBottom()
|
||||
editorView.moveCursorToBottom()
|
||||
|
||||
describe "on-first-line.", ->
|
||||
benchmark "inserting-newline", 5, ->
|
||||
editor.insertNewline()
|
||||
editorView.insertNewline()
|
||||
|
||||
describe "on-last-visible-line.", ->
|
||||
beforeEach ->
|
||||
editor.setCursorScreenPosition([editor.getLastVisibleScreenRow(), 0])
|
||||
editorView.setCursorScreenPosition([editorView.getLastVisibleScreenRow(), 0])
|
||||
|
||||
benchmark "move-down-and-scroll", 300, ->
|
||||
editor.trigger 'move-down'
|
||||
editorView.trigger 'move-down'
|
||||
|
||||
describe "at-eof.", ->
|
||||
endPosition = null
|
||||
|
||||
beforeEach ->
|
||||
editor.moveCursorToBottom()
|
||||
endPosition = editor.getCursorScreenPosition()
|
||||
editorView.moveCursorToBottom()
|
||||
endPosition = editorView.getCursorScreenPosition()
|
||||
|
||||
benchmark "move-to-beginning-of-word", ->
|
||||
editor.moveCursorToBeginningOfWord()
|
||||
editor.setCursorScreenPosition(endPosition)
|
||||
editorView.moveCursorToBeginningOfWord()
|
||||
editorView.setCursorScreenPosition(endPosition)
|
||||
|
||||
benchmark "insert", ->
|
||||
editor.insertText('x')
|
||||
editorView.insertText('x')
|
||||
|
||||
describe "TokenizedBuffer.", ->
|
||||
describe "coffee-script-grammar.", ->
|
||||
[languageMode, buffer] = []
|
||||
|
||||
beforeEach ->
|
||||
editSession = benchmarkFixturesProject.openSync('medium.coffee')
|
||||
{ languageMode, buffer } = editSession
|
||||
editor = benchmarkFixturesProject.openSync('medium.coffee')
|
||||
{ languageMode, buffer } = editor
|
||||
|
||||
benchmark "construction", 20, ->
|
||||
new TokenizedBuffer(buffer, { languageMode, tabLength: 2})
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
## Building Atom
|
||||
|
||||
These guide is meant only for users who wish to help develop atom core,
|
||||
if you're just interested in using atom you should just [download
|
||||
atom][download].
|
||||
|
||||
## OSX
|
||||
|
||||
* Use Mountain Lion
|
||||
* Install the latest node 0.10.x release (32bit preferable)
|
||||
* Clone [atom][atom-git] to `~/github/atom`
|
||||
* Run `~/github/atom/script/bootstrap`
|
||||
|
||||
## Windows
|
||||
|
||||
* Install [Visual C++ 2010 Express][win-vs2010]
|
||||
* Install the [latest 32bit Node 0.10.x][win-node]
|
||||
* Install the [latest Python 2.7.x][win-python]
|
||||
* Install [GitHub for Windows][win-github]
|
||||
* Clone [atom/atom][atom-git] to `C:\Users\<user>\github\atom\`
|
||||
* Add `C:\Python27;C:\Program Files\nodejs;C:\Users\<user>\github\atom\node_modules\`
|
||||
to your PATH
|
||||
* Set ATOM_ACCESS_TOKEN to your oauth2 credentials (run `security -q
|
||||
find-generic-password -ws 'GitHub API Token'` on OSX to get your
|
||||
credentials).
|
||||
* Use the Windows GitHub shell and cd into `C:\Users\<user>\github\atom`
|
||||
* Run `script\bootstrap`
|
||||
|
||||
[download]: http://www.atom.io
|
||||
[win-node]: http://nodejs.org/download/
|
||||
[win-python]: http://www.python.org/download/
|
||||
[win-github]: http://windows.github.com/
|
||||
[win-vs2010]: http://www.microsoft.com/visualstudio/eng/products/visual-studio-2010-express
|
||||
[atom-git]: https://github.com/atom/atom/
|
||||
+53
-287
@@ -1,12 +1,11 @@
|
||||
# Authoring Packages
|
||||
# Creating Packages
|
||||
|
||||
Packages are at the core of Atom. Nearly everything outside of the main editor
|
||||
is handled by a package. That includes "core" pieces like the file tree, status
|
||||
bar and more.
|
||||
is handled by a package. That includes "core" pieces like the [file tree][file-tree],
|
||||
[status bar][status-bar], [syntax highlighting][cs-syntax], 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):
|
||||
behavior. The basic package layout is as follows:
|
||||
|
||||
```text
|
||||
my-package/
|
||||
@@ -21,9 +20,13 @@ my-package/
|
||||
package.json
|
||||
```
|
||||
|
||||
Not every package will have (or need) all of these directories.
|
||||
|
||||
We have [a tutorial on creating your first package][first-package].
|
||||
|
||||
## package.json
|
||||
|
||||
Similar to [npm packages][npm], Atom packages can contain a _package.json_ file
|
||||
Similar to [npm packages][npm], Atom packages 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.
|
||||
@@ -83,20 +86,22 @@ you don't need to worry because that's getting torn down anyway.
|
||||
|
||||
### Simple Package Code
|
||||
|
||||
Your directory would look like this:
|
||||
|
||||
```text
|
||||
my-package/
|
||||
package.json # optional
|
||||
package.json
|
||||
index.coffee
|
||||
lib/
|
||||
my-package.coffee
|
||||
```
|
||||
|
||||
`index.coffee`:
|
||||
`index.coffee` might be:
|
||||
```coffeescript
|
||||
module.exports = require "./lib/my-package"
|
||||
```
|
||||
|
||||
`my-package/my-package.coffee`:
|
||||
`my-package/my-package.coffee` might start:
|
||||
```coffeescript
|
||||
module.exports =
|
||||
activate: (rootView, state) -> # ...
|
||||
@@ -111,8 +116,6 @@ 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.
|
||||
|
||||
Check out [wrap-guide] for a simple example of Atom's package API in action.
|
||||
|
||||
## Stylesheets
|
||||
|
||||
Stylesheets for your package should be placed in the _stylesheets_ directory.
|
||||
@@ -120,14 +123,16 @@ 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] (but LESS
|
||||
is recommended).
|
||||
|
||||
Ideally you will not need much in the way of styling. We've provided a standard
|
||||
set of components. You can view all components by opening the styleguide: open
|
||||
the command palette (`cmd-p`) and search for _styleguide_ or just
|
||||
`cmd-ctrl-shift-g`.
|
||||
Ideally, you won't need much in the way of styling. We've provided a standard
|
||||
set of components which define both the colors and UI elements for any package
|
||||
that fits into Atom seamlessly. You can view all of Atom's UI components by opening
|
||||
the styleguide: open the command palette (`cmd-p`) and search for _styleguide_,
|
||||
or just type `cmd-ctrl-G`.
|
||||
|
||||
If you do need styling, we try to keep only structural styles in the package
|
||||
stylesheets. Colors and sizing should be taken from the active theme's
|
||||
[ui-variables.less][ui-variables]. If you follow this guideline, your package
|
||||
If you _do_ need special styling, try to keep only structural styles in the package
|
||||
stylesheets. If you _must_ specify colors and sizing, these should be taken from
|
||||
the active theme's [ui-variables.less][ui-variables]. For more information, see the
|
||||
[theme variables docs][theme-variables]. If you follow this guideline, your package
|
||||
will look good out of the box with any theme!
|
||||
|
||||
An optional `stylesheets` array in your _package.json_ can list the stylesheets
|
||||
@@ -136,18 +141,23 @@ alphabetically.
|
||||
|
||||
## Keymaps
|
||||
|
||||
It's recommended that you provide key bindings for commonly used actions for
|
||||
your extension, especially if you're also adding a new command:
|
||||
|
||||
```coffeescript
|
||||
'.tree-view-scroller':
|
||||
'ctrl-V': 'changer:magic'
|
||||
```
|
||||
|
||||
It's recommended that you provide key bindings for commonly used actions for
|
||||
your extension, especially if you're also adding a new command.
|
||||
|
||||
Keymaps are placed in the _keymaps_ subdirectory. 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.
|
||||
|
||||
|
||||
Keybindings are executed by determining which element the keypress occured on. In
|
||||
the example above, `changer:magic` command is executed when pressing `ctrl-V` on
|
||||
the `.tree-view-scroller` element.
|
||||
|
||||
See the [main keymaps documentation][keymaps] for more detailed information on
|
||||
how keymaps work.
|
||||
|
||||
@@ -159,6 +169,9 @@ specify which menus to load and in what order.
|
||||
|
||||
### Application Menu
|
||||
|
||||
It's recommended that you create an application menu item for common actions
|
||||
with your package that aren't tied to a specific element:
|
||||
|
||||
```coffee-script
|
||||
'menu': [
|
||||
{
|
||||
@@ -178,18 +191,17 @@ specify which menus to load and in what order.
|
||||
]
|
||||
```
|
||||
|
||||
It's recommended that you create an application menu item for common actions
|
||||
with your package that aren't tied to a specific element.
|
||||
|
||||
To add your own item to the application menu simply create a top level `menu`
|
||||
key in any menu configuration file in _menus_ (since the above is [CSON] it
|
||||
should end with `.cson`)
|
||||
To add your own item to the application menu, simply create a top level `menu`
|
||||
key in any menu configuration file in _menus_. This can be a JSON or [CSON] file.
|
||||
|
||||
The menu templates you specify are merged with all other templates provided
|
||||
by other packages in the order which they were loaded.
|
||||
|
||||
### Context Menu
|
||||
|
||||
It's recommended to specify a context menu item for commands that are linked to
|
||||
specific parts of the interface, like adding a file in the tree-view:
|
||||
|
||||
```coffee-script
|
||||
'context-menu':
|
||||
'.tree-view':
|
||||
@@ -198,12 +210,9 @@ by other packages in the order which they were loaded.
|
||||
'Inspect Element': 'core:inspect'
|
||||
```
|
||||
|
||||
It's recommended to specify a context menu item for commands that are linked to
|
||||
specific parts of the interface, like adding a file in the tree-view.
|
||||
|
||||
To add your own item to the application menu simply create a top level
|
||||
`context-menu` key in any menu configuration file in _menus_ (since the above is
|
||||
[CSON] it should end with `.cson`)
|
||||
`context-menu` key in any menu configuration file in _menus_. This can be a
|
||||
JSON or [CSON] file.
|
||||
|
||||
Context menus are created by determining which element was selected and
|
||||
then adding all of the menu items whose selectors match that element (in
|
||||
@@ -216,7 +225,7 @@ or one of its parents has the `tree-view` class applied to it.
|
||||
## Snippets
|
||||
|
||||
An extension can supply language snippets in the _snippets_ directory which
|
||||
allows the user to enter repetitive text quickly.
|
||||
allows the user to enter repetitive text quickly:
|
||||
|
||||
```coffeescript
|
||||
".source.coffee .specs":
|
||||
@@ -312,8 +321,8 @@ You can also use the `atom` protocol URLs in themes.
|
||||
Your package **should** have tests, and if they're placed in the _spec_
|
||||
directory, they can be run by Atom.
|
||||
|
||||
Under the hood, [Jasmine] is being used to execute the tests, so you can
|
||||
assume that any DSL available there is available to your package as well.
|
||||
Under the hood, [Jasmine] executes your tests, so you can assume that any DSL
|
||||
available there is available to your package as well.
|
||||
|
||||
**FIXME: Explain the following**
|
||||
|
||||
@@ -323,7 +332,9 @@ assume that any DSL available there is available to your package as well.
|
||||
* setTimeout
|
||||
* whatever else is different in spec-helper
|
||||
|
||||
## Running tests
|
||||
## Running Tests
|
||||
|
||||
TODO: Probably use the menu option now.
|
||||
|
||||
Once you've got your test suite written, the recommended way to run it is `apm
|
||||
test`. `apm test` prints its output to the console and returns the proper status
|
||||
@@ -349,257 +360,7 @@ registry.
|
||||
Run `apm help publish` to see all the available options and `apm help` to see
|
||||
all the other available commands.
|
||||
|
||||
|
||||
# Full Example
|
||||
|
||||
Let's take a look at creating our first package.
|
||||
|
||||
To get started hit `cmd-p`, and start typing "Package Generator." to generate
|
||||
the package. Once you select the package generator command, 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
|
||||
key binding that works only when the tree is focused. Instead of using the
|
||||
default `toggle`, our keybinding executes a new command called `magic`.
|
||||
|
||||
_keymaps/changer.cson_ can easily become this:
|
||||
|
||||
```coffeescript
|
||||
'.tree-view':
|
||||
'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` 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 'atom'
|
||||
```
|
||||
|
||||
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 Panel
|
||||
|
||||
The next goal of this package is to append a panel to the Atom editor that lists
|
||||
some information about the modified files.
|
||||
|
||||
To do that, we're going to first 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], 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 interface during the
|
||||
initialization, then immediately call `hide()` on the element to remove it from
|
||||
the view. You can then swap between `show()` and `hide()`, and instead of
|
||||
forcing Atom to add and remove the element as we're doing here, it'll just set a
|
||||
CSS property to control your package's visibility.
|
||||
|
||||
Refresh Atom, hit the key combo, and see your 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][path] 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
|
||||
## Included Libraries
|
||||
|
||||
FIXME: Describe `require 'atom'
|
||||
|
||||
@@ -612,12 +373,16 @@ popular libraries into their packages:
|
||||
|
||||
Additional libraries can be found by browsing Atom's *node_modules* folder.
|
||||
|
||||
[file-tree]: https://github.com/atom/tree-view
|
||||
[status-bar]: https://github.com/atom/status-bar
|
||||
[cs-syntax]: https://github.com/atom/language-coffee-script
|
||||
[npm]: http://en.wikipedia.org/wiki/Npm_(software)
|
||||
[npm-keys]: https://npmjs.org/doc/json.html
|
||||
[apm]: https://github.com/atom/apm
|
||||
[git-tag]: http://git-scm.com/book/en/Git-Basics-Tagging
|
||||
[wrap-guide]: https://github.com/atom/wrap-guide/
|
||||
[keymaps]: internals/keymaps.md
|
||||
[theme-variables]: theme-variables.md
|
||||
[tm-tokens]: http://manual.macromates.com/en/language_grammars.html
|
||||
[spacepen]: https://github.com/nathansobo/space-pen
|
||||
[path]: http://nodejs.org/docs/latest/api/path.html
|
||||
@@ -627,3 +392,4 @@ Additional libraries can be found by browsing Atom's *node_modules* folder.
|
||||
[cson]: https://github.com/atom/season
|
||||
[less]: http://lesscss.org
|
||||
[ui-variables]: https://github.com/atom/atom-dark-ui/blob/master/stylesheets/ui-variables.less
|
||||
[first-package]: your-first-package.html
|
||||
|
||||
+61
-61
@@ -1,72 +1,67 @@
|
||||
# Creating a Theme
|
||||
|
||||
Atom's interface is rendered using HTML and it's styled via [LESS] (a superset
|
||||
of CSS). Don't worry if you haven't heard of LESS before, it's just like CSS but
|
||||
Atom's interface is rendered using HTML, and it's styled via [LESS] (a superset
|
||||
of CSS). Don't worry if you haven't heard of LESS before; it's just like CSS, but
|
||||
with a few handy extensions.
|
||||
|
||||
Since CSS is the basis of the theming system, we can load multiple themes within
|
||||
Atom and they behaves just as they would on a website. Themes loaded first are overridden by
|
||||
themes which are loaded later (the order is controlled from within the Settings
|
||||
pane).
|
||||
Atom, and the themes behave just as they would on a website. Themes loaded first
|
||||
are overridden by themes which are loaded later. The order of theme loading is
|
||||
controlled within the Settings/Themes pane.
|
||||
|
||||
This flexibility is helpful for users which prefer a light interface with a dark
|
||||
syntax theme. Atom currently has only interface and syntax themes but it is
|
||||
possible to create a theme to style something specific — say a changing
|
||||
This flexibility is helpful for users that prefer a light interface with a dark
|
||||
syntax theme. Atom currently has only interface and syntax themes, but it is
|
||||
possible to create a theme to style something specific — say, changing
|
||||
the colors in the tree view or creating a language specific syntax theme.
|
||||
|
||||
## Getting Started
|
||||
|
||||
To create your own theme you'll need a few things:
|
||||
|
||||
* A working install of [Atom], so you can work on your new theme.
|
||||
* A working install of [git] to track changes.
|
||||
* And a [GitHub] account, so you can distribute your themes.
|
||||
|
||||
Themes are pretty straight forward but it's still helpful to be familiar with
|
||||
a few things before starting:
|
||||
|
||||
* LESS is a superset of CSS but it has some really handy features like
|
||||
* LESS is a superset of CSS, but it has some really handy features like
|
||||
variables. If you aren't familiar with its syntax take a few minutes
|
||||
to [familiarize yourself][less-tutorial].
|
||||
* Atom uses Chrome at its core, so you can use Chrome devtools to
|
||||
inspect the current state of the interface. Checkout Google's
|
||||
[extensive tutorial][devtools-tutorial] for a short introduction.
|
||||
* You may also want to review the concept of a _[package.json]_, too. This file
|
||||
is used to help distribute your theme to Atom users.
|
||||
|
||||
## Creating a Minimal Syntax Theme
|
||||
There are two types of themes you can create: syntax themes and interface themes.
|
||||
The differences between them are simply a matter of what they target and what
|
||||
they provide. Syntax themes focus on the entire editor pane, while interface themes
|
||||
target elements which are outside of the editor.
|
||||
|
||||
1. Open the Command Palette (`cmd-p`)
|
||||
1. Search for `Package Generator: Generate Syntax Theme` and select it.
|
||||
1. Choose a name for your theme. A folder will be created with this name.
|
||||
1. Press enter to create your theme. After creation, your theme will be
|
||||
installed and enabled.
|
||||
1. An Atom window will open with your newly created theme.
|
||||
1. Open `package.json` and update the relevant parts.
|
||||
1. Open `stylesheets/colors.less` to change the various colors variables which
|
||||
have been already been defined.
|
||||
1. Open `stylesheets/base.less` and modify the various syntax CSS selectors
|
||||
that have been already been defined.
|
||||
1. When you're ready, update the `README.md` and include an example screenshot
|
||||
of your new theme in action.
|
||||
1. Reload atom (`cmd-r`) to see changes you made reflected in your Atom window.
|
||||
1. Look in the theme settings, your new theme should be show in the enabled themes section
|
||||
![themesettings-img]
|
||||
1. Open a terminal to your new theme directory; it should be in `~/.atom/packages/<my-name>`.
|
||||
1. To publish, initialize a git repository, push to GitHub, and run
|
||||
`apm publish`.
|
||||
## Creating a Syntax Theme
|
||||
|
||||
Want to make edits and have them immediately show up without reloading? Open a
|
||||
development window (__View > Developer > Open in Dev Mode__ menu) to the
|
||||
directory of your choice — even the new theme itself. Then just edit away!
|
||||
Changes will be instantly reflected in the editor without having to reload.
|
||||
Let's create your first theme.
|
||||
|
||||
## Interface Themes
|
||||
To get started, hit `cmd-p`, and start typing "Generate Theme" to generate
|
||||
a package. Select "Generate Theme," and you'll be asked for a theme name. Let's
|
||||
call ours _motif_.
|
||||
|
||||
There are only two differences between interface and syntax themes - what
|
||||
they target and what they provide. Interface themes only target elements which
|
||||
are outside of the editor and **must** provide a `ui-variables.less` file which
|
||||
contains all of the variables provided by the [core themes][ui-variables].
|
||||
Atom will pop open a new window, showing the _motif_ theme, with a default set of
|
||||
folders and files created for us. If you hit `cmd-,` and navigate to the Themes
|
||||
menu option, you'll see the `motif` theme already available. Drag it over from
|
||||
"Enabled Themes" to "Available Themes."
|
||||
|
||||
To create a UI theme, do the following:
|
||||
Open up _stylesheets/colors.less_ to change the various colors variables which
|
||||
have been already been defined. For example, turn `@red` into `#f4c2c1`.
|
||||
|
||||
Then, open _stylesheets/base.less_, and modify the various syntax CSS selectors
|
||||
that have been already been defined. Each of these selectors represents a different
|
||||
part of the Atom window. Themes that don't need to modify a particular region
|
||||
can simply remove the selectors they don't need.
|
||||
|
||||
As an example, let's make the `.gutter` `background-color` into `@red`.
|
||||
|
||||
Reload Atom by hitting `cmd-r` to see the changes you made reflected in your Atom
|
||||
window. Pretty neat!
|
||||
|
||||
## Creating an Interface Theme
|
||||
|
||||
Interface themes **must** provide a `ui-variables.less` file which contains all
|
||||
of the variables provided by the [core themes][ui-variables].
|
||||
|
||||
To create an interface UI theme, do the following:
|
||||
|
||||
1. Fork one of the following repos
|
||||
1. [atom-dark-ui]
|
||||
@@ -82,32 +77,37 @@ To create a UI theme, do the following:
|
||||
|
||||
## Development workflow
|
||||
|
||||
There are a few of tools to help make theme development fast.
|
||||
There are a few of tools to help make theme development faster.
|
||||
|
||||
### Live Reload
|
||||
|
||||
Reloading via `cmd-r` after you make changes to your theme less than ideal. Atom
|
||||
supports [live updating][livereload] of styles on Dev Mode Atom windows.
|
||||
Reloading by hitting `cmd-r` after you make changes to your theme is less than ideal.
|
||||
Atom supports [live updating][livereload] of styles on Dev Mode Atom windows.
|
||||
|
||||
1. Open your theme directory in a dev window by either using the
|
||||
__View > Developer > Open in Dev Mode__ menu or the `cmd-shift-o` shortcut
|
||||
1. Make a change to your theme file and save — your change should be
|
||||
To enable a Dev Mode window:
|
||||
|
||||
1. Open your theme directory in a dev window by either going to the
|
||||
__View > Developer > Open in Dev Mode__ menu or by hitting the `cmd-shift-o`
|
||||
shortcut
|
||||
1. Make a change to your theme file and save it. Your change should be
|
||||
immediately applied!
|
||||
|
||||
If you'd like to reload all styles at any time, you can use the shortcut
|
||||
`cmd-ctrl-shift-r`.
|
||||
If you'd like to reload all the styles at any time, you can use the shortcut
|
||||
`cmd-ctrl-R`.
|
||||
|
||||
### Developer Tools
|
||||
|
||||
Atom is based on the Chrome browser, and supports Chrome's Developer Tools. You
|
||||
can open them by selecting the __View > Toggle Developer Tools__ menu or by using the
|
||||
`cmd-option-i` shortcut.
|
||||
can open them by selecting the __View > Toggle Developer Tools__ menu, or by using
|
||||
the `cmd-option-i` shortcut.
|
||||
|
||||
The dev tools allow you to inspect elements and take a look at their CSS
|
||||
properties.
|
||||
|
||||
![devtools-img]
|
||||
|
||||
Check out Google's [extensive tutorial][devtools-tutorial] for a short introduction.
|
||||
|
||||
### Atom Styleguide
|
||||
|
||||
If you are creating an interface theme, you'll want a way to see how your theme
|
||||
@@ -115,17 +115,17 @@ changes affect all the components in the system. The [styleguide] is a page with
|
||||
every component Atom supports rendered.
|
||||
|
||||
To open the styleguide, open the command palette (`cmd-p`) and search for
|
||||
_styleguide_ or use the shortcut `cmd-ctrl-shift-g`.
|
||||
_styleguide_, or use the shortcut `cmd-ctrl-shift-g`.
|
||||
|
||||
![styleguide-img]
|
||||
|
||||
[less]: http://lesscss.org/
|
||||
[git]: http://git-scm.com/
|
||||
[atom]: https://atom.io/
|
||||
[github]: https://github.com/
|
||||
[package.json]: ./creating-a-package.html#package-json
|
||||
[less-tutorial]: https://speakerdeck.com/danmatthews/less-css
|
||||
[devtools-tutorial]: https://developers.google.com/chrome-developer-tools/docs/elements
|
||||
[ui-variables]: https://github.com/atom/atom-dark-ui/blob/master/stylesheets/ui-variables.less
|
||||
[ui-variables]: ./theme-variables.html
|
||||
[livereload]: https://github.com/atom/dev-live-reload
|
||||
[styleguide]: https://github.com/atom/styleguide
|
||||
[atom-dark-ui]: https://github.com/atom/atom-dark-ui
|
||||
|
||||
@@ -30,8 +30,9 @@ built-in keymaps:
|
||||
'.editor':
|
||||
'enter': 'editor:newline'
|
||||
|
||||
".select-list .editor.mini":
|
||||
'enter': 'core:confirm'
|
||||
'body':
|
||||
'ctrl-P': 'core:move-up'
|
||||
'ctrl-p': 'core:move-down'
|
||||
```
|
||||
|
||||
This keymap defines the meaning of `enter` in two different contexts. In a
|
||||
@@ -40,10 +41,13 @@ 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 are 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.
|
||||
By default, `~/.atom/keymap.cson` is loaded when Atom is started. It will always
|
||||
be loaded last, giving you the chance to override bindings that are defined by
|
||||
Atom's core keymaps or third-party packages.
|
||||
|
||||
You'll want to know all the commands available to you. Open the Settings panel
|
||||
(`cmd-,`) and select the _Keybindings_ tab. It will show you all the keybindings
|
||||
currently in use.
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
@@ -63,7 +67,6 @@ namespaces: `core` and `editor`.
|
||||
### Configuration Key Reference
|
||||
|
||||
- `core`
|
||||
- `autosave`: Save a buffer when its view loses focus
|
||||
- `disabledPackages`: An array of package names to disable
|
||||
- `excludeVcsIgnoredPaths`: Don't search within files specified by _.gitignore_
|
||||
- `hideGitIgnoredFiles`: Whether files in the _.gitignore_ should be hidden
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* [Creating a Theme](creating-a-theme.md)
|
||||
|
||||
### Advanced Topics
|
||||
|
||||
* [Configuration](internals/configuration.md)
|
||||
* [Keymaps](internals/keymaps.md)
|
||||
* [Serialization](internals/serialization.md)
|
||||
|
||||
@@ -7,7 +7,7 @@ read config settings. You can read a value from `config` with `config.get`:
|
||||
|
||||
```coffeescript
|
||||
# read a value with `config.get`
|
||||
@autosave() if config.get "core.autosave"
|
||||
@showInvisibles() if config.get "edtior.showInvisibles"
|
||||
```
|
||||
|
||||
Or you can use `observeConfig` to track changes from a view object.
|
||||
@@ -47,7 +47,7 @@ the following way:
|
||||
|
||||
```coffeescript
|
||||
# basic key update
|
||||
config.set("core.autosave", true)
|
||||
config.set("core.showInvisibles", true)
|
||||
|
||||
config.pushAtKeyPath("core.disabledPackages", "wrap-guide")
|
||||
```
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
<!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="/docs/api/<%= tag %>/api/index.html">API</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<%= content %>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,116 @@
|
||||
# Style variables
|
||||
|
||||
Atom's UI provides a set of variables you can use in your own themes and packages.
|
||||
|
||||
## Use in Themes
|
||||
|
||||
Each custom theme must specify a `ui-variables.less` file with all of the
|
||||
following variables defined. The top-most theme specified in the theme settings
|
||||
will be loaded and available for import.
|
||||
|
||||
## Use in Packages
|
||||
|
||||
In any of your package's `.less` files, you can access the theme variables
|
||||
by importing the `ui-variables` file from Atom.
|
||||
|
||||
Your package should generally only specify structural styling, and these should
|
||||
come from [the style guide][styleguide]. Your package shouldn't specify colors,
|
||||
padding sizes, or anything in absolute pixels. You should instead use the theme
|
||||
variables. If you follow this guideline, your package will look good out of the
|
||||
box with any theme!
|
||||
|
||||
Here's an example `.less` file that a package can define using theme variables:
|
||||
|
||||
```css
|
||||
@import "ui-variables";
|
||||
|
||||
.my-selector{
|
||||
background-color: @base-background-color;
|
||||
padding: @component-padding;
|
||||
}
|
||||
```
|
||||
|
||||
## Variables
|
||||
|
||||
### Text colors
|
||||
|
||||
* `@text-color`
|
||||
* `@text-color-subtle`
|
||||
* `@text-color-highlight`
|
||||
* `@text-color-selected`
|
||||
* `@text-color-info` - A blue
|
||||
* `@text-color-success`- A green
|
||||
* `@text-color-warning`- An orange or yellow
|
||||
* `@text-color-error` - A red
|
||||
|
||||
### Background colors
|
||||
|
||||
* `@background-color-info` - A blue
|
||||
* `@background-color-success` - A green
|
||||
* `@background-color-warning` - An orange or yellow
|
||||
* `@background-color-error` - A red
|
||||
* `@background-color-highlight`
|
||||
* `@background-color-selected`
|
||||
* `@app-background-color` - The app's background under all the editor components
|
||||
|
||||
### Component colors
|
||||
|
||||
* `@base-background-color` -
|
||||
* `@base-border-color` -
|
||||
|
||||
* `@pane-item-background-color` -
|
||||
* `@pane-item-border-color` -
|
||||
|
||||
* `@input-background-color` -
|
||||
* `@input-border-color` -
|
||||
|
||||
* `@tool-panel-background-color` -
|
||||
* `@tool-panel-border-color` -
|
||||
|
||||
* `@inset-panel-background-color` -
|
||||
* `@inset-panel-border-color` -
|
||||
|
||||
* `@panel-heading-background-color` -
|
||||
* `@panel-heading-border-color` -
|
||||
|
||||
* `@overlay-background-color` -
|
||||
* `@overlay-border-color` -
|
||||
|
||||
* `@button-background-color` -
|
||||
* `@button-background-color-hover` -
|
||||
* `@button-background-color-selected` -
|
||||
* `@button-border-color` -
|
||||
|
||||
* `@tab-bar-background-color` -
|
||||
* `@tab-bar-border-color` -
|
||||
* `@tab-background-color` -
|
||||
* `@tab-background-color-active` -
|
||||
* `@tab-border-color` -
|
||||
|
||||
* `@tree-view-background-color` -
|
||||
* `@tree-view-border-color` -
|
||||
|
||||
* `@ui-site-color-1` -
|
||||
* `@ui-site-color-2` -
|
||||
* `@ui-site-color-3` -
|
||||
* `@ui-site-color-4` -
|
||||
* `@ui-site-color-5` -
|
||||
|
||||
### Component sizes
|
||||
|
||||
* `@disclosure-arrow-size` -
|
||||
|
||||
* `@component-padding` -
|
||||
* `@component-icon-padding` -
|
||||
* `@component-icon-size` -
|
||||
* `@component-line-height` -
|
||||
* `@component-border-radius` -
|
||||
|
||||
* `@tab-height` -
|
||||
|
||||
### Fonts
|
||||
|
||||
* `@font-size` -
|
||||
* `@font-family` -
|
||||
|
||||
[styleguide]: https://github.com/atom/styleguide
|
||||
@@ -0,0 +1,334 @@
|
||||
# Creating Your First Package
|
||||
|
||||
Let's take a look at creating your first package.
|
||||
|
||||
To get started, hit `cmd-p`, and start typing "Generate Package" to generate
|
||||
a package. Once you select the "Generate Package" command, it'll ask you for a
|
||||
name for your new package. Let's call ours _changer_.
|
||||
|
||||
Atom will pop open a new window, showing the _changer_ package with a default set of
|
||||
folders and files created for us. Hit `cmd-p` and start typing "Changer." You'll
|
||||
see a new `Changer:Toggle` command which, if selected, pops up a greeting. So far,
|
||||
so good!
|
||||
|
||||
In order to demonstrate the capabilities of Atom and its API, our Changer plugin
|
||||
is going to do two things:
|
||||
|
||||
1. It'll show only modified files in the file tree
|
||||
2. It'll append a new pane to the editor with some information about the modified
|
||||
files
|
||||
|
||||
Let's get started!
|
||||
|
||||
## Changing Keybindings and Commands
|
||||
|
||||
Since Changer is primarily concerned with the file tree, let's write a
|
||||
key binding that works only when the tree is focused. Instead of using the
|
||||
default `toggle`, our keybinding executes a new command called `magic`.
|
||||
|
||||
_keymaps/changer.cson_ should change to look like this:
|
||||
|
||||
```coffeescript
|
||||
'.tree-view':
|
||||
'ctrl-V': 'changer:magic'
|
||||
```
|
||||
|
||||
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` represents the parent container for the tree view.
|
||||
Keybindings only work within the context of where they're entered. In this case,
|
||||
hitting `ctrl-V` anywhere other than tree won't do anything. Obviously, you can
|
||||
bind to any part of the editor using element, id, or class names. For example,
|
||||
you can map to `body` if you want to scope to anywhere in Atom, or just `.editor`
|
||||
for the editor portion.
|
||||
|
||||
To bind keybindings to a command, we'll need to do a bit of association in our
|
||||
CoffeeScript code using the `rootView.command` method. This method takes a command
|
||||
name and executes a callback function. Open up _lib/changer-view.coffee_, and
|
||||
change `rootView.command "changer:toggle"` to look like this:
|
||||
|
||||
```coffeescript
|
||||
rootView.command "changer:magic", => @magic()
|
||||
```
|
||||
|
||||
It's common practice to namespace your commands with your package name, separated
|
||||
with a colon (`:`). Make sure to rename your `toggle` method to `magic` as well.
|
||||
|
||||
Every time you reload the Atom editor, changes to your package code will be reevaluated,
|
||||
just as if you were writing a script for the browser. Reload the editor, click on
|
||||
the tree, hit your keybinding, and...nothing happens! What the heck?!
|
||||
|
||||
Open up the _package.json_ file, and find the property called `activationEvents`.
|
||||
Basically, this key tells Atom to not load a package until it hears a certain event.
|
||||
Change the event to `changer:magic` and reload the editor:
|
||||
|
||||
```json
|
||||
"activationEvents": ["changer:magic"]
|
||||
```
|
||||
|
||||
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, there's [a list of
|
||||
the bundled libraries Atom provides by default][bundled-libs].
|
||||
|
||||
We bring in jQuery by requiring the `atom` package and binding it to the `$` variable:
|
||||
|
||||
```coffeescript
|
||||
{$, View} = require 'atom'
|
||||
```
|
||||
|
||||
Now, we can define the `magic` method to 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("status-modified")
|
||||
console.log el
|
||||
```
|
||||
|
||||
You can access the dev console by hitting `alt-cmd-i`. Here, you'll see all the
|
||||
statements from `console` calls. When we execute the `changer:magic` command, the
|
||||
browser console lists items that are not being modified (_i.e._, those without the
|
||||
`status-modified` class). Let's add a class to each of these elements called `hide-me`:
|
||||
|
||||
```coffeescript
|
||||
magic: ->
|
||||
$('ol.entries li').each (i, el) ->
|
||||
if !$(el).hasClass("status-modified")
|
||||
$(el).addClass("hide-me")
|
||||
```
|
||||
|
||||
With our newly added class, we can manipulate the visibility of the elements
|
||||
with a simple stylesheet. Open up _changer.css_ in the _stylesheets_ directory,
|
||||
and add a single entry:
|
||||
|
||||
```css
|
||||
ol.entries .hide-me {
|
||||
display: none;
|
||||
}
|
||||
```
|
||||
|
||||
Refresh Atom, and run the `changer` command. You'll see all the non-changed
|
||||
files disappear from the tree. Success!
|
||||
|
||||
![Changer_File_View]
|
||||
|
||||
There are a number of ways you can get the list back; let's just naively iterate
|
||||
over the same elements and remove the class:
|
||||
|
||||
```coffeescript
|
||||
magic: ->
|
||||
$('ol.entries li').each (i, el) ->
|
||||
if !$(el).hasClass("status-modified")
|
||||
if !$(el).hasClass("hide-me")
|
||||
$(el).addClass("hide-me")
|
||||
else
|
||||
$(el).removeClass("hide-me")
|
||||
```
|
||||
|
||||
## Creating a New Panel
|
||||
|
||||
The next goal of this package is to append a panel to the Atom editor that lists
|
||||
some information about the modified files.
|
||||
|
||||
To do that, we're going to first open up [the style guide][styleguide]. The Style
|
||||
Guide lists every type of UI element that can be created by an Atom package. Aside
|
||||
from helping you avoid writing fresh code from scratch, it ensures that packages
|
||||
have the same look and feel no matter how they're built.
|
||||
|
||||
Every package that extends from the `View` class can provide an optional class
|
||||
method called `content`. The `content` method constructs the DOM that your
|
||||
package uses as its UI. The principals of `content` are built entirely on
|
||||
[SpacePen][space-pen], which we'll touch upon only briefly here.
|
||||
|
||||
Our display will simply be an unordered list of the file names, and their
|
||||
modified times. We'll append this list to a panel on the bottom of the editor. A
|
||||
basic `panel` element inside a `tool-panel` will work well for us. Let's start by carving out a
|
||||
`div` to hold the filenames:
|
||||
|
||||
```coffeescript
|
||||
@content: ->
|
||||
@div class: "changer tool-panel panel-bottom", =>
|
||||
@div class: "panel", =>
|
||||
@div class: "panel-heading", "Modified Files"
|
||||
@div class: "panel-body padded", outlet: 'modifiedFilesContainer', =>
|
||||
@ul class: 'modified-files-list', outlet: 'modifiedFilesList', =>
|
||||
@li 'Modified File Test'
|
||||
@li 'Modified File Test'
|
||||
```
|
||||
|
||||
You can add any HTML attribute you like. `outlet` names the variable your
|
||||
package can use to manipulate the element directly. The fat arrow (`=>`)
|
||||
indicates that the next DOM set are nested children.
|
||||
|
||||
Once again, you can style `li` elements using your stylesheets. Let's test that
|
||||
out by adding these lines to the _changer.css_ file:
|
||||
|
||||
```css
|
||||
ul.modified-files-list {
|
||||
color: white;
|
||||
}
|
||||
```
|
||||
|
||||
We'll add one more line to the end of the `magic` method to make this pane appear:
|
||||
|
||||
```coffeescript
|
||||
rootView.vertical.append(this)
|
||||
```
|
||||
|
||||
If you refresh Atom and hit the key command, you'll see a box appear right underneath
|
||||
the editor:
|
||||
|
||||
![Changer_Panel_Append]
|
||||
|
||||
As you might have guessed, `rootView.vertical.append` tells Atom to append `this`
|
||||
item (_i.e._, whatever is defined by`@content`) _vertically_ to the editor. If
|
||||
we had called `rootView.horizontal.append`, the pane would be attached to the
|
||||
right-hand side of the editor.
|
||||
|
||||
Before we populate this panel for real, let's apply some logic to toggle the pane
|
||||
off and on, just like we did with the tree view. Replace the `rootView.vertical.append`
|
||||
call with this code:
|
||||
|
||||
```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 interface during the
|
||||
initialization, then immediately call `hide()` on the element to remove it from
|
||||
the view. You can then swap between `show()` and `hide()`, and instead of
|
||||
forcing Atom to add and remove the element as we're doing here, it'll just set a
|
||||
CSS property to control your package's visibility.
|
||||
|
||||
Refresh Atom, hit the key combo, and watch your test list appear and disappear.
|
||||
|
||||
## Calling Node.js Code
|
||||
|
||||
Since Atom is built on top of [Node.js][node], you can call any of its libraries,
|
||||
including other modules that your package requires.
|
||||
|
||||
We'll iterate through our resulting tree, and construct the path to our modified
|
||||
file based on its depth in the tree. We'll use Node to handle path joining for
|
||||
directories.
|
||||
|
||||
Add the following Node module to the top of your file:
|
||||
|
||||
```coffeescript
|
||||
path = require 'path'
|
||||
```
|
||||
|
||||
Then, add these lines to your `magic` method, _before_ your pane drawing code:
|
||||
|
||||
```coffeescript
|
||||
modifiedFiles = []
|
||||
# for each single entry...
|
||||
$('ol.entries li.file.status-modified span.name').each (i, el) ->
|
||||
filePath = []
|
||||
# ...grab its name...
|
||||
filePath.unshift($(el).text())
|
||||
|
||||
# ... then find its parent directories, and grab their names
|
||||
parents = $(el).parents('.directory.status-modified')
|
||||
parents.each (i, el) ->
|
||||
filePath.unshift($(el).find('div.header span.name').eq(0).text())
|
||||
|
||||
modifiedFilePath = path.join(atom.project.rootDirectory.path, filePath.join(path.sep))
|
||||
modifiedFiles.push modifiedFilePath
|
||||
```
|
||||
|
||||
`modifiedFiles` is an array containing a list of our modified files. We're also
|
||||
using the node.js [`path` library][path] to get the proper directory separator
|
||||
for our system.
|
||||
|
||||
Remove the two `@li` elements we added in `@content`, so that we can
|
||||
populate our `modifiedFilesList` with real information. We'll do that by
|
||||
iterating over `modifiedFiles`, accessing a file's last modified time, and
|
||||
appending it to `modifiedFilesList`:
|
||||
|
||||
```coffeescript
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
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:
|
||||
|
||||
![Changer_Panel_Timestamps]
|
||||
|
||||
You might notice that subsequent calls to this command reduplicate information.
|
||||
We could provide an elegant way of rechecking files already in the list, but for
|
||||
this demonstration, we'll just clear the `modifiedFilesList` each time it's closed:
|
||||
|
||||
```coffeescript
|
||||
# toggles the pane
|
||||
if @hasParent()
|
||||
@modifiedFilesList.empty() # added this to clear the list on close
|
||||
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)
|
||||
```
|
||||
|
||||
## Coloring UI Elements
|
||||
|
||||
For packages that create new UI elements, adhering to the style guide is just one
|
||||
part to keeping visual consistency. Packages dealing with color, fonts, padding,
|
||||
margins, and other visual cues should rely on [Theme Variables][theme-vars], instead
|
||||
of developing individual styles. Theme variables are variables defined by Atom
|
||||
for use in packages and themes. They're only available in [`LESS`](http://lesscss.org/)
|
||||
stylesheets.
|
||||
|
||||
For our package, let's remove the style defined by `ul.modified-files-list` in
|
||||
_changer.css_. Create a new file under the _stylesheets_ directory called _text-colors.less_.
|
||||
Here, we'll import the _ui-variables.less_ file, and define some Atom-specific
|
||||
styles:
|
||||
|
||||
```less
|
||||
@import "ui-variables";
|
||||
|
||||
ul.modified-files-list {
|
||||
color: @text-color;
|
||||
background-color: @background-color-info;
|
||||
}
|
||||
```
|
||||
|
||||
Using theme variables ensures that packages look great alongside any theme.
|
||||
|
||||
## Further reading
|
||||
|
||||
For more information on the mechanics of packages, check out
|
||||
[Creating a Package][creating-a-package].
|
||||
|
||||
[bundled-libs]: creating-a-package.html#included-libraries
|
||||
[styleguide]: https://github.com/atom/styleguide
|
||||
[space-pen]: https://github.com/atom/space-pen
|
||||
[node]: http://nodejs.org/
|
||||
[path]: http://nodejs.org/docs/latest/api/path.html
|
||||
[changer_file_view]: https://f.cloud.github.com/assets/69169/1441187/d7a7cb46-41a7-11e3-8128-d93f70a5d5c1.png
|
||||
[changer_panel_append]: https://f.cloud.github.com/assets/69169/1441189/db0c74da-41a7-11e3-8286-b82dd9190c34.png
|
||||
[changer_panel_timestamps]: https://f.cloud.github.com/assets/69169/1441190/dcc8eeb6-41a7-11e3-830f-1f1b33072fcd.png
|
||||
[theme-vars]: theme-variables.html
|
||||
[creating-a-package]: creating-a-package.html
|
||||
@@ -0,0 +1,13 @@
|
||||
# User keymap
|
||||
#
|
||||
# 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:
|
||||
#
|
||||
# '.editor':
|
||||
# 'enter': 'editor:newline'
|
||||
#
|
||||
# 'body':
|
||||
# 'ctrl-P': 'core:move-up'
|
||||
# 'ctrl-p': 'core:move-down'
|
||||
@@ -1,18 +1,16 @@
|
||||
{Document, Point, Range, Site} = require 'telepath'
|
||||
_ = require 'underscore-plus'
|
||||
{Document, Point, Range} = require 'telepath'
|
||||
|
||||
module.exports =
|
||||
_: _
|
||||
_: require 'underscore-plus'
|
||||
BufferedNodeProcess: require '../src/buffered-node-process'
|
||||
BufferedProcess: require '../src/buffered-process'
|
||||
Directory: require '../src/directory'
|
||||
Document: Document
|
||||
File: require '../src/file'
|
||||
fs: require '../src/fs-utils'
|
||||
fs: require 'fs-plus'
|
||||
Git: require '../src/git'
|
||||
Point: Point
|
||||
Range: Range
|
||||
Site: Site
|
||||
|
||||
# The following classes can't be used from a Task handler and should therefore
|
||||
# only be exported when not running as a child node process
|
||||
@@ -22,8 +20,7 @@ unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
|
||||
module.exports.$ = $
|
||||
module.exports.$$ = $$
|
||||
module.exports.$$$ = $$$
|
||||
module.exports.Editor = require '../src/editor'
|
||||
module.exports.pathForRepositoryUrl = require('../src/project').pathForRepositoryUrl
|
||||
module.exports.Editor = require '../src/editor-view'
|
||||
module.exports.RootView = require '../src/root-view'
|
||||
module.exports.SelectList = require '../src/select-list'
|
||||
module.exports.ScrollView = require '../src/scroll-view'
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
'body':
|
||||
'meta-up': 'core:move-to-top'
|
||||
'meta-down': 'core:move-to-bottom'
|
||||
'meta-shift-up': 'core:select-to-top'
|
||||
'meta-shift-down': 'core:select-to-bottom'
|
||||
|
||||
'.editor':
|
||||
'meta-left': 'editor:move-to-first-character-of-line'
|
||||
'meta-right': 'editor:move-to-end-of-line'
|
||||
'meta-shift-left': 'editor:select-to-first-character-of-line'
|
||||
'meta-shift-right': 'editor:select-to-end-of-line'
|
||||
|
||||
'home': 'editor:move-to-first-character-of-line'
|
||||
'end': 'editor:move-to-end-of-line'
|
||||
'shift-home': 'editor:select-to-first-character-of-line'
|
||||
'shift-end': 'editor:select-to-end-of-line'
|
||||
|
||||
'alt-left': 'editor:move-to-previous-word-boundary'
|
||||
'alt-right': 'editor:move-to-next-word-boundary'
|
||||
'alt-shift-left': 'editor:select-to-previous-word-boundary'
|
||||
'alt-shift-right': 'editor:select-to-next-word-boundary'
|
||||
|
||||
'alt-backspace': 'editor:backspace-to-beginning-of-word'
|
||||
'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,97 +0,0 @@
|
||||
'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'
|
||||
'escape': 'core:cancel'
|
||||
'meta-w': 'core:close'
|
||||
'up': 'core:move-up'
|
||||
'down': 'core:move-down'
|
||||
'left': 'core:move-left'
|
||||
'right': 'core:move-right'
|
||||
'shift-up': 'core:select-up'
|
||||
'shift-down': 'core:select-down'
|
||||
'shift-left': 'core:select-left'
|
||||
'shift-right': 'core:select-right'
|
||||
'meta-a': 'core:select-all'
|
||||
'backspace': 'core:backspace'
|
||||
'shift-backspace': 'core:backspace'
|
||||
'delete': 'core:delete'
|
||||
'meta-z': 'core:undo'
|
||||
'meta-Z': 'core:redo'
|
||||
'meta-y': 'core:redo'
|
||||
'meta-x': 'core:cut'
|
||||
'meta-c': 'core:copy'
|
||||
'meta-v': 'core:paste'
|
||||
'pageup': 'core:page-up'
|
||||
'pagedown': 'core:page-down'
|
||||
|
||||
'meta-alt-s': 'window:save-all'
|
||||
'meta-W': 'window:close'
|
||||
'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'
|
||||
'ctrl-alt-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'
|
||||
'ctrl--': 'pane:split-down'
|
||||
'ctrl-w s': 'pane:split-down'
|
||||
|
||||
'meta-{': 'pane:show-previous-item'
|
||||
'meta-}': 'pane:show-next-item'
|
||||
'alt-meta-left': 'pane:show-previous-item'
|
||||
'alt-meta-right': 'pane:show-next-item'
|
||||
'meta-1': 'pane:show-item-1'
|
||||
'meta-2': 'pane:show-item-2'
|
||||
'meta-3': 'pane:show-item-3'
|
||||
'meta-4': 'pane:show-item-4'
|
||||
'meta-5': 'pane:show-item-5'
|
||||
'meta-6': 'pane:show-item-6'
|
||||
'meta-7': 'pane:show-item-7'
|
||||
'meta-8': 'pane:show-item-8'
|
||||
'meta-9': 'pane:show-item-9'
|
||||
'meta-T': 'pane:reopen-closed-item'
|
||||
'alt-meta-w': 'pane:close-other-items'
|
||||
'meta-P': 'pane:close'
|
||||
|
||||
'.tool-panel':
|
||||
'meta-escape': 'tool-panel:unfocus'
|
||||
'escape': 'core:close'
|
||||
'meta-w': 'noop'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
'input:not(.hidden-input)':
|
||||
'tab': 'core:focus-next'
|
||||
'shift-tab': 'core:focus-previous'
|
||||
'left': 'native!'
|
||||
'right': 'native!'
|
||||
'shift-left': 'native!'
|
||||
'shift-right': 'native!'
|
||||
'backspace': 'native!'
|
||||
'shift-backspace': 'native!'
|
||||
'delete': 'native!'
|
||||
'meta-z': 'native!'
|
||||
'meta-Z': 'native!'
|
||||
'meta-x': 'native!'
|
||||
'meta-c': 'native!'
|
||||
'meta-v': 'native!'
|
||||
|
||||
'button':
|
||||
'tab': 'core:focus-next'
|
||||
'shift-tab': 'core:focus-previous'
|
||||
@@ -0,0 +1,52 @@
|
||||
'.editor':
|
||||
# Platform Bindings
|
||||
'alt-left': 'editor:move-to-previous-word-boundary'
|
||||
'alt-right': 'editor:move-to-next-word-boundary'
|
||||
'alt-shift-left': 'editor:select-to-previous-word-boundary'
|
||||
'alt-shift-right': 'editor:select-to-next-word-boundary'
|
||||
'home': 'editor:move-to-first-character-of-line'
|
||||
'end': 'editor:move-to-end-of-line'
|
||||
'shift-home': 'editor:select-to-first-character-of-line'
|
||||
'shift-end': 'editor:select-to-end-of-line'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-t': 'editor:transpose'
|
||||
|
||||
'.editor:not(.mini)':
|
||||
# Atom Specific
|
||||
'ctrl-C': 'editor:copy-path'
|
||||
|
||||
# Sublime Parity
|
||||
'tab': 'editor:indent'
|
||||
'enter': 'editor:newline'
|
||||
'shift-tab': 'editor:outdent-selected-rows'
|
||||
'ctrl-K': 'editor:delete-line'
|
||||
'ctrl-shift-up': 'editor:add-selection-above'
|
||||
'ctrl-shift-down': 'editor:add-selection-below'
|
||||
|
||||
'.tool-panel':
|
||||
'escape': 'core:close'
|
||||
|
||||
'.tool-panel.panel-left, .tool-panel.panel-right':
|
||||
'escape': 'tool-panel:unfocus'
|
||||
|
||||
'.editor !important, .editor.mini !important':
|
||||
'escape': 'editor:consolidate-selections'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
'input:not(.hidden-input), .native-key-bindings':
|
||||
'tab': 'core:focus-next'
|
||||
'shift-tab': 'core:focus-previous'
|
||||
'left': 'native!'
|
||||
'right': 'native!'
|
||||
'shift-left': 'native!'
|
||||
'shift-right': 'native!'
|
||||
'backspace': 'native!'
|
||||
'shift-backspace': 'native!'
|
||||
'delete': 'native!'
|
||||
'ctrl-b': 'native!'
|
||||
'ctrl-f': 'native!'
|
||||
'ctrl-F': 'native!'
|
||||
'ctrl-B': 'native!'
|
||||
'ctrl-h': 'native!'
|
||||
'ctrl-d': 'native!'
|
||||
@@ -0,0 +1,154 @@
|
||||
'.platform-darwin':
|
||||
# Apple specific
|
||||
'cmd-q': 'application:quit'
|
||||
'cmd-h': 'application:hide'
|
||||
'cmd-H': 'application:hide-other-applications'
|
||||
'cmd-m': 'application:minimize'
|
||||
'alt-cmd-ctrl-m': 'application:zoom'
|
||||
|
||||
'ctrl-p': 'core:move-up'
|
||||
'ctrl-n': 'core:move-down'
|
||||
'ctrl-b': 'core:move-left'
|
||||
'ctrl-f': 'core:move-right'
|
||||
'ctrl-P': 'core:select-up'
|
||||
'ctrl-N': 'core:select-down'
|
||||
'ctrl-F': 'core:select-right'
|
||||
'ctrl-B': 'core:select-left'
|
||||
'ctrl-h': 'core:backspace'
|
||||
'ctrl-d': 'core:delete'
|
||||
|
||||
# Atom Specific
|
||||
'cmd-O': 'application:open-dev'
|
||||
'cmd-alt-ctrl-s': 'application:run-all-specs'
|
||||
'enter': 'core:confirm'
|
||||
'escape': 'core:cancel'
|
||||
'up': 'core:move-up'
|
||||
'down': 'core:move-down'
|
||||
'left': 'core:move-left'
|
||||
'right': 'core:move-right'
|
||||
'ctrl-alt-cmd-r': 'window:reload'
|
||||
'alt-cmd-i': 'window:toggle-dev-tools'
|
||||
'cmd-alt-ctrl-p': 'window:run-package-specs'
|
||||
|
||||
# Sublime Parity
|
||||
'cmd-,': 'application:show-settings'
|
||||
'cmd-N': 'application:new-window'
|
||||
'cmd-W': 'window:close'
|
||||
'cmd-o': 'application:open'
|
||||
'cmd-T': 'pane:reopen-closed-item'
|
||||
'cmd-n': 'application:new-file'
|
||||
'cmd-s': 'core:save'
|
||||
'cmd-S': 'core:save-as'
|
||||
'cmd-alt-s': 'window:save-all'
|
||||
'cmd-w': 'core:close'
|
||||
'cmd-ctrl-f': 'window:toggle-full-screen'
|
||||
'cmd-z': 'core:undo'
|
||||
'cmd-Z': 'core:redo'
|
||||
'cmd-y': 'core:redo'
|
||||
'cmd-x': 'core:cut'
|
||||
'cmd-c': 'core:copy'
|
||||
'cmd-v': 'core:paste'
|
||||
'shift-up': 'core:select-up'
|
||||
'shift-down': 'core:select-down'
|
||||
'shift-left': 'core:select-left'
|
||||
'shift-right': 'core:select-right'
|
||||
'delete': 'core:delete'
|
||||
'pageup': 'core:page-up'
|
||||
'pagedown': 'core:page-down'
|
||||
'backspace': 'core:backspace'
|
||||
'shift-backspace': 'core:backspace'
|
||||
'cmd-up': 'core:move-to-top'
|
||||
'cmd-down': 'core:move-to-bottom'
|
||||
'cmd-shift-up': 'core:select-to-top'
|
||||
'cmd-shift-down': 'core:select-to-bottom'
|
||||
'cmd-{': 'pane:show-previous-item'
|
||||
'cmd-}': 'pane:show-next-item'
|
||||
'cmd-alt-left': 'pane:show-previous-item'
|
||||
'cmd-alt-right': 'pane:show-next-item'
|
||||
'cmd-=': 'window:increase-font-size'
|
||||
'cmd-+': 'window:increase-font-size'
|
||||
'cmd--': 'window:decrease-font-size'
|
||||
|
||||
'cmd-k up': 'pane:split-up' # Atom Specific
|
||||
'cmd-k down': 'pane:split-down' # Atom Specific
|
||||
'cmd-k left': 'pane:split-left' # Atom Specific
|
||||
'cmd-k right': 'pane:split-right' # Atom Specific
|
||||
'cmd-k cmd-w': 'pane:close' # Atom Specific
|
||||
'cmd-k alt-cmd-w': 'pane:close-other-items' # Atom Specific
|
||||
'cmd-k cmd-left': 'window:focus-previous-pane'
|
||||
'cmd-k cmd-right': 'window:focus-next-pane'
|
||||
'cmd-1': 'pane:show-item-1'
|
||||
'cmd-2': 'pane:show-item-2'
|
||||
'cmd-3': 'pane:show-item-3'
|
||||
'cmd-4': 'pane:show-item-4'
|
||||
'cmd-5': 'pane:show-item-5'
|
||||
'cmd-6': 'pane:show-item-6'
|
||||
'cmd-7': 'pane:show-item-7'
|
||||
'cmd-8': 'pane:show-item-8'
|
||||
'cmd-9': 'pane:show-item-9'
|
||||
|
||||
'.platform-darwin .editor':
|
||||
# Apple Specific
|
||||
'cmd-backspace': 'editor:backspace-to-beginning-of-line'
|
||||
'cmd-delete': 'editor:backspace-to-beginning-of-line'
|
||||
'ctrl-A': 'editor:select-to-first-character-of-line'
|
||||
'ctrl-E': 'editor:select-to-end-of-line'
|
||||
'cmd-left': 'editor:move-to-first-character-of-line'
|
||||
'cmd-right': 'editor:move-to-end-of-line'
|
||||
'cmd-shift-left': 'editor:select-to-first-character-of-line'
|
||||
'cmd-shift-right': 'editor:select-to-end-of-line'
|
||||
'alt-backspace': 'editor:backspace-to-beginning-of-word'
|
||||
'alt-delete': 'editor:delete-to-end-of-word'
|
||||
'ctrl-a': 'editor:move-to-first-character-of-line'
|
||||
'ctrl-e': 'editor:move-to-end-of-line'
|
||||
'ctrl-k': 'editor:cut-to-end-of-line'
|
||||
|
||||
# Atom Specific
|
||||
'ctrl-W': 'editor:select-word'
|
||||
|
||||
# Sublime Parity
|
||||
'cmd-a': 'core:select-all'
|
||||
'cmd-alt-p': 'editor:log-cursor-scope'
|
||||
'cmd-k cmd-u': 'editor:upper-case'
|
||||
'cmd-k cmd-l': 'editor:lower-case'
|
||||
|
||||
'body.platform-darwin .editor:not(.mini)':
|
||||
# Atom specific
|
||||
'alt-cmd-z': 'editor:checkout-head-revision'
|
||||
'cmd-<': 'editor:scroll-to-cursor'
|
||||
'alt-cmd-ctrl-f': 'editor:fold-selection'
|
||||
'cmd-=': 'editor:auto-indent'
|
||||
|
||||
# Sublime Parity
|
||||
'cmd-enter': 'editor:newline-below'
|
||||
'cmd-shift-enter': 'editor:newline-above'
|
||||
'cmd-]': 'editor:indent-selected-rows'
|
||||
'cmd-[': 'editor:outdent-selected-rows'
|
||||
'ctrl-cmd-up': 'editor:move-line-up'
|
||||
'ctrl-cmd-down': 'editor:move-line-down'
|
||||
'cmd-/': 'editor:toggle-line-comments'
|
||||
'cmd-j': 'editor:join-line'
|
||||
'cmd-D': 'editor:duplicate-line'
|
||||
|
||||
'cmd-alt-[': 'editor:fold-current-row'
|
||||
'cmd-alt-]': 'editor:unfold-current-row'
|
||||
'cmd-alt-{': 'editor:fold-all' # Atom Specific
|
||||
'cmd-alt-}': 'editor:unfold-all' # Atom Specific
|
||||
'cmd-k cmd-0': 'editor:unfold-all'
|
||||
'cmd-k cmd-1': 'editor:fold-at-indent-level-1'
|
||||
'cmd-k cmd-2': 'editor:fold-at-indent-level-2'
|
||||
'cmd-k cmd-3': 'editor:fold-at-indent-level-3'
|
||||
'cmd-k cmd-4': 'editor:fold-at-indent-level-4'
|
||||
'cmd-k cmd-5': 'editor:fold-at-indent-level-5'
|
||||
'cmd-k cmd-6': 'editor:fold-at-indent-level-6'
|
||||
'cmd-k cmd-7': 'editor:fold-at-indent-level-7'
|
||||
'cmd-k cmd-8': 'editor:fold-at-indent-level-8'
|
||||
'cmd-k cmd-9': 'editor:fold-at-indent-level-9'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
'body.platform-darwin input:not(.hidden-input), body.platform-darwin .native-key-bindings':
|
||||
'cmd-z': 'native!'
|
||||
'cmd-Z': 'native!'
|
||||
'cmd-x': 'native!'
|
||||
'cmd-c': 'native!'
|
||||
'cmd-v': 'native!'
|
||||
@@ -1,50 +0,0 @@
|
||||
'.editor':
|
||||
'ctrl-K': 'editor:delete-line'
|
||||
'ctrl-W': 'editor:select-word'
|
||||
'meta-alt-p': 'editor:log-cursor-scope'
|
||||
'meta-u': 'editor:upper-case'
|
||||
'meta-U': 'editor:lower-case'
|
||||
|
||||
'.editor:not(.mini)':
|
||||
'enter': 'editor:newline'
|
||||
'meta-enter': 'editor:newline-below'
|
||||
'meta-shift-enter': 'editor:newline-above'
|
||||
'tab': 'editor:indent'
|
||||
'meta-=': 'editor:auto-indent'
|
||||
|
||||
'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'
|
||||
'shift-tab': 'editor:outdent-selected-rows'
|
||||
'meta-[': 'editor:outdent-selected-rows'
|
||||
'meta-]': 'editor:indent-selected-rows'
|
||||
|
||||
'meta-/': 'editor:toggle-line-comments'
|
||||
'ctrl-C': 'editor:copy-path'
|
||||
'ctrl-meta-up': 'editor:move-line-up'
|
||||
'ctrl-meta-down': 'editor:move-line-down'
|
||||
'meta-D': 'editor:duplicate-line'
|
||||
'meta-j': 'editor:join-line'
|
||||
'meta-<': 'editor:scroll-to-cursor'
|
||||
|
||||
'.editor.mini':
|
||||
'enter': 'core:confirm',
|
||||
'escape': 'core:cancel'
|
||||
'meta-w': 'core:cancel'
|
||||
|
||||
'.editor !important, .editor.mini !important':
|
||||
'escape': 'editor:consolidate-selections'
|
||||
@@ -1,33 +1,7 @@
|
||||
'body':
|
||||
'ctrl-p': 'core:move-up'
|
||||
'ctrl-n': 'core:move-down'
|
||||
'ctrl-b': 'core:move-left'
|
||||
'ctrl-f': 'core:move-right'
|
||||
'ctrl-P': 'core:select-up'
|
||||
'ctrl-N': 'core:select-down'
|
||||
'ctrl-F': 'core:select-right'
|
||||
'ctrl-B': 'core:select-left'
|
||||
'alt-ctrl-n': 'editor:add-selection-below'
|
||||
'alt-ctrl-p': 'editor:add-selection-above'
|
||||
'ctrl-h': 'core:backspace'
|
||||
'ctrl-d': 'core:delete'
|
||||
|
||||
'.editor':
|
||||
'alt-f': 'editor:move-to-end-of-word'
|
||||
'alt-F': 'editor:select-to-end-of-word'
|
||||
'alt-b': 'editor:move-to-beginning-of-word'
|
||||
'alt-B': 'editor:select-to-beginning-of-word'
|
||||
'ctrl-a': 'editor:move-to-first-character-of-line'
|
||||
'ctrl-e': 'editor:move-to-end-of-line'
|
||||
'alt-h': 'editor:backspace-to-beginning-of-word'
|
||||
'alt-d': 'editor:delete-to-end-of-word'
|
||||
'ctrl-k': 'editor:cut-to-end-of-line'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
'input:not(.hidden-input)':
|
||||
'ctrl-b': 'native!'
|
||||
'ctrl-f': 'native!'
|
||||
'ctrl-F': 'native!'
|
||||
'ctrl-B': 'native!'
|
||||
'ctrl-h': 'native!'
|
||||
'ctrl-d': 'native!'
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
'.editor':
|
||||
'alt-meta-z': 'editor:checkout-head-revision'
|
||||
@@ -1,4 +0,0 @@
|
||||
'.select-list .mini.editor':
|
||||
'enter': 'core:confirm'
|
||||
'escape': 'core:cancel'
|
||||
'meta-w': 'core:cancel'
|
||||
@@ -0,0 +1,102 @@
|
||||
'.platform-win32':
|
||||
# Atom Specific
|
||||
'enter': 'core:confirm'
|
||||
'escape': 'core:cancel'
|
||||
'up': 'core:move-up'
|
||||
'down': 'core:move-down'
|
||||
'left': 'core:move-left'
|
||||
'right': 'core:move-right'
|
||||
'ctrl-alt-r': 'window:reload'
|
||||
'ctrl-alt-i': 'window:toggle-dev-tools'
|
||||
'ctrl-alt-p': 'window:run-package-specs'
|
||||
'ctrl-alt-s': 'application:run-all-specs'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-N': 'application:new-window'
|
||||
'ctrl-W': 'window:close'
|
||||
'ctrl-o': 'application:open'
|
||||
'ctrl-T': 'pane:reopen-closed-item'
|
||||
'ctrl-n': 'application:new-file'
|
||||
'ctrl-s': 'core:save'
|
||||
'ctrl-S': 'core:save-as'
|
||||
'ctrl-w': 'core:close'
|
||||
'ctrl-z': 'core:undo'
|
||||
'ctrl-y': 'core:redo'
|
||||
'ctrl-x': 'core:cut'
|
||||
'ctrl-c': 'core:copy'
|
||||
'ctrl-v': 'core:paste'
|
||||
'shift-up': 'core:select-up'
|
||||
'shift-down': 'core:select-down'
|
||||
'shift-left': 'core:select-left'
|
||||
'shift-right': 'core:select-right'
|
||||
'delete': 'core:delete'
|
||||
'pageup': 'core:page-up'
|
||||
'pagedown': 'core:page-down'
|
||||
'backspace': 'core:backspace'
|
||||
'ctrl-tab': 'pane:show-next-item'
|
||||
'ctrl-shift-tab': 'pane:show-previous-item'
|
||||
'ctrl-shift-up': 'core:move-up'
|
||||
'ctrl-shift-down': 'core:move-down'
|
||||
'ctrl-=': 'window:increase-font-size'
|
||||
'ctrl-+': 'window:increase-font-size'
|
||||
'ctrl--': 'window:decrease-font-size'
|
||||
|
||||
'ctrl-k up': 'pane:split-up' # Atom Specific
|
||||
'ctrl-k down': 'pane:split-down' # Atom Specific
|
||||
'ctrl-k left': 'pane:split-left' # Atom Specific
|
||||
'ctrl-k right': 'pane:split-right' # Atom Specific
|
||||
'ctrl-k ctrl-w': 'pane:close' # Atom Specific
|
||||
'ctrl-k alt-ctrl-w': 'pane:close-other-items' # Atom Specific
|
||||
'ctrl-k ctrl-left': 'window:focus-previous-pane'
|
||||
'ctrl-k ctrl-right': 'window:focus-next-pane'
|
||||
|
||||
'.platform-win32 .editor':
|
||||
# Windows specific
|
||||
'ctrl-delete': 'editor:backspace-to-beginning-of-word'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-a': 'core:select-all'
|
||||
'ctrl-alt-p': 'editor:log-cursor-scope'
|
||||
'ctrl-k ctrl-u': 'editor:upper-case'
|
||||
'ctrl-k ctrl-l': 'editor:lower-case'
|
||||
|
||||
'.platform-win32 .editor:not(.mini)':
|
||||
# Atom specific
|
||||
'alt-ctrl-z': 'editor:checkout-head-revision'
|
||||
'ctrl-<': 'editor:scroll-to-cursor'
|
||||
'alt-ctrl-f': 'editor:fold-selection'
|
||||
'ctrl-=': 'editor:auto-indent'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-enter': 'editor:newline-below'
|
||||
'ctrl-shift-enter': 'editor:newline-above'
|
||||
'ctrl-]': 'editor:indent-selected-rows'
|
||||
'ctrl-[': 'editor:outdent-selected-rows'
|
||||
'ctrl-up': 'editor:move-line-up'
|
||||
'ctrl-down': 'editor:move-line-down'
|
||||
'ctrl-/': 'editor:toggle-line-comments'
|
||||
'ctrl-j': 'editor:join-line'
|
||||
'ctrl-D': 'editor:duplicate-line'
|
||||
|
||||
'ctrl-alt-[': 'editor:fold-current-row'
|
||||
'ctrl-alt-]': 'editor:unfold-current-row'
|
||||
'ctrl-alt-{': 'editor:fold-all' # Atom Specific
|
||||
'ctrl-alt-}': 'editor:unfold-all' # Atom Specific
|
||||
'ctrl-k ctrl-0': 'editor:unfold-all'
|
||||
'ctrl-k ctrl-1': 'editor:fold-at-indent-level-1'
|
||||
'ctrl-k ctrl-2': 'editor:fold-at-indent-level-2'
|
||||
'ctrl-k ctrl-3': 'editor:fold-at-indent-level-3'
|
||||
'ctrl-k ctrl-4': 'editor:fold-at-indent-level-4'
|
||||
'ctrl-k ctrl-5': 'editor:fold-at-indent-level-5'
|
||||
'ctrl-k ctrl-6': 'editor:fold-at-indent-level-6'
|
||||
'ctrl-k ctrl-7': 'editor:fold-at-indent-level-7'
|
||||
'ctrl-k ctrl-8': 'editor:fold-at-indent-level-8'
|
||||
'ctrl-k ctrl-9': 'editor:fold-at-indent-level-9'
|
||||
|
||||
# allow standard input fields to work correctly
|
||||
'.platform-win32 input:not(.hidden-input), .platform-win32 .native-key-bindings':
|
||||
'ctrl-z': 'native!'
|
||||
'ctrl-Z': 'native!'
|
||||
'ctrl-x': 'native!'
|
||||
'ctrl-c': 'native!'
|
||||
'ctrl-v': 'native!'
|
||||
@@ -154,11 +154,6 @@
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: 'Collaboration'
|
||||
submenu: []
|
||||
}
|
||||
|
||||
{
|
||||
label: 'Packages'
|
||||
submenu: []
|
||||
@@ -0,0 +1,172 @@
|
||||
'menu': [
|
||||
{
|
||||
label: '&File'
|
||||
submenu: [
|
||||
{ label: 'New &Window', command: 'application:new-window' }
|
||||
{ label: '&New File', command: 'application:new-file' }
|
||||
{ label: '&Open...', command: 'application:open' }
|
||||
{ label: 'Reopen Last &Item', command: 'pane:reopen-closed-item' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Preferences...', command: 'application:show-settings' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Save', command: 'core:save' }
|
||||
{ label: 'Save &As...', command: 'core:save-as' }
|
||||
{ label: 'Save A&ll', command: 'window:save-all' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Close Buffer', command: 'core:close' }
|
||||
{ label: 'Close All &Buffers', command: 'pane:close' }
|
||||
{ label: 'Clos&e Window', command: 'window:close' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'E&xit', command: 'application:quit' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Edit'
|
||||
submenu: [
|
||||
{ label: '&Undo', command: 'core:undo' }
|
||||
{ label: '&Redo', command: 'core:redo' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Cut', command: 'core:cut' }
|
||||
{ label: 'C&opy', command: 'core:copy' }
|
||||
{ label: 'Copy Pat&h', command: 'editor:copy-path' }
|
||||
{ label: '&Paste', command: 'core:paste' }
|
||||
{ label: 'Select &All', command: 'core:select-all' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Toggle Comments', command: 'editor:toggle-line-comments' }
|
||||
{
|
||||
label: 'Lines',
|
||||
submenu: [
|
||||
{ label: '&Indent', command: 'editor:indent-selected-rows' }
|
||||
{ label: '&Outdent', command: 'editor:outdent-selected-rows' }
|
||||
{ label: '&Auto Indent', command: 'editor:auto-indent' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Move Line &Up', command: 'editor:move-line-up' }
|
||||
{ label: 'Move Line &Down', command: 'editor:move-line-down' }
|
||||
{ label: 'Du&plicate Line', command: 'editor:duplicate-line' }
|
||||
{ label: 'D&elete Line', command: 'editor:delete-line' }
|
||||
{ label: '&Join Lines', command: 'editor:join-line' }
|
||||
]
|
||||
}
|
||||
{
|
||||
label: 'Text',
|
||||
submenu: [
|
||||
{ label: '&Upper Case', command: 'editor:upper-case' }
|
||||
{ label: '&Lower Case', command: 'editor:lower-case' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Delete to End of &Word', command: 'editor:delete-to-end-of-word' }
|
||||
{ label: '&Delete Line', command: 'editor:delete-line' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Transpose', command: 'editor:transpose' }
|
||||
]
|
||||
}
|
||||
{
|
||||
label: 'Folding',
|
||||
submenu: [
|
||||
{ label: '&Fold', command: 'editor:fold-current-row' }
|
||||
{ label: '&Unfold', command: 'editor:unfold-current-row' }
|
||||
{ label: 'Unfold &All', command: 'editor:unfold-all' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Fol&d All', command: 'editor:fold-all' }
|
||||
{ label: 'Fold Level 1', command: 'editor:fold-at-indent-level-1' }
|
||||
{ label: 'Fold Level 2', command: 'editor:fold-at-indent-level-2' }
|
||||
{ label: 'Fold Level 3', command: 'editor:fold-at-indent-level-3' }
|
||||
{ label: 'Fold Level 4', command: 'editor:fold-at-indent-level-4' }
|
||||
{ label: 'Fold Level 5', command: 'editor:fold-at-indent-level-5' }
|
||||
{ label: 'Fold Level 6', command: 'editor:fold-at-indent-level-6' }
|
||||
{ label: 'Fold Level 7', command: 'editor:fold-at-indent-level-7' }
|
||||
{ label: 'Fold Level 8', command: 'editor:fold-at-indent-level-8' }
|
||||
{ label: 'Fold Level 9', command: 'editor:fold-at-indent-level-9' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: '&View'
|
||||
submenu: [
|
||||
{ label: '&Reload', command: 'window:reload' }
|
||||
{ label: 'Toggle &Full Screen', command: 'window:toggle-full-screen' }
|
||||
{
|
||||
label: 'Developer'
|
||||
submenu: [
|
||||
{ label: 'Open In &Dev Mode...', command: 'application:open-dev' }
|
||||
{ label: 'Run &Atom Specs', command: 'application:run-all-specs' }
|
||||
{ label: 'Run Package &Specs', command: 'window:run-package-specs' }
|
||||
{ label: 'Toggle Developer &Tools', command: 'window:toggle-dev-tools' }
|
||||
]
|
||||
}
|
||||
{ type: 'separator' }
|
||||
{ label: 'Toggle Soft &Wrap', command: 'editor:toggle-soft-wrap' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Selection'
|
||||
submenu: [
|
||||
{ label: 'Add Selection &Above', command: 'editor:add-selection-above' }
|
||||
{ label: 'Add Selection &Below', command: 'editor:add-selection-below' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Select to &Top', command: 'core:select-to-top' }
|
||||
{ label: 'Select to Botto&m', command: 'core:select-to-bottom' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Select &Line', command: 'editor:select-line' }
|
||||
{ label: 'Select &Word', command: 'editor:select-word' }
|
||||
{ label: 'Select to Beginning of W&ord', command: 'editor:select-to-beginning-of-word' }
|
||||
{ label: 'Select to Beginning of L&ine', command: 'editor:select-to-beginning-of-line' }
|
||||
{ label: 'Select to First &Character of Line', command: 'editor:select-to-first-character-of-line' }
|
||||
{ label: 'Select to End of Wor&d', command: 'editor:select-to-end-of-word' }
|
||||
{ label: 'Select to End of Lin&e', command: 'editor:select-to-end-of-line' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Movement'
|
||||
submenu: [
|
||||
{ label: 'Move to &Top', command: 'core:move-to-top' }
|
||||
{ label: 'Move to &Bottom', command: 'core:move-to-bottom' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Move to Beginning of &Line', command: 'editor:move-to-beginning-of-line' }
|
||||
{ label: 'Move to &First Character of Line', command: 'editor:move-to-first-character-of-line' }
|
||||
{ label: 'Move to &End of Line', command: 'editor:move-to-end-of-line' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Move to Beginning of &Word', command: 'editor:move-to-beginning-of-word' }
|
||||
{ label: 'Move to End of Wor&d', command: 'editor:move-to-end-of-word' }
|
||||
{ label: 'Move to &Next Word', command: 'editor:move-to-next-word-boundary' }
|
||||
{ label: 'Move to &Previous Word', command: 'editor:move-to-previous-word-boundary' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: 'F&ind'
|
||||
submenu: []
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Packages'
|
||||
submenu: []
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Window'
|
||||
submenu: [
|
||||
{ label: 'Mi&nimize', command: 'application:minimize' }
|
||||
{ label: 'Ma&ximize', command: 'application:zoom' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Bring &All to Front', command: 'application:bring-all-windows-to-front' }
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
label: '&Help'
|
||||
submenu: [
|
||||
{ label: '&About Atom...', command: 'application:about' }
|
||||
{ label: "VERSION", enabled: false }
|
||||
{ label: "Install &update", command: 'application:install-update', visible: false }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Documentation', command: 'application:open-documentation' }
|
||||
{ label: 'Report an &Issue', command: 'application:report-issue' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
}
|
||||
]
|
||||
+95
-95
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"version": "32.0.0",
|
||||
"version": "39.0.0",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -9,37 +9,35 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/atom/atom/issues"
|
||||
},
|
||||
"atomShellVersion": "0.6.4",
|
||||
"atomShellVersion": "0.6.11",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"bootstrap": "git://github.com/twbs/bootstrap.git#v3.0.0",
|
||||
"clear-cut": "0.1.0",
|
||||
"coffee-script": "1.6.2",
|
||||
"bootstrap": "git://github.com/benogle/bootstrap.git",
|
||||
"clear-cut": "0.2.0",
|
||||
"coffee-script": "1.6.3",
|
||||
"coffeestack": "0.6.0",
|
||||
"emissary": "0.6.0",
|
||||
"first-mate": "0.4.0",
|
||||
"emissary": "0.19.0",
|
||||
"first-mate": "0.5.0",
|
||||
"fs-plus": "0.9.0",
|
||||
"fuzzaldrin": "0.1.0",
|
||||
"git-utils": "0.26.0",
|
||||
"git-utils": "0.29.0",
|
||||
"guid": "0.0.10",
|
||||
"jasmine-focused": "~0.15.0",
|
||||
"mkdirp": "0.3.5",
|
||||
"less": "git://github.com/nathansobo/less.js.git",
|
||||
"less-cache": "0.8.0",
|
||||
"less-cache": "0.10.0",
|
||||
"nslog": "0.1.0",
|
||||
"oniguruma": "0.21.0",
|
||||
"oniguruma": "0.24.0",
|
||||
"optimist": "0.4.0",
|
||||
"pathwatcher": "0.5.0",
|
||||
"pathwatcher": "0.10.0",
|
||||
"pegjs": "0.7.0",
|
||||
"plist": "git://github.com/nathansobo/node-plist.git",
|
||||
"q": "0.9.7",
|
||||
"rimraf": "2.1.4",
|
||||
"scandal": "0.5.0",
|
||||
"season": "0.13.0",
|
||||
"scandal": "0.8.0",
|
||||
"season": "0.14.0",
|
||||
"semver": "1.1.4",
|
||||
"space-pen": "2.0.0",
|
||||
"telepath": "0.8.1",
|
||||
"telepath": "0.45.0",
|
||||
"temp": "0.5.0",
|
||||
"underscore-plus": "0.2.0"
|
||||
"underscore-plus": "0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"biscotto": "0.0.17",
|
||||
@@ -47,105 +45,107 @@
|
||||
"fstream": "0.1.24",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-cli": "~0.1.9",
|
||||
"grunt-coffeelint": "0.0.6",
|
||||
"grunt-coffeelint": "git://github.com/atom/grunt-coffeelint.git",
|
||||
"grunt-lesslint": "0.13.0",
|
||||
"grunt-cson": "0.5.0",
|
||||
"grunt-contrib-csslint": "~0.1.2",
|
||||
"grunt-contrib-coffee": "~0.7.0",
|
||||
"grunt-contrib-less": "~0.6.4",
|
||||
"grunt-contrib-less": "~0.8.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",
|
||||
"request": "~2.27.0"
|
||||
"jasmine-tagged": "0.2.0",
|
||||
"request": "~2.27.0",
|
||||
"unzip": "~0.1.9",
|
||||
"rcedit": "~0.1.2",
|
||||
"rimraf": "~2.2.2"
|
||||
},
|
||||
"packageDependencies" : {
|
||||
"atom-light-ui": "0.5.0",
|
||||
"atom-light-syntax": "0.5.0",
|
||||
"atom-dark-ui": "0.5.0",
|
||||
"atom-dark-syntax": "0.5.0",
|
||||
"base16-tomorrow-dark-theme": "0.2.0",
|
||||
"solarized-dark-syntax": "0.3.0",
|
||||
|
||||
"archive-view": "0.11.0",
|
||||
"autocomplete": "0.11.0",
|
||||
"autoflow": "0.5.0",
|
||||
"bookmarks": "0.8.0",
|
||||
"bracket-matcher": "0.7.0",
|
||||
"collaboration": "0.29.0",
|
||||
"command-logger": "0.6.0",
|
||||
"command-palette": "0.5.0",
|
||||
"dev-live-reload": "0.11.0",
|
||||
"editor-stats": "0.5.0",
|
||||
"exception-reporting": "0.5.0",
|
||||
"find-and-replace": "0.31.0",
|
||||
"fuzzy-finder": "0.15.0",
|
||||
"gfm": "0.5.0",
|
||||
"git-diff": "0.12.0",
|
||||
"gists": "0.5.0",
|
||||
"github-sign-in": "0.8.0",
|
||||
"go-to-line": "0.8.0",
|
||||
"grammar-selector": "0.8.0",
|
||||
"image-view": "0.7.0",
|
||||
"link": "0.7.0",
|
||||
"markdown-preview": "0.12.0",
|
||||
"metrics": "0.8.0",
|
||||
"package-generator": "0.14.0",
|
||||
"release-notes": "0.8.0",
|
||||
"settings-view": "0.33.0",
|
||||
"snippets": "0.11.0",
|
||||
"spell-check": "0.8.0",
|
||||
"status-bar": "0.15.0",
|
||||
"styleguide": "0.9.0",
|
||||
"symbols-view": "0.14.0",
|
||||
"tabs": "0.7.0",
|
||||
"terminal": "0.14.0",
|
||||
"timecop": "0.7.0",
|
||||
"to-the-hubs": "0.8.0",
|
||||
"toml": "0.3.0",
|
||||
"tree-view": "0.21.0",
|
||||
"visual-bell": "0.3.0",
|
||||
"whitespace": "0.7.0",
|
||||
"wrap-guide": "0.4.0",
|
||||
|
||||
"language-c": "0.1.0",
|
||||
"packageDependencies": {
|
||||
"atom-light-ui": "0.8.0",
|
||||
"atom-light-syntax": "0.6.0",
|
||||
"atom-dark-ui": "0.8.0",
|
||||
"atom-dark-syntax": "0.6.0",
|
||||
"base16-tomorrow-dark-theme": "0.6.0",
|
||||
"solarized-dark-syntax": "0.4.0",
|
||||
"archive-view": "0.14.0",
|
||||
"autocomplete": "0.15.0",
|
||||
"autoflow": "0.9.0",
|
||||
"autosave": "0.7.0",
|
||||
"bookmarks": "0.13.0",
|
||||
"bracket-matcher": "0.12.0",
|
||||
"command-logger": "0.7.0",
|
||||
"command-palette": "0.10.0",
|
||||
"dev-live-reload": "0.17.0",
|
||||
"editor-stats": "0.7.0",
|
||||
"exception-reporting": "0.8.0",
|
||||
"find-and-replace": "0.49.0",
|
||||
"fuzzy-finder": "0.25.0",
|
||||
"gists": "0.11.0",
|
||||
"git-diff": "0.16.0",
|
||||
"github-sign-in": "0.11.0",
|
||||
"go-to-line": "0.10.0",
|
||||
"grammar-selector": "0.11.0",
|
||||
"image-view": "0.9.0",
|
||||
"keybinding-resolver": "0.5.0",
|
||||
"link": "0.9.0",
|
||||
"markdown-preview": "0.19.0",
|
||||
"metrics": "0.12.0",
|
||||
"package-generator": "0.21.0",
|
||||
"release-notes": "0.12.0",
|
||||
"settings-view": "0.47.0",
|
||||
"snippets": "0.15.0",
|
||||
"spell-check": "0.15.0",
|
||||
"status-bar": "0.22.0",
|
||||
"styleguide": "0.15.0",
|
||||
"symbols-view": "0.24.0",
|
||||
"tabs": "0.12.0",
|
||||
"terminal": "0.22.0",
|
||||
"timecop": "0.10.0",
|
||||
"to-the-hubs": "0.13.0",
|
||||
"tree-view": "0.39.0",
|
||||
"visual-bell": "0.4.0",
|
||||
"whitespace": "0.9.0",
|
||||
"wrap-guide": "0.6.0",
|
||||
"language-c": "0.2.0",
|
||||
"language-clojure": "0.1.0",
|
||||
"language-coffee-script": "0.1.0",
|
||||
"language-css": "0.1.0",
|
||||
"language-git": "0.1.0",
|
||||
"language-coffee-script": "0.3.0",
|
||||
"language-css": "0.2.0",
|
||||
"language-gfm": "0.9.0",
|
||||
"language-git": "0.3.0",
|
||||
"language-go": "0.2.0",
|
||||
"language-html": "0.1.0",
|
||||
"language-hyperlink": "0.2.0",
|
||||
"language-java": "0.1.0",
|
||||
"language-javascript": "0.1.0",
|
||||
"language-json": "0.1.0",
|
||||
"language-html": "0.2.0",
|
||||
"language-hyperlink": "0.3.0",
|
||||
"language-java": "0.2.0",
|
||||
"language-javascript": "0.3.0",
|
||||
"language-json": "0.2.0",
|
||||
"language-less": "0.1.0",
|
||||
"language-make": "0.1.0",
|
||||
"language-mustache": "0.1.0",
|
||||
"language-objective-c": "0.1.0",
|
||||
"language-objective-c": "0.2.0",
|
||||
"language-pegjs": "0.1.0",
|
||||
"language-perl": "0.1.0",
|
||||
"language-php": "0.1.0",
|
||||
"language-perl": "0.2.0",
|
||||
"language-php": "0.2.0",
|
||||
"language-property-list": "0.2.0",
|
||||
"language-puppet": "0.1.0",
|
||||
"language-python": "0.1.0",
|
||||
"language-ruby-on-rails": "0.2.0",
|
||||
"language-ruby": "0.2.0",
|
||||
"language-sass": "0.2.0",
|
||||
"language-shellscript": "0.1.0",
|
||||
"language-source": "0.1.0",
|
||||
"language-sql": "0.1.0",
|
||||
"language-text": "0.1.0",
|
||||
"language-puppet": "0.2.0",
|
||||
"language-python": "0.2.0",
|
||||
"language-ruby": "0.4.0",
|
||||
"language-ruby-on-rails": "0.3.0",
|
||||
"language-sass": "0.3.0",
|
||||
"language-shellscript": "0.2.0",
|
||||
"language-source": "0.2.0",
|
||||
"language-sql": "0.2.0",
|
||||
"language-text": "0.2.0",
|
||||
"language-todo": "0.2.0",
|
||||
"language-xml": "0.1.0",
|
||||
"language-toml": "0.7.0",
|
||||
"language-xml": "0.2.0",
|
||||
"language-yaml": "0.1.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"preinstall": "true",
|
||||
"test": "script/test"
|
||||
"preinstall": "node -e 'process.exit(0)'",
|
||||
"test": "node script/test"
|
||||
}
|
||||
}
|
||||
|
||||
Arquivo executável
BIN
Arquivo binário não exibido.
|
Depois Largura: | Altura: | Tamanho: 52 KiB |
Arquivo binário não exibido.
|
Depois Largura: | Altura: | Tamanho: 345 KiB |
+13
-6
@@ -5,9 +5,15 @@ var path = require('path');
|
||||
// Executes an array of commands one by one.
|
||||
function executeCommands(commands, done, index) {
|
||||
index = (index == undefined ? 0 : index);
|
||||
if (index < commands.length)
|
||||
safeExec(commands[index], executeCommands.bind(this, commands, done, index + 1));
|
||||
else
|
||||
if (index < commands.length) {
|
||||
var command = commands[index];
|
||||
var options = null;
|
||||
if (typeof command !== 'string') {
|
||||
options = command.options;
|
||||
command = command.command;
|
||||
}
|
||||
safeExec(command, options, executeCommands.bind(this, commands, done, index + 1));
|
||||
} else
|
||||
done(null);
|
||||
}
|
||||
|
||||
@@ -21,10 +27,11 @@ var echoNewLine = process.platform == 'win32' ? 'echo.' : 'echo';
|
||||
var commands = [
|
||||
'git submodule --quiet sync',
|
||||
'git submodule --quiet update --recursive --init',
|
||||
joinCommands('cd vendor/apm', 'npm install --silent .'),
|
||||
'npm install --silent vendor/apm',
|
||||
{command: joinCommands('cd vendor/apm', 'npm install --silent .'), options: {ignoreStdout: true}},
|
||||
{command: 'npm install --silent vendor/apm', options: {ignoreStdout: true}},
|
||||
echoNewLine,
|
||||
'node vendor/apm/bin/apm install --silent',
|
||||
'node node_modules/atom-package-manager/bin/apm clean',
|
||||
'node node_modules/atom-package-manager/bin/apm install --silent',
|
||||
];
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@ var path = require('path');
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
||||
cp.safeExec('node script/bootstrap', function() {
|
||||
// ./node_modules/.bin/grunt "$@"
|
||||
var gruntPath = path.join('node_modules', '.bin', 'grunt');
|
||||
// node node_modules/grunt-cli/bin/grunt "$@"
|
||||
var gruntPath = path.join('node_modules', 'grunt-cli', 'bin', 'grunt');
|
||||
var args = [gruntPath].concat(process.argv.slice(2));
|
||||
cp.safeSpawn(process.execPath, args, process.exit);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
@IF EXIST "%~dp0\node.exe" (
|
||||
"%~dp0\node.exe" "%~dp0\build" %*
|
||||
) ELSE (
|
||||
node "%~dp0\build" %*
|
||||
)
|
||||
+24
-25
@@ -10,31 +10,30 @@ if (process.platform != 'darwin')
|
||||
|
||||
var homeDir = process.platform == 'win32' ? process.env.USERPROFILE : process.env.HOME;
|
||||
|
||||
function readEnvironmentVariables(callback) {
|
||||
var credenticalsPath = '/var/lib/jenkins/config/atomcredentials';
|
||||
fs.readFile(credenticalsPath, function(error, data) {
|
||||
if (!error) {
|
||||
var lines = String(data).trim().split('\n');
|
||||
for (i in lines) {
|
||||
var parts = lines[i].split('=');
|
||||
var key = parts[0].trim();
|
||||
var value = parts[1].trim().substr(1, parts[1].length - 2);
|
||||
process.env[key] = value;
|
||||
}
|
||||
function readEnvironmentVariables() {
|
||||
var credentialsPath = '/var/lib/jenkins/config/atomcredentials';
|
||||
try {
|
||||
var credentials = fs.readFileSync(credentialsPath, 'utf8');
|
||||
var lines = credentials.trim().split('\n');
|
||||
for (i in lines) {
|
||||
var parts = lines[i].split('=');
|
||||
var key = parts[0].trim();
|
||||
var value = parts[1].trim().substr(1, parts[1].length - 2);
|
||||
process.env[key] = value;
|
||||
}
|
||||
// Do not quit when got error.
|
||||
callback(null);
|
||||
});
|
||||
} catch(error) { }
|
||||
}
|
||||
|
||||
var async = require('async');
|
||||
async.series([
|
||||
readEnvironmentVariables,
|
||||
cp.safeExec.bind(global, 'node script/bootstrap'),
|
||||
require('rimraf').bind(global, path.join(homeDir, '.atom')),
|
||||
cp.safeExec.bind(global, 'git clean -dff'),
|
||||
cp.safeExec.bind(global, 'node node_modules/.bin/apm clean'),
|
||||
cp.safeExec.bind(global, 'node node_modules/.bin/grunt ci --stack --no-color'),
|
||||
], function(error) {
|
||||
process.exit(error ? 1 : 0);
|
||||
});
|
||||
readEnvironmentVariables();
|
||||
cp.safeExec.bind(global, 'node script/bootstrap', function(error) {
|
||||
if (error)
|
||||
process.exit(1);
|
||||
var async = require('async');
|
||||
async.series([
|
||||
require('rimraf').bind(global, path.join(homeDir, '.atom')),
|
||||
cp.safeExec.bind(global, 'git clean -dff'),
|
||||
cp.safeExec.bind(global, 'node node_modules/grunt-cli/bin/grunt ci --stack --no-color'),
|
||||
], function(error) {
|
||||
process.exit(error ? 1 : 0);
|
||||
});
|
||||
})();
|
||||
|
||||
+1
-1
@@ -5,5 +5,5 @@ var path = require('path');
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
||||
safeExec('node script/bootstrap', function() {
|
||||
safeExec('node node_modules/.bin/grunt ci --stack --no-color', process.exit);
|
||||
safeExec('node node_modules/grunt-cli/bin/grunt ci --stack --no-color', process.exit);
|
||||
});
|
||||
|
||||
@@ -7,14 +7,22 @@ exports.safeExec = function(command, options, callback) {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
// This needed to be increased for `apm test` runs that generate many failures
|
||||
// The default is 200KB.
|
||||
options.maxBuffer = 1024 * 1024;
|
||||
|
||||
var child = childProcess.exec(command, options, function(error, stdout, stderr) {
|
||||
if (error)
|
||||
process.exit(error.code);
|
||||
process.exit(error.code || 1);
|
||||
else
|
||||
callback(null);
|
||||
});
|
||||
child.stderr.pipe(process.stderr);
|
||||
child.stdout.pipe(process.stdout);
|
||||
if (!options.ignoreStdout)
|
||||
child.stdout.pipe(process.stdout);
|
||||
}
|
||||
|
||||
// Same with safeExec but call child_process.spawn instead.
|
||||
|
||||
@@ -15,14 +15,14 @@ describe "AtomPackage", ->
|
||||
describe "when the theme contains a single style file", ->
|
||||
it "loads and applies css", ->
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "1234px"
|
||||
themePath = project.resolve('packages/theme-with-index-css')
|
||||
themePath = atom.project.resolve('packages/theme-with-index-css')
|
||||
theme = Package.load(themePath)
|
||||
theme.activate()
|
||||
expect($(".editor").css("padding-top")).toBe "1234px"
|
||||
|
||||
it "parses, loads and applies less", ->
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "1234px"
|
||||
themePath = project.resolve('packages/theme-with-index-less')
|
||||
themePath = atom.project.resolve('packages/theme-with-index-less')
|
||||
theme = Package.load(themePath)
|
||||
theme.activate()
|
||||
expect($(".editor").css("padding-top")).toBe "4321px"
|
||||
@@ -33,7 +33,7 @@ describe "AtomPackage", ->
|
||||
expect($(".editor").css("padding-right")).not.toBe("102px")
|
||||
expect($(".editor").css("padding-bottom")).not.toBe("103px")
|
||||
|
||||
themePath = project.resolve('packages/theme-with-package-file')
|
||||
themePath = atom.project.resolve('packages/theme-with-package-file')
|
||||
theme = Package.load(themePath)
|
||||
theme.activate()
|
||||
expect($(".editor").css("padding-top")).toBe("101px")
|
||||
@@ -46,7 +46,7 @@ describe "AtomPackage", ->
|
||||
expect($(".editor").css("padding-right")).not.toBe "20px"
|
||||
expect($(".editor").css("padding-bottom")).not.toBe "30px"
|
||||
|
||||
themePath = project.resolve('packages/theme-without-package-file')
|
||||
themePath = atom.project.resolve('packages/theme-without-package-file')
|
||||
theme = Package.load(themePath)
|
||||
theme.activate()
|
||||
expect($(".editor").css("padding-top")).toBe "10px"
|
||||
@@ -55,7 +55,7 @@ describe "AtomPackage", ->
|
||||
|
||||
describe "reloading a theme", ->
|
||||
beforeEach ->
|
||||
themePath = project.resolve('packages/theme-with-package-file')
|
||||
themePath = atom.project.resolve('packages/theme-with-package-file')
|
||||
theme = Package.load(themePath)
|
||||
theme.activate()
|
||||
|
||||
@@ -66,7 +66,7 @@ describe "AtomPackage", ->
|
||||
|
||||
describe "events", ->
|
||||
beforeEach ->
|
||||
themePath = project.resolve('packages/theme-with-package-file')
|
||||
themePath = atom.project.resolve('packages/theme-with-package-file')
|
||||
theme = Package.load(themePath)
|
||||
theme.activate()
|
||||
|
||||
|
||||
+128
-116
@@ -5,16 +5,16 @@ ThemeManager = require '../src/theme-manager'
|
||||
|
||||
describe "the `atom` global", ->
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
atom.rootView = new RootView
|
||||
|
||||
describe "package lifecycle methods", ->
|
||||
describe ".loadPackage(name)", ->
|
||||
describe "when the package has deferred deserializers", ->
|
||||
it "requires the package's main module if one of its deferred deserializers is referenced", ->
|
||||
pack = atom.loadPackage('package-with-activation-events')
|
||||
pack = atom.packages.loadPackage('package-with-activation-events')
|
||||
spyOn(pack, 'activateStylesheets').andCallThrough()
|
||||
expect(pack.mainModule).toBeNull()
|
||||
object = deserialize({deserializer: 'Foo', data: 5})
|
||||
object = atom.deserializers.deserialize({deserializer: 'Foo', data: 5})
|
||||
expect(pack.mainModule).toBeDefined()
|
||||
expect(object.constructor.name).toBe 'Foo'
|
||||
expect(object.data).toBe 5
|
||||
@@ -22,35 +22,35 @@ describe "the `atom` global", ->
|
||||
|
||||
it "continues if the package has an invalid package.json", ->
|
||||
spyOn(console, 'warn')
|
||||
config.set("core.disabledPackages", [])
|
||||
expect(-> atom.loadPackage("package-with-broken-package-json")).not.toThrow()
|
||||
atom.config.set("core.disabledPackages", [])
|
||||
expect(-> atom.packages.loadPackage("package-with-broken-package-json")).not.toThrow()
|
||||
|
||||
it "continues if the package has an invalid keymap", ->
|
||||
config.set("core.disabledPackages", [])
|
||||
expect(-> atom.loadPackage("package-with-broken-keymap")).not.toThrow()
|
||||
atom.config.set("core.disabledPackages", [])
|
||||
expect(-> atom.packages.loadPackage("package-with-broken-keymap")).not.toThrow()
|
||||
|
||||
describe ".unloadPackage(name)", ->
|
||||
describe "when the package is active", ->
|
||||
it "throws an error", ->
|
||||
pack = atom.activatePackage('package-with-main')
|
||||
expect(atom.isPackageLoaded(pack.name)).toBeTruthy()
|
||||
expect(atom.isPackageActive(pack.name)).toBeTruthy()
|
||||
expect( -> atom.unloadPackage(pack.name)).toThrow()
|
||||
expect(atom.isPackageLoaded(pack.name)).toBeTruthy()
|
||||
expect(atom.isPackageActive(pack.name)).toBeTruthy()
|
||||
pack = atom.packages.activatePackage('package-with-main')
|
||||
expect(atom.packages.isPackageLoaded(pack.name)).toBeTruthy()
|
||||
expect(atom.packages.isPackageActive(pack.name)).toBeTruthy()
|
||||
expect( -> atom.packages.unloadPackage(pack.name)).toThrow()
|
||||
expect(atom.packages.isPackageLoaded(pack.name)).toBeTruthy()
|
||||
expect(atom.packages.isPackageActive(pack.name)).toBeTruthy()
|
||||
|
||||
describe "when the package is not loaded", ->
|
||||
it "throws an error", ->
|
||||
expect(atom.isPackageLoaded('unloaded')).toBeFalsy()
|
||||
expect( -> atom.unloadPackage('unloaded')).toThrow()
|
||||
expect(atom.isPackageLoaded('unloaded')).toBeFalsy()
|
||||
expect(atom.packages.isPackageLoaded('unloaded')).toBeFalsy()
|
||||
expect( -> atom.packages.unloadPackage('unloaded')).toThrow()
|
||||
expect(atom.packages.isPackageLoaded('unloaded')).toBeFalsy()
|
||||
|
||||
describe "when the package is loaded", ->
|
||||
it "no longers reports it as being loaded", ->
|
||||
pack = atom.loadPackage('package-with-main')
|
||||
expect(atom.isPackageLoaded(pack.name)).toBeTruthy()
|
||||
atom.unloadPackage(pack.name)
|
||||
expect(atom.isPackageLoaded(pack.name)).toBeFalsy()
|
||||
pack = atom.packages.loadPackage('package-with-main')
|
||||
expect(atom.packages.isPackageLoaded(pack.name)).toBeTruthy()
|
||||
atom.packages.unloadPackage(pack.name)
|
||||
expect(atom.packages.isPackageLoaded(pack.name)).toBeFalsy()
|
||||
|
||||
describe ".activatePackage(id)", ->
|
||||
describe "atom packages", ->
|
||||
@@ -59,7 +59,7 @@ describe "the `atom` global", ->
|
||||
it "requires the module at the specified path", ->
|
||||
mainModule = require('./fixtures/packages/package-with-main/main-module')
|
||||
spyOn(mainModule, 'activate')
|
||||
pack = atom.activatePackage('package-with-main')
|
||||
pack = atom.packages.activatePackage('package-with-main')
|
||||
expect(mainModule.activate).toHaveBeenCalled()
|
||||
expect(pack.mainModule).toBe mainModule
|
||||
|
||||
@@ -67,15 +67,15 @@ describe "the `atom` global", ->
|
||||
it "requires index.coffee", ->
|
||||
indexModule = require('./fixtures/packages/package-with-index/index')
|
||||
spyOn(indexModule, 'activate')
|
||||
pack = atom.activatePackage('package-with-index')
|
||||
pack = atom.packages.activatePackage('package-with-index')
|
||||
expect(indexModule.activate).toHaveBeenCalled()
|
||||
expect(pack.mainModule).toBe indexModule
|
||||
|
||||
it "assigns config defaults from the module", ->
|
||||
expect(config.get('package-with-config-defaults.numbers.one')).toBeUndefined()
|
||||
atom.activatePackage('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
|
||||
expect(atom.config.get('package-with-config-defaults.numbers.one')).toBeUndefined()
|
||||
atom.packages.activatePackage('package-with-config-defaults')
|
||||
expect(atom.config.get('package-with-config-defaults.numbers.one')).toBe 1
|
||||
expect(atom.config.get('package-with-config-defaults.numbers.two')).toBe 2
|
||||
|
||||
describe "when the package metadata includes activation events", ->
|
||||
[mainModule, pack] = []
|
||||
@@ -85,24 +85,24 @@ describe "the `atom` global", ->
|
||||
spyOn(mainModule, 'activate').andCallThrough()
|
||||
AtomPackage = require '../src/atom-package'
|
||||
spyOn(AtomPackage.prototype, 'requireMainModule').andCallThrough()
|
||||
pack = atom.activatePackage('package-with-activation-events')
|
||||
pack = atom.packages.activatePackage('package-with-activation-events')
|
||||
|
||||
it "defers requiring/activating the main module until an activation event bubbles to the root view", ->
|
||||
expect(pack.requireMainModule).not.toHaveBeenCalled()
|
||||
expect(mainModule.activate).not.toHaveBeenCalled()
|
||||
rootView.trigger 'activation-event'
|
||||
atom.rootView.trigger 'activation-event'
|
||||
expect(mainModule.activate).toHaveBeenCalled()
|
||||
|
||||
it "triggers the activation event on all handlers registered during activation", ->
|
||||
rootView.openSync()
|
||||
editor = rootView.getActiveView()
|
||||
atom.rootView.openSync()
|
||||
editorView = atom.rootView.getActiveView()
|
||||
eventHandler = jasmine.createSpy("activation-event")
|
||||
editor.command 'activation-event', eventHandler
|
||||
editor.trigger 'activation-event'
|
||||
editorView.command 'activation-event', eventHandler
|
||||
editorView.trigger 'activation-event'
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
expect(mainModule.activationEventCallCount).toBe 1
|
||||
expect(eventHandler.callCount).toBe 1
|
||||
editor.trigger 'activation-event'
|
||||
editorView.trigger 'activation-event'
|
||||
expect(mainModule.activationEventCallCount).toBe 2
|
||||
expect(eventHandler.callCount).toBe 2
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
@@ -111,23 +111,23 @@ describe "the `atom` global", ->
|
||||
it "does not throw an exception", ->
|
||||
spyOn(console, "error")
|
||||
spyOn(console, "warn").andCallThrough()
|
||||
expect(-> atom.activatePackage('package-without-module')).not.toThrow()
|
||||
expect(-> atom.packages.activatePackage('package-without-module')).not.toThrow()
|
||||
expect(console.error).not.toHaveBeenCalled()
|
||||
expect(console.warn).not.toHaveBeenCalled()
|
||||
|
||||
it "passes the activate method the package's previously serialized state if it exists", ->
|
||||
pack = atom.activatePackage("package-with-serialization")
|
||||
pack = atom.packages.activatePackage("package-with-serialization")
|
||||
expect(pack.mainModule.someNumber).not.toBe 77
|
||||
pack.mainModule.someNumber = 77
|
||||
atom.deactivatePackage("package-with-serialization")
|
||||
atom.packages.deactivatePackage("package-with-serialization")
|
||||
spyOn(pack.mainModule, 'activate').andCallThrough()
|
||||
atom.activatePackage("package-with-serialization")
|
||||
atom.packages.activatePackage("package-with-serialization")
|
||||
expect(pack.mainModule.activate).toHaveBeenCalledWith({someNumber: 77})
|
||||
|
||||
it "logs warning instead of throwing an exception if the package fails to load", ->
|
||||
config.set("core.disabledPackages", [])
|
||||
atom.config.set("core.disabledPackages", [])
|
||||
spyOn(console, "warn")
|
||||
expect(-> atom.activatePackage("package-that-throws-an-exception")).not.toThrow()
|
||||
expect(-> atom.packages.activatePackage("package-that-throws-an-exception")).not.toThrow()
|
||||
expect(console.warn).toHaveBeenCalled()
|
||||
|
||||
describe "keymap loading", ->
|
||||
@@ -137,28 +137,28 @@ describe "the `atom` global", ->
|
||||
element2 = $$ -> @div class: 'test-2'
|
||||
element3 = $$ -> @div class: 'test-3'
|
||||
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-z']).toBeUndefined()
|
||||
expect(keymap.bindingsForElement(element2)['ctrl-z']).toBeUndefined()
|
||||
expect(keymap.bindingsForElement(element3)['ctrl-z']).toBeUndefined()
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)).toHaveLength 0
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element2)).toHaveLength 0
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element3)).toHaveLength 0
|
||||
|
||||
atom.activatePackage("package-with-keymaps")
|
||||
atom.packages.activatePackage("package-with-keymaps")
|
||||
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-z']).toBe "test-1"
|
||||
expect(keymap.bindingsForElement(element2)['ctrl-z']).toBe "test-2"
|
||||
expect(keymap.bindingsForElement(element3)['ctrl-z']).toBeUndefined()
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)[0].command).toBe "test-1"
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element2)[0].command).toBe "test-2"
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element3)).toHaveLength 0
|
||||
|
||||
describe "when the metadata contains a 'keymaps' manifest", ->
|
||||
it "loads only the keymaps specified by the manifest, in the specified order", ->
|
||||
element1 = $$ -> @div class: 'test-1'
|
||||
element3 = $$ -> @div class: 'test-3'
|
||||
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-z']).toBeUndefined()
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)).toHaveLength 0
|
||||
|
||||
atom.activatePackage("package-with-keymaps-manifest")
|
||||
atom.packages.activatePackage("package-with-keymaps-manifest")
|
||||
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-z']).toBe 'keymap-1'
|
||||
expect(keymap.bindingsForElement(element1)['ctrl-n']).toBe 'keymap-2'
|
||||
expect(keymap.bindingsForElement(element3)['ctrl-y']).toBeUndefined()
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)[0].command).toBe 'keymap-1'
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-n', element1)[0].command).toBe 'keymap-2'
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-y', element3)).toHaveLength 0
|
||||
|
||||
describe "menu loading", ->
|
||||
beforeEach ->
|
||||
@@ -171,7 +171,7 @@ describe "the `atom` global", ->
|
||||
|
||||
expect(atom.contextMenu.definitionsForElement(element)).toEqual []
|
||||
|
||||
atom.activatePackage("package-with-menus")
|
||||
atom.packages.activatePackage("package-with-menus")
|
||||
|
||||
expect(atom.menu.template.length).toBe 2
|
||||
expect(atom.menu.template[0].label).toBe "Second to Last"
|
||||
@@ -186,7 +186,7 @@ describe "the `atom` global", ->
|
||||
|
||||
expect(atom.contextMenu.definitionsForElement(element)).toEqual []
|
||||
|
||||
atom.activatePackage("package-with-menus-manifest")
|
||||
atom.packages.activatePackage("package-with-menus-manifest")
|
||||
|
||||
expect(atom.menu.template[0].label).toBe "Second to Last"
|
||||
expect(atom.menu.template[1].label).toBe "Last"
|
||||
@@ -200,11 +200,16 @@ describe "the `atom` global", ->
|
||||
one = require.resolve("./fixtures/packages/package-with-stylesheets-manifest/stylesheets/1.css")
|
||||
two = require.resolve("./fixtures/packages/package-with-stylesheets-manifest/stylesheets/2.less")
|
||||
three = require.resolve("./fixtures/packages/package-with-stylesheets-manifest/stylesheets/3.css")
|
||||
|
||||
one = atom.themes.stringToId(one)
|
||||
two = atom.themes.stringToId(two)
|
||||
three = atom.themes.stringToId(three)
|
||||
|
||||
expect(atom.themes.stylesheetElementForId(one)).not.toExist()
|
||||
expect(atom.themes.stylesheetElementForId(two)).not.toExist()
|
||||
expect(atom.themes.stylesheetElementForId(three)).not.toExist()
|
||||
|
||||
atom.activatePackage("package-with-stylesheets-manifest")
|
||||
atom.packages.activatePackage("package-with-stylesheets-manifest")
|
||||
|
||||
expect(atom.themes.stylesheetElementForId(one)).toExist()
|
||||
expect(atom.themes.stylesheetElementForId(two)).toExist()
|
||||
@@ -216,11 +221,17 @@ describe "the `atom` global", ->
|
||||
one = require.resolve("./fixtures/packages/package-with-stylesheets/stylesheets/1.css")
|
||||
two = require.resolve("./fixtures/packages/package-with-stylesheets/stylesheets/2.less")
|
||||
three = require.resolve("./fixtures/packages/package-with-stylesheets/stylesheets/3.css")
|
||||
|
||||
|
||||
one = atom.themes.stringToId(one)
|
||||
two = atom.themes.stringToId(two)
|
||||
three = atom.themes.stringToId(three)
|
||||
|
||||
expect(atom.themes.stylesheetElementForId(one)).not.toExist()
|
||||
expect(atom.themes.stylesheetElementForId(two)).not.toExist()
|
||||
expect(atom.themes.stylesheetElementForId(three)).not.toExist()
|
||||
|
||||
atom.activatePackage("package-with-stylesheets")
|
||||
atom.packages.activatePackage("package-with-stylesheets")
|
||||
expect(atom.themes.stylesheetElementForId(one)).toExist()
|
||||
expect(atom.themes.stylesheetElementForId(two)).toExist()
|
||||
expect(atom.themes.stylesheetElementForId(three)).toExist()
|
||||
@@ -228,90 +239,91 @@ describe "the `atom` global", ->
|
||||
|
||||
describe "grammar loading", ->
|
||||
it "loads the package's grammars", ->
|
||||
atom.activatePackage('package-with-grammars')
|
||||
expect(syntax.selectGrammar('a.alot').name).toBe 'Alot'
|
||||
expect(syntax.selectGrammar('a.alittle').name).toBe 'Alittle'
|
||||
atom.packages.activatePackage('package-with-grammars')
|
||||
expect(atom.syntax.selectGrammar('a.alot').name).toBe 'Alot'
|
||||
expect(atom.syntax.selectGrammar('a.alittle').name).toBe 'Alittle'
|
||||
|
||||
describe "scoped-property loading", ->
|
||||
it "loads the scoped properties", ->
|
||||
atom.activatePackage("package-with-scoped-properties")
|
||||
expect(syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a'
|
||||
atom.packages.activatePackage("package-with-scoped-properties")
|
||||
expect(atom.syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a'
|
||||
|
||||
describe "textmate packages", ->
|
||||
it "loads the package's grammars", ->
|
||||
expect(syntax.selectGrammar("file.rb").name).toBe "Null Grammar"
|
||||
atom.activatePackage('language-ruby', sync: true)
|
||||
expect(syntax.selectGrammar("file.rb").name).toBe "Ruby"
|
||||
expect(atom.syntax.selectGrammar("file.rb").name).toBe "Null Grammar"
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
expect(atom.syntax.selectGrammar("file.rb").name).toBe "Ruby"
|
||||
|
||||
it "translates the package's scoped properties to Atom terms", ->
|
||||
expect(syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined()
|
||||
atom.activatePackage('language-ruby', sync: true)
|
||||
expect(syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBe '# '
|
||||
expect(atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined()
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
expect(atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBe '# '
|
||||
|
||||
describe "when the package has no grammars but does have preferences", ->
|
||||
it "loads the package's preferences as scoped properties", ->
|
||||
jasmine.unspy(window, 'setTimeout')
|
||||
spyOn(syntax, 'addProperties').andCallThrough()
|
||||
spyOn(atom.syntax, 'addProperties').andCallThrough()
|
||||
|
||||
atom.activatePackage('package-with-preferences-tmbundle')
|
||||
atom.packages.activatePackage('package-with-preferences-tmbundle')
|
||||
|
||||
waitsFor ->
|
||||
syntax.addProperties.callCount > 0
|
||||
atom.syntax.addProperties.callCount > 0
|
||||
|
||||
runs ->
|
||||
expect(syntax.getProperty(['.source.pref'], 'editor.increaseIndentPattern')).toBe '^abc$'
|
||||
expect(atom.syntax.getProperty(['.source.pref'], 'editor.increaseIndentPattern')).toBe '^abc$'
|
||||
|
||||
describe ".deactivatePackage(id)", ->
|
||||
describe "atom packages", ->
|
||||
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()
|
||||
pack = atom.packages.activatePackage("package-with-deactivate")
|
||||
expect(atom.packages.isPackageActive("package-with-deactivate")).toBeTruthy()
|
||||
spyOn(pack.mainModule, 'deactivate').andCallThrough()
|
||||
|
||||
atom.deactivatePackage("package-with-deactivate")
|
||||
atom.packages.deactivatePackage("package-with-deactivate")
|
||||
expect(pack.mainModule.deactivate).toHaveBeenCalled()
|
||||
expect(atom.isPackageActive("package-with-module")).toBeFalsy()
|
||||
expect(atom.packages.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()
|
||||
badPack = atom.packages.activatePackage("package-that-throws-on-activate")
|
||||
expect(atom.packages.isPackageActive("package-that-throws-on-activate")).toBeTruthy()
|
||||
spyOn(badPack.mainModule, 'deactivate').andCallThrough()
|
||||
|
||||
atom.deactivatePackage("package-that-throws-on-activate")
|
||||
atom.packages.deactivatePackage("package-that-throws-on-activate")
|
||||
expect(badPack.mainModule.deactivate).not.toHaveBeenCalled()
|
||||
expect(atom.isPackageActive("package-that-throws-on-activate")).toBeFalsy()
|
||||
expect(atom.packages.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")
|
||||
badPack = atom.packages.activatePackage("package-that-throws-on-activate")
|
||||
spyOn(badPack.mainModule, 'serialize').andCallThrough()
|
||||
|
||||
atom.deactivatePackage("package-that-throws-on-activate")
|
||||
atom.packages.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)
|
||||
atom.activatePackage('package-with-serialization', immediate: true)
|
||||
atom.deactivatePackages()
|
||||
atom.packages.activatePackage('package-with-serialize-error', immediate: true)
|
||||
atom.packages.activatePackage('package-with-serialization', immediate: true)
|
||||
atom.packages.deactivatePackages()
|
||||
expect(atom.packages.packageStates['package-with-serialize-error']).toBeUndefined()
|
||||
expect(atom.packages.packageStates['package-with-serialization']).toEqual someNumber: 1
|
||||
expect(console.error).toHaveBeenCalled()
|
||||
|
||||
it "removes the package's grammars", ->
|
||||
atom.activatePackage('package-with-grammars')
|
||||
atom.deactivatePackage('package-with-grammars')
|
||||
expect(syntax.selectGrammar('a.alot').name).toBe 'Null Grammar'
|
||||
expect(syntax.selectGrammar('a.alittle').name).toBe 'Null Grammar'
|
||||
atom.packages.activatePackage('package-with-grammars')
|
||||
atom.packages.deactivatePackage('package-with-grammars')
|
||||
expect(atom.syntax.selectGrammar('a.alot').name).toBe 'Null Grammar'
|
||||
expect(atom.syntax.selectGrammar('a.alittle').name).toBe 'Null Grammar'
|
||||
|
||||
it "removes the package's keymaps", ->
|
||||
atom.activatePackage('package-with-keymaps')
|
||||
atom.deactivatePackage('package-with-keymaps')
|
||||
expect(keymap.bindingsForElement($$ -> @div class: 'test-1')['ctrl-z']).toBeUndefined()
|
||||
expect(keymap.bindingsForElement($$ -> @div class: 'test-2')['ctrl-z']).toBeUndefined()
|
||||
atom.packages.activatePackage('package-with-keymaps')
|
||||
atom.packages.deactivatePackage('package-with-keymaps')
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', $$ -> @div class: 'test-1')).toHaveLength 0
|
||||
expect(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', $$ -> @div class: 'test-2')).toHaveLength 0
|
||||
|
||||
it "removes the package's stylesheets", ->
|
||||
atom.activatePackage('package-with-stylesheets')
|
||||
atom.deactivatePackage('package-with-stylesheets')
|
||||
atom.packages.activatePackage('package-with-stylesheets')
|
||||
atom.packages.deactivatePackage('package-with-stylesheets')
|
||||
one = require.resolve("./fixtures/packages/package-with-stylesheets-manifest/stylesheets/1.css")
|
||||
two = require.resolve("./fixtures/packages/package-with-stylesheets-manifest/stylesheets/2.less")
|
||||
three = require.resolve("./fixtures/packages/package-with-stylesheets-manifest/stylesheets/3.css")
|
||||
@@ -320,23 +332,23 @@ describe "the `atom` global", ->
|
||||
expect(atom.themes.stylesheetElementForId(three)).not.toExist()
|
||||
|
||||
it "removes the package's scoped-properties", ->
|
||||
atom.activatePackage("package-with-scoped-properties")
|
||||
expect(syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a'
|
||||
atom.deactivatePackage("package-with-scoped-properties")
|
||||
expect(syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBeUndefined()
|
||||
atom.packages.activatePackage("package-with-scoped-properties")
|
||||
expect(atom.syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a'
|
||||
atom.packages.deactivatePackage("package-with-scoped-properties")
|
||||
expect(atom.syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBeUndefined()
|
||||
|
||||
describe "textmate packages", ->
|
||||
it "removes the package's grammars", ->
|
||||
expect(syntax.selectGrammar("file.rb").name).toBe "Null Grammar"
|
||||
atom.activatePackage('language-ruby', sync: true)
|
||||
expect(syntax.selectGrammar("file.rb").name).toBe "Ruby"
|
||||
atom.deactivatePackage('language-ruby')
|
||||
expect(syntax.selectGrammar("file.rb").name).toBe "Null Grammar"
|
||||
expect(atom.syntax.selectGrammar("file.rb").name).toBe "Null Grammar"
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
expect(atom.syntax.selectGrammar("file.rb").name).toBe "Ruby"
|
||||
atom.packages.deactivatePackage('language-ruby')
|
||||
expect(atom.syntax.selectGrammar("file.rb").name).toBe "Null Grammar"
|
||||
|
||||
it "removes the package's scoped properties", ->
|
||||
atom.activatePackage('language-ruby', sync: true)
|
||||
atom.deactivatePackage('language-ruby')
|
||||
expect(syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined()
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
atom.packages.deactivatePackage('language-ruby')
|
||||
expect(atom.syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined()
|
||||
|
||||
describe ".activate()", ->
|
||||
packageActivator = null
|
||||
@@ -376,7 +388,7 @@ describe "the `atom` global", ->
|
||||
packageName = 'package-with-main'
|
||||
atom.config.pushAtKeyPath('core.disabledPackages', packageName)
|
||||
atom.packages.observeDisabledPackages()
|
||||
expect(config.get('core.disabledPackages')).toContain packageName
|
||||
expect(atom.config.get('core.disabledPackages')).toContain packageName
|
||||
|
||||
pack = atom.packages.enablePackage(packageName)
|
||||
|
||||
@@ -384,19 +396,19 @@ describe "the `atom` global", ->
|
||||
activatedPackages = atom.packages.getActivePackages()
|
||||
expect(loadedPackages).toContain(pack)
|
||||
expect(activatedPackages).toContain(pack)
|
||||
expect(config.get('core.disabledPackages')).not.toContain packageName
|
||||
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
|
||||
|
||||
it ".disablePackage() disables an enabled package", ->
|
||||
packageName = 'package-with-main'
|
||||
atom.packages.activatePackage(packageName)
|
||||
atom.packages.observeDisabledPackages()
|
||||
expect(config.get('core.disabledPackages')).not.toContain packageName
|
||||
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
|
||||
|
||||
pack = atom.packages.disablePackage(packageName)
|
||||
|
||||
activatedPackages = atom.packages.getActivePackages()
|
||||
expect(activatedPackages).not.toContain(pack)
|
||||
expect(config.get('core.disabledPackages')).toContain packageName
|
||||
expect(atom.config.get('core.disabledPackages')).toContain packageName
|
||||
|
||||
describe "with themes", ->
|
||||
beforeEach ->
|
||||
@@ -409,20 +421,20 @@ describe "the `atom` global", ->
|
||||
it ".enablePackage() and .disablePackage() enables and disables a theme", ->
|
||||
packageName = 'theme-with-package-file'
|
||||
|
||||
expect(config.get('core.themes')).not.toContain packageName
|
||||
expect(config.get('core.disabledPackages')).not.toContain packageName
|
||||
expect(atom.config.get('core.themes')).not.toContain packageName
|
||||
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
|
||||
|
||||
# enabling of theme
|
||||
pack = atom.packages.enablePackage(packageName)
|
||||
activatedPackages = atom.packages.getActivePackages()
|
||||
expect(activatedPackages).toContain(pack)
|
||||
expect(config.get('core.themes')).toContain packageName
|
||||
expect(config.get('core.disabledPackages')).not.toContain packageName
|
||||
expect(atom.config.get('core.themes')).toContain packageName
|
||||
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
|
||||
|
||||
# disabling of theme
|
||||
pack = atom.packages.disablePackage(packageName)
|
||||
activatedPackages = atom.packages.getActivePackages()
|
||||
expect(activatedPackages).not.toContain(pack)
|
||||
expect(config.get('core.themes')).not.toContain packageName
|
||||
expect(config.get('core.themes')).not.toContain packageName
|
||||
expect(config.get('core.disabledPackages')).not.toContain packageName
|
||||
expect(atom.config.get('core.themes')).not.toContain packageName
|
||||
expect(atom.config.get('core.themes')).not.toContain packageName
|
||||
expect(atom.config.get('core.disabledPackages')).not.toContain packageName
|
||||
|
||||
@@ -12,24 +12,25 @@ describe "install(commandPath, callback)", ->
|
||||
spyOn(installer, 'findInstallDirectory').andCallFake (callback) ->
|
||||
callback(directory)
|
||||
|
||||
fs.remove(directory) if fs.exists(directory)
|
||||
fs.removeSync(directory) if fs.existsSync(directory)
|
||||
|
||||
it "symlinks the command and makes it executable", ->
|
||||
fs.writeSync(commandPath, 'test')
|
||||
expect(fs.isFileSync(commandPath)).toBeTruthy()
|
||||
expect(fs.isExecutableSync(commandPath)).toBeFalsy()
|
||||
expect(fs.isFileSync(destinationPath)).toBeFalsy()
|
||||
describe "on #darwin", ->
|
||||
it "symlinks the command and makes it executable", ->
|
||||
fs.writeFileSync(commandPath, 'test')
|
||||
expect(fs.isFileSync(commandPath)).toBeTruthy()
|
||||
expect(fs.isExecutableSync(commandPath)).toBeFalsy()
|
||||
expect(fs.isFileSync(destinationPath)).toBeFalsy()
|
||||
|
||||
installDone = false
|
||||
installError = null
|
||||
installer.install commandPath, (error) ->
|
||||
installDone = true
|
||||
installError = error
|
||||
installDone = false
|
||||
installError = null
|
||||
installer.install commandPath, (error) ->
|
||||
installDone = true
|
||||
installError = error
|
||||
|
||||
waitsFor -> installDone
|
||||
waitsFor -> installDone
|
||||
|
||||
runs ->
|
||||
expect(installError).toBeNull()
|
||||
expect(fs.isFileSync(destinationPath)).toBeTruthy()
|
||||
expect(fs.realpathSync(destinationPath)).toBe fs.realpathSync(commandPath)
|
||||
expect(fs.isExecutableSync(destinationPath)).toBeTruthy()
|
||||
runs ->
|
||||
expect(installError).toBeNull()
|
||||
expect(fs.isFileSync(destinationPath)).toBeTruthy()
|
||||
expect(fs.realpathSync(destinationPath)).toBe fs.realpathSync(commandPath)
|
||||
expect(fs.isExecutableSync(destinationPath)).toBeTruthy()
|
||||
|
||||
+146
-111
@@ -8,261 +8,296 @@ describe "Config", ->
|
||||
|
||||
describe ".get(keyPath)", ->
|
||||
it "allows a key path's value to be read", ->
|
||||
expect(config.set("foo.bar.baz", 42)).toBe 42
|
||||
expect(config.get("foo.bar.baz")).toBe 42
|
||||
expect(config.get("bogus.key.path")).toBeUndefined()
|
||||
expect(atom.config.set("foo.bar.baz", 42)).toBe 42
|
||||
expect(atom.config.get("foo.bar.baz")).toBe 42
|
||||
expect(atom.config.get("bogus.key.path")).toBeUndefined()
|
||||
|
||||
it "returns a deep clone of the key path's value", ->
|
||||
config.set('value', array: [1, b: 2, 3])
|
||||
retrievedValue = config.get('value')
|
||||
atom.config.set('value', array: [1, b: 2, 3])
|
||||
retrievedValue = atom.config.get('value')
|
||||
retrievedValue.array[0] = 4
|
||||
retrievedValue.array[1].b = 2.1
|
||||
expect(config.get('value')).toEqual(array: [1, b: 2, 3])
|
||||
expect(atom.config.get('value')).toEqual(array: [1, b: 2, 3])
|
||||
|
||||
describe ".set(keyPath, value)", ->
|
||||
it "allows a key path's value to be written", ->
|
||||
expect(config.set("foo.bar.baz", 42)).toBe 42
|
||||
expect(config.get("foo.bar.baz")).toBe 42
|
||||
expect(atom.config.set("foo.bar.baz", 42)).toBe 42
|
||||
expect(atom.config.get("foo.bar.baz")).toBe 42
|
||||
|
||||
it "updates observers and saves when a key path is set", ->
|
||||
observeHandler = jasmine.createSpy "observeHandler"
|
||||
config.observe "foo.bar.baz", observeHandler
|
||||
atom.config.observe "foo.bar.baz", observeHandler
|
||||
observeHandler.reset()
|
||||
|
||||
config.set("foo.bar.baz", 42)
|
||||
atom.config.set("foo.bar.baz", 42)
|
||||
|
||||
expect(config.save).toHaveBeenCalled()
|
||||
expect(atom.config.save).toHaveBeenCalled()
|
||||
expect(observeHandler).toHaveBeenCalledWith 42, {previous: undefined}
|
||||
|
||||
describe "when the value equals the default value", ->
|
||||
it "does not store the value", ->
|
||||
config.setDefaults("foo", same: 1, changes: 1)
|
||||
expect(config.settings.foo).toBeUndefined()
|
||||
config.set('foo.same', 1)
|
||||
config.set('foo.changes', 2)
|
||||
expect(config.settings.foo).toEqual {changes: 2}
|
||||
atom.config.setDefaults("foo", same: 1, changes: 1)
|
||||
expect(atom.config.settings.foo).toBeUndefined()
|
||||
atom.config.set('foo.same', 1)
|
||||
atom.config.set('foo.changes', 2)
|
||||
expect(atom.config.settings.foo).toEqual {changes: 2}
|
||||
|
||||
config.set('foo.changes', 1)
|
||||
expect(config.settings.foo).toEqual {}
|
||||
atom.config.set('foo.changes', 1)
|
||||
expect(atom.config.settings.foo).toEqual {}
|
||||
|
||||
describe ".toggle(keyPath)", ->
|
||||
it "negates the boolean value of the current key path value", ->
|
||||
atom.config.set('foo.a', 1)
|
||||
atom.config.toggle('foo.a')
|
||||
expect(atom.config.get('foo.a')).toBe false
|
||||
|
||||
atom.config.set('foo.a', '')
|
||||
atom.config.toggle('foo.a')
|
||||
expect(atom.config.get('foo.a')).toBe true
|
||||
|
||||
atom.config.set('foo.a', null)
|
||||
atom.config.toggle('foo.a')
|
||||
expect(atom.config.get('foo.a')).toBe true
|
||||
|
||||
atom.config.set('foo.a', true)
|
||||
atom.config.toggle('foo.a')
|
||||
expect(atom.config.get('foo.a')).toBe false
|
||||
|
||||
describe ".pushAtKeyPath(keyPath, value)", ->
|
||||
it "pushes the given value to the array at the key path and updates observers", ->
|
||||
config.set("foo.bar.baz", ["a"])
|
||||
atom.config.set("foo.bar.baz", ["a"])
|
||||
observeHandler = jasmine.createSpy "observeHandler"
|
||||
config.observe "foo.bar.baz", observeHandler
|
||||
atom.config.observe "foo.bar.baz", observeHandler
|
||||
observeHandler.reset()
|
||||
|
||||
expect(config.pushAtKeyPath("foo.bar.baz", "b")).toBe 2
|
||||
expect(config.get("foo.bar.baz")).toEqual ["a", "b"]
|
||||
expect(observeHandler).toHaveBeenCalledWith config.get("foo.bar.baz"), {previous: ['a']}
|
||||
expect(atom.config.pushAtKeyPath("foo.bar.baz", "b")).toBe 2
|
||||
expect(atom.config.get("foo.bar.baz")).toEqual ["a", "b"]
|
||||
expect(observeHandler).toHaveBeenCalledWith atom.config.get("foo.bar.baz"), {previous: ['a']}
|
||||
|
||||
describe ".unshiftAtKeyPath(keyPath, value)", ->
|
||||
it "unshifts the given value to the array at the key path and updates observers", ->
|
||||
atom.config.set("foo.bar.baz", ["b"])
|
||||
observeHandler = jasmine.createSpy "observeHandler"
|
||||
atom.config.observe "foo.bar.baz", observeHandler
|
||||
observeHandler.reset()
|
||||
|
||||
expect(atom.config.unshiftAtKeyPath("foo.bar.baz", "a")).toBe 2
|
||||
expect(atom.config.get("foo.bar.baz")).toEqual ["a", "b"]
|
||||
expect(observeHandler).toHaveBeenCalledWith atom.config.get("foo.bar.baz"), {previous: ['b']}
|
||||
|
||||
describe ".removeAtKeyPath(keyPath, value)", ->
|
||||
it "removes the given value from the array at the key path and updates observers", ->
|
||||
config.set("foo.bar.baz", ["a", "b", "c"])
|
||||
atom.config.set("foo.bar.baz", ["a", "b", "c"])
|
||||
observeHandler = jasmine.createSpy "observeHandler"
|
||||
config.observe "foo.bar.baz", observeHandler
|
||||
atom.config.observe "foo.bar.baz", observeHandler
|
||||
observeHandler.reset()
|
||||
|
||||
expect(config.removeAtKeyPath("foo.bar.baz", "b")).toEqual ["a", "c"]
|
||||
expect(config.get("foo.bar.baz")).toEqual ["a", "c"]
|
||||
expect(observeHandler).toHaveBeenCalledWith config.get("foo.bar.baz"), {previous: ['a', 'b', 'c']}
|
||||
expect(atom.config.removeAtKeyPath("foo.bar.baz", "b")).toEqual ["a", "c"]
|
||||
expect(atom.config.get("foo.bar.baz")).toEqual ["a", "c"]
|
||||
expect(observeHandler).toHaveBeenCalledWith atom.config.get("foo.bar.baz"), {previous: ['a', 'b', 'c']}
|
||||
|
||||
describe ".getPositiveInt(keyPath, defaultValue)", ->
|
||||
it "returns the proper current or default value", ->
|
||||
config.set('editor.preferredLineLength', 0)
|
||||
expect(config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
config.set('editor.preferredLineLength', -1234)
|
||||
expect(config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
config.set('editor.preferredLineLength', 'abcd')
|
||||
expect(config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
config.set('editor.preferredLineLength', null)
|
||||
expect(config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
atom.config.set('editor.preferredLineLength', 0)
|
||||
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
atom.config.set('editor.preferredLineLength', -1234)
|
||||
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
atom.config.set('editor.preferredLineLength', 'abcd')
|
||||
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
atom.config.set('editor.preferredLineLength', null)
|
||||
expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80
|
||||
|
||||
describe ".save()", ->
|
||||
nodeFs = require 'fs'
|
||||
|
||||
beforeEach ->
|
||||
spyOn(nodeFs, 'writeFileSync')
|
||||
jasmine.unspy config, 'save'
|
||||
jasmine.unspy atom.config, 'save'
|
||||
|
||||
describe "when ~/.atom/config.json exists", ->
|
||||
it "writes any non-default properties to ~/.atom/config.json", ->
|
||||
config.configFilePath = path.join(config.configDirPath, "config.json")
|
||||
config.set("a.b.c", 1)
|
||||
config.set("a.b.d", 2)
|
||||
config.set("x.y.z", 3)
|
||||
config.setDefaults("a.b", e: 4, f: 5)
|
||||
atom.config.configFilePath = path.join(atom.config.configDirPath, "atom.config.json")
|
||||
atom.config.set("a.b.c", 1)
|
||||
atom.config.set("a.b.d", 2)
|
||||
atom.config.set("x.y.z", 3)
|
||||
atom.config.setDefaults("a.b", e: 4, f: 5)
|
||||
|
||||
nodeFs.writeFileSync.reset()
|
||||
config.save()
|
||||
atom.config.save()
|
||||
|
||||
expect(nodeFs.writeFileSync.argsForCall[0][0]).toBe(path.join(config.configDirPath, "config.json"))
|
||||
expect(nodeFs.writeFileSync.argsForCall[0][0]).toBe(path.join(atom.config.configDirPath, "atom.config.json"))
|
||||
writtenConfig = JSON.parse(nodeFs.writeFileSync.argsForCall[0][1])
|
||||
expect(writtenConfig).toEqual config.settings
|
||||
expect(writtenConfig).toEqual atom.config.settings
|
||||
|
||||
describe "when ~/.atom/config.json doesn't exist", ->
|
||||
it "writes any non-default properties to ~/.atom/config.cson", ->
|
||||
config.configFilePath = path.join(config.configDirPath, "config.cson")
|
||||
config.set("a.b.c", 1)
|
||||
config.set("a.b.d", 2)
|
||||
config.set("x.y.z", 3)
|
||||
config.setDefaults("a.b", e: 4, f: 5)
|
||||
atom.config.configFilePath = path.join(atom.config.configDirPath, "atom.config.cson")
|
||||
atom.config.set("a.b.c", 1)
|
||||
atom.config.set("a.b.d", 2)
|
||||
atom.config.set("x.y.z", 3)
|
||||
atom.config.setDefaults("a.b", e: 4, f: 5)
|
||||
|
||||
nodeFs.writeFileSync.reset()
|
||||
config.save()
|
||||
atom.config.save()
|
||||
|
||||
expect(nodeFs.writeFileSync.argsForCall[0][0]).toBe(path.join(config.configDirPath, "config.cson"))
|
||||
expect(nodeFs.writeFileSync.argsForCall[0][0]).toBe(path.join(atom.config.configDirPath, "atom.config.cson"))
|
||||
CoffeeScript = require 'coffee-script'
|
||||
writtenConfig = CoffeeScript.eval(nodeFs.writeFileSync.argsForCall[0][1], bare: true)
|
||||
expect(writtenConfig).toEqual config.settings
|
||||
expect(writtenConfig).toEqual atom.config.settings
|
||||
|
||||
describe ".setDefaults(keyPath, defaults)", ->
|
||||
it "assigns any previously-unassigned keys to the object at the key path", ->
|
||||
config.set("foo.bar.baz", a: 1)
|
||||
config.setDefaults("foo.bar.baz", a: 2, b: 3, c: 4)
|
||||
expect(config.get("foo.bar.baz.a")).toBe 1
|
||||
expect(config.get("foo.bar.baz.b")).toBe 3
|
||||
expect(config.get("foo.bar.baz.c")).toBe 4
|
||||
atom.config.set("foo.bar.baz", a: 1)
|
||||
atom.config.setDefaults("foo.bar.baz", a: 2, b: 3, c: 4)
|
||||
expect(atom.config.get("foo.bar.baz.a")).toBe 1
|
||||
expect(atom.config.get("foo.bar.baz.b")).toBe 3
|
||||
expect(atom.config.get("foo.bar.baz.c")).toBe 4
|
||||
|
||||
config.setDefaults("foo.quux", x: 0, y: 1)
|
||||
expect(config.get("foo.quux.x")).toBe 0
|
||||
expect(config.get("foo.quux.y")).toBe 1
|
||||
atom.config.setDefaults("foo.quux", x: 0, y: 1)
|
||||
expect(atom.config.get("foo.quux.x")).toBe 0
|
||||
expect(atom.config.get("foo.quux.y")).toBe 1
|
||||
|
||||
describe ".observe(keyPath)", ->
|
||||
observeHandler = null
|
||||
[observeHandler, observeSubscription] = []
|
||||
|
||||
beforeEach ->
|
||||
observeHandler = jasmine.createSpy("observeHandler")
|
||||
config.set("foo.bar.baz", "value 1")
|
||||
config.observe "foo.bar.baz", observeHandler
|
||||
atom.config.set("foo.bar.baz", "value 1")
|
||||
observeSubscription = atom.config.observe "foo.bar.baz", observeHandler
|
||||
|
||||
it "fires the given callback with the current value at the keypath", ->
|
||||
expect(observeHandler).toHaveBeenCalledWith("value 1")
|
||||
|
||||
it "fires the callback every time the observed value changes", ->
|
||||
observeHandler.reset() # clear the initial call
|
||||
config.set('foo.bar.baz', "value 2")
|
||||
atom.config.set('foo.bar.baz', "value 2")
|
||||
expect(observeHandler).toHaveBeenCalledWith("value 2", {previous: 'value 1'})
|
||||
observeHandler.reset()
|
||||
|
||||
config.set('foo.bar.baz', "value 1")
|
||||
atom.config.set('foo.bar.baz', "value 1")
|
||||
expect(observeHandler).toHaveBeenCalledWith("value 1", {previous: 'value 2'})
|
||||
|
||||
it "fires the callback when the observed value is deleted", ->
|
||||
observeHandler.reset() # clear the initial call
|
||||
config.set('foo.bar.baz', undefined)
|
||||
atom.config.set('foo.bar.baz', undefined)
|
||||
expect(observeHandler).toHaveBeenCalledWith(undefined, {previous: 'value 1'})
|
||||
|
||||
it "fires the callback when the full key path goes into and out of existence", ->
|
||||
observeHandler.reset() # clear the initial call
|
||||
config.set("foo.bar", undefined)
|
||||
atom.config.set("foo.bar", undefined)
|
||||
|
||||
expect(observeHandler).toHaveBeenCalledWith(undefined, {previous: 'value 1'})
|
||||
observeHandler.reset()
|
||||
|
||||
config.set("foo.bar.baz", "i'm back")
|
||||
atom.config.set("foo.bar.baz", "i'm back")
|
||||
expect(observeHandler).toHaveBeenCalledWith("i'm back", {previous: undefined})
|
||||
|
||||
it "does not fire the callback once the observe subscription is off'ed", ->
|
||||
observeHandler.reset() # clear the initial call
|
||||
observeSubscription.off()
|
||||
atom.config.set('foo.bar.baz', "value 2")
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe ".initializeConfigDirectory()", ->
|
||||
beforeEach ->
|
||||
config.configDirPath = dotAtomPath
|
||||
expect(fs.exists(config.configDirPath)).toBeFalsy()
|
||||
atom.config.configDirPath = dotAtomPath
|
||||
expect(fs.existsSync(atom.config.configDirPath)).toBeFalsy()
|
||||
|
||||
afterEach ->
|
||||
fs.remove(dotAtomPath) if fs.exists(dotAtomPath)
|
||||
fs.removeSync(dotAtomPath) if fs.existsSync(dotAtomPath)
|
||||
|
||||
describe "when the configDirPath doesn't exist", ->
|
||||
it "copies the contents of dot-atom to ~/.atom", ->
|
||||
initializationDone = false
|
||||
jasmine.unspy(window, "setTimeout")
|
||||
config.initializeConfigDirectory ->
|
||||
atom.config.initializeConfigDirectory ->
|
||||
initializationDone = true
|
||||
|
||||
waitsFor -> initializationDone
|
||||
|
||||
runs ->
|
||||
expect(fs.exists(config.configDirPath)).toBeTruthy()
|
||||
expect(fs.exists(path.join(config.configDirPath, 'packages'))).toBeTruthy()
|
||||
expect(fs.exists(path.join(config.configDirPath, 'snippets'))).toBeTruthy()
|
||||
expect(fs.isFileSync(path.join(config.configDirPath, 'config.cson'))).toBeTruthy()
|
||||
expect(fs.existsSync(atom.config.configDirPath)).toBeTruthy()
|
||||
expect(fs.existsSync(path.join(atom.config.configDirPath, 'packages'))).toBeTruthy()
|
||||
expect(fs.existsSync(path.join(atom.config.configDirPath, 'snippets'))).toBeTruthy()
|
||||
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'config.cson'))).toBeTruthy()
|
||||
|
||||
describe ".loadUserConfig()", ->
|
||||
beforeEach ->
|
||||
config.configDirPath = dotAtomPath
|
||||
config.configFilePath = path.join(config.configDirPath, "config.cson")
|
||||
expect(fs.exists(config.configDirPath)).toBeFalsy()
|
||||
atom.config.configDirPath = dotAtomPath
|
||||
atom.config.configFilePath = path.join(atom.config.configDirPath, "atom.config.cson")
|
||||
expect(fs.existsSync(atom.config.configDirPath)).toBeFalsy()
|
||||
|
||||
afterEach ->
|
||||
fs.remove(dotAtomPath) if fs.exists(dotAtomPath)
|
||||
fs.removeSync(dotAtomPath) if fs.existsSync(dotAtomPath)
|
||||
|
||||
describe "when the config file contains valid cson", ->
|
||||
beforeEach ->
|
||||
fs.writeSync(config.configFilePath, "foo: bar: 'baz'")
|
||||
config.loadUserConfig()
|
||||
fs.writeFileSync(atom.config.configFilePath, "foo: bar: 'baz'")
|
||||
atom.config.loadUserConfig()
|
||||
|
||||
it "updates the config data based on the file contents", ->
|
||||
expect(config.get("foo.bar")).toBe 'baz'
|
||||
expect(atom.config.get("foo.bar")).toBe 'baz'
|
||||
|
||||
describe "when the config file contains invalid cson", ->
|
||||
beforeEach ->
|
||||
spyOn(console, 'error')
|
||||
fs.writeSync(config.configFilePath, "{{{{{")
|
||||
fs.writeFileSync(atom.config.configFilePath, "{{{{{")
|
||||
|
||||
it "logs an error to the console and does not overwrite the config file on a subsequent save", ->
|
||||
config.loadUserConfig()
|
||||
atom.config.loadUserConfig()
|
||||
expect(console.error).toHaveBeenCalled()
|
||||
config.set("hair", "blonde") # trigger a save
|
||||
expect(config.save).not.toHaveBeenCalled()
|
||||
atom.config.set("hair", "blonde") # trigger a save
|
||||
expect(atom.config.save).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the config file does not exist", ->
|
||||
it "creates it with an empty object", ->
|
||||
fs.makeTree(config.configDirPath)
|
||||
config.loadUserConfig()
|
||||
expect(fs.exists(config.configFilePath)).toBe true
|
||||
expect(CSON.readFileSync(config.configFilePath)).toEqual {}
|
||||
fs.makeTreeSync(atom.config.configDirPath)
|
||||
atom.config.loadUserConfig()
|
||||
expect(fs.existsSync(atom.config.configFilePath)).toBe true
|
||||
expect(CSON.readFileSync(atom.config.configFilePath)).toEqual {}
|
||||
|
||||
describe ".observeUserConfig()", ->
|
||||
updatedHandler = null
|
||||
|
||||
beforeEach ->
|
||||
config.configDirPath = dotAtomPath
|
||||
config.configFilePath = path.join(config.configDirPath, "config.cson")
|
||||
expect(fs.exists(config.configDirPath)).toBeFalsy()
|
||||
fs.writeSync(config.configFilePath, "foo: bar: 'baz'")
|
||||
config.loadUserConfig()
|
||||
config.observeUserConfig()
|
||||
atom.config.configDirPath = dotAtomPath
|
||||
atom.config.configFilePath = path.join(atom.config.configDirPath, "atom.config.cson")
|
||||
expect(fs.existsSync(atom.config.configDirPath)).toBeFalsy()
|
||||
fs.writeFileSync(atom.config.configFilePath, "foo: bar: 'baz'")
|
||||
atom.config.loadUserConfig()
|
||||
atom.config.observeUserConfig()
|
||||
updatedHandler = jasmine.createSpy("updatedHandler")
|
||||
config.on 'updated', updatedHandler
|
||||
atom.config.on 'updated', updatedHandler
|
||||
|
||||
afterEach ->
|
||||
config.unobserveUserConfig()
|
||||
fs.remove(dotAtomPath) if fs.exists(dotAtomPath)
|
||||
atom.config.unobserveUserConfig()
|
||||
fs.removeSync(dotAtomPath) if fs.existsSync(dotAtomPath)
|
||||
|
||||
describe "when the config file changes to contain valid cson", ->
|
||||
it "updates the config data", ->
|
||||
fs.writeSync(config.configFilePath, "foo: { bar: 'quux', baz: 'bar'}")
|
||||
fs.writeFileSync(atom.config.configFilePath, "foo: { bar: 'quux', baz: 'bar'}")
|
||||
waitsFor 'update event', -> updatedHandler.callCount > 0
|
||||
runs ->
|
||||
expect(config.get('foo.bar')).toBe 'quux'
|
||||
expect(config.get('foo.baz')).toBe 'bar'
|
||||
expect(atom.config.get('foo.bar')).toBe 'quux'
|
||||
expect(atom.config.get('foo.baz')).toBe 'bar'
|
||||
|
||||
describe "when the config file changes to contain invalid cson", ->
|
||||
beforeEach ->
|
||||
spyOn(console, 'error')
|
||||
fs.writeSync(config.configFilePath, "}}}")
|
||||
fs.writeFileSync(atom.config.configFilePath, "}}}")
|
||||
waitsFor "error to be logged", -> console.error.callCount > 0
|
||||
|
||||
it "logs a warning and does not update config data", ->
|
||||
expect(updatedHandler.callCount).toBe 0
|
||||
expect(config.get('foo.bar')).toBe 'baz'
|
||||
config.set("hair", "blonde") # trigger a save
|
||||
expect(config.save).not.toHaveBeenCalled()
|
||||
expect(atom.config.get('foo.bar')).toBe 'baz'
|
||||
atom.config.set("hair", "blonde") # trigger a save
|
||||
expect(atom.config.save).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the config file subsequently changes again to contain valid cson", ->
|
||||
beforeEach ->
|
||||
fs.writeSync(config.configFilePath, "foo: bar: 'baz'")
|
||||
fs.writeFileSync(atom.config.configFilePath, "foo: bar: 'baz'")
|
||||
waitsFor 'update event', -> updatedHandler.callCount > 0
|
||||
|
||||
it "updates the config data and resumes saving", ->
|
||||
config.set("hair", "blonde")
|
||||
expect(config.save).toHaveBeenCalled()
|
||||
atom.config.set("hair", "blonde")
|
||||
expect(atom.config.save).toHaveBeenCalled()
|
||||
|
||||
@@ -16,10 +16,10 @@ describe "Directory", ->
|
||||
|
||||
beforeEach ->
|
||||
temporaryFilePath = path.join(__dirname, 'fixtures', 'temporary')
|
||||
fs.remove(temporaryFilePath) if fs.exists(temporaryFilePath)
|
||||
fs.removeSync(temporaryFilePath) if fs.existsSync(temporaryFilePath)
|
||||
|
||||
afterEach ->
|
||||
fs.remove(temporaryFilePath) if fs.exists(temporaryFilePath)
|
||||
fs.removeSync(temporaryFilePath) if fs.existsSync(temporaryFilePath)
|
||||
|
||||
it "triggers 'contents-changed' event handlers", ->
|
||||
changeHandler = null
|
||||
@@ -27,13 +27,13 @@ describe "Directory", ->
|
||||
runs ->
|
||||
changeHandler = jasmine.createSpy('changeHandler')
|
||||
directory.on 'contents-changed', changeHandler
|
||||
fs.writeSync(temporaryFilePath, '')
|
||||
fs.writeFileSync(temporaryFilePath, '')
|
||||
|
||||
waitsFor "first change", -> changeHandler.callCount > 0
|
||||
|
||||
runs ->
|
||||
changeHandler.reset()
|
||||
fs.remove(temporaryFilePath)
|
||||
fs.removeSync(temporaryFilePath)
|
||||
|
||||
waitsFor "second change", -> changeHandler.callCount > 0
|
||||
|
||||
@@ -42,10 +42,10 @@ describe "Directory", ->
|
||||
|
||||
beforeEach ->
|
||||
temporaryFilePath = path.join(directory.path, 'temporary')
|
||||
fs.remove(temporaryFilePath) if fs.exists(temporaryFilePath)
|
||||
fs.removeSync(temporaryFilePath) if fs.existsSync(temporaryFilePath)
|
||||
|
||||
afterEach ->
|
||||
fs.remove(temporaryFilePath) if fs.exists(temporaryFilePath)
|
||||
fs.removeSync(temporaryFilePath) if fs.existsSync(temporaryFilePath)
|
||||
|
||||
it "no longer triggers events", ->
|
||||
changeHandler = null
|
||||
@@ -53,7 +53,7 @@ describe "Directory", ->
|
||||
runs ->
|
||||
changeHandler = jasmine.createSpy('changeHandler')
|
||||
directory.on 'contents-changed', changeHandler
|
||||
fs.writeSync(temporaryFilePath, '')
|
||||
fs.writeFileSync(temporaryFilePath, '')
|
||||
|
||||
waitsFor "change event", -> changeHandler.callCount > 0
|
||||
|
||||
@@ -62,36 +62,49 @@ describe "Directory", ->
|
||||
directory.off()
|
||||
waits 20
|
||||
|
||||
runs -> fs.remove(temporaryFilePath)
|
||||
runs -> fs.removeSync(temporaryFilePath)
|
||||
waits 20
|
||||
runs -> expect(changeHandler.callCount).toBe 0
|
||||
|
||||
it "includes symlink information about entries", ->
|
||||
entries = directory.getEntries()
|
||||
for entry in entries
|
||||
name = entry.getBaseName()
|
||||
if name is 'symlink-to-dir' or name is 'symlink-to-file'
|
||||
expect(entry.symlink).toBeTruthy()
|
||||
else
|
||||
expect(entry.symlink).toBeFalsy()
|
||||
describe "on #darwin or #linux", ->
|
||||
it "includes symlink information about entries", ->
|
||||
entries = directory.getEntries()
|
||||
for entry in entries
|
||||
name = entry.getBaseName()
|
||||
if name is 'symlink-to-dir' or name is 'symlink-to-file'
|
||||
expect(entry.symlink).toBeTruthy()
|
||||
else
|
||||
expect(entry.symlink).toBeFalsy()
|
||||
|
||||
describe ".relativize(path)", ->
|
||||
it "returns a relative path based on the directory's path", ->
|
||||
absolutePath = directory.getPath()
|
||||
expect(directory.relativize(absolutePath)).toBe ''
|
||||
expect(directory.relativize(path.join(absolutePath, "b"))).toBe "b"
|
||||
expect(directory.relativize(path.join(absolutePath, "b/file.coffee"))).toBe "b/file.coffee"
|
||||
expect(directory.relativize(path.join(absolutePath, "file.coffee"))).toBe "file.coffee"
|
||||
describe "on #darwin or #linux", ->
|
||||
it "returns a relative path based on the directory's path", ->
|
||||
absolutePath = directory.getPath()
|
||||
expect(directory.relativize(absolutePath)).toBe ''
|
||||
expect(directory.relativize(path.join(absolutePath, "b"))).toBe "b"
|
||||
expect(directory.relativize(path.join(absolutePath, "b/file.coffee"))).toBe "b/file.coffee"
|
||||
expect(directory.relativize(path.join(absolutePath, "file.coffee"))).toBe "file.coffee"
|
||||
|
||||
it "returns a relative path based on the directory's symlinked source path", ->
|
||||
symlinkPath = path.join(__dirname, 'fixtures', 'symlink-to-dir')
|
||||
symlinkDirectory = new Directory(symlinkPath)
|
||||
realFilePath = require.resolve('./fixtures/dir/a')
|
||||
expect(symlinkDirectory.relativize(symlinkPath)).toBe ''
|
||||
expect(symlinkDirectory.relativize(realFilePath)).toBe 'a'
|
||||
it "returns a relative path based on the directory's symlinked source path", ->
|
||||
symlinkPath = path.join(__dirname, 'fixtures', 'symlink-to-dir')
|
||||
symlinkDirectory = new Directory(symlinkPath)
|
||||
realFilePath = require.resolve('./fixtures/dir/a')
|
||||
expect(symlinkDirectory.relativize(symlinkPath)).toBe ''
|
||||
expect(symlinkDirectory.relativize(realFilePath)).toBe 'a'
|
||||
|
||||
it "returns the full path if the directory's path is not a prefix of the path", ->
|
||||
expect(directory.relativize('/not/relative')).toBe '/not/relative'
|
||||
it "returns the full path if the directory's path is not a prefix of the path", ->
|
||||
expect(directory.relativize('/not/relative')).toBe '/not/relative'
|
||||
|
||||
describe "on #win32", ->
|
||||
it "returns a relative path based on the directory's path", ->
|
||||
absolutePath = directory.getPath()
|
||||
expect(directory.relativize(absolutePath)).toBe ''
|
||||
expect(directory.relativize(path.join(absolutePath, "b"))).toBe "b"
|
||||
expect(directory.relativize(path.join(absolutePath, "b/file.coffee"))).toBe "b\\file.coffee"
|
||||
expect(directory.relativize(path.join(absolutePath, "file.coffee"))).toBe "file.coffee"
|
||||
|
||||
it "returns the full path if the directory's path is not a prefix of the path", ->
|
||||
expect(directory.relativize('/not/relative')).toBe "\\not\\relative"
|
||||
|
||||
describe ".contains(path)", ->
|
||||
it "returns true if the path is a child of the directory's path", ->
|
||||
@@ -100,11 +113,12 @@ describe "Directory", ->
|
||||
expect(directory.contains(path.join(absolutePath, "b", "file.coffee"))).toBe true
|
||||
expect(directory.contains(path.join(absolutePath, "file.coffee"))).toBe true
|
||||
|
||||
it "returns true if the path is a child of the directory's symlinked source path", ->
|
||||
symlinkPath = path.join(__dirname, 'fixtures', 'symlink-to-dir')
|
||||
symlinkDirectory = new Directory(symlinkPath)
|
||||
realFilePath = require.resolve('./fixtures/dir/a')
|
||||
expect(symlinkDirectory.contains(realFilePath)).toBe true
|
||||
|
||||
it "returns false if the directory's path is not a prefix of the path", ->
|
||||
expect(directory.contains('/not/relative')).toBe false
|
||||
|
||||
describe "on #darwin or #linux", ->
|
||||
it "returns true if the path is a child of the directory's symlinked source path", ->
|
||||
symlinkPath = path.join(__dirname, 'fixtures', 'symlink-to-dir')
|
||||
symlinkDirectory = new Directory(symlinkPath)
|
||||
realFilePath = require.resolve('./fixtures/dir/a')
|
||||
expect(symlinkDirectory.contains(realFilePath)).toBe true
|
||||
|
||||
@@ -5,8 +5,8 @@ describe "DisplayBuffer", ->
|
||||
[displayBuffer, buffer, changeHandler, tabLength] = []
|
||||
beforeEach ->
|
||||
tabLength = 2
|
||||
atom.activatePackage('language-javascript', sync: true)
|
||||
buffer = project.bufferForPathSync('sample.js')
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength})
|
||||
changeHandler = jasmine.createSpy 'changeHandler'
|
||||
displayBuffer.on 'changed', changeHandler
|
||||
@@ -20,7 +20,7 @@ describe "DisplayBuffer", ->
|
||||
displayBuffer.setTabLength(4)
|
||||
displayBuffer.setEditorWidthInChars(64)
|
||||
displayBuffer.createFold(2, 4)
|
||||
displayBuffer2 = deserialize(displayBuffer.serialize())
|
||||
displayBuffer2 = atom.deserializers.deserialize(displayBuffer.serialize())
|
||||
expect(displayBuffer2.id).toBe displayBuffer.id
|
||||
expect(displayBuffer2.buffer).toBe displayBuffer.buffer
|
||||
expect(displayBuffer2.tokenizedBuffer.buffer).toBe displayBuffer.tokenizedBuffer.buffer
|
||||
@@ -66,14 +66,14 @@ describe "DisplayBuffer", ->
|
||||
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)
|
||||
atom.config.set('editor.preferredLineLength', 100)
|
||||
atom.config.set('editor.softWrapAtPreferredLineLength', true)
|
||||
expect(displayBuffer.lineForRow(10).text).toBe ' return '
|
||||
|
||||
config.set('editor.preferredLineLength', 5)
|
||||
atom.config.set('editor.preferredLineLength', 5)
|
||||
expect(displayBuffer.lineForRow(10).text).toBe 'funct'
|
||||
|
||||
config.set('editor.softWrapAtPreferredLineLength', false)
|
||||
atom.config.set('editor.softWrapAtPreferredLineLength', false)
|
||||
expect(displayBuffer.lineForRow(10).text).toBe ' return '
|
||||
|
||||
describe "when the line is shorter than the max line length", ->
|
||||
@@ -154,7 +154,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe "when a newline is inserted, deleted, and re-inserted at the end of a wrapped line (regression)", ->
|
||||
it "correctly renders the original wrapped line", ->
|
||||
buffer = project.buildBufferSync(null, '')
|
||||
buffer = atom.project.buildBufferSync(null, '')
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength, editorWidthInChars: 30, softWrap: true})
|
||||
|
||||
buffer.insert([0, 0], "the quick brown fox jumps over the lazy dog.")
|
||||
@@ -206,7 +206,7 @@ describe "DisplayBuffer", ->
|
||||
beforeEach ->
|
||||
displayBuffer.destroy()
|
||||
buffer.release()
|
||||
buffer = project.bufferForPathSync('two-hundred.txt')
|
||||
buffer = atom.project.bufferForPathSync('two-hundred.txt')
|
||||
displayBuffer = new DisplayBuffer({buffer, tabLength})
|
||||
displayBuffer.on 'changed', changeHandler
|
||||
|
||||
@@ -600,7 +600,7 @@ describe "DisplayBuffer", ->
|
||||
it "unsubscribes all display buffer markers from their underlying buffer marker (regression)", ->
|
||||
marker = displayBuffer.markBufferPosition([12, 2])
|
||||
displayBuffer.destroy()
|
||||
expect(marker.bufferMarker.subscriptionCount()).toBe 0
|
||||
expect(marker.bufferMarker.getSubscriptionCount()).toBe 0
|
||||
expect( -> buffer.insert([12, 2], '\n')).not.toThrow()
|
||||
|
||||
describe "markers", ->
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
{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.openSync('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
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -1,37 +0,0 @@
|
||||
{Site} = require 'telepath'
|
||||
Editor = require '../src/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.openSync('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
|
||||
+2613
-2713
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
@@ -1,59 +0,0 @@
|
||||
path = require 'path'
|
||||
{Site} = require 'telepath'
|
||||
{fs} = require 'atom'
|
||||
Project = require '../src/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 ? path.join(__dirname, '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
|
||||
+15
-15
@@ -6,19 +6,19 @@ describe 'File', ->
|
||||
|
||||
beforeEach ->
|
||||
filePath = path.join(__dirname, 'fixtures', 'atom-file-test.txt') # Don't put in /tmp because /tmp symlinks to /private/tmp and screws up the rename test
|
||||
fs.remove(filePath) if fs.exists(filePath)
|
||||
fs.writeSync(filePath, "this is old!")
|
||||
fs.removeSync(filePath) if fs.existsSync(filePath)
|
||||
fs.writeFileSync(filePath, "this is old!")
|
||||
file = new File(filePath)
|
||||
|
||||
afterEach ->
|
||||
file.off()
|
||||
fs.remove(filePath) if fs.exists(filePath)
|
||||
fs.removeSync(filePath) if fs.existsSync(filePath)
|
||||
|
||||
describe "when the file has not been read", ->
|
||||
describe "when the contents of the file change", ->
|
||||
it "triggers 'contents-changed' event handlers", ->
|
||||
file.on 'contents-changed', changeHandler = jasmine.createSpy('changeHandler')
|
||||
fs.writeSync(file.getPath(), "this is new!")
|
||||
fs.writeFileSync(file.getPath(), "this is new!")
|
||||
|
||||
waitsFor "change event", ->
|
||||
changeHandler.callCount > 0
|
||||
@@ -32,14 +32,14 @@ describe 'File', ->
|
||||
changeHandler = null
|
||||
changeHandler = jasmine.createSpy('changeHandler')
|
||||
file.on 'contents-changed', changeHandler
|
||||
fs.writeSync(file.getPath(), "this is new!")
|
||||
fs.writeFileSync(file.getPath(), "this is new!")
|
||||
|
||||
waitsFor "change event", ->
|
||||
changeHandler.callCount > 0
|
||||
|
||||
runs ->
|
||||
changeHandler.reset()
|
||||
fs.writeSync(file.getPath(), "this is newer!")
|
||||
fs.writeFileSync(file.getPath(), "this is newer!")
|
||||
|
||||
waitsFor "second change event", ->
|
||||
changeHandler.callCount > 0
|
||||
@@ -49,7 +49,7 @@ describe 'File', ->
|
||||
removeHandler = null
|
||||
removeHandler = jasmine.createSpy('removeHandler')
|
||||
file.on 'removed', removeHandler
|
||||
fs.remove(file.getPath())
|
||||
fs.removeSync(file.getPath())
|
||||
|
||||
waitsFor "remove event", ->
|
||||
removeHandler.callCount > 0
|
||||
@@ -61,8 +61,8 @@ describe 'File', ->
|
||||
newPath = path.join(path.dirname(filePath), "atom-file-was-moved-test.txt")
|
||||
|
||||
afterEach ->
|
||||
if fs.exists(newPath)
|
||||
fs.remove(newPath)
|
||||
if fs.existsSync(newPath)
|
||||
fs.removeSync(newPath)
|
||||
waitsFor "remove event", (done) -> file.on 'removed', done
|
||||
|
||||
it "it updates its path", ->
|
||||
@@ -71,7 +71,7 @@ describe 'File', ->
|
||||
moveHandler = jasmine.createSpy('moveHandler')
|
||||
file.on 'moved', moveHandler
|
||||
|
||||
fs.move(filePath, newPath)
|
||||
fs.moveSync(filePath, newPath)
|
||||
|
||||
waitsFor "move event", ->
|
||||
moveHandler.callCount > 0
|
||||
@@ -88,14 +88,14 @@ describe 'File', ->
|
||||
changeHandler = jasmine.createSpy('changeHandler')
|
||||
file.on 'contents-changed', changeHandler
|
||||
|
||||
fs.move(filePath, newPath)
|
||||
fs.moveSync(filePath, newPath)
|
||||
|
||||
waitsFor "move event", ->
|
||||
moveHandler.callCount > 0
|
||||
|
||||
runs ->
|
||||
expect(changeHandler).not.toHaveBeenCalled()
|
||||
fs.writeSync(file.getPath(), "this is new!")
|
||||
fs.writeFileSync(file.getPath(), "this is new!")
|
||||
|
||||
waitsFor "change event", ->
|
||||
changeHandler.callCount > 0
|
||||
@@ -112,12 +112,12 @@ describe 'File', ->
|
||||
|
||||
expect(changeHandler).not.toHaveBeenCalled()
|
||||
|
||||
fs.remove(filePath)
|
||||
fs.removeSync(filePath)
|
||||
|
||||
expect(changeHandler).not.toHaveBeenCalled()
|
||||
waits 20
|
||||
runs ->
|
||||
fs.writeSync(filePath, "HE HAS RISEN!")
|
||||
fs.writeFileSync(filePath, "HE HAS RISEN!")
|
||||
expect(changeHandler).not.toHaveBeenCalled()
|
||||
|
||||
waitsFor "resurrection change event", ->
|
||||
@@ -125,7 +125,7 @@ describe 'File', ->
|
||||
|
||||
runs ->
|
||||
expect(removeHandler).not.toHaveBeenCalled()
|
||||
fs.writeSync(filePath, "Hallelujah!")
|
||||
fs.writeFileSync(filePath, "Hallelujah!")
|
||||
changeHandler.reset()
|
||||
|
||||
waitsFor "post-resurrection change event", ->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class Foo
|
||||
registerDeserializer(this)
|
||||
atom.deserializers.add(this)
|
||||
@deserialize: ({data}) -> new Foo(data)
|
||||
constructor: (@data) ->
|
||||
|
||||
@@ -9,5 +9,5 @@ module.exports =
|
||||
|
||||
activate: ->
|
||||
@activateCallCount++
|
||||
rootView.getActiveView()?.command 'activation-event', =>
|
||||
atom.rootView.getActiveView()?.command 'activation-event', =>
|
||||
@activationEventCallCount++
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
{fs} = require 'atom'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
|
||||
describe "fs", ->
|
||||
fixturesDir = path.join(__dirname, 'fixtures')
|
||||
|
||||
describe ".read(path)", ->
|
||||
it "return contents of file", ->
|
||||
expect(fs.read(require.resolve("./fixtures/sample.txt"))).toBe "Some text.\n"
|
||||
|
||||
it "does not through an exception when the path is a binary file", ->
|
||||
expect(-> fs.read(require.resolve("./fixtures/binary-file.png"))).not.toThrow()
|
||||
|
||||
describe ".isFileSync(path)", ->
|
||||
it "returns true with a file path", ->
|
||||
expect(fs.isFileSync(path.join(fixturesDir, 'sample.js'))).toBe true
|
||||
|
||||
it "returns false with a directory path", ->
|
||||
expect(fs.isFileSync(fixturesDir)).toBe false
|
||||
|
||||
it "returns false with a non-existent path", ->
|
||||
expect(fs.isFileSync(path.join(fixturesDir, 'non-existent'))).toBe false
|
||||
expect(fs.isFileSync(null)).toBe false
|
||||
|
||||
describe ".exists(path)", ->
|
||||
it "returns true when path exsits", ->
|
||||
expect(fs.exists(fixturesDir)).toBe true
|
||||
|
||||
it "returns false when path doesn't exsit", ->
|
||||
expect(fs.exists(path.join(fixturesDir, "-nope-does-not-exist"))).toBe false
|
||||
expect(fs.exists("")).toBe false
|
||||
expect(fs.exists(null)).toBe false
|
||||
|
||||
describe ".makeTree(path)", ->
|
||||
aPath = path.join(temp.dir, 'a')
|
||||
|
||||
beforeEach ->
|
||||
fs.remove(aPath) if fs.exists(aPath)
|
||||
|
||||
it "creates all directories in path including any missing parent directories", ->
|
||||
abcPath = path.join(aPath, 'b', 'c')
|
||||
fs.makeTree(abcPath)
|
||||
expect(fs.exists(abcPath)).toBeTruthy()
|
||||
|
||||
describe ".traverseTreeSync(path, onFile, onDirectory)", ->
|
||||
it "calls fn for every path in the tree at the given path", ->
|
||||
paths = []
|
||||
onPath = (childPath) ->
|
||||
paths.push(childPath)
|
||||
true
|
||||
fs.traverseTreeSync fixturesDir, onPath, onPath
|
||||
expect(paths).toEqual fs.listTreeSync(fixturesDir)
|
||||
|
||||
it "does not recurse into a directory if it is pruned", ->
|
||||
paths = []
|
||||
onPath = (childPath) ->
|
||||
if childPath.match(/\/dir$/)
|
||||
false
|
||||
else
|
||||
paths.push(childPath)
|
||||
true
|
||||
fs.traverseTreeSync fixturesDir, onPath, onPath
|
||||
|
||||
expect(paths.length).toBeGreaterThan 0
|
||||
for filePath in paths
|
||||
expect(filePath).not.toMatch /\/dir\//
|
||||
|
||||
it "returns entries if path is a symlink", ->
|
||||
symlinkPath = path.join(fixturesDir, 'symlink-to-dir')
|
||||
symlinkPaths = []
|
||||
onSymlinkPath = (path) -> symlinkPaths.push(path.substring(symlinkPath.length + 1))
|
||||
|
||||
regularPath = path.join(fixturesDir, 'dir')
|
||||
paths = []
|
||||
onPath = (path) -> paths.push(path.substring(regularPath.length + 1))
|
||||
|
||||
fs.traverseTreeSync(symlinkPath, onSymlinkPath, onSymlinkPath)
|
||||
fs.traverseTreeSync(regularPath, onPath, onPath)
|
||||
|
||||
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'))
|
||||
fs.traverseTreeSync(directory, onPath)
|
||||
expect(paths.length).toBe 0
|
||||
|
||||
describe ".md5ForPath(path)", ->
|
||||
it "returns the MD5 hash of the file at the given path", ->
|
||||
expect(fs.md5ForPath(require.resolve('./fixtures/sample.js'))).toBe 'dd38087d0d7e3e4802a6d3f9b9745f2b'
|
||||
|
||||
describe ".list(path, extensions)", ->
|
||||
it "returns the absolute paths of entries within the given directory", ->
|
||||
paths = fs.listSync(project.getPath())
|
||||
expect(paths).toContain project.resolve('css.css')
|
||||
expect(paths).toContain project.resolve('coffee.coffee')
|
||||
expect(paths).toContain project.resolve('two-hundred.txt')
|
||||
|
||||
it "returns an empty array for paths that aren't directories or don't exist", ->
|
||||
expect(fs.listSync(project.resolve('sample.js'))).toEqual []
|
||||
expect(fs.listSync('/non/existent/directory')).toEqual []
|
||||
|
||||
it "can filter the paths by an optional array of file extensions", ->
|
||||
paths = fs.listSync(project.getPath(), ['.css', 'coffee'])
|
||||
expect(paths).toContain project.resolve('css.css')
|
||||
expect(paths).toContain project.resolve('coffee.coffee')
|
||||
expect(listedPath).toMatch /(css|coffee)$/ for listedPath in paths
|
||||
|
||||
describe ".list(path, [extensions,] callback)", ->
|
||||
paths = null
|
||||
|
||||
it "calls the callback with the absolute paths of entries within the given directory", ->
|
||||
waitsFor (done) ->
|
||||
fs.list project.getPath(), (err, result) ->
|
||||
paths = result
|
||||
done()
|
||||
runs ->
|
||||
expect(paths).toContain project.resolve('css.css')
|
||||
expect(paths).toContain project.resolve('coffee.coffee')
|
||||
expect(paths).toContain project.resolve('two-hundred.txt')
|
||||
|
||||
it "can filter the paths by an optional array of file extensions", ->
|
||||
waitsFor (done) ->
|
||||
fs.list project.getPath(), ['css', '.coffee'], (err, result) ->
|
||||
paths = result
|
||||
done()
|
||||
runs ->
|
||||
expect(paths).toContain project.resolve('css.css')
|
||||
expect(paths).toContain project.resolve('coffee.coffee')
|
||||
expect(listedPath).toMatch /(css|coffee)$/ for listedPath in paths
|
||||
|
||||
describe ".absolute(relativePath)", ->
|
||||
it "converts a leading ~ segment to the HOME directory", ->
|
||||
homeDir = atom.getHomeDirPath()
|
||||
expect(fs.absolute('~')).toBe fs.realpathSync(homeDir)
|
||||
expect(fs.absolute(path.join('~', 'does', 'not', 'exist'))).toBe path.join(homeDir, 'does', 'not', 'exist')
|
||||
expect(fs.absolute('~test')).toBe '~test'
|
||||
+66
-60
@@ -9,7 +9,7 @@ describe "Git", ->
|
||||
|
||||
beforeEach ->
|
||||
gitPath = path.join(temp.dir, '.git')
|
||||
fs.remove(gitPath) if fs.isDirectorySync(gitPath)
|
||||
fs.removeSync(gitPath) if fs.isDirectorySync(gitPath)
|
||||
|
||||
afterEach ->
|
||||
repo.destroy() if repo?.repo?
|
||||
@@ -47,22 +47,22 @@ describe "Git", ->
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'working-dir'))
|
||||
filePath = require.resolve('./fixtures/git/working-dir/file.txt')
|
||||
newPath = path.join(__dirname, 'fixtures', 'git', 'working-dir', 'new-path.txt')
|
||||
originalPathText = fs.read(filePath)
|
||||
originalPathText = fs.readFileSync(filePath, 'utf8')
|
||||
|
||||
afterEach ->
|
||||
fs.writeSync(filePath, originalPathText)
|
||||
fs.remove(newPath) if fs.exists(newPath)
|
||||
fs.writeFileSync(filePath, originalPathText)
|
||||
fs.removeSync(newPath) if fs.existsSync(newPath)
|
||||
|
||||
describe "when the path is unstaged", ->
|
||||
it "returns false if the path has not been modified", ->
|
||||
expect(repo.isPathModified(filePath)).toBeFalsy()
|
||||
|
||||
it "returns true if the path is modified", ->
|
||||
fs.writeSync(filePath, "change")
|
||||
fs.writeFileSync(filePath, "change")
|
||||
expect(repo.isPathModified(filePath)).toBeTruthy()
|
||||
|
||||
it "returns true if the path is deleted", ->
|
||||
fs.remove(filePath)
|
||||
fs.removeSync(filePath)
|
||||
expect(repo.isPathModified(filePath)).toBeTruthy()
|
||||
|
||||
it "returns false if the path is new", ->
|
||||
@@ -75,10 +75,10 @@ describe "Git", ->
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'working-dir'))
|
||||
filePath = require.resolve('./fixtures/git/working-dir/file.txt')
|
||||
newPath = path.join(__dirname, 'fixtures', 'git', 'working-dir', 'new-path.txt')
|
||||
fs.writeSync(newPath, "i'm new here")
|
||||
fs.writeFileSync(newPath, "i'm new here")
|
||||
|
||||
afterEach ->
|
||||
fs.remove(newPath) if fs.exists(newPath)
|
||||
fs.removeSync(newPath) if fs.existsSync(newPath)
|
||||
|
||||
describe "when the path is unstaged", ->
|
||||
it "returns true if the path is new", ->
|
||||
@@ -93,35 +93,35 @@ describe "Git", ->
|
||||
beforeEach ->
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'working-dir'))
|
||||
path1 = require.resolve('./fixtures/git/working-dir/file.txt')
|
||||
originalPath1Text = fs.read(path1)
|
||||
originalPath1Text = fs.readFileSync(path1, 'utf8')
|
||||
path2 = require.resolve('./fixtures/git/working-dir/other.txt')
|
||||
originalPath2Text = fs.read(path2)
|
||||
originalPath2Text = fs.readFileSync(path2, 'utf8')
|
||||
|
||||
afterEach ->
|
||||
fs.writeSync(path1, originalPath1Text)
|
||||
fs.writeSync(path2, originalPath2Text)
|
||||
fs.writeFileSync(path1, originalPath1Text)
|
||||
fs.writeFileSync(path2, originalPath2Text)
|
||||
|
||||
it "no longer reports a path as modified after checkout", ->
|
||||
expect(repo.isPathModified(path1)).toBeFalsy()
|
||||
fs.writeSync(path1, '')
|
||||
fs.writeFileSync(path1, '')
|
||||
expect(repo.isPathModified(path1)).toBeTruthy()
|
||||
expect(repo.checkoutHead(path1)).toBeTruthy()
|
||||
expect(repo.isPathModified(path1)).toBeFalsy()
|
||||
|
||||
it "restores the contents of the path to the original text", ->
|
||||
fs.writeSync(path1, '')
|
||||
fs.writeFileSync(path1, '')
|
||||
expect(repo.checkoutHead(path1)).toBeTruthy()
|
||||
expect(fs.read(path1)).toBe(originalPath1Text)
|
||||
expect(fs.readFileSync(path1, 'utf8')).toBe(originalPath1Text)
|
||||
|
||||
it "only restores the path specified", ->
|
||||
fs.writeSync(path2, 'path 2 is edited')
|
||||
fs.writeFileSync(path2, 'path 2 is edited')
|
||||
expect(repo.isPathModified(path2)).toBeTruthy()
|
||||
expect(repo.checkoutHead(path1)).toBeTruthy()
|
||||
expect(fs.read(path2)).toBe('path 2 is edited')
|
||||
expect(fs.readFileSync(path2, 'utf8')).toBe('path 2 is edited')
|
||||
expect(repo.isPathModified(path2)).toBeTruthy()
|
||||
|
||||
it "fires a status-changed event if the checkout completes successfully", ->
|
||||
fs.writeSync(path1, '')
|
||||
fs.writeFileSync(path1, '')
|
||||
repo.getPathStatus(path1)
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
repo.on 'status-changed', statusHandler
|
||||
@@ -144,14 +144,14 @@ describe "Git", ->
|
||||
beforeEach ->
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'working-dir'))
|
||||
filePath = require.resolve('./fixtures/git/working-dir/file.txt')
|
||||
originalPathText = fs.read(filePath)
|
||||
originalPathText = fs.readFileSync(filePath, 'utf8')
|
||||
|
||||
afterEach ->
|
||||
fs.writeSync(filePath, originalPathText)
|
||||
fs.writeFileSync(filePath, originalPathText)
|
||||
|
||||
it "returns the number of lines added and deleted", ->
|
||||
expect(repo.getDiffStats(filePath)).toEqual {added: 0, deleted: 0}
|
||||
fs.writeSync(filePath, "#{originalPathText} edited line")
|
||||
fs.writeFileSync(filePath, "#{originalPathText} edited line")
|
||||
expect(repo.getDiffStats(filePath)).toEqual {added: 1, deleted: 1}
|
||||
|
||||
describe ".getPathStatus(path)", ->
|
||||
@@ -160,20 +160,20 @@ describe "Git", ->
|
||||
beforeEach ->
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'working-dir'))
|
||||
filePath = require.resolve('./fixtures/git/working-dir/file.txt')
|
||||
originalPathText = fs.read(filePath)
|
||||
originalPathText = fs.readFileSync(filePath, 'utf8')
|
||||
|
||||
afterEach ->
|
||||
fs.writeSync(filePath, originalPathText)
|
||||
fs.writeFileSync(filePath, originalPathText)
|
||||
|
||||
it "trigger a status-changed event when the new status differs from the last cached one", ->
|
||||
statusHandler = jasmine.createSpy("statusHandler")
|
||||
repo.on 'status-changed', statusHandler
|
||||
fs.writeSync(filePath, '')
|
||||
fs.writeFileSync(filePath, '')
|
||||
status = repo.getPathStatus(filePath)
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler.argsForCall[0][0..1]).toEqual [filePath, status]
|
||||
|
||||
fs.writeSync(filePath, 'abc')
|
||||
fs.writeFileSync(filePath, 'abc')
|
||||
status = repo.getPathStatus(filePath)
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
|
||||
@@ -182,18 +182,18 @@ describe "Git", ->
|
||||
|
||||
beforeEach ->
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'working-dir'))
|
||||
modifiedPath = project.resolve('git/working-dir/file.txt')
|
||||
originalModifiedPathText = fs.read(modifiedPath)
|
||||
newPath = project.resolve('git/working-dir/untracked.txt')
|
||||
cleanPath = project.resolve('git/working-dir/other.txt')
|
||||
fs.writeSync(newPath, '')
|
||||
modifiedPath = atom.project.resolve('git/working-dir/file.txt')
|
||||
originalModifiedPathText = fs.readFileSync(modifiedPath, 'utf8')
|
||||
newPath = atom.project.resolve('git/working-dir/untracked.txt')
|
||||
cleanPath = atom.project.resolve('git/working-dir/other.txt')
|
||||
fs.writeFileSync(newPath, '')
|
||||
|
||||
afterEach ->
|
||||
fs.writeSync(modifiedPath, originalModifiedPathText)
|
||||
fs.remove(newPath) if fs.exists(newPath)
|
||||
fs.writeFileSync(modifiedPath, originalModifiedPathText)
|
||||
fs.removeSync(newPath) if fs.existsSync(newPath)
|
||||
|
||||
it "returns status information for all new and modified files", ->
|
||||
fs.writeSync(modifiedPath, 'making this path modified')
|
||||
fs.writeFileSync(modifiedPath, 'making this path modified')
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
repo.on 'statuses-changed', statusHandler
|
||||
repo.refreshStatus()
|
||||
@@ -207,52 +207,58 @@ describe "Git", ->
|
||||
expect(repo.isStatusNew(statuses[newPath])).toBeTruthy()
|
||||
expect(repo.isStatusModified(statuses[modifiedPath])).toBeTruthy()
|
||||
|
||||
describe "when a buffer is changed and then saved", ->
|
||||
[originalContent, editSession] = []
|
||||
describe "buffer events", ->
|
||||
[originalContent, editor] = []
|
||||
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
originalContent = editor.getText()
|
||||
|
||||
afterEach ->
|
||||
fs.writeSync(editSession.getPath(), originalContent)
|
||||
fs.writeFileSync(editor.getPath(), originalContent)
|
||||
|
||||
it "emits a status-changed event", ->
|
||||
editSession = project.openSync('sample.js')
|
||||
originalContent = editSession.getText()
|
||||
editSession.insertNewline()
|
||||
it "emits a status-changed event when a buffer is saved", ->
|
||||
editor.insertNewline()
|
||||
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
project.getRepo().on 'status-changed', statusHandler
|
||||
editSession.save()
|
||||
atom.project.getRepo().on 'status-changed', statusHandler
|
||||
editor.save()
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler).toHaveBeenCalledWith editSession.getPath(), 256
|
||||
expect(statusHandler).toHaveBeenCalledWith editor.getPath(), 256
|
||||
|
||||
describe "when a buffer is reloaded and has been changed", ->
|
||||
[originalContent, editSession] = []
|
||||
|
||||
afterEach ->
|
||||
fs.writeSync(editSession.getPath(), originalContent)
|
||||
|
||||
it "emits a status-changed event", ->
|
||||
editSession = project.openSync('sample.js')
|
||||
originalContent = editSession.getText()
|
||||
fs.writeSync(editSession.getPath(), 'changed')
|
||||
it "emits a status-changed event when a buffer is reloaded", ->
|
||||
fs.writeFileSync(editor.getPath(), 'changed')
|
||||
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
project.getRepo().on 'status-changed', statusHandler
|
||||
editSession.getBuffer().reload()
|
||||
atom.project.getRepo().on 'status-changed', statusHandler
|
||||
editor.getBuffer().reload()
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler).toHaveBeenCalledWith editSession.getPath(), 256
|
||||
editSession.getBuffer().reload()
|
||||
expect(statusHandler).toHaveBeenCalledWith editor.getPath(), 256
|
||||
editor.getBuffer().reload()
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
|
||||
it "emits a status-changed event when a buffer's path changes", ->
|
||||
fs.writeFileSync(editor.getPath(), 'changed')
|
||||
|
||||
statusHandler = jasmine.createSpy('statusHandler')
|
||||
atom.project.getRepo().on 'status-changed', statusHandler
|
||||
editor.getBuffer().emit 'path-changed'
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
expect(statusHandler).toHaveBeenCalledWith editor.getPath(), 256
|
||||
editor.getBuffer().emit 'path-changed'
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
|
||||
describe "when a project is deserialized", ->
|
||||
[originalContent, buffer, project2] = []
|
||||
|
||||
afterEach ->
|
||||
fs.writeSync(buffer.getPath(), originalContent)
|
||||
fs.writeFileSync(buffer.getPath(), originalContent)
|
||||
project2?.destroy()
|
||||
|
||||
it "subscribes to all the serialized buffers in the project", ->
|
||||
project.openSync('sample.js')
|
||||
project2 = deserialize(project.serialize())
|
||||
atom.project.openSync('sample.js')
|
||||
#TODO Replace with atom.replicate().project when Atom is a telepath model
|
||||
project2 = atom.replicate().get('project')
|
||||
buffer = project2.getBuffers()[0]
|
||||
|
||||
waitsFor ->
|
||||
|
||||
@@ -3,6 +3,7 @@ module.exports.runSpecSuite = (specSuite, logErrors=true) ->
|
||||
window[key] = value for key, value of require '../vendor/jasmine'
|
||||
|
||||
require 'jasmine-focused'
|
||||
require 'jasmine-tagged'
|
||||
|
||||
TimeReporter = require './time-reporter'
|
||||
timeReporter = new TimeReporter()
|
||||
@@ -27,6 +28,7 @@ module.exports.runSpecSuite = (specSuite, logErrors=true) ->
|
||||
jasmineEnv = jasmine.getEnv()
|
||||
jasmineEnv.addReporter(reporter)
|
||||
jasmineEnv.addReporter(timeReporter)
|
||||
jasmineEnv.setIncludedTags([process.platform])
|
||||
|
||||
$('body').append $$ -> @div id: 'jasmine-content'
|
||||
|
||||
|
||||
+78
-110
@@ -1,12 +1,15 @@
|
||||
path = require 'path'
|
||||
|
||||
Keymap = require '../src/keymap'
|
||||
{$, $$, RootView} = require 'atom'
|
||||
|
||||
describe "Keymap", ->
|
||||
fragment = null
|
||||
keymap = null
|
||||
resourcePath = atom.getLoadSettings().resourcePath
|
||||
|
||||
beforeEach ->
|
||||
keymap = new Keymap
|
||||
keymap = new Keymap({configDirPath: atom.getConfigDirPath(), resourcePath})
|
||||
fragment = $ """
|
||||
<div class="command-mode">
|
||||
<div class="child-node">
|
||||
@@ -18,25 +21,19 @@ describe "Keymap", ->
|
||||
describe ".handleKeyEvent(event)", ->
|
||||
deleteCharHandler = null
|
||||
insertCharHandler = null
|
||||
commandZHandler = null
|
||||
|
||||
beforeEach ->
|
||||
keymap.bindKeys '.command-mode', 'x': 'deleteChar'
|
||||
keymap.bindKeys '.insert-mode', 'x': 'insertChar'
|
||||
keymap.bindKeys 'name', '.command-mode', 'x': 'deleteChar'
|
||||
keymap.bindKeys 'name', '.insert-mode', 'x': 'insertChar'
|
||||
keymap.bindKeys 'name', '.command-mode', 'cmd-z': 'commandZPressed'
|
||||
|
||||
deleteCharHandler = jasmine.createSpy('deleteCharHandler')
|
||||
insertCharHandler = jasmine.createSpy('insertCharHandler')
|
||||
commandZHandler = jasmine.createSpy('commandZHandler')
|
||||
fragment.on 'deleteChar', deleteCharHandler
|
||||
fragment.on 'insertChar', insertCharHandler
|
||||
|
||||
it "adds a 'keystrokes' string to the event object", ->
|
||||
event = keydownEvent('x', altKey: true, metaKey: true)
|
||||
keymap.handleKeyEvent(event)
|
||||
expect(event.keystrokes).toBe 'alt-meta-x'
|
||||
|
||||
event = keydownEvent(',', metaKey: true)
|
||||
event.which = 188
|
||||
keymap.handleKeyEvent(event)
|
||||
expect(event.keystrokes).toBe 'meta-,'
|
||||
fragment.on 'commandZPressed', commandZHandler
|
||||
|
||||
describe "when no binding matches the event's keystroke", ->
|
||||
it "does not return false so the event continues to propagate", ->
|
||||
@@ -44,10 +41,11 @@ describe "Keymap", ->
|
||||
|
||||
describe "when a non-English keyboard language is used", ->
|
||||
it "uses the physical character pressed instead of the character it maps to in the current language", ->
|
||||
event = keydownEvent('U+03B6', metaKey: true) # This is the 'z' key using the Greek keyboard layout
|
||||
event.which = 122
|
||||
keymap.handleKeyEvent(event)
|
||||
expect(event.keystrokes).toBe 'meta-z'
|
||||
event = keydownEvent('U+03B6', metaKey: true, which: 122, target: fragment[0]) # This is the 'z' key using the Greek keyboard layout
|
||||
result = keymap.handleKeyEvent(event)
|
||||
|
||||
expect(result).toBe(false)
|
||||
expect(commandZHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when at least one binding fully matches the event's keystroke", ->
|
||||
describe "when the event's target node matches a selector with a matching binding", ->
|
||||
@@ -64,9 +62,6 @@ describe "Keymap", ->
|
||||
keymap.handleKeyEvent(event)
|
||||
expect(deleteCharHandler).not.toHaveBeenCalled()
|
||||
expect(insertCharHandler).toHaveBeenCalled()
|
||||
commandEvent = insertCharHandler.argsForCall[0][0]
|
||||
expect(commandEvent.keyEvent).toBe event
|
||||
expect(event.keystrokes).toBe 'x'
|
||||
|
||||
describe "when the event's target node *descends* from a selector with a matching binding", ->
|
||||
it "triggers the command event associated with that binding on the target node and returns false", ->
|
||||
@@ -85,7 +80,7 @@ describe "Keymap", ->
|
||||
|
||||
describe "when the event's target node descends from multiple nodes that match selectors with a binding", ->
|
||||
beforeEach ->
|
||||
keymap.bindKeys '.child-node', 'x': 'foo'
|
||||
keymap.bindKeys 'name', '.child-node', 'x': 'foo'
|
||||
|
||||
it "only triggers bindings on selectors associated with the closest ancestor node", ->
|
||||
fooHandler = jasmine.createSpy 'fooHandler'
|
||||
@@ -122,10 +117,10 @@ describe "Keymap", ->
|
||||
describe "when the event bubbles to a node that matches multiple selectors", ->
|
||||
describe "when the matching selectors differ in specificity", ->
|
||||
it "triggers the binding for the most specific selector", ->
|
||||
keymap.bindKeys 'div .child-node', 'x': 'foo'
|
||||
keymap.bindKeys '.command-mode .child-node !important', 'x': 'baz'
|
||||
keymap.bindKeys '.command-mode .child-node', 'x': 'quux'
|
||||
keymap.bindKeys '.child-node', 'x': 'bar'
|
||||
keymap.bindKeys 'name', 'div .child-node', 'x': 'foo'
|
||||
keymap.bindKeys 'name', '.command-mode .child-node !important', 'x': 'baz'
|
||||
keymap.bindKeys 'name', '.command-mode .child-node', 'x': 'quux'
|
||||
keymap.bindKeys 'name', '.child-node', 'x': 'bar'
|
||||
|
||||
fooHandler = jasmine.createSpy 'fooHandler'
|
||||
barHandler = jasmine.createSpy 'barHandler'
|
||||
@@ -143,8 +138,8 @@ describe "Keymap", ->
|
||||
|
||||
describe "when the matching selectors have the same specificity", ->
|
||||
it "triggers the bindings for the most recently declared selector", ->
|
||||
keymap.bindKeys '.child-node', 'x': 'foo', 'y': 'baz'
|
||||
keymap.bindKeys '.child-node', 'x': 'bar'
|
||||
keymap.bindKeys 'name', '.child-node', 'x': 'foo', 'y': 'baz'
|
||||
keymap.bindKeys 'name', '.child-node', 'x': 'bar'
|
||||
|
||||
fooHandler = jasmine.createSpy 'fooHandler'
|
||||
barHandler = jasmine.createSpy 'barHandler'
|
||||
@@ -164,10 +159,11 @@ describe "Keymap", ->
|
||||
|
||||
describe "when the event's target is the document body", ->
|
||||
it "triggers the mapped event on the rootView", ->
|
||||
window.rootView = new RootView
|
||||
keymap.bindKeys 'body', 'x': 'foo'
|
||||
atom.rootView = new RootView
|
||||
atom.rootView.attachToDom()
|
||||
keymap.bindKeys 'name', 'body', 'x': 'foo'
|
||||
fooHandler = jasmine.createSpy("fooHandler")
|
||||
rootView.on 'foo', fooHandler
|
||||
atom.rootView.on 'foo', fooHandler
|
||||
|
||||
result = keymap.handleKeyEvent(keydownEvent('x', target: document.body))
|
||||
expect(result).toBe(false)
|
||||
@@ -177,7 +173,7 @@ describe "Keymap", ->
|
||||
|
||||
describe "when the event matches a 'native!' binding", ->
|
||||
it "returns true, allowing the browser's native key handling to process the event", ->
|
||||
keymap.bindKeys '.grandchild-node', 'x': 'native!'
|
||||
keymap.bindKeys 'name', '.grandchild-node', 'x': 'native!'
|
||||
nativeHandler = jasmine.createSpy("nativeHandler")
|
||||
fragment.on 'native!', nativeHandler
|
||||
expect(keymap.handleKeyEvent(keydownEvent('x', target: fragment.find('.grandchild-node')[0]))).toBe true
|
||||
@@ -187,7 +183,7 @@ describe "Keymap", ->
|
||||
[quitHandler, closeOtherWindowsHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
keymap.bindKeys "*",
|
||||
keymap.bindKeys 'name', "*",
|
||||
'ctrl-x ctrl-c': 'quit'
|
||||
'ctrl-x 1': 'close-other-windows'
|
||||
|
||||
@@ -203,6 +199,7 @@ describe "Keymap", ->
|
||||
describe "when a second keystroke added to the first to match a multi-stroke binding completely", ->
|
||||
it "triggers the event associated with the matched multi-stroke binding", ->
|
||||
expect(keymap.handleKeyEvent(keydownEvent('x', target: fragment[0], ctrlKey: true))).toBeFalsy()
|
||||
expect(keymap.handleKeyEvent(keydownEvent('ctrl', target: fragment[0]))).toBeFalsy() # This simulates actual key event behavior
|
||||
expect(keymap.handleKeyEvent(keydownEvent('c', target: fragment[0], ctrlKey: true))).toBeFalsy()
|
||||
|
||||
expect(quitHandler).toHaveBeenCalled()
|
||||
@@ -216,7 +213,7 @@ describe "Keymap", ->
|
||||
expect(closeOtherWindowsHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when a second keystroke added to the first doesn't match any bindings", ->
|
||||
it "clears the queued keystrokes without triggering any events", ->
|
||||
it "clears the queued keystroke without triggering any events", ->
|
||||
expect(keymap.handleKeyEvent(keydownEvent('x', target: fragment[0], ctrlKey: true))).toBe false
|
||||
expect(keymap.handleKeyEvent(keydownEvent('c', target: fragment[0]))).toBe false
|
||||
expect(quitHandler).not.toHaveBeenCalled()
|
||||
@@ -226,7 +223,7 @@ describe "Keymap", ->
|
||||
|
||||
describe "when the event's target node descends from multiple nodes that match selectors with a partial binding match", ->
|
||||
it "allows any of the bindings to be triggered upon a second keystroke, favoring the most specific selector", ->
|
||||
keymap.bindKeys ".grandchild-node", 'ctrl-x ctrl-c': 'more-specific-quit'
|
||||
keymap.bindKeys 'name', ".grandchild-node", 'ctrl-x ctrl-c': 'more-specific-quit'
|
||||
grandchildNode = fragment.find('.grandchild-node')[0]
|
||||
moreSpecificQuitHandler = jasmine.createSpy('moreSpecificQuitHandler')
|
||||
fragment.on 'more-specific-quit', moreSpecificQuitHandler
|
||||
@@ -250,39 +247,17 @@ describe "Keymap", ->
|
||||
describe "when there is a complete binding with a more specific selector", ->
|
||||
it "favors the more specific complete match", ->
|
||||
|
||||
describe "when a tab keystroke does not match any bindings", ->
|
||||
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)", ->
|
||||
describe ".bindKeys(name, selector, bindings)", ->
|
||||
it "normalizes the key patterns in the hash to put the modifiers in alphabetical order", ->
|
||||
fooHandler = jasmine.createSpy('fooHandler')
|
||||
fragment.on 'foo', fooHandler
|
||||
keymap.bindKeys '*', 'ctrl-alt-delete': 'foo'
|
||||
keymap.bindKeys 'name', '*', 'ctrl-alt-delete': 'foo'
|
||||
result = keymap.handleKeyEvent(keydownEvent('delete', ctrlKey: true, altKey: true, target: fragment[0]))
|
||||
expect(result).toBe(false)
|
||||
expect(fooHandler).toHaveBeenCalled()
|
||||
|
||||
fooHandler.reset()
|
||||
keymap.bindKeys '*', 'ctrl-alt--': 'foo'
|
||||
keymap.bindKeys 'name', '*', 'ctrl-alt--': 'foo'
|
||||
result = keymap.handleKeyEvent(keydownEvent('-', ctrlKey: true, altKey: true, target: fragment[0]))
|
||||
expect(result).toBe(false)
|
||||
expect(fooHandler).toHaveBeenCalled()
|
||||
@@ -295,15 +270,17 @@ describe "Keymap", ->
|
||||
'.brown':
|
||||
'ctrl-h': 'harvest'
|
||||
|
||||
expect(keymap.bindingsForElement($$ -> @div class: 'green')).toEqual { 'ctrl-c': 'cultivate' }
|
||||
expect(keymap.bindingsForElement($$ -> @div class: 'brown')).toEqual { 'ctrl-h': 'harvest' }
|
||||
keymap.add 'medical',
|
||||
'.green':
|
||||
'ctrl-v': 'vomit'
|
||||
|
||||
expect(keymap.keyBindingsMatchingElement($$ -> @div class: 'green')).toHaveLength 2
|
||||
expect(keymap.keyBindingsMatchingElement($$ -> @div class: 'brown')).toHaveLength 1
|
||||
|
||||
keymap.remove('nature')
|
||||
|
||||
expect(keymap.bindingsForElement($$ -> @div class: 'green')).toEqual {}
|
||||
expect(keymap.bindingsForElement($$ -> @div class: 'brown')).toEqual {}
|
||||
expect(keymap.bindingSetsByFirstKeystroke['ctrl-c']).toEqual []
|
||||
expect(keymap.bindingSetsByFirstKeystroke['ctrl-h']).toEqual []
|
||||
expect(keymap.keyBindingsMatchingElement($$ -> @div class: 'green')).toHaveLength 1
|
||||
expect(keymap.keyBindingsMatchingElement($$ -> @div class: 'brown')).toEqual []
|
||||
|
||||
describe ".keystrokeStringForEvent(event)", ->
|
||||
describe "when no modifiers are pressed", ->
|
||||
@@ -314,12 +291,12 @@ describe "Keymap", ->
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('left'))).toBe 'left'
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('\b'))).toBe 'backspace'
|
||||
|
||||
describe "when ctrl, alt or meta is pressed with a non-modifier key", ->
|
||||
describe "when ctrl, alt or command is pressed with a non-modifier key", ->
|
||||
it "returns a string that identifies the key pressed", ->
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('a', altKey: true))).toBe 'alt-a'
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('[', metaKey: true))).toBe 'meta-['
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('[', metaKey: true))).toBe 'cmd-['
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('*', ctrlKey: true))).toBe 'ctrl-*'
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('left', ctrlKey: true, metaKey: true, altKey: true))).toBe 'alt-ctrl-meta-left'
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('left', ctrlKey: true, metaKey: true, altKey: true))).toBe 'alt-cmd-ctrl-left'
|
||||
|
||||
describe "when shift is pressed when a non-modifer key", ->
|
||||
it "returns a string that identifies the key pressed", ->
|
||||
@@ -328,54 +305,45 @@ describe "Keymap", ->
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('left', shiftKey: true))).toBe 'shift-left'
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('Left', shiftKey: true))).toBe 'shift-left'
|
||||
|
||||
describe ".bindingsForElement(element)", ->
|
||||
describe ".keyBindingsMatchingElement(element)", ->
|
||||
it "returns the matching bindings for the element", ->
|
||||
keymap.bindKeys '.command-mode', 'c': 'c'
|
||||
keymap.bindKeys '.grandchild-node', 'g': 'g'
|
||||
keymap.bindKeys 'name', '.command-mode', 'c': 'c'
|
||||
keymap.bindKeys 'name', '.grandchild-node', 'g': 'g'
|
||||
|
||||
bindings = keymap.bindingsForElement(fragment.find('.grandchild-node'))
|
||||
expect(Object.keys(bindings).length).toBe 2
|
||||
expect(bindings['c']).toEqual "c"
|
||||
expect(bindings['g']).toEqual "g"
|
||||
bindings = keymap.keyBindingsMatchingElement(fragment.find('.grandchild-node'))
|
||||
expect(bindings).toHaveLength 2
|
||||
expect(bindings[0].command).toEqual "g"
|
||||
expect(bindings[1].command).toEqual "c"
|
||||
|
||||
describe "when multiple bindings match a keystroke", ->
|
||||
it "only returns bindings that match the most specific selector", ->
|
||||
keymap.bindKeys '.command-mode', 'g': 'command-mode'
|
||||
keymap.bindKeys '.command-mode .grandchild-node', 'g': 'command-and-grandchild-node'
|
||||
keymap.bindKeys '.grandchild-node', 'g': 'grandchild-node'
|
||||
keymap.bindKeys 'name', '.command-mode', 'g': 'cmd-mode'
|
||||
keymap.bindKeys 'name', '.command-mode .grandchild-node', 'g': 'cmd-and-grandchild-node'
|
||||
keymap.bindKeys 'name', '.grandchild-node', 'g': 'grandchild-node'
|
||||
|
||||
bindings = keymap.bindingsForElement(fragment.find('.grandchild-node'))
|
||||
expect(Object.keys(bindings).length).toBe 1
|
||||
expect(bindings['g']).toEqual "command-and-grandchild-node"
|
||||
bindings = keymap.keyBindingsMatchingElement(fragment.find('.grandchild-node'))
|
||||
expect(bindings).toHaveLength 3
|
||||
expect(bindings[0].command).toEqual "cmd-and-grandchild-node"
|
||||
|
||||
describe ".getAllKeyMappings", ->
|
||||
it "returns the all bindings", ->
|
||||
keymap.bindKeys '~/.atom/packages/dummy/keymaps/a.cson', '.command-mode', 'k': 'c'
|
||||
describe ".keyBindingsForCommandMatchingElement(element)", ->
|
||||
beforeEach ->
|
||||
keymap.add 'nature',
|
||||
'.green':
|
||||
'ctrl-c': 'cultivate'
|
||||
'.green-2':
|
||||
'ctrl-o': 'cultivate'
|
||||
'.brown':
|
||||
'ctrl-h': 'harvest'
|
||||
'.blue':
|
||||
'ctrl-c': 'fly'
|
||||
|
||||
mappings = keymap.getAllKeyMappings()
|
||||
expect(mappings.length).toBe 1
|
||||
expect(mappings[0].source).toEqual 'dummy'
|
||||
expect(mappings[0].keystrokes).toEqual 'k'
|
||||
expect(mappings[0].command).toEqual 'c'
|
||||
expect(mappings[0].selector).toEqual '.command-mode'
|
||||
it "finds a keymap for an element", ->
|
||||
el = $$ -> @div class: 'green'
|
||||
bindings = keymap.keyBindingsForCommandMatchingElement('cultivate', el)
|
||||
expect(bindings).toHaveLength 1
|
||||
expect(bindings[0].keystroke).toEqual "ctrl-c"
|
||||
|
||||
describe ".determineSource", ->
|
||||
describe "for a package", ->
|
||||
it "returns '<package-name>'", ->
|
||||
expect(keymap.determineSource('~/.atom/packages/dummy/keymaps/a.cson')).toEqual 'dummy'
|
||||
|
||||
describe "for a linked package", ->
|
||||
it "returns '<package-name>'", ->
|
||||
expect(keymap.determineSource('/Users/john/github/dummy/keymaps/a.cson')).toEqual 'dummy'
|
||||
|
||||
describe "for a user defined keymap", ->
|
||||
it "returns 'User'", ->
|
||||
expect(keymap.determineSource('~/.atom/keymaps/a.cson')).toEqual 'User'
|
||||
|
||||
describe "for a core keymap", ->
|
||||
it "returns 'Core'", ->
|
||||
expect(keymap.determineSource('/Applications/Atom.app/.../node_modules/dummy/keymaps/a.cson')).toEqual 'Core'
|
||||
|
||||
describe "for a linked core keymap", ->
|
||||
it "returns 'Core'", ->
|
||||
expect(keymap.determineSource('/Users/john/github/atom/keymaps/a.cson')).toEqual 'Core'
|
||||
it "no keymap an element without that map", ->
|
||||
el = $$ -> @div class: 'brown'
|
||||
bindings = keymap.keyBindingsForCommandMatchingElement('cultivate', el)
|
||||
expect(bindings).toHaveLength 0
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
describe "LanguageMode", ->
|
||||
[editSession, buffer, languageMode] = []
|
||||
[editor, buffer, languageMode] = []
|
||||
|
||||
afterEach ->
|
||||
editSession.destroy()
|
||||
editor.destroy()
|
||||
|
||||
describe "javascript", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-javascript', sync: true)
|
||||
editSession = project.openSync('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
editor = atom.project.openSync('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
describe ".minIndentLevelForRowRange(startRow, endRow)", ->
|
||||
it "returns the minimum indent level for the given row range", ->
|
||||
@@ -32,6 +32,18 @@ describe "LanguageMode", ->
|
||||
expect(buffer.lineForRow(6)).toBe " // current < pivot ? left.push(current) : right.push(current);"
|
||||
expect(buffer.lineForRow(7)).toBe " // }"
|
||||
|
||||
buffer.setText('\tvar i;')
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 0)
|
||||
expect(buffer.lineForRow(0)).toBe "\t// var i;"
|
||||
|
||||
buffer.setText('var i;')
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 0)
|
||||
expect(buffer.lineForRow(0)).toBe "// var i;"
|
||||
|
||||
buffer.setText(' var i;')
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 0)
|
||||
expect(buffer.lineForRow(0)).toBe " // var i;"
|
||||
|
||||
describe "fold suggestion", ->
|
||||
describe ".doesBufferRowStartFold(bufferRow)", ->
|
||||
it "returns true only when the buffer row starts a foldable region", ->
|
||||
@@ -97,9 +109,9 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "coffeescript", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-coffee-script', sync: true)
|
||||
editSession = project.openSync('coffee.coffee', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
atom.packages.activatePackage('language-coffee-script', sync: true)
|
||||
editor = atom.project.openSync('coffee.coffee', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
describe ".toggleLineCommentsForBufferRows(start, end)", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
@@ -144,9 +156,9 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "css", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-css', sync: true)
|
||||
editSession = project.openSync('css.css', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
atom.packages.activatePackage('language-css', sync: true)
|
||||
editor = atom.project.openSync('css.css', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
describe ".toggleLineCommentsForBufferRows(start, end)", ->
|
||||
it "comments/uncomments lines in the given range", ->
|
||||
@@ -185,10 +197,10 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "less", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-less', sync: true)
|
||||
atom.activatePackage('language-css', sync: true)
|
||||
editSession = project.openSync('sample.less', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
atom.packages.activatePackage('language-less', sync: true)
|
||||
atom.packages.activatePackage('language-css', sync: true)
|
||||
editor = atom.project.openSync('sample.less', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
describe "when commenting lines", ->
|
||||
it "only uses the `commentEnd` pattern if it comes from the same grammar as the `commentStart`", ->
|
||||
@@ -197,68 +209,68 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "folding", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-javascript', sync: true)
|
||||
editSession = project.openSync('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
editor = atom.project.openSync('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
it "maintains cursor buffer position when a folding/unfolding", ->
|
||||
editSession.setCursorBufferPosition([5,5])
|
||||
editor.setCursorBufferPosition([5,5])
|
||||
languageMode.foldAll()
|
||||
expect(editSession.getCursorBufferPosition()).toEqual([5,5])
|
||||
expect(editor.getCursorBufferPosition()).toEqual([5,5])
|
||||
|
||||
describe ".unfoldAll()", ->
|
||||
it "unfolds every folded line", ->
|
||||
initialScreenLineCount = editSession.getScreenLineCount()
|
||||
initialScreenLineCount = editor.getScreenLineCount()
|
||||
languageMode.foldBufferRow(0)
|
||||
languageMode.foldBufferRow(1)
|
||||
expect(editSession.getScreenLineCount()).toBeLessThan initialScreenLineCount
|
||||
expect(editor.getScreenLineCount()).toBeLessThan initialScreenLineCount
|
||||
languageMode.unfoldAll()
|
||||
expect(editSession.getScreenLineCount()).toBe initialScreenLineCount
|
||||
expect(editor.getScreenLineCount()).toBe initialScreenLineCount
|
||||
|
||||
describe ".foldAll()", ->
|
||||
it "folds every foldable line", ->
|
||||
languageMode.foldAll()
|
||||
|
||||
fold1 = editSession.lineForScreenRow(0).fold
|
||||
fold1 = editor.lineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 12]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(1).fold
|
||||
fold2 = editor.lineForScreenRow(1).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 9]
|
||||
fold2.destroy()
|
||||
|
||||
fold3 = editSession.lineForScreenRow(4).fold
|
||||
fold3 = editor.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
|
||||
fold = editor.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
|
||||
fold = editor.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()
|
||||
expect(editor.lineForScreenRow(1).fold).toBeDefined()
|
||||
expect(editor.lineForScreenRow(0).fold).not.toBeDefined()
|
||||
|
||||
languageMode.foldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(0).fold).toBeDefined()
|
||||
expect(editor.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
|
||||
fold = editor.lineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 3
|
||||
|
||||
@@ -266,7 +278,7 @@ describe "LanguageMode", ->
|
||||
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
|
||||
fold = editor.lineForScreenRow(0).fold
|
||||
expect(fold.getStartRow()).toBe 0
|
||||
expect(fold.getEndRow()).toBe 13
|
||||
|
||||
@@ -274,77 +286,77 @@ describe "LanguageMode", ->
|
||||
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()
|
||||
expect(editor.lineForScreenRow(1).fold).toBeDefined()
|
||||
|
||||
languageMode.unfoldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
expect(editor.lineForScreenRow(1).fold).toBeUndefined()
|
||||
|
||||
describe "when bufferRow can't be unfolded", ->
|
||||
it "does not throw an error", ->
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
expect(editor.lineForScreenRow(1).fold).toBeUndefined()
|
||||
languageMode.unfoldBufferRow(1)
|
||||
expect(editSession.lineForScreenRow(1).fold).toBeUndefined()
|
||||
expect(editor.lineForScreenRow(1).fold).toBeUndefined()
|
||||
|
||||
describe "folding with comments", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-javascript', sync: true)
|
||||
editSession = project.openSync('sample-with-comments.js', autoIndent: false)
|
||||
{buffer, languageMode} = editSession
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
editor = atom.project.openSync('sample-with-comments.js', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
describe ".unfoldAll()", ->
|
||||
it "unfolds every folded line", ->
|
||||
initialScreenLineCount = editSession.getScreenLineCount()
|
||||
initialScreenLineCount = editor.getScreenLineCount()
|
||||
languageMode.foldBufferRow(0)
|
||||
languageMode.foldBufferRow(5)
|
||||
expect(editSession.getScreenLineCount()).toBeLessThan initialScreenLineCount
|
||||
expect(editor.getScreenLineCount()).toBeLessThan initialScreenLineCount
|
||||
languageMode.unfoldAll()
|
||||
expect(editSession.getScreenLineCount()).toBe initialScreenLineCount
|
||||
expect(editor.getScreenLineCount()).toBe initialScreenLineCount
|
||||
|
||||
describe ".foldAll()", ->
|
||||
it "folds every foldable line", ->
|
||||
languageMode.foldAll()
|
||||
|
||||
fold1 = editSession.lineForScreenRow(0).fold
|
||||
fold1 = editor.lineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(1).fold
|
||||
fold2 = editor.lineForScreenRow(1).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 4]
|
||||
|
||||
fold3 = editSession.lineForScreenRow(2).fold.destroy()
|
||||
fold3 = editor.lineForScreenRow(2).fold.destroy()
|
||||
|
||||
fold4 = editSession.lineForScreenRow(3).fold
|
||||
fold4 = editor.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
|
||||
fold1 = editor.lineForScreenRow(6).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [6, 8]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(11).fold
|
||||
fold2 = editor.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
|
||||
fold1 = editor.lineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editSession.lineForScreenRow(5).fold
|
||||
fold2 = editor.lineForScreenRow(5).fold
|
||||
expect(fold2).toBeFalsy()
|
||||
|
||||
describe "css", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-source', sync: true)
|
||||
atom.activatePackage('language-css', sync: true)
|
||||
editSession = project.openSync('css.css', autoIndent: true)
|
||||
atom.packages.activatePackage('language-source', sync: true)
|
||||
atom.packages.activatePackage('language-css', sync: true)
|
||||
editor = atom.project.openSync('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
|
||||
editor.setText('.test {\npadding: 0;\n}')
|
||||
expect(editor.suggestedIndentForBufferRow(2)).toBe 0
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
{Site} = require 'telepath'
|
||||
{View} = require 'atom'
|
||||
PaneContainer = require '../src/pane-container'
|
||||
Pane = require '../src/pane'
|
||||
Environment = require './environment'
|
||||
|
||||
describe "PaneContainer replication", ->
|
||||
[env1, env2, envConnection, container1, container2, pane1a, pane1b, pane1c] = []
|
||||
|
||||
class TestView extends View
|
||||
@deserialize: ({name}) -> new TestView(name)
|
||||
@content: -> @div tabindex: -1
|
||||
initialize: (@name) -> @text(@name)
|
||||
serialize: -> { deserializer: 'TestView', @name }
|
||||
getState: -> @serialize()
|
||||
getUri: -> path.join(temp.dir, @name)
|
||||
isEqual: (other) -> @name is other.name
|
||||
|
||||
beforeEach ->
|
||||
registerDeserializer(TestView)
|
||||
|
||||
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", ->
|
||||
expect(container1.find('.row > :eq(0):contains(A)')).toExist()
|
||||
expect(container1.find('.row > :eq(1)')).toHaveClass 'column'
|
||||
expect(container1.find('.row > :eq(1) > :eq(0):contains(B)')).toExist()
|
||||
expect(container1.find('.row > :eq(1) > :eq(1):contains(C)')).toExist()
|
||||
|
||||
expect(container2.find('.row > :eq(0):contains(A)')).toExist()
|
||||
expect(container2.find('.row > :eq(1)')).toHaveClass 'column'
|
||||
expect(container2.find('.row > :eq(1) > :eq(0):contains(B)')).toExist()
|
||||
expect(container2.find('.row > :eq(1) > :eq(1):contains(C)')).toExist()
|
||||
|
||||
it "replicates the splitting of panes", ->
|
||||
container1.attachToDom().width(400).height(200)
|
||||
container2.attachToDom().width(400).height(200)
|
||||
|
||||
pane1d = pane1a.splitRight(new TestView('D'))
|
||||
|
||||
expect(container1.find('.row > :eq(1):contains(D)')).toExist()
|
||||
expect(container2.find('.row > :eq(1):contains(D)')).toExist()
|
||||
|
||||
expect(container2.find('.row > :eq(1):contains(D)').outerWidth()).toBe container1.find('.row > :eq(1):contains(D)').outerWidth()
|
||||
|
||||
pane1d.splitDown(new TestView('E'))
|
||||
|
||||
expect(container1.find('.row > :eq(1)')).toHaveClass('column')
|
||||
expect(container1.find('.row > :eq(1) > :eq(0):contains(D)')).toExist()
|
||||
expect(container1.find('.row > :eq(1) > :eq(1):contains(E)')).toExist()
|
||||
|
||||
expect(container2.find('.row > :eq(1)')).toHaveClass('column')
|
||||
expect(container2.find('.row > :eq(1) > :eq(0):contains(D)')).toExist()
|
||||
expect(container2.find('.row > :eq(1) > :eq(1):contains(E)')).toExist()
|
||||
|
||||
it "replicates removal of panes", ->
|
||||
pane1c.remove()
|
||||
|
||||
expect(container1.find('.row > :eq(0):contains(A)')).toExist()
|
||||
expect(container1.find('.row > :eq(1):contains(B)')).toExist()
|
||||
expect(container2.find('.row > :eq(0):contains(A)')).toExist()
|
||||
expect(container2.find('.row > :eq(1):contains(B)')).toExist()
|
||||
|
||||
pane1b.remove()
|
||||
|
||||
expect(container1.find('> :eq(0):contains(A)')).toExist()
|
||||
expect(container2.find('> :eq(0):contains(A)')).toExist()
|
||||
|
||||
pane1a.remove()
|
||||
|
||||
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.openSync('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'
|
||||
@@ -9,7 +9,7 @@ describe "PaneContainer", ->
|
||||
|
||||
beforeEach ->
|
||||
class TestView extends View
|
||||
registerDeserializer(this)
|
||||
atom.deserializers.add(this)
|
||||
@deserialize: ({name}) -> new TestView(name)
|
||||
@content: -> @div tabindex: -1
|
||||
initialize: (@name) -> @text(@name)
|
||||
@@ -25,7 +25,7 @@ describe "PaneContainer", ->
|
||||
pane3 = pane2.splitDown(new TestView('3'))
|
||||
|
||||
afterEach ->
|
||||
unregisterDeserializer(TestView)
|
||||
atom.deserializers.remove(TestView)
|
||||
|
||||
describe ".focusNextPane()", ->
|
||||
it "focuses the pane following the focused pane or the first pane if no pane has focus", ->
|
||||
@@ -80,11 +80,11 @@ describe "PaneContainer", ->
|
||||
expect(panes).toEqual [pane1, pane2, pane3]
|
||||
|
||||
panes = []
|
||||
pane4 = pane3.splitRight()
|
||||
pane4 = pane3.splitRight(pane3.copyActiveItem())
|
||||
expect(panes).toEqual [pane4]
|
||||
|
||||
panes = []
|
||||
subscription.cancel()
|
||||
subscription.off()
|
||||
pane4.splitDown()
|
||||
expect(panes).toEqual []
|
||||
|
||||
@@ -111,11 +111,11 @@ describe "PaneContainer", ->
|
||||
|
||||
describe "when the last-closed pane item is an edit session", ->
|
||||
it "reopens the edit session (regression)", ->
|
||||
editSession = project.openSync('sample.js')
|
||||
pane3.showItem(editSession)
|
||||
pane3.destroyItem(editSession)
|
||||
editor = atom.project.openSync('sample.js')
|
||||
pane3.showItem(editor)
|
||||
pane3.destroyItem(editor)
|
||||
expect(container.reopenItem()).toBeTruthy()
|
||||
expect(pane3.activeItem.getPath()).toBe editSession.getPath()
|
||||
expect(pane3.activeItem.getPath()).toBe editor.getPath()
|
||||
expect(container.reopenItem()).toBeFalsy()
|
||||
|
||||
describe "when there is no active pane", ->
|
||||
@@ -167,30 +167,30 @@ describe "PaneContainer", ->
|
||||
|
||||
describe ".confirmClose()", ->
|
||||
it "returns true after modified files are saved", ->
|
||||
pane1.itemAtIndex(0).isModified = -> true
|
||||
pane2.itemAtIndex(0).isModified = -> true
|
||||
spyOn(atom, "confirmSync").andReturn(0)
|
||||
pane1.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
pane2.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
spyOn(atom, "confirm").andReturn(0)
|
||||
|
||||
saved = container.confirmClose()
|
||||
|
||||
runs ->
|
||||
expect(saved).toBeTruthy()
|
||||
expect(atom.confirmSync).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "returns false if the user cancels saving", ->
|
||||
pane1.itemAtIndex(0).isModified = -> true
|
||||
pane2.itemAtIndex(0).isModified = -> true
|
||||
spyOn(atom, "confirmSync").andReturn(1)
|
||||
pane1.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
pane2.itemAtIndex(0).shouldPromptToSave = -> true
|
||||
spyOn(atom, "confirm").andReturn(1)
|
||||
|
||||
saved = container.confirmClose()
|
||||
|
||||
runs ->
|
||||
expect(saved).toBeFalsy()
|
||||
expect(atom.confirmSync).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
describe "serialization", ->
|
||||
it "can be serialized and deserialized, and correctly adjusts dimensions of deserialized panes after attach", ->
|
||||
newContainer = deserialize(container.serialize())
|
||||
newContainer = atom.deserializers.deserialize(container.serialize())
|
||||
expect(newContainer.find('.row > :contains(1)')).toExist()
|
||||
expect(newContainer.find('.row > .column > :contains(2)')).toExist()
|
||||
expect(newContainer.find('.row > .column > :contains(3)')).toExist()
|
||||
@@ -202,7 +202,7 @@ describe "PaneContainer", ->
|
||||
xit "removes empty panes on deserialization", ->
|
||||
# only deserialize pane 1's view successfully
|
||||
TestView.deserialize = ({name}) -> new TestView(name) if name is '1'
|
||||
newContainer = deserialize(container.serialize())
|
||||
newContainer = atom.deserializers.deserialize(container.serialize())
|
||||
expect(newContainer.find('.row, .column')).not.toExist()
|
||||
expect(newContainer.find('> :contains(1)')).toExist()
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
PaneContainer = require '../src/pane-container'
|
||||
Pane = require '../src/pane'
|
||||
{Site} = require 'telepath'
|
||||
|
||||
describe "Pane replication", ->
|
||||
[editSession1a, editSession1b, container1, pane1, doc1] = []
|
||||
[editSession2a, editSession2b, container2, pane2, doc2] = []
|
||||
|
||||
beforeEach ->
|
||||
editSession1a = project.openSync('sample.js')
|
||||
editSession1b = project.openSync('sample.txt')
|
||||
container1 = new PaneContainer
|
||||
pane1 = new Pane(editSession1a, editSession1b)
|
||||
container1.setRoot(pane1)
|
||||
|
||||
doc1 = container1.getState()
|
||||
doc2 = doc1.clone(new Site(2))
|
||||
doc1.connect(doc2)
|
||||
|
||||
container2 = deserialize(doc2)
|
||||
pane2 = container2.getRoot()
|
||||
|
||||
it "replicates the initial state of the panes", ->
|
||||
expect(pane2.items).toEqual(pane1.items)
|
||||
|
||||
it "replicates addition and removal of pane items", ->
|
||||
pane1.addItem(project.openSync('css.css'), 1)
|
||||
expect(pane2.items).toEqual(pane1.items)
|
||||
pane1.removeItemAtIndex(2)
|
||||
expect(pane2.items).toEqual(pane1.items)
|
||||
|
||||
it "replicates the movement of pane items", ->
|
||||
pane1.moveItem(editSession1a, 1)
|
||||
expect(pane2.items).toEqual(pane1.items)
|
||||
|
||||
it "replicates which pane item is active", ->
|
||||
pane1.showNextItem()
|
||||
expect(pane2.activeItem).toEqual pane1.activeItem
|
||||
pane1.showNextItem()
|
||||
expect(pane2.activeItem).toEqual pane1.activeItem
|
||||
+142
-192
@@ -5,7 +5,7 @@ path = require 'path'
|
||||
temp = require 'temp'
|
||||
|
||||
describe "Pane", ->
|
||||
[container, view1, view2, editSession1, editSession2, pane] = []
|
||||
[container, view1, view2, editor1, editor2, pane] = []
|
||||
|
||||
class TestView extends View
|
||||
@deserialize: ({id, text}) -> new TestView({id, text})
|
||||
@@ -16,17 +16,17 @@ describe "Pane", ->
|
||||
isEqual: (other) -> @id == other.id and @text == other.text
|
||||
|
||||
beforeEach ->
|
||||
registerDeserializer(TestView)
|
||||
atom.deserializers.add(TestView)
|
||||
container = new PaneContainer
|
||||
view1 = new TestView(id: 'view-1', text: 'View 1')
|
||||
view2 = new TestView(id: 'view-2', text: 'View 2')
|
||||
editSession1 = project.openSync('sample.js')
|
||||
editSession2 = project.openSync('sample.txt')
|
||||
pane = new Pane(view1, editSession1, view2, editSession2)
|
||||
editor1 = atom.project.openSync('sample.js')
|
||||
editor2 = atom.project.openSync('sample.txt')
|
||||
pane = new Pane(view1, editor1, view2, editor2)
|
||||
container.setRoot(pane)
|
||||
|
||||
afterEach ->
|
||||
unregisterDeserializer(TestView)
|
||||
atom.deserializers.remove(TestView)
|
||||
|
||||
describe ".initialize(items...)", ->
|
||||
it "displays the first item in the pane", ->
|
||||
@@ -52,9 +52,9 @@ describe "Pane", ->
|
||||
expect(itemChangedHandler.argsForCall[0][1]).toBe view2
|
||||
itemChangedHandler.reset()
|
||||
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editor1)
|
||||
expect(itemChangedHandler).toHaveBeenCalled()
|
||||
expect(itemChangedHandler.argsForCall[0][1]).toBe editSession1
|
||||
expect(itemChangedHandler.argsForCall[0][1]).toBe editor1
|
||||
itemChangedHandler.reset()
|
||||
|
||||
describe "if the pane's active view is focused before calling showItem", ->
|
||||
@@ -70,12 +70,12 @@ describe "Pane", ->
|
||||
view3 = null
|
||||
beforeEach ->
|
||||
view3 = new TestView(id: 'view-3', text: "View 3")
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editor1)
|
||||
expect(pane.getActiveItemIndex()).toBe 1
|
||||
|
||||
it "adds it to the items list after the active item", ->
|
||||
pane.showItem(view3)
|
||||
expect(pane.getItems()).toEqual [view1, editSession1, view3, view2, editSession2]
|
||||
expect(pane.getItems()).toEqual [view1, editor1, view3, view2, editor2]
|
||||
expect(pane.activeItem).toBe view3
|
||||
expect(pane.getActiveItemIndex()).toBe 2
|
||||
|
||||
@@ -89,19 +89,19 @@ describe "Pane", ->
|
||||
describe "when showing a model item", ->
|
||||
describe "when no view has yet been appended for that item", ->
|
||||
it "appends and shows a view to display the item based on its `.getViewClass` method", ->
|
||||
pane.showItem(editSession1)
|
||||
editor = pane.activeView
|
||||
expect(editor.css('display')).not.toBe 'none'
|
||||
expect(editor.activeEditSession).toBe editSession1
|
||||
pane.showItem(editor1)
|
||||
editorView = pane.activeView
|
||||
expect(editorView.css('display')).not.toBe 'none'
|
||||
expect(editorView.activeEditSession).toBe editor1
|
||||
|
||||
describe "when a valid view has already been appended for another item", ->
|
||||
it "multiple views are created for multiple items", ->
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editSession2)
|
||||
pane.showItem(editor1)
|
||||
pane.showItem(editor2)
|
||||
expect(pane.itemViews.find('.editor').length).toBe 2
|
||||
editor = pane.activeView
|
||||
expect(editor.css('display')).not.toBe 'none'
|
||||
expect(editor.activeEditSession).toBe editSession2
|
||||
editorView = pane.activeView
|
||||
expect(editorView.css('display')).not.toBe 'none'
|
||||
expect(editorView.activeEditSession).toBe editor2
|
||||
|
||||
it "creates a new view with the item", ->
|
||||
initialViewCount = pane.itemViews.find('.test-view').length
|
||||
@@ -141,77 +141,77 @@ describe "Pane", ->
|
||||
describe ".destroyItem(item)", ->
|
||||
describe "if the item is not modified", ->
|
||||
it "removes the item and tries to call destroy on it", ->
|
||||
pane.destroyItem(editSession2)
|
||||
expect(pane.getItems().indexOf(editSession2)).toBe -1
|
||||
expect(editSession2.destroyed).toBeTruthy()
|
||||
pane.destroyItem(editor2)
|
||||
expect(pane.getItems().indexOf(editor2)).toBe -1
|
||||
expect(editor2.destroyed).toBeTruthy()
|
||||
|
||||
describe "if the item is modified", ->
|
||||
beforeEach ->
|
||||
spyOn(editSession2, 'save')
|
||||
spyOn(editSession2, 'saveAs')
|
||||
spyOn(editor2, 'save')
|
||||
spyOn(editor2, 'saveAs')
|
||||
|
||||
editSession2.insertText('a')
|
||||
expect(editSession2.isModified()).toBeTruthy()
|
||||
editor2.insertText('a')
|
||||
expect(editor2.isModified()).toBeTruthy()
|
||||
|
||||
describe "if the [Save] option is selected", ->
|
||||
describe "when the item has a uri", ->
|
||||
it "saves the item before removing and destroying it", ->
|
||||
spyOn(atom, 'confirmSync').andReturn(0)
|
||||
pane.destroyItem(editSession2)
|
||||
spyOn(atom, 'confirm').andReturn(0)
|
||||
pane.destroyItem(editor2)
|
||||
|
||||
expect(editSession2.save).toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editSession2)).toBe -1
|
||||
expect(editSession2.destroyed).toBeTruthy()
|
||||
expect(editor2.save).toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editor2)).toBe -1
|
||||
expect(editor2.destroyed).toBeTruthy()
|
||||
|
||||
describe "when the item has no uri", ->
|
||||
it "presents a save-as dialog, then saves the item with the given uri before removing and destroying it", ->
|
||||
editSession2.buffer.setPath(undefined)
|
||||
editor2.buffer.setPath(undefined)
|
||||
|
||||
spyOn(atom, 'showSaveDialogSync').andReturn("/selected/path")
|
||||
spyOn(atom, 'confirmSync').andReturn(0)
|
||||
pane.destroyItem(editSession2)
|
||||
spyOn(atom, 'confirm').andReturn(0)
|
||||
pane.destroyItem(editor2)
|
||||
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||
|
||||
expect(editSession2.saveAs).toHaveBeenCalledWith("/selected/path")
|
||||
expect(pane.getItems().indexOf(editSession2)).toBe -1
|
||||
expect(editSession2.destroyed).toBeTruthy()
|
||||
expect(editor2.saveAs).toHaveBeenCalledWith("/selected/path")
|
||||
expect(pane.getItems().indexOf(editor2)).toBe -1
|
||||
expect(editor2.destroyed).toBeTruthy()
|
||||
|
||||
describe "if the [Don't Save] option is selected", ->
|
||||
it "removes and destroys the item without saving it", ->
|
||||
spyOn(atom, 'confirmSync').andReturn(2)
|
||||
pane.destroyItem(editSession2)
|
||||
spyOn(atom, 'confirm').andReturn(2)
|
||||
pane.destroyItem(editor2)
|
||||
|
||||
expect(editSession2.save).not.toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editSession2)).toBe -1
|
||||
expect(editSession2.destroyed).toBeTruthy()
|
||||
expect(editor2.save).not.toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editor2)).toBe -1
|
||||
expect(editor2.destroyed).toBeTruthy()
|
||||
|
||||
describe "if the [Cancel] option is selected", ->
|
||||
it "does not save, remove, or destroy the item", ->
|
||||
spyOn(atom, 'confirmSync').andReturn(1)
|
||||
pane.destroyItem(editSession2)
|
||||
spyOn(atom, 'confirm').andReturn(1)
|
||||
pane.destroyItem(editor2)
|
||||
|
||||
expect(editSession2.save).not.toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editSession2)).not.toBe -1
|
||||
expect(editSession2.destroyed).toBeFalsy()
|
||||
expect(editor2.save).not.toHaveBeenCalled()
|
||||
expect(pane.getItems().indexOf(editor2)).not.toBe -1
|
||||
expect(editor2.destroyed).toBeFalsy()
|
||||
|
||||
describe ".removeItem(item)", ->
|
||||
it "removes the item from the items list and shows the next item if it was showing", ->
|
||||
pane.removeItem(view1)
|
||||
expect(pane.getItems()).toEqual [editSession1, view2, editSession2]
|
||||
expect(pane.activeItem).toBe editSession1
|
||||
expect(pane.getItems()).toEqual [editor1, view2, editor2]
|
||||
expect(pane.activeItem).toBe editor1
|
||||
|
||||
pane.showItem(editSession2)
|
||||
pane.removeItem(editSession2)
|
||||
expect(pane.getItems()).toEqual [editSession1, view2]
|
||||
expect(pane.activeItem).toBe editSession1
|
||||
pane.showItem(editor2)
|
||||
pane.removeItem(editor2)
|
||||
expect(pane.getItems()).toEqual [editor1, view2]
|
||||
expect(pane.activeItem).toBe editor1
|
||||
|
||||
it "triggers 'pane:item-removed' with the item and its former index", ->
|
||||
itemRemovedHandler = jasmine.createSpy("itemRemovedHandler")
|
||||
pane.on 'pane:item-removed', itemRemovedHandler
|
||||
pane.removeItem(editSession1)
|
||||
pane.removeItem(editor1)
|
||||
expect(itemRemovedHandler).toHaveBeenCalled()
|
||||
expect(itemRemovedHandler.argsForCall[0][1..2]).toEqual [editSession1, 1]
|
||||
expect(itemRemovedHandler.argsForCall[0][1..2]).toEqual [editor1, 1]
|
||||
|
||||
describe "when removing the last item", ->
|
||||
it "removes the pane", ->
|
||||
@@ -236,11 +236,11 @@ describe "Pane", ->
|
||||
|
||||
describe "when the item is a model", ->
|
||||
it "removes the associated view only when all items that require it have been removed", ->
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editSession2)
|
||||
pane.removeItem(editSession2)
|
||||
pane.showItem(editor1)
|
||||
pane.showItem(editor2)
|
||||
pane.removeItem(editor2)
|
||||
expect(pane.itemViews.find('.editor')).toExist()
|
||||
pane.removeItem(editSession1)
|
||||
pane.removeItem(editor1)
|
||||
expect(pane.itemViews.find('.editor')).not.toExist()
|
||||
|
||||
describe ".moveItem(item, index)", ->
|
||||
@@ -249,21 +249,21 @@ describe "Pane", ->
|
||||
pane.on 'pane:item-moved', itemMovedHandler
|
||||
|
||||
pane.moveItem(view1, 2)
|
||||
expect(pane.getItems()).toEqual [editSession1, view2, view1, editSession2]
|
||||
expect(pane.getItems()).toEqual [editor1, view2, view1, editor2]
|
||||
expect(itemMovedHandler).toHaveBeenCalled()
|
||||
expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [view1, 2]
|
||||
itemMovedHandler.reset()
|
||||
|
||||
pane.moveItem(editSession1, 3)
|
||||
expect(pane.getItems()).toEqual [view2, view1, editSession2, editSession1]
|
||||
pane.moveItem(editor1, 3)
|
||||
expect(pane.getItems()).toEqual [view2, view1, editor2, editor1]
|
||||
expect(itemMovedHandler).toHaveBeenCalled()
|
||||
expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [editSession1, 3]
|
||||
expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [editor1, 3]
|
||||
itemMovedHandler.reset()
|
||||
|
||||
pane.moveItem(editSession1, 1)
|
||||
expect(pane.getItems()).toEqual [view2, editSession1, view1, editSession2]
|
||||
pane.moveItem(editor1, 1)
|
||||
expect(pane.getItems()).toEqual [view2, editor1, view1, editor2]
|
||||
expect(itemMovedHandler).toHaveBeenCalled()
|
||||
expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [editSession1, 1]
|
||||
expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [editor1, 1]
|
||||
itemMovedHandler.reset()
|
||||
|
||||
describe ".moveItemToPane(item, pane, index)", ->
|
||||
@@ -275,21 +275,21 @@ describe "Pane", ->
|
||||
|
||||
it "moves the item to the given pane at the given index", ->
|
||||
pane.moveItemToPane(view1, pane2, 1)
|
||||
expect(pane.getItems()).toEqual [editSession1, view2, editSession2]
|
||||
expect(pane.getItems()).toEqual [editor1, view2, editor2]
|
||||
expect(pane2.getItems()).toEqual [view3, view1]
|
||||
|
||||
describe "when it is the last item on the source pane", ->
|
||||
it "removes the source pane, but does not destroy the item", ->
|
||||
pane.removeItem(view1)
|
||||
pane.removeItem(view2)
|
||||
pane.removeItem(editSession2)
|
||||
pane.removeItem(editor2)
|
||||
|
||||
expect(pane.getItems()).toEqual [editSession1]
|
||||
pane.moveItemToPane(editSession1, pane2, 1)
|
||||
expect(pane.getItems()).toEqual [editor1]
|
||||
pane.moveItemToPane(editor1, pane2, 1)
|
||||
|
||||
expect(pane.hasParent()).toBeFalsy()
|
||||
expect(pane2.getItems()).toEqual [view3, editSession1]
|
||||
expect(editSession1.destroyed).toBeFalsy()
|
||||
expect(pane2.getItems()).toEqual [view3, editor1]
|
||||
expect(editor1.destroyed).toBeFalsy()
|
||||
|
||||
describe "when the item is a jQuery object", ->
|
||||
it "preserves data by detaching instead of removing", ->
|
||||
@@ -303,37 +303,37 @@ describe "Pane", ->
|
||||
containerCloseHandler = jasmine.createSpy("containerCloseHandler")
|
||||
container.on 'core:close', containerCloseHandler
|
||||
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editor1)
|
||||
initialItemCount = pane.getItems().length
|
||||
pane.trigger 'core:close'
|
||||
expect(pane.getItems().length).toBe initialItemCount - 1
|
||||
expect(editSession1.destroyed).toBeTruthy()
|
||||
expect(editor1.destroyed).toBeTruthy()
|
||||
|
||||
expect(containerCloseHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe "pane:close", ->
|
||||
it "destroys all items and removes the pane", ->
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editor1)
|
||||
pane.trigger 'pane:close'
|
||||
expect(pane.hasParent()).toBeFalsy()
|
||||
expect(editSession2.destroyed).toBeTruthy()
|
||||
expect(editSession1.destroyed).toBeTruthy()
|
||||
expect(editor2.destroyed).toBeTruthy()
|
||||
expect(editor1.destroyed).toBeTruthy()
|
||||
|
||||
describe "pane:close-other-items", ->
|
||||
it "destroys all items except the current", ->
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editor1)
|
||||
pane.trigger 'pane:close-other-items'
|
||||
expect(editSession2.destroyed).toBeTruthy()
|
||||
expect(pane.getItems()).toEqual [editSession1]
|
||||
expect(editor2.destroyed).toBeTruthy()
|
||||
expect(pane.getItems()).toEqual [editor1]
|
||||
|
||||
describe "core:save", ->
|
||||
describe "when the current item has a uri", ->
|
||||
describe "when the current item has a save method", ->
|
||||
it "saves the current item", ->
|
||||
spyOn(editSession2, 'save')
|
||||
pane.showItem(editSession2)
|
||||
spyOn(editor2, 'save')
|
||||
pane.showItem(editor2)
|
||||
pane.trigger 'core:save'
|
||||
expect(editSession2.save).toHaveBeenCalled()
|
||||
expect(editor2.save).toHaveBeenCalled()
|
||||
|
||||
describe "when the current item has no save method", ->
|
||||
it "does nothing", ->
|
||||
@@ -347,14 +347,14 @@ describe "Pane", ->
|
||||
|
||||
describe "when the current item has a saveAs method", ->
|
||||
it "opens a save dialog and saves the current item as the selected path", ->
|
||||
spyOn(editSession2, 'saveAs')
|
||||
editSession2.buffer.setPath(undefined)
|
||||
pane.showItem(editSession2)
|
||||
spyOn(editor2, 'saveAs')
|
||||
editor2.buffer.setPath(undefined)
|
||||
pane.showItem(editor2)
|
||||
|
||||
pane.trigger 'core:save'
|
||||
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalled()
|
||||
expect(editSession2.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
expect(editor2.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
|
||||
describe "when the current item has no saveAs method", ->
|
||||
it "does nothing", ->
|
||||
@@ -368,13 +368,13 @@ describe "Pane", ->
|
||||
|
||||
describe "when the current item has a saveAs method", ->
|
||||
it "opens the save dialog and calls saveAs on the item with the selected path", ->
|
||||
spyOn(editSession2, 'saveAs')
|
||||
pane.showItem(editSession2)
|
||||
spyOn(editor2, 'saveAs')
|
||||
pane.showItem(editor2)
|
||||
|
||||
pane.trigger 'core:save-as'
|
||||
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalledWith(path.dirname(editSession2.getPath()))
|
||||
expect(editSession2.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalledWith(path.dirname(editor2.getPath()))
|
||||
expect(editor2.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
|
||||
describe "when the current item does not have a saveAs method", ->
|
||||
it "does nothing", ->
|
||||
@@ -386,11 +386,11 @@ describe "Pane", ->
|
||||
it "advances forward/backward through the pane's items, looping around at either end", ->
|
||||
expect(pane.activeItem).toBe view1
|
||||
pane.trigger 'pane:show-previous-item'
|
||||
expect(pane.activeItem).toBe editSession2
|
||||
expect(pane.activeItem).toBe editor2
|
||||
pane.trigger 'pane:show-previous-item'
|
||||
expect(pane.activeItem).toBe view2
|
||||
pane.trigger 'pane:show-next-item'
|
||||
expect(pane.activeItem).toBe editSession2
|
||||
expect(pane.activeItem).toBe editor2
|
||||
pane.trigger 'pane:show-next-item'
|
||||
expect(pane.activeItem).toBe view1
|
||||
|
||||
@@ -424,8 +424,8 @@ describe "Pane", ->
|
||||
describe ".remove()", ->
|
||||
it "destroys all the pane's items", ->
|
||||
pane.remove()
|
||||
expect(editSession1.destroyed).toBeTruthy()
|
||||
expect(editSession2.destroyed).toBeTruthy()
|
||||
expect(editor1.destroyed).toBeTruthy()
|
||||
expect(editor2.destroyed).toBeTruthy()
|
||||
|
||||
it "triggers a 'pane:removed' event with the pane", ->
|
||||
removedHandler = jasmine.createSpy("removedHandler")
|
||||
@@ -438,9 +438,9 @@ describe "Pane", ->
|
||||
[paneToLeft, paneToRight] = []
|
||||
|
||||
beforeEach ->
|
||||
pane.showItem(editSession1)
|
||||
paneToLeft = pane.splitLeft()
|
||||
paneToRight = pane.splitRight()
|
||||
pane.showItem(editor1)
|
||||
paneToLeft = pane.splitLeft(pane.copyActiveItem())
|
||||
paneToRight = pane.splitRight(pane.copyActiveItem())
|
||||
container.attachToDom()
|
||||
|
||||
describe "when the removed pane is focused", ->
|
||||
@@ -475,26 +475,26 @@ describe "Pane", ->
|
||||
describe "when it is the last pane", ->
|
||||
beforeEach ->
|
||||
expect(container.getPanes().length).toBe 1
|
||||
window.rootView = focus: jasmine.createSpy("rootView.focus")
|
||||
atom.rootView = focus: jasmine.createSpy("rootView.focus")
|
||||
|
||||
describe "when the removed pane is focused", ->
|
||||
it "calls focus on rootView so we don't lose focus", ->
|
||||
container.attachToDom()
|
||||
pane.focus()
|
||||
pane.remove()
|
||||
expect(rootView.focus).toHaveBeenCalled()
|
||||
expect(atom.rootView.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the removed pane is not focused", ->
|
||||
it "does not call focus on root view", ->
|
||||
expect(pane).not.toMatchSelector ':has(:focus)'
|
||||
pane.remove()
|
||||
expect(rootView.focus).not.toHaveBeenCalled()
|
||||
expect(atom.rootView.focus).not.toHaveBeenCalled()
|
||||
|
||||
describe ".getNextPane()", ->
|
||||
it "returns the next pane if one exists, wrapping around from the last pane to the first", ->
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editor1)
|
||||
expect(pane.getNextPane()).toBeUndefined
|
||||
pane2 = pane.splitRight()
|
||||
pane2 = pane.splitRight(pane.copyActiveItem())
|
||||
expect(pane.getNextPane()).toBe pane2
|
||||
expect(pane2.getNextPane()).toBe pane
|
||||
|
||||
@@ -529,7 +529,7 @@ describe "Pane", ->
|
||||
expect(pane.isActive()).toBeFalsy()
|
||||
pane.focusin()
|
||||
expect(pane.isActive()).toBeTruthy()
|
||||
pane.splitRight()
|
||||
pane.splitRight(pane.copyActiveItem())
|
||||
expect(pane.isActive()).toBeFalsy()
|
||||
|
||||
expect(becameInactiveHandler.callCount).toBe 1
|
||||
@@ -538,29 +538,41 @@ describe "Pane", ->
|
||||
[pane1, view3, view4] = []
|
||||
beforeEach ->
|
||||
pane1 = pane
|
||||
pane.showItem(editSession1)
|
||||
pane.showItem(editor1)
|
||||
view3 = new TestView(id: 'view-3', text: 'View 3')
|
||||
view4 = new TestView(id: 'view-4', text: 'View 4')
|
||||
|
||||
describe "splitRight(items...)", ->
|
||||
it "builds a row if needed, then appends a new pane after itself", ->
|
||||
# creates the new pane with a copy of the active item if none are given
|
||||
pane2 = pane1.splitRight()
|
||||
pane2 = pane1.splitRight(pane1.copyActiveItem())
|
||||
expect(container.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
expect(pane2.items).toEqual [editSession1]
|
||||
expect(pane2.activeItem).not.toBe editSession1 # it's a copy
|
||||
expect(pane2.items).toEqual [editor1]
|
||||
expect(pane2.activeItem).not.toBe editor1 # it's a copy
|
||||
|
||||
pane3 = pane2.splitRight(view3, view4)
|
||||
expect(pane3.getItems()).toEqual [view3, view4]
|
||||
expect(container.find('.row .pane').toArray()).toEqual [pane[0], pane2[0], pane3[0]]
|
||||
|
||||
describe "splitRight(items...)", ->
|
||||
it "builds a row if needed, then appends a new pane after itself ", ->
|
||||
# creates the new pane with a copy of the active item if none are given
|
||||
pane2 = pane1.splitRight()
|
||||
expect(container.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
expect(pane2.items).toEqual []
|
||||
expect(pane2.activeItem).toBe null
|
||||
|
||||
pane3 = pane2.splitRight()
|
||||
expect(container.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0], pane3[0]]
|
||||
expect(pane3.items).toEqual []
|
||||
expect(pane3.activeItem).toBe null
|
||||
|
||||
describe "splitLeft(items...)", ->
|
||||
it "builds a row if needed, then appends a new pane before itself", ->
|
||||
# creates the new pane with a copy of the active item if none are given
|
||||
pane2 = pane.splitLeft()
|
||||
pane2 = pane.splitLeft(pane1.copyActiveItem())
|
||||
expect(container.find('.row .pane').toArray()).toEqual [pane2[0], pane[0]]
|
||||
expect(pane2.items).toEqual [editSession1]
|
||||
expect(pane2.activeItem).not.toBe editSession1 # it's a copy
|
||||
expect(pane2.items).toEqual [editor1]
|
||||
expect(pane2.activeItem).not.toBe editor1 # it's a copy
|
||||
|
||||
pane3 = pane2.splitLeft(view3, view4)
|
||||
expect(pane3.getItems()).toEqual [view3, view4]
|
||||
@@ -569,10 +581,10 @@ describe "Pane", ->
|
||||
describe "splitDown(items...)", ->
|
||||
it "builds a column if needed, then appends a new pane after itself", ->
|
||||
# creates the new pane with a copy of the active item if none are given
|
||||
pane2 = pane.splitDown()
|
||||
pane2 = pane.splitDown(pane1.copyActiveItem())
|
||||
expect(container.find('.column .pane').toArray()).toEqual [pane[0], pane2[0]]
|
||||
expect(pane2.items).toEqual [editSession1]
|
||||
expect(pane2.activeItem).not.toBe editSession1 # it's a copy
|
||||
expect(pane2.items).toEqual [editor1]
|
||||
expect(pane2.activeItem).not.toBe editor1 # it's a copy
|
||||
|
||||
pane3 = pane2.splitDown(view3, view4)
|
||||
expect(pane3.getItems()).toEqual [view3, view4]
|
||||
@@ -581,10 +593,10 @@ describe "Pane", ->
|
||||
describe "splitUp(items...)", ->
|
||||
it "builds a column if needed, then appends a new pane before itself", ->
|
||||
# creates the new pane with a copy of the active item if none are given
|
||||
pane2 = pane.splitUp()
|
||||
pane2 = pane.splitUp(pane1.copyActiveItem())
|
||||
expect(container.find('.column .pane').toArray()).toEqual [pane2[0], pane[0]]
|
||||
expect(pane2.items).toEqual [editSession1]
|
||||
expect(pane2.activeItem).not.toBe editSession1 # it's a copy
|
||||
expect(pane2.items).toEqual [editor1]
|
||||
expect(pane2.activeItem).not.toBe editor1 # it's a copy
|
||||
|
||||
pane3 = pane2.splitUp(view3, view4)
|
||||
expect(pane3.getItems()).toEqual [view3, view4]
|
||||
@@ -659,82 +671,20 @@ describe "Pane", ->
|
||||
expect(container.children('.pane').length).toBe 1
|
||||
expect(pane1.outerWidth()).toBe container.width()
|
||||
|
||||
describe "autosave", ->
|
||||
[initialActiveItem, initialActiveItemUri] = []
|
||||
|
||||
beforeEach ->
|
||||
initialActiveItem = pane.activeItem
|
||||
initialActiveItemUri = null
|
||||
pane.activeItem.getUri = -> initialActiveItemUri
|
||||
pane.activeItem.save = jasmine.createSpy("activeItem.save")
|
||||
spyOn(pane, 'saveItem').andCallThrough()
|
||||
|
||||
describe "when the active view loses focus", ->
|
||||
it "saves the item if core.autosave is true and the item has a uri", ->
|
||||
pane.activeView.trigger 'focusout'
|
||||
expect(pane.saveItem).not.toHaveBeenCalled()
|
||||
expect(pane.activeItem.save).not.toHaveBeenCalled()
|
||||
|
||||
config.set('core.autosave', true)
|
||||
pane.activeView.trigger 'focusout'
|
||||
expect(pane.saveItem).not.toHaveBeenCalled()
|
||||
expect(pane.activeItem.save).not.toHaveBeenCalled()
|
||||
|
||||
initialActiveItemUri = path.join(temp.dir, 'hi')
|
||||
pane.activeView.trigger 'focusout'
|
||||
expect(pane.activeItem.save).toHaveBeenCalled()
|
||||
|
||||
describe "when an item becomes inactive", ->
|
||||
it "saves the item if core.autosave is true and the item has a uri", ->
|
||||
expect(view2).not.toBe pane.activeItem
|
||||
expect(pane.saveItem).not.toHaveBeenCalled()
|
||||
expect(initialActiveItem.save).not.toHaveBeenCalled()
|
||||
pane.showItem(view2)
|
||||
|
||||
pane.showItem(initialActiveItem)
|
||||
config.set('core.autosave', true)
|
||||
pane.showItem(view2)
|
||||
expect(pane.saveItem).not.toHaveBeenCalled()
|
||||
expect(initialActiveItem.save).not.toHaveBeenCalled()
|
||||
|
||||
pane.showItem(initialActiveItem)
|
||||
initialActiveItemUri = path.join(temp.dir, 'hi')
|
||||
pane.showItem(view2)
|
||||
expect(initialActiveItem.save).toHaveBeenCalled()
|
||||
|
||||
describe "when an item is destroyed", ->
|
||||
it "saves the item if core.autosave is true and the item has a uri", ->
|
||||
# doesn't have to be the active item
|
||||
expect(view2).not.toBe pane.activeItem
|
||||
pane.showItem(view2)
|
||||
|
||||
pane.destroyItem(editSession1)
|
||||
expect(pane.saveItem).not.toHaveBeenCalled()
|
||||
|
||||
config.set("core.autosave", true)
|
||||
view2.getUri = -> undefined
|
||||
view2.save = ->
|
||||
pane.destroyItem(view2)
|
||||
expect(pane.saveItem).not.toHaveBeenCalled()
|
||||
|
||||
initialActiveItemUri = path.join(temp.dir, 'hi')
|
||||
pane.destroyItem(initialActiveItem)
|
||||
expect(initialActiveItem.save).toHaveBeenCalled()
|
||||
|
||||
describe ".itemForUri(uri)", ->
|
||||
it "returns the item for which a call to .getUri() returns the given uri", ->
|
||||
expect(pane.itemForUri(editSession1.getUri())).toBe editSession1
|
||||
expect(pane.itemForUri(editSession2.getUri())).toBe editSession2
|
||||
expect(pane.itemForUri(editor1.getUri())).toBe editor1
|
||||
expect(pane.itemForUri(editor2.getUri())).toBe editor2
|
||||
|
||||
describe "serialization", ->
|
||||
it "can serialize and deserialize the pane and all its items", ->
|
||||
newPane = deserialize(pane.serialize())
|
||||
expect(newPane.getItems()).toEqual [view1, editSession1, view2, editSession2]
|
||||
newPane = atom.deserializers.deserialize(pane.serialize())
|
||||
expect(newPane.getItems()).toEqual [view1, editor1, view2, editor2]
|
||||
|
||||
it "restores the active item on deserialization", ->
|
||||
pane.showItem(editSession2)
|
||||
newPane = deserialize(pane.serialize())
|
||||
expect(newPane.activeItem).toEqual editSession2
|
||||
pane.showItem(editor2)
|
||||
newPane = atom.deserializers.deserialize(pane.serialize())
|
||||
expect(newPane.activeItem).toEqual editor2
|
||||
|
||||
it "does not show items that cannot be deserialized", ->
|
||||
spyOn(console, 'warn')
|
||||
@@ -743,18 +693,18 @@ describe "Pane", ->
|
||||
paneState = pane.serialize()
|
||||
paneState.get('items').set(pane.items.indexOf(view2), {deserializer: 'Bogus'}) # nuke serialized state of active item
|
||||
|
||||
newPane = deserialize(paneState)
|
||||
newPane = atom.deserializers.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", ->
|
||||
reloadContainer = ->
|
||||
projectState = project.serialize()
|
||||
projectReplica = atom.replicate().get('project')
|
||||
containerState = container.serialize()
|
||||
container.remove()
|
||||
project.destroy()
|
||||
window.project = deserialize(projectState)
|
||||
container = deserialize(containerState)
|
||||
atom.project = projectReplica
|
||||
atom.project.destroy()
|
||||
container = atom.deserializers.deserialize(containerState)
|
||||
pane = container.getRoot()
|
||||
container.attachToDom()
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
describe "Pasteboard", ->
|
||||
describe "write(text, metadata) and read()", ->
|
||||
it "writes and reads text to/from the native pasteboard", ->
|
||||
expect(pasteboard.read()).toEqual ['initial pasteboard content']
|
||||
pasteboard.write('next')
|
||||
expect(pasteboard.read()[0]).toBe 'next'
|
||||
expect(atom.pasteboard.read()).toEqual ['initial pasteboard content']
|
||||
atom.pasteboard.write('next')
|
||||
expect(atom.pasteboard.read()[0]).toBe 'next'
|
||||
|
||||
it "returns metadata if the item on the native pasteboard matches the last written item", ->
|
||||
pasteboard.write('next', {meta: 'data'})
|
||||
expect(pasteboard.read()).toEqual ['next', {meta: 'data'}]
|
||||
atom.pasteboard.write('next', {meta: 'data'})
|
||||
expect(atom.pasteboard.read()).toEqual ['next', {meta: 'data'}]
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
path = require 'path'
|
||||
{Site} = require 'telepath'
|
||||
Project = require '../src/project'
|
||||
Git = require '../src/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: ->
|
||||
|
||||
projectHome1 = path.join(__dirname, 'fixtures', 'replication', 'home-1')
|
||||
projectHome2 = path.join(__dirname, 'fixtures', 'replication', 'home-2')
|
||||
config.set('core.projectHome', projectHome1)
|
||||
project1 = new Project(path.join(projectHome1, 'project'))
|
||||
project1.bufferForPathSync('file-1.txt')
|
||||
project1.bufferForPathSync('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', projectHome2)
|
||||
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.bufferForPathSync('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.bufferForPathSync('file-2.txt'))
|
||||
expect(project1.getBuffers().length).toBe 1
|
||||
expect(project2.getBuffers()[0].getRelativePath()).toBe project1.getBuffers()[0].getRelativePath()
|
||||
+237
-135
@@ -8,7 +8,7 @@ BufferedProcess = require '../src/buffered-process'
|
||||
|
||||
describe "Project", ->
|
||||
beforeEach ->
|
||||
project.setPath(project.resolve('dir'))
|
||||
atom.project.setPath(atom.project.resolve('dir'))
|
||||
|
||||
describe "serialization", ->
|
||||
deserializedProject = null
|
||||
@@ -17,105 +17,134 @@ describe "Project", ->
|
||||
deserializedProject?.destroy()
|
||||
|
||||
it "destroys unretained buffers and does not include them in the serialized state", ->
|
||||
project.bufferForPathSync('a')
|
||||
expect(project.getBuffers().length).toBe 1
|
||||
deserializedProject = deserialize(project.serialize())
|
||||
atom.project.bufferForPathSync('a')
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
|
||||
atom.project.getState().serializeForPersistence()
|
||||
deserializedProject = atom.replicate().get('project')
|
||||
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
expect(atom.project.getBuffers().length).toBe 0
|
||||
|
||||
it "listens for destroyed events on deserialized buffers and removes them when they are destroyed", ->
|
||||
atom.project.openSync('a')
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
atom.project.getState().serializeForPersistence()
|
||||
deserializedProject = atom.replicate().get('project')
|
||||
|
||||
expect(deserializedProject.getBuffers().length).toBe 1
|
||||
deserializedProject.getBuffers()[0].destroy()
|
||||
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.openSync("a")
|
||||
anotherEditSession = project.openSync("a")
|
||||
editor = atom.project.openSync("a")
|
||||
anotherEditSession = atom.project.openSync("a")
|
||||
|
||||
expect(project.editSessions.length).toBe 2
|
||||
expect(editSession.buffer).toBe anotherEditSession.buffer
|
||||
expect(atom.project.editors.length).toBe 2
|
||||
expect(editor.buffer).toBe anotherEditSession.buffer
|
||||
|
||||
editSession.destroy()
|
||||
expect(project.editSessions.length).toBe 1
|
||||
editor.destroy()
|
||||
expect(atom.project.editors.length).toBe 1
|
||||
|
||||
anotherEditSession.destroy()
|
||||
expect(project.editSessions.length).toBe 0
|
||||
expect(atom.project.editors.length).toBe 0
|
||||
|
||||
describe "when an edit session is saved and the project has no path", ->
|
||||
it "sets the project's path to the saved file's parent directory", ->
|
||||
tempFile = temp.openSync().path
|
||||
project.setPath(undefined)
|
||||
expect(project.getPath()).toBeUndefined()
|
||||
editSession = project.openSync()
|
||||
editSession.saveAs(tempFile)
|
||||
expect(project.getPath()).toBe path.dirname(tempFile)
|
||||
atom.project.setPath(undefined)
|
||||
expect(atom.project.getPath()).toBeUndefined()
|
||||
editor = atom.project.openSync()
|
||||
editor.saveAs(tempFile)
|
||||
expect(atom.project.getPath()).toBe path.dirname(tempFile)
|
||||
|
||||
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
|
||||
it "emits an 'editor-created' event and stores the edit session", ->
|
||||
handler = jasmine.createSpy('editorCreatedHandler')
|
||||
atom.project.on 'editor-created', handler
|
||||
|
||||
editSession1 = project.openSync("a")
|
||||
editor1 = atom.project.openSync("a")
|
||||
expect(handler.callCount).toBe 1
|
||||
expect(project.getEditSessions().length).toBe 1
|
||||
expect(project.getEditSessions()[0]).toBe editSession1
|
||||
expect(atom.project.getEditSessions().length).toBe 1
|
||||
expect(atom.project.getEditSessions()[0]).toBe editor1
|
||||
|
||||
editSession2 = deserialize(editSession1.serialize())
|
||||
editor2 = atom.deserializers.deserialize(editor1.serialize())
|
||||
expect(handler.callCount).toBe 2
|
||||
expect(project.getEditSessions().length).toBe 2
|
||||
expect(project.getEditSessions()[0]).toBe editSession1
|
||||
expect(project.getEditSessions()[1]).toBe editSession2
|
||||
expect(atom.project.getEditSessions().length).toBe 2
|
||||
expect(atom.project.getEditSessions()[0]).toBe editor1
|
||||
expect(atom.project.getEditSessions()[1]).toBe editor2
|
||||
|
||||
describe "when an edit session is copied", ->
|
||||
it "emits an 'editor-created' event and stores the edit session", ->
|
||||
handler = jasmine.createSpy('editorCreatedHandler')
|
||||
atom.project.on 'editor-created', handler
|
||||
|
||||
editor1 = atom.project.openSync("a")
|
||||
expect(handler.callCount).toBe 1
|
||||
expect(atom.project.getEditSessions().length).toBe 1
|
||||
expect(atom.project.getEditSessions()[0]).toBe editor1
|
||||
|
||||
editor2 = editor1.copy()
|
||||
expect(handler.callCount).toBe 2
|
||||
expect(atom.project.getEditSessions().length).toBe 2
|
||||
expect(atom.project.getEditSessions()[0]).toBe editor1
|
||||
expect(atom.project.getEditSessions()[1]).toBe editor2
|
||||
|
||||
describe ".openSync(path)", ->
|
||||
[fooOpener, barOpener, absolutePath, newBufferHandler, newEditSessionHandler] = []
|
||||
beforeEach ->
|
||||
absolutePath = require.resolve('./fixtures/dir/a')
|
||||
newBufferHandler = jasmine.createSpy('newBufferHandler')
|
||||
project.on 'buffer-created', newBufferHandler
|
||||
atom.project.on 'buffer-created', newBufferHandler
|
||||
newEditSessionHandler = jasmine.createSpy('newEditSessionHandler')
|
||||
project.on 'edit-session-created', newEditSessionHandler
|
||||
atom.project.on 'editor-created', newEditSessionHandler
|
||||
|
||||
fooOpener = (pathToOpen, options) -> { foo: pathToOpen, options } if pathToOpen?.match(/\.foo/)
|
||||
barOpener = (pathToOpen) -> { bar: pathToOpen } if pathToOpen?.match(/^bar:\/\//)
|
||||
project.registerOpener(fooOpener)
|
||||
project.registerOpener(barOpener)
|
||||
atom.project.registerOpener(fooOpener)
|
||||
atom.project.registerOpener(barOpener)
|
||||
|
||||
afterEach ->
|
||||
project.unregisterOpener(fooOpener)
|
||||
project.unregisterOpener(barOpener)
|
||||
atom.project.unregisterOpener(fooOpener)
|
||||
atom.project.unregisterOpener(barOpener)
|
||||
|
||||
describe "when passed a path that doesn't match a custom opener", ->
|
||||
describe "when given an absolute path that hasn't been opened previously", ->
|
||||
it "returns a new edit session for the given path and emits 'buffer-created' and 'edit-session-created' events", ->
|
||||
editSession = project.openSync(absolutePath)
|
||||
expect(editSession.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editSession.buffer
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
|
||||
it "returns a new edit session for the given path and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = atom.project.openSync(absolutePath)
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when given a relative path that hasn't been opened previously", ->
|
||||
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'edit-session-created' events", ->
|
||||
editSession = project.openSync('a')
|
||||
expect(editSession.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editSession.buffer
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
|
||||
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = atom.project.openSync('a')
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed the path to a buffer that has already been opened", ->
|
||||
it "returns a new edit session containing previously opened buffer and emits a 'edit-session-created' event", ->
|
||||
editSession = project.openSync(absolutePath)
|
||||
it "returns a new edit session containing previously opened buffer and emits a 'editor-created' event", ->
|
||||
editor = atom.project.openSync(absolutePath)
|
||||
newBufferHandler.reset()
|
||||
expect(project.openSync(absolutePath).buffer).toBe editSession.buffer
|
||||
expect(project.openSync('a').buffer).toBe editSession.buffer
|
||||
expect(atom.project.openSync(absolutePath).buffer).toBe editor.buffer
|
||||
expect(atom.project.openSync('a').buffer).toBe editor.buffer
|
||||
expect(newBufferHandler).not.toHaveBeenCalled()
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when not passed a path", ->
|
||||
it "returns a new edit session and emits 'buffer-created' and 'edit-session-created' events", ->
|
||||
editSession = project.openSync()
|
||||
expect(editSession.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editSession.buffer)
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
|
||||
it "returns a new edit session and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = atom.project.openSync()
|
||||
expect(editor.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer)
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed a path that matches a custom opener", ->
|
||||
it "returns the resource returned by the custom opener", ->
|
||||
pathToOpen = project.resolve('a.foo')
|
||||
expect(project.openSync(pathToOpen, hey: "there")).toEqual { foo: pathToOpen, options: {hey: "there"} }
|
||||
expect(project.openSync("bar://baz")).toEqual { bar: "bar://baz" }
|
||||
pathToOpen = atom.project.resolve('a.foo')
|
||||
expect(atom.project.openSync(pathToOpen, hey: "there")).toEqual { foo: pathToOpen, options: {hey: "there"} }
|
||||
expect(atom.project.openSync("bar://baz")).toEqual { bar: "bar://baz" }
|
||||
|
||||
describe ".open(path)", ->
|
||||
[fooOpener, barOpener, absolutePath, newBufferHandler, newEditSessionHandler] = []
|
||||
@@ -123,81 +152,81 @@ describe "Project", ->
|
||||
beforeEach ->
|
||||
absolutePath = require.resolve('./fixtures/dir/a')
|
||||
newBufferHandler = jasmine.createSpy('newBufferHandler')
|
||||
project.on 'buffer-created', newBufferHandler
|
||||
atom.project.on 'buffer-created', newBufferHandler
|
||||
newEditSessionHandler = jasmine.createSpy('newEditSessionHandler')
|
||||
project.on 'edit-session-created', newEditSessionHandler
|
||||
atom.project.on 'editor-created', newEditSessionHandler
|
||||
|
||||
fooOpener = (pathToOpen, options) -> { foo: pathToOpen, options } if pathToOpen?.match(/\.foo/)
|
||||
barOpener = (pathToOpen) -> { bar: pathToOpen } if pathToOpen?.match(/^bar:\/\//)
|
||||
project.registerOpener(fooOpener)
|
||||
project.registerOpener(barOpener)
|
||||
atom.project.registerOpener(fooOpener)
|
||||
atom.project.registerOpener(barOpener)
|
||||
|
||||
afterEach ->
|
||||
project.unregisterOpener(fooOpener)
|
||||
project.unregisterOpener(barOpener)
|
||||
atom.project.unregisterOpener(fooOpener)
|
||||
atom.project.unregisterOpener(barOpener)
|
||||
|
||||
describe "when passed a path that doesn't match a custom opener", ->
|
||||
describe "when given an absolute path that isn't currently open", ->
|
||||
it "returns a new edit session for the given path and emits 'buffer-created' and 'edit-session-created' events", ->
|
||||
editSession = null
|
||||
it "returns a new edit session for the given path and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
project.open(absolutePath).then (o) -> editSession = o
|
||||
atom.project.open(absolutePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editSession.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editSession.buffer
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when given a relative path that isn't currently opened", ->
|
||||
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'edit-session-created' events", ->
|
||||
editSession = null
|
||||
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
project.open(absolutePath).then (o) -> editSession = o
|
||||
atom.project.open(absolutePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editSession.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editSession.buffer
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed the path to a buffer that is currently opened", ->
|
||||
it "returns a new edit session containing currently opened buffer and emits a 'edit-session-created' event", ->
|
||||
editSession = null
|
||||
it "returns a new edit session containing currently opened buffer and emits a 'editor-created' event", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
project.open(absolutePath).then (o) -> editSession = o
|
||||
atom.project.open(absolutePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
newBufferHandler.reset()
|
||||
expect(project.openSync(absolutePath).buffer).toBe editSession.buffer
|
||||
expect(project.openSync('a').buffer).toBe editSession.buffer
|
||||
expect(atom.project.openSync(absolutePath).buffer).toBe editor.buffer
|
||||
expect(atom.project.openSync('a').buffer).toBe editor.buffer
|
||||
expect(newBufferHandler).not.toHaveBeenCalled()
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when not passed a path", ->
|
||||
it "returns a new edit session and emits 'buffer-created' and 'edit-session-created' events", ->
|
||||
editSession = null
|
||||
it "returns a new edit session and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
project.open().then (o) -> editSession = o
|
||||
atom.project.open().then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editSession.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editSession.buffer)
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editSession
|
||||
expect(editor.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer)
|
||||
expect(newEditSessionHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed a path that matches a custom opener", ->
|
||||
it "returns the resource returned by the custom opener", ->
|
||||
waitsForPromise ->
|
||||
pathToOpen = project.resolve('a.foo')
|
||||
project.open(pathToOpen, hey: "there").then (item) ->
|
||||
pathToOpen = atom.project.resolve('a.foo')
|
||||
atom.project.open(pathToOpen, hey: "there").then (item) ->
|
||||
expect(item).toEqual { foo: pathToOpen, options: {hey: "there"} }
|
||||
|
||||
waitsForPromise ->
|
||||
project.open("bar://baz").then (item) ->
|
||||
atom.project.open("bar://baz").then (item) ->
|
||||
expect(item).toEqual { bar: "bar://baz" }
|
||||
|
||||
it "returns number of read bytes as progress indicator", ->
|
||||
filePath = project.resolve 'a'
|
||||
filePath = atom.project.resolve 'a'
|
||||
totalBytes = 0
|
||||
promise = project.open(filePath)
|
||||
promise = atom.project.open(filePath)
|
||||
promise.progress (bytesRead) -> totalBytes = bytesRead
|
||||
|
||||
waitsForPromise ->
|
||||
@@ -209,22 +238,22 @@ describe "Project", ->
|
||||
describe ".bufferForPathSync(path)", ->
|
||||
describe "when opening a previously opened path", ->
|
||||
it "does not create a new buffer", ->
|
||||
buffer = project.bufferForPathSync("a").retain()
|
||||
expect(project.bufferForPathSync("a")).toBe buffer
|
||||
buffer = atom.project.bufferForPathSync("a").retain()
|
||||
expect(atom.project.bufferForPathSync("a")).toBe buffer
|
||||
|
||||
alternativeBuffer = project.bufferForPathSync("b").retain().release()
|
||||
alternativeBuffer = atom.project.bufferForPathSync("b").retain().release()
|
||||
expect(alternativeBuffer).not.toBe buffer
|
||||
buffer.release()
|
||||
|
||||
it "creates a new buffer if the previous buffer was destroyed", ->
|
||||
buffer = project.bufferForPathSync("a").retain().release()
|
||||
expect(project.bufferForPathSync("a").retain().release()).not.toBe buffer
|
||||
buffer = atom.project.bufferForPathSync("a").retain().release()
|
||||
expect(atom.project.bufferForPathSync("a").retain().release()).not.toBe buffer
|
||||
|
||||
describe ".bufferForPath(path)", ->
|
||||
[buffer] = []
|
||||
beforeEach ->
|
||||
waitsForPromise ->
|
||||
project.bufferForPath("a").then (o) ->
|
||||
atom.project.bufferForPath("a").then (o) ->
|
||||
buffer = o
|
||||
buffer.retain()
|
||||
|
||||
@@ -234,63 +263,124 @@ describe "Project", ->
|
||||
describe "when opening a previously opened path", ->
|
||||
it "does not create a new buffer", ->
|
||||
waitsForPromise ->
|
||||
project.bufferForPath("a").then (anotherBuffer) ->
|
||||
atom.project.bufferForPath("a").then (anotherBuffer) ->
|
||||
expect(anotherBuffer).toBe buffer
|
||||
|
||||
waitsForPromise ->
|
||||
project.bufferForPath("b").then (anotherBuffer) ->
|
||||
atom.project.bufferForPath("b").then (anotherBuffer) ->
|
||||
expect(anotherBuffer).not.toBe buffer
|
||||
|
||||
it "creates a new buffer if the previous buffer was destroyed", ->
|
||||
buffer.release()
|
||||
|
||||
waitsForPromise ->
|
||||
project.bufferForPath("b").then (anotherBuffer) ->
|
||||
atom.project.bufferForPath("b").then (anotherBuffer) ->
|
||||
expect(anotherBuffer).not.toBe buffer
|
||||
|
||||
describe ".resolve(uri)", ->
|
||||
describe "when passed an absolute or relative path", ->
|
||||
it "returns an absolute path based on the project's root", ->
|
||||
it "returns an absolute path based on the atom.project's root", ->
|
||||
absolutePath = require.resolve('./fixtures/dir/a')
|
||||
expect(project.resolve('a')).toBe absolutePath
|
||||
expect(project.resolve(absolutePath + '/../a')).toBe absolutePath
|
||||
expect(project.resolve('a/../a')).toBe absolutePath
|
||||
expect(atom.project.resolve('a')).toBe absolutePath
|
||||
expect(atom.project.resolve(absolutePath + '/../a')).toBe absolutePath
|
||||
expect(atom.project.resolve('a/../a')).toBe absolutePath
|
||||
|
||||
describe "when passed a uri with a scheme", ->
|
||||
it "does not modify uris that begin with a scheme", ->
|
||||
expect(project.resolve('http://zombo.com')).toBe 'http://zombo.com'
|
||||
expect(atom.project.resolve('http://zombo.com')).toBe 'http://zombo.com'
|
||||
|
||||
describe ".setPath(path)", ->
|
||||
describe "when path is a file", ->
|
||||
it "sets its path to the files parent directory and updates the root directory", ->
|
||||
project.setPath(require.resolve('./fixtures/dir/a'))
|
||||
expect(project.getPath()).toEqual path.dirname(require.resolve('./fixtures/dir/a'))
|
||||
expect(project.getRootDirectory().path).toEqual path.dirname(require.resolve('./fixtures/dir/a'))
|
||||
atom.project.setPath(require.resolve('./fixtures/dir/a'))
|
||||
expect(atom.project.getPath()).toEqual path.dirname(require.resolve('./fixtures/dir/a'))
|
||||
expect(atom.project.getRootDirectory().path).toEqual path.dirname(require.resolve('./fixtures/dir/a'))
|
||||
|
||||
describe "when path is a directory", ->
|
||||
it "sets its path to the directory and updates the root directory", ->
|
||||
directory = fs.absolute(path.join(__dirname, 'fixtures', 'dir', 'a-dir'))
|
||||
project.setPath(directory)
|
||||
expect(project.getPath()).toEqual directory
|
||||
expect(project.getRootDirectory().path).toEqual directory
|
||||
atom.project.setPath(directory)
|
||||
expect(atom.project.getPath()).toEqual directory
|
||||
expect(atom.project.getRootDirectory().path).toEqual directory
|
||||
|
||||
describe "when path is null", ->
|
||||
it "sets its path and root directory to null", ->
|
||||
project.setPath(null)
|
||||
expect(project.getPath()?).toBeFalsy()
|
||||
expect(project.getRootDirectory()?).toBeFalsy()
|
||||
atom.project.setPath(null)
|
||||
expect(atom.project.getPath()?).toBeFalsy()
|
||||
expect(atom.project.getRootDirectory()?).toBeFalsy()
|
||||
|
||||
describe ".replace()", ->
|
||||
[filePath, commentFilePath, sampleContent, sampleCommentContent] = []
|
||||
|
||||
beforeEach ->
|
||||
atom.project.setPath(atom.project.resolve('../'))
|
||||
|
||||
filePath = atom.project.resolve('sample.js')
|
||||
commentFilePath = atom.project.resolve('sample-with-comments.js')
|
||||
sampleContent = fs.readFileSync(filePath).toString()
|
||||
sampleCommentContent = fs.readFileSync(commentFilePath).toString()
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(filePath, sampleContent)
|
||||
fs.writeFileSync(commentFilePath, sampleCommentContent)
|
||||
|
||||
describe "when called with unopened files", ->
|
||||
it "replaces properly", ->
|
||||
results = []
|
||||
waitsForPromise ->
|
||||
atom.project.replace /items/gi, 'items', [filePath], (result) ->
|
||||
results.push(result)
|
||||
|
||||
runs ->
|
||||
expect(results).toHaveLength 1
|
||||
expect(results[0].filePath).toBe filePath
|
||||
expect(results[0].replacements).toBe 6
|
||||
|
||||
describe "when a buffer is already open", ->
|
||||
it "replaces properly and saves when not modified", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
expect(editor.isModified()).toBeFalsy()
|
||||
|
||||
results = []
|
||||
waitsForPromise ->
|
||||
atom.project.replace /items/gi, 'items', [filePath], (result) ->
|
||||
results.push(result)
|
||||
|
||||
runs ->
|
||||
expect(results).toHaveLength 1
|
||||
expect(results[0].filePath).toBe filePath
|
||||
expect(results[0].replacements).toBe 6
|
||||
|
||||
expect(editor.isModified()).toBeFalsy()
|
||||
|
||||
it "does NOT save when modified", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor.buffer.change([[0,0],[0,0]], 'omg')
|
||||
expect(editor.isModified()).toBeTruthy()
|
||||
|
||||
results = []
|
||||
waitsForPromise ->
|
||||
atom.project.replace /items/gi, 'okthen', [filePath], (result) ->
|
||||
results.push(result)
|
||||
|
||||
runs ->
|
||||
expect(results).toHaveLength 1
|
||||
expect(results[0].filePath).toBe filePath
|
||||
expect(results[0].replacements).toBe 6
|
||||
|
||||
expect(editor.isModified()).toBeTruthy()
|
||||
|
||||
describe ".scan(options, callback)", ->
|
||||
describe "when called with a regex", ->
|
||||
it "calls the callback with all regex results in all files in the project", ->
|
||||
results = []
|
||||
waitsForPromise ->
|
||||
project.scan /(a)+/, (result) ->
|
||||
atom.project.scan /(a)+/, (result) ->
|
||||
results.push(result)
|
||||
|
||||
runs ->
|
||||
expect(results).toHaveLength(3)
|
||||
expect(results[0].filePath).toBe project.resolve('a')
|
||||
expect(results[0].filePath).toBe atom.project.resolve('a')
|
||||
expect(results[0].matches).toHaveLength(3)
|
||||
expect(results[0].matches[0]).toEqual
|
||||
matchText: 'aaa'
|
||||
@@ -301,13 +391,13 @@ describe "Project", ->
|
||||
it "works with with escaped literals (like $ and ^)", ->
|
||||
results = []
|
||||
waitsForPromise ->
|
||||
project.scan /\$\w+/, (result) -> results.push(result)
|
||||
atom.project.scan /\$\w+/, (result) -> results.push(result)
|
||||
|
||||
runs ->
|
||||
expect(results.length).toBe 1
|
||||
|
||||
{filePath, matches} = results[0]
|
||||
expect(filePath).toBe project.resolve('a')
|
||||
expect(filePath).toBe atom.project.resolve('a')
|
||||
expect(matches).toHaveLength 1
|
||||
expect(matches[0]).toEqual
|
||||
matchText: '$bill'
|
||||
@@ -316,11 +406,11 @@ describe "Project", ->
|
||||
range: [[2, 6], [2, 11]]
|
||||
|
||||
it "works on evil filenames", ->
|
||||
project.setPath(path.join(__dirname, 'fixtures', 'evil-files'))
|
||||
atom.project.setPath(path.join(__dirname, 'fixtures', 'evil-files'))
|
||||
paths = []
|
||||
matches = []
|
||||
waitsForPromise ->
|
||||
project.scan /evil/, (result) ->
|
||||
atom.project.scan /evil/, (result) ->
|
||||
paths.push(result.filePath)
|
||||
matches = matches.concat(result.matches)
|
||||
|
||||
@@ -343,7 +433,7 @@ describe "Project", ->
|
||||
it "ignores case if the regex includes the `i` flag", ->
|
||||
results = []
|
||||
waitsForPromise ->
|
||||
project.scan /DOLLAR/i, (result) -> results.push(result)
|
||||
atom.project.scan /DOLLAR/i, (result) -> results.push(result)
|
||||
|
||||
runs ->
|
||||
expect(results).toHaveLength 1
|
||||
@@ -365,17 +455,17 @@ describe "Project", ->
|
||||
runs ->
|
||||
fs.rename(path.join(projectPath, 'git.git'), path.join(projectPath, '.git'))
|
||||
ignoredPath = path.join(projectPath, 'ignored.txt')
|
||||
fs.writeSync(ignoredPath, 'this match should not be included')
|
||||
fs.writeFileSync(ignoredPath, 'this match should not be included')
|
||||
|
||||
afterEach ->
|
||||
fs.remove(projectPath) if fs.exists(projectPath)
|
||||
fs.removeSync(projectPath) if fs.existsSync(projectPath)
|
||||
|
||||
it "excludes ignored files", ->
|
||||
project.setPath(projectPath)
|
||||
config.set('core.excludeVcsIgnoredPaths', true)
|
||||
atom.project.setPath(projectPath)
|
||||
atom.config.set('core.excludeVcsIgnoredPaths', true)
|
||||
resultHandler = jasmine.createSpy("result found")
|
||||
waitsForPromise ->
|
||||
project.scan /match/, (results) ->
|
||||
atom.project.scan /match/, (results) ->
|
||||
resultHandler()
|
||||
|
||||
runs ->
|
||||
@@ -383,14 +473,14 @@ describe "Project", ->
|
||||
|
||||
it "includes only files when a directory filter is specified", ->
|
||||
projectPath = path.join(path.join(__dirname, 'fixtures', 'dir'))
|
||||
project.setPath(projectPath)
|
||||
atom.project.setPath(projectPath)
|
||||
|
||||
filePath = path.join(projectPath, 'a-dir', 'oh-git')
|
||||
|
||||
paths = []
|
||||
matches = []
|
||||
waitsForPromise ->
|
||||
project.scan /aaa/, paths: ['a-dir/'], (result) ->
|
||||
atom.project.scan /aaa/, paths: ["a-dir#{path.sep}"], (result) ->
|
||||
paths.push(result.filePath)
|
||||
matches = matches.concat(result.matches)
|
||||
|
||||
@@ -402,12 +492,12 @@ describe "Project", ->
|
||||
it "includes files and folders that begin with a '.'", ->
|
||||
projectPath = temp.mkdirSync()
|
||||
filePath = path.join(projectPath, '.text')
|
||||
fs.writeSync(filePath, 'match this')
|
||||
project.setPath(projectPath)
|
||||
fs.writeFileSync(filePath, 'match this')
|
||||
atom.project.setPath(projectPath)
|
||||
paths = []
|
||||
matches = []
|
||||
waitsForPromise ->
|
||||
project.scan /match this/, (result) ->
|
||||
atom.project.scan /match this/, (result) ->
|
||||
paths.push(result.filePath)
|
||||
matches = matches.concat(result.matches)
|
||||
|
||||
@@ -418,15 +508,27 @@ describe "Project", ->
|
||||
|
||||
it "excludes values in core.ignoredNames", ->
|
||||
projectPath = path.join(__dirname, 'fixtures', 'git', 'working-dir')
|
||||
ignoredNames = config.get("core.ignoredNames")
|
||||
ignoredNames = atom.config.get("core.ignoredNames")
|
||||
ignoredNames.push("a")
|
||||
config.set("core.ignoredNames", ignoredNames)
|
||||
atom.config.set("core.ignoredNames", ignoredNames)
|
||||
|
||||
resultHandler = jasmine.createSpy("result found")
|
||||
waitsForPromise ->
|
||||
project.scan /dollar/, (results) ->
|
||||
console.log results
|
||||
atom.project.scan /dollar/, (results) ->
|
||||
resultHandler()
|
||||
|
||||
runs ->
|
||||
expect(resultHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "scans buffer contents if the buffer is modified", ->
|
||||
editor = atom.project.openSync("a")
|
||||
editor.setText("Elephant")
|
||||
results = []
|
||||
waitsForPromise ->
|
||||
atom.project.scan /a|Elephant/, (result) -> results.push result
|
||||
|
||||
runs ->
|
||||
expect(results).toHaveLength 3
|
||||
resultForA = _.find results, ({filePath}) -> path.basename(filePath) == 'a'
|
||||
expect(resultForA.matches).toHaveLength 1
|
||||
expect(resultForA.matches[0].matchText).toBe 'Elephant'
|
||||
|
||||
+249
-172
@@ -8,67 +8,68 @@ describe "RootView", ->
|
||||
pathToOpen = null
|
||||
|
||||
beforeEach ->
|
||||
project.setPath(project.resolve('dir'))
|
||||
pathToOpen = project.resolve('a')
|
||||
window.rootView = new RootView
|
||||
rootView.enableKeymap()
|
||||
rootView.openSync(pathToOpen)
|
||||
rootView.focus()
|
||||
atom.project.setPath(atom.project.resolve('dir'))
|
||||
pathToOpen = atom.project.resolve('a')
|
||||
atom.rootView = new RootView
|
||||
atom.rootView.enableKeymap()
|
||||
atom.rootView.openSync(pathToOpen)
|
||||
atom.rootView.focus()
|
||||
|
||||
describe "@deserialize()", ->
|
||||
viewState = null
|
||||
|
||||
refreshRootViewAndProject = ->
|
||||
rootViewState = rootView.serialize()
|
||||
projectState = project.serialize()
|
||||
rootView.remove()
|
||||
project.destroy()
|
||||
window.project = deserialize(projectState)
|
||||
window.rootView = deserialize(rootViewState)
|
||||
rootView.attachToDom()
|
||||
rootViewState = atom.rootView.serialize()
|
||||
atom.project.getState().serializeForPersistence()
|
||||
project2 = atom.replicate().get('project')
|
||||
atom.rootView.remove()
|
||||
atom.project.destroy()
|
||||
atom.project = project2
|
||||
atom.rootView = atom.deserializers.deserialize(rootViewState)
|
||||
atom.rootView.attachToDom()
|
||||
|
||||
describe "when the serialized RootView has an unsaved buffer", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
rootView.attachToDom()
|
||||
rootView.openSync()
|
||||
editor1 = rootView.getActiveView()
|
||||
atom.rootView.attachToDom()
|
||||
atom.rootView.openSync()
|
||||
editor1 = atom.rootView.getActiveView()
|
||||
buffer = editor1.getBuffer()
|
||||
editor1.splitRight()
|
||||
expect(rootView.getActiveView()).toBe rootView.getEditors()[2]
|
||||
expect(atom.rootView.getActiveView()).toBe atom.rootView.getEditors()[2]
|
||||
|
||||
refreshRootViewAndProject()
|
||||
|
||||
expect(rootView.getEditors().length).toBe 2
|
||||
expect(rootView.getActiveView()).toBe rootView.getEditors()[1]
|
||||
expect(rootView.title).toBe "untitled - #{project.getPath()}"
|
||||
expect(atom.rootView.getEditors().length).toBe 2
|
||||
expect(atom.rootView.getActiveView()).toBe atom.rootView.getEditors()[1]
|
||||
expect(atom.rootView.title).toBe "untitled - #{atom.project.getPath()}"
|
||||
|
||||
describe "when there are open editors", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
rootView.attachToDom()
|
||||
pane1 = rootView.getActivePane()
|
||||
atom.rootView.attachToDom()
|
||||
pane1 = atom.rootView.getActivePane()
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitRight()
|
||||
pane4 = pane2.splitDown()
|
||||
pane2.showItem(project.openSync('b'))
|
||||
pane3.showItem(project.openSync('../sample.js'))
|
||||
pane2.showItem(atom.project.openSync('b'))
|
||||
pane3.showItem(atom.project.openSync('../sample.js'))
|
||||
pane3.activeItem.setCursorScreenPosition([2, 4])
|
||||
pane4.showItem(project.openSync('../sample.txt'))
|
||||
pane4.showItem(atom.project.openSync('../sample.txt'))
|
||||
pane4.activeItem.setCursorScreenPosition([0, 2])
|
||||
pane2.focus()
|
||||
|
||||
refreshRootViewAndProject()
|
||||
|
||||
expect(rootView.getEditors().length).toBe 4
|
||||
editor1 = rootView.panes.find('.row > .pane .editor:eq(0)').view()
|
||||
editor3 = rootView.panes.find('.row > .pane .editor:eq(1)').view()
|
||||
editor2 = rootView.panes.find('.row > .column > .pane .editor:eq(0)').view()
|
||||
editor4 = rootView.panes.find('.row > .column > .pane .editor:eq(1)').view()
|
||||
expect(atom.rootView.getEditors().length).toBe 4
|
||||
editor1 = atom.rootView.panes.find('.row > .pane .editor:eq(0)').view()
|
||||
editor3 = atom.rootView.panes.find('.row > .pane .editor:eq(1)').view()
|
||||
editor2 = atom.rootView.panes.find('.row > .column > .pane .editor:eq(0)').view()
|
||||
editor4 = atom.rootView.panes.find('.row > .column > .pane .editor:eq(1)').view()
|
||||
|
||||
expect(editor1.getPath()).toBe project.resolve('a')
|
||||
expect(editor2.getPath()).toBe project.resolve('b')
|
||||
expect(editor3.getPath()).toBe project.resolve('../sample.js')
|
||||
expect(editor1.getPath()).toBe atom.project.resolve('a')
|
||||
expect(editor2.getPath()).toBe atom.project.resolve('b')
|
||||
expect(editor3.getPath()).toBe atom.project.resolve('../sample.js')
|
||||
expect(editor3.getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(editor4.getPath()).toBe project.resolve('../sample.txt')
|
||||
expect(editor4.getPath()).toBe atom.project.resolve('../sample.txt')
|
||||
expect(editor4.getCursorScreenPosition()).toEqual [0, 2]
|
||||
|
||||
# ensure adjust pane dimensions is called
|
||||
@@ -83,178 +84,184 @@ describe "RootView", ->
|
||||
expect(editor3.isFocused).toBeFalsy()
|
||||
expect(editor4.isFocused).toBeFalsy()
|
||||
|
||||
expect(rootView.title).toBe "#{path.basename(editor2.getPath())} - #{project.getPath()}"
|
||||
expect(atom.rootView.title).toBe "#{path.basename(editor2.getPath())} - #{atom.project.getPath()}"
|
||||
|
||||
describe "where there are no open editors", ->
|
||||
it "constructs the view with no open editors", ->
|
||||
rootView.getActivePane().remove()
|
||||
expect(rootView.getEditors().length).toBe 0
|
||||
atom.rootView.getActivePane().remove()
|
||||
expect(atom.rootView.getEditors().length).toBe 0
|
||||
refreshRootViewAndProject()
|
||||
expect(rootView.getEditors().length).toBe 0
|
||||
expect(atom.rootView.getEditors().length).toBe 0
|
||||
|
||||
describe "focus", ->
|
||||
beforeEach ->
|
||||
rootView.attachToDom()
|
||||
atom.rootView.attachToDom()
|
||||
|
||||
describe "when there is an active view", ->
|
||||
it "hands off focus to the active view", ->
|
||||
editor = rootView.getActiveView()
|
||||
editor.isFocused = false
|
||||
rootView.focus()
|
||||
expect(editor.isFocused).toBeTruthy()
|
||||
editorView = atom.rootView.getActiveView()
|
||||
editorView.isFocused = false
|
||||
atom.rootView.focus()
|
||||
expect(editorView.isFocused).toBeTruthy()
|
||||
|
||||
describe "when there is no active view", ->
|
||||
beforeEach ->
|
||||
rootView.getActivePane().remove()
|
||||
expect(rootView.getActiveView()).toBeUndefined()
|
||||
rootView.attachToDom()
|
||||
atom.rootView.getActivePane().remove()
|
||||
expect(atom.rootView.getActiveView()).toBeUndefined()
|
||||
atom.rootView.attachToDom()
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
describe "when are visible focusable elements (with a -1 tabindex)", ->
|
||||
it "passes focus to the first focusable element", ->
|
||||
focusable1 = $$ -> @div "One", id: 'one', tabindex: -1
|
||||
focusable2 = $$ -> @div "Two", id: 'two', tabindex: -1
|
||||
rootView.horizontal.append(focusable1, focusable2)
|
||||
atom.rootView.horizontal.append(focusable1, focusable2)
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
rootView.focus()
|
||||
atom.rootView.focus()
|
||||
expect(document.activeElement).toBe focusable1[0]
|
||||
|
||||
describe "when there are no visible focusable elements", ->
|
||||
it "surrenders focus to the body", ->
|
||||
focusable = $$ -> @div "One", id: 'one', tabindex: -1
|
||||
rootView.horizontal.append(focusable)
|
||||
atom.rootView.horizontal.append(focusable)
|
||||
focusable.hide()
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
rootView.focus()
|
||||
atom.rootView.focus()
|
||||
expect(document.activeElement).toBe document.body
|
||||
|
||||
describe "keymap wiring", ->
|
||||
commandHandler = null
|
||||
beforeEach ->
|
||||
commandHandler = jasmine.createSpy('commandHandler')
|
||||
rootView.on('foo-command', commandHandler)
|
||||
atom.rootView.on('foo-command', commandHandler)
|
||||
|
||||
window.keymap.bindKeys('*', 'x': 'foo-command')
|
||||
atom.keymap.bindKeys('name', '*', 'x': 'foo-command')
|
||||
|
||||
describe "when a keydown event is triggered in the RootView", ->
|
||||
it "triggers matching keybindings for that event", ->
|
||||
event = keydownEvent 'x', target: rootView[0]
|
||||
event = keydownEvent 'x', target: atom.rootView[0]
|
||||
|
||||
rootView.trigger(event)
|
||||
atom.rootView.trigger(event)
|
||||
expect(commandHandler).toHaveBeenCalled()
|
||||
|
||||
describe "window title", ->
|
||||
describe "when the project has no path", ->
|
||||
it "sets the title to 'untitled'", ->
|
||||
project.setPath(undefined)
|
||||
expect(rootView.title).toBe 'untitled'
|
||||
atom.project.setPath(undefined)
|
||||
expect(atom.rootView.title).toBe 'untitled'
|
||||
|
||||
describe "when the project has a path", ->
|
||||
beforeEach ->
|
||||
rootView.openSync('b')
|
||||
atom.rootView.openSync('b')
|
||||
|
||||
describe "when there is an active pane item", ->
|
||||
it "sets the title to the pane item's title plus the project path", ->
|
||||
item = rootView.getActivePaneItem()
|
||||
expect(rootView.title).toBe "#{item.getTitle()} - #{project.getPath()}"
|
||||
item = atom.rootView.getActivePaneItem()
|
||||
expect(atom.rootView.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
|
||||
describe "when the title of the active pane item changes", ->
|
||||
it "updates the window title based on the item's new title", ->
|
||||
editSession = rootView.getActivePaneItem()
|
||||
editSession.buffer.setPath(path.join(temp.dir, 'hi'))
|
||||
expect(rootView.title).toBe "#{editSession.getTitle()} - #{project.getPath()}"
|
||||
editor = atom.rootView.getActivePaneItem()
|
||||
editor.buffer.setPath(path.join(temp.dir, 'hi'))
|
||||
expect(atom.rootView.title).toBe "#{editor.getTitle()} - #{atom.project.getPath()}"
|
||||
|
||||
describe "when the active pane's item changes", ->
|
||||
it "updates the title to the new item's title plus the project path", ->
|
||||
rootView.getActivePane().showNextItem()
|
||||
item = rootView.getActivePaneItem()
|
||||
expect(rootView.title).toBe "#{item.getTitle()} - #{project.getPath()}"
|
||||
atom.rootView.getActivePane().showNextItem()
|
||||
item = atom.rootView.getActivePaneItem()
|
||||
expect(atom.rootView.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
|
||||
describe "when the last pane item is removed", ->
|
||||
it "updates the title to contain the project's path", ->
|
||||
rootView.getActivePane().remove()
|
||||
expect(rootView.getActivePaneItem()).toBeUndefined()
|
||||
expect(rootView.title).toBe project.getPath()
|
||||
atom.rootView.getActivePane().remove()
|
||||
expect(atom.rootView.getActivePaneItem()).toBeUndefined()
|
||||
expect(atom.rootView.title).toBe atom.project.getPath()
|
||||
|
||||
describe "when an inactive pane's item changes", ->
|
||||
it "does not update the title", ->
|
||||
pane = rootView.getActivePane()
|
||||
pane = atom.rootView.getActivePane()
|
||||
pane.splitRight()
|
||||
initialTitle = rootView.title
|
||||
initialTitle = atom.rootView.title
|
||||
pane.showNextItem()
|
||||
expect(rootView.title).toBe initialTitle
|
||||
expect(atom.rootView.title).toBe initialTitle
|
||||
|
||||
describe "when the root view is deserialized", ->
|
||||
it "updates the title to contain the project's path", ->
|
||||
rootView2 = atom.deserializers.deserialize(rootView.serialize())
|
||||
item = rootView.getActivePaneItem()
|
||||
expect(rootView2.title).toBe "#{item.getTitle()} - #{project.getPath()}"
|
||||
rootView2 = atom.deserializers.deserialize(atom.rootView.serialize())
|
||||
item = atom.rootView.getActivePaneItem()
|
||||
expect(rootView2.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
rootView2.remove()
|
||||
|
||||
describe "font size adjustment", ->
|
||||
it "increases/decreases font size when increase/decrease-font-size events are triggered", ->
|
||||
fontSizeBefore = config.get('editor.fontSize')
|
||||
rootView.trigger 'window:increase-font-size'
|
||||
expect(config.get('editor.fontSize')).toBe fontSizeBefore + 1
|
||||
rootView.trigger 'window:increase-font-size'
|
||||
expect(config.get('editor.fontSize')).toBe fontSizeBefore + 2
|
||||
rootView.trigger 'window:decrease-font-size'
|
||||
expect(config.get('editor.fontSize')).toBe fontSizeBefore + 1
|
||||
rootView.trigger 'window:decrease-font-size'
|
||||
expect(config.get('editor.fontSize')).toBe fontSizeBefore
|
||||
fontSizeBefore = atom.config.get('editor.fontSize')
|
||||
atom.rootView.trigger 'window:increase-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 1
|
||||
atom.rootView.trigger 'window:increase-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 2
|
||||
atom.rootView.trigger 'window:decrease-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore + 1
|
||||
atom.rootView.trigger 'window:decrease-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe fontSizeBefore
|
||||
|
||||
it "does not allow the font size to be less than 1", ->
|
||||
config.set("editor.fontSize", 1)
|
||||
rootView.trigger 'window:decrease-font-size'
|
||||
expect(config.get('editor.fontSize')).toBe 1
|
||||
atom.config.set("editor.fontSize", 1)
|
||||
atom.rootView.trigger 'window:decrease-font-size'
|
||||
expect(atom.config.get('editor.fontSize')).toBe 1
|
||||
|
||||
describe ".open(filePath, options)", ->
|
||||
describe ".openSync(filePath, options)", ->
|
||||
describe "when there is no active pane", ->
|
||||
beforeEach ->
|
||||
spyOn(Pane.prototype, 'focus')
|
||||
rootView.getActivePane().remove()
|
||||
expect(rootView.getActivePane()).toBeUndefined()
|
||||
atom.rootView.getActivePane().remove()
|
||||
expect(atom.rootView.getActivePane()).toBeUndefined()
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "creates a empty edit session as an item on a new pane, and focuses the pane", ->
|
||||
editSession = rootView.openSync()
|
||||
expect(rootView.getActivePane().activeItem).toBe editSession
|
||||
expect(editSession.getPath()).toBeUndefined()
|
||||
expect(rootView.getActivePane().focus).toHaveBeenCalled()
|
||||
editor = atom.rootView.openSync()
|
||||
expect(atom.rootView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(atom.rootView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
it "can create multiple empty edit sessions as an item on a new pane", ->
|
||||
editSession = rootView.openSync()
|
||||
editSession2 = rootView.openSync()
|
||||
expect(rootView.getActivePane().getItems().length).toBe 2
|
||||
expect(editSession).not.toBe editSession2
|
||||
editor = atom.rootView.openSync()
|
||||
editor2 = atom.rootView.openSync()
|
||||
expect(atom.rootView.getActivePane().getItems().length).toBe 2
|
||||
expect(editor).not.toBe editor2
|
||||
|
||||
describe "when called with a path", ->
|
||||
it "creates an edit session for the given path as an item on a new pane, and focuses the pane", ->
|
||||
editSession = rootView.openSync('b')
|
||||
expect(rootView.getActivePane().activeItem).toBe editSession
|
||||
expect(editSession.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
expect(rootView.getActivePane().focus).toHaveBeenCalled()
|
||||
editor = atom.rootView.openSync('b')
|
||||
expect(atom.rootView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
expect(atom.rootView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the changeFocus option is false", ->
|
||||
it "does not focus the new pane", ->
|
||||
editSession = rootView.openSync('b', changeFocus: false)
|
||||
expect(rootView.getActivePane().focus).not.toHaveBeenCalled()
|
||||
editor = atom.rootView.openSync('b', changeFocus: false)
|
||||
expect(atom.rootView.getActivePane().focus).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the split option is 'right'", ->
|
||||
it "creates a new pane and opens the file in said pane", ->
|
||||
editor = atom.rootView.openSync('b', split: 'right')
|
||||
expect(atom.rootView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
|
||||
describe "when there is an active pane", ->
|
||||
[activePane, initialItemCount] = []
|
||||
beforeEach ->
|
||||
activePane = rootView.getActivePane()
|
||||
activePane = atom.rootView.getActivePane()
|
||||
spyOn(activePane, 'focus')
|
||||
initialItemCount = activePane.getItems().length
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "opens an edit session with an empty buffer as an item in the active pane and focuses it", ->
|
||||
editSession = rootView.openSync()
|
||||
editor = atom.rootView.openSync()
|
||||
expect(activePane.getItems().length).toBe initialItemCount + 1
|
||||
expect(activePane.activeItem).toBe editSession
|
||||
expect(editSession.getPath()).toBeUndefined()
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a path", ->
|
||||
@@ -262,93 +269,163 @@ describe "RootView", ->
|
||||
it "shows the existing edit session in the pane", ->
|
||||
previousEditSession = activePane.activeItem
|
||||
|
||||
editSession = rootView.openSync('b')
|
||||
expect(activePane.activeItem).toBe editSession
|
||||
expect(editSession).not.toBe previousEditSession
|
||||
editor = atom.rootView.openSync('b')
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor).not.toBe previousEditSession
|
||||
|
||||
editSession = rootView.openSync(previousEditSession.getPath())
|
||||
expect(editSession).toBe previousEditSession
|
||||
expect(activePane.activeItem).toBe editSession
|
||||
editor = atom.rootView.openSync(previousEditSession.getPath())
|
||||
expect(editor).toBe previousEditSession
|
||||
expect(activePane.activeItem).toBe editor
|
||||
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the active pane does not have an edit session item for the path being opened", ->
|
||||
it "creates a new edit session for the given path in the active editor", ->
|
||||
editSession = rootView.openSync('b')
|
||||
editor = atom.rootView.openSync('b')
|
||||
expect(activePane.items.length).toBe 2
|
||||
expect(activePane.activeItem).toBe editSession
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the changeFocus option is false", ->
|
||||
it "does not focus the active pane", ->
|
||||
editSession = rootView.openSync('b', changeFocus: false)
|
||||
editor = atom.rootView.openSync('b', changeFocus: false)
|
||||
expect(activePane.focus).not.toHaveBeenCalled()
|
||||
|
||||
describe ".openAsync(filePath)", ->
|
||||
describe "when the split option is 'right'", ->
|
||||
it "creates a new pane and opens the file in said pane", ->
|
||||
pane1 = atom.rootView.getActivePane()
|
||||
|
||||
editor = atom.rootView.openSync('b', split: 'right')
|
||||
pane2 = atom.rootView.getActivePane()
|
||||
expect(pane2[0]).not.toBe pane1[0]
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
|
||||
expect(atom.rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
editor = atom.rootView.openSync('file1', split: 'right')
|
||||
pane3 = atom.rootView.getActivePane()
|
||||
expect(pane3[0]).toBe pane2[0]
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/file1')
|
||||
|
||||
expect(atom.rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
describe ".openSingletonSync(filePath, options)", ->
|
||||
describe "when there is an active pane", ->
|
||||
[pane1] = []
|
||||
beforeEach ->
|
||||
spyOn(Pane.prototype, 'focus').andCallFake -> @makeActive()
|
||||
pane1 = atom.rootView.getActivePane()
|
||||
|
||||
it "creates a new pane and reuses the file when already open", ->
|
||||
atom.rootView.openSingletonSync('b', split: 'right')
|
||||
pane2 = atom.rootView.getActivePane()
|
||||
expect(pane2[0]).not.toBe pane1[0]
|
||||
expect(pane1.itemForUri('b')).toBeFalsy()
|
||||
expect(pane2.itemForUri('b')).not.toBeFalsy()
|
||||
expect(atom.rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
pane1.focus()
|
||||
expect(atom.rootView.getActivePane()[0]).toBe pane1[0]
|
||||
|
||||
atom.rootView.openSingletonSync('b', split: 'right')
|
||||
pane3 = atom.rootView.getActivePane()
|
||||
expect(pane3[0]).toBe pane2[0]
|
||||
expect(pane1.itemForUri('b')).toBeFalsy()
|
||||
expect(pane2.itemForUri('b')).not.toBeFalsy()
|
||||
expect(atom.rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
it "handles split: left by opening to the left pane when necessary", ->
|
||||
atom.rootView.openSingletonSync('b', split: 'right')
|
||||
pane2 = atom.rootView.getActivePane()
|
||||
expect(pane2[0]).not.toBe pane1[0]
|
||||
|
||||
atom.rootView.openSingletonSync('file1', split: 'left')
|
||||
|
||||
activePane = atom.rootView.getActivePane()
|
||||
expect(activePane[0]).toBe pane1[0]
|
||||
|
||||
expect(pane1.itemForUri('file1')).toBeTruthy()
|
||||
expect(pane2.itemForUri('file1')).toBeFalsy()
|
||||
expect(atom.rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
pane2.focus()
|
||||
expect(atom.rootView.getActivePane()[0]).toBe pane2[0]
|
||||
|
||||
atom.rootView.openSingletonSync('file1', split: 'left')
|
||||
activePane = atom.rootView.getActivePane()
|
||||
expect(activePane[0]).toBe pane1[0]
|
||||
expect(atom.rootView.panes.find('.row .pane').toArray()).toEqual [pane1[0], pane2[0]]
|
||||
|
||||
it "reuses the file when already open", ->
|
||||
atom.rootView.openSync('b')
|
||||
atom.rootView.openSingletonSync('b', split: 'right')
|
||||
expect(atom.rootView.panes.find('.pane').toArray()).toEqual [pane1[0]]
|
||||
|
||||
describe ".open(filePath)", ->
|
||||
beforeEach ->
|
||||
spyOn(Pane.prototype, 'focus')
|
||||
|
||||
describe "when there is no active pane", ->
|
||||
beforeEach ->
|
||||
rootView.getActivePane().remove()
|
||||
expect(rootView.getActivePane()).toBeUndefined()
|
||||
atom.rootView.getActivePane().remove()
|
||||
expect(atom.rootView.getActivePane()).toBeUndefined()
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "creates a empty edit session as an item on a new pane, and focuses the pane", ->
|
||||
editSession = null
|
||||
editor = null
|
||||
|
||||
waitsForPromise ->
|
||||
rootView.open().then (o) -> editSession = o
|
||||
atom.rootView.open().then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(rootView.getActivePane().activeItem).toBe editSession
|
||||
expect(editSession.getPath()).toBeUndefined()
|
||||
expect(rootView.getActivePane().focus).toHaveBeenCalled()
|
||||
expect(atom.rootView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(atom.rootView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
it "can create multiple empty edit sessions as items on a pane", ->
|
||||
editSession1 = null
|
||||
editSession2 = null
|
||||
editor1 = null
|
||||
editor2 = null
|
||||
|
||||
waitsForPromise ->
|
||||
rootView.open()
|
||||
atom.rootView.open()
|
||||
.then (o) ->
|
||||
editSession1 = o
|
||||
rootView.open()
|
||||
editor1 = o
|
||||
atom.rootView.open()
|
||||
.then (o) ->
|
||||
editSession2 = o
|
||||
editor2 = o
|
||||
|
||||
runs ->
|
||||
expect(rootView.getActivePane().getItems().length).toBe 2
|
||||
expect(editSession1).not.toBe editSession2
|
||||
expect(atom.rootView.getActivePane().getItems().length).toBe 2
|
||||
expect(editor1).not.toBe editor2
|
||||
|
||||
describe "when called with a path", ->
|
||||
it "creates an edit session for the given path as an item on a new pane, and focuses the pane", ->
|
||||
editSession = null
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
rootView.open('b').then (o) -> editSession = o
|
||||
atom.rootView.open('b').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(rootView.getActivePane().activeItem).toBe editSession
|
||||
expect(editSession.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
expect(rootView.getActivePane().focus).toHaveBeenCalled()
|
||||
expect(atom.rootView.getActivePane().activeItem).toBe editor
|
||||
expect(editor.getPath()).toBe require.resolve('./fixtures/dir/b')
|
||||
expect(atom.rootView.getActivePane().focus).toHaveBeenCalled()
|
||||
|
||||
describe "when there is an active pane", ->
|
||||
[activePane] = []
|
||||
|
||||
beforeEach ->
|
||||
activePane = rootView.getActivePane()
|
||||
activePane = atom.rootView.getActivePane()
|
||||
|
||||
describe "when called with no path", ->
|
||||
it "opens an edit session with an empty buffer as an item in the active pane and focuses it", ->
|
||||
editSession = null
|
||||
editor = null
|
||||
|
||||
waitsForPromise ->
|
||||
rootView.open().then (o) -> editSession = o
|
||||
atom.rootView.open().then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(activePane.getItems().length).toBe 2
|
||||
expect(activePane.activeItem).toBe editSession
|
||||
expect(editSession.getPath()).toBeUndefined()
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a path", ->
|
||||
@@ -356,39 +433,39 @@ describe "RootView", ->
|
||||
it "shows the existing edit session in the pane", ->
|
||||
previousEditSession = activePane.activeItem
|
||||
|
||||
editSession = null
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
rootView.open('b').then (o) -> editSession = o
|
||||
atom.rootView.open('b').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(activePane.activeItem).toBe editSession
|
||||
expect(editSession).not.toBe previousEditSession
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor).not.toBe previousEditSession
|
||||
|
||||
waitsForPromise ->
|
||||
rootView.open(previousEditSession.getPath()).then (o) -> editSession = o
|
||||
atom.rootView.open(previousEditSession.getPath()).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editSession).toBe previousEditSession
|
||||
expect(activePane.activeItem).toBe editSession
|
||||
expect(editor).toBe previousEditSession
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when the active pane does not have an existing item for the given path", ->
|
||||
it "creates a new edit session for the given path in the active pane", ->
|
||||
editSession = null
|
||||
editor = null
|
||||
|
||||
waitsForPromise ->
|
||||
rootView.open('b').then (o) -> editSession = o
|
||||
atom.rootView.open('b').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(activePane.activeItem).toBe editSession
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(activePane.getItems().length).toBe 2
|
||||
expect(activePane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "window:toggle-invisibles event", ->
|
||||
it "shows/hides invisibles in all open and future editors", ->
|
||||
rootView.height(200)
|
||||
rootView.attachToDom()
|
||||
rightEditor = rootView.getActiveView()
|
||||
atom.rootView.height(200)
|
||||
atom.rootView.attachToDom()
|
||||
rightEditor = atom.rootView.getActiveView()
|
||||
rightEditor.setText(" \t ")
|
||||
leftEditor = rightEditor.splitLeft()
|
||||
expect(rightEditor.find(".line:first").text()).toBe " "
|
||||
@@ -396,14 +473,14 @@ describe "RootView", ->
|
||||
|
||||
withInvisiblesShowing = "#{rightEditor.invisibles.space}#{rightEditor.invisibles.tab} #{rightEditor.invisibles.space}#{rightEditor.invisibles.eol}"
|
||||
|
||||
rootView.trigger "window:toggle-invisibles"
|
||||
atom.rootView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
expect(leftEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
lowerLeftEditor = leftEditor.splitDown()
|
||||
expect(lowerLeftEditor.find(".line:first").text()).toBe withInvisiblesShowing
|
||||
|
||||
rootView.trigger "window:toggle-invisibles"
|
||||
atom.rootView.trigger "window:toggle-invisibles"
|
||||
expect(rightEditor.find(".line:first").text()).toBe " "
|
||||
expect(leftEditor.find(".line:first").text()).toBe " "
|
||||
|
||||
@@ -412,7 +489,7 @@ describe "RootView", ->
|
||||
|
||||
describe ".eachEditor(callback)", ->
|
||||
beforeEach ->
|
||||
rootView.attachToDom()
|
||||
atom.rootView.attachToDom()
|
||||
|
||||
it "invokes the callback for existing editor", ->
|
||||
count = 0
|
||||
@@ -420,9 +497,9 @@ describe "RootView", ->
|
||||
callback = (editor) ->
|
||||
callbackEditor = editor
|
||||
count++
|
||||
rootView.eachEditor(callback)
|
||||
atom.rootView.eachEditor(callback)
|
||||
expect(count).toBe 1
|
||||
expect(callbackEditor).toBe rootView.getActiveView()
|
||||
expect(callbackEditor).toBe atom.rootView.getActiveView()
|
||||
|
||||
it "invokes the callback for new editor", ->
|
||||
count = 0
|
||||
@@ -431,28 +508,28 @@ describe "RootView", ->
|
||||
callbackEditor = editor
|
||||
count++
|
||||
|
||||
rootView.eachEditor(callback)
|
||||
atom.rootView.eachEditor(callback)
|
||||
count = 0
|
||||
callbackEditor = null
|
||||
rootView.getActiveView().splitRight()
|
||||
atom.rootView.getActiveView().splitRight()
|
||||
expect(count).toBe 1
|
||||
expect(callbackEditor).toBe rootView.getActiveView()
|
||||
expect(callbackEditor).toBe atom.rootView.getActiveView()
|
||||
|
||||
it "returns a subscription that can be disabled", ->
|
||||
count = 0
|
||||
callback = (editor) -> count++
|
||||
|
||||
subscription = rootView.eachEditor(callback)
|
||||
subscription = atom.rootView.eachEditor(callback)
|
||||
expect(count).toBe 1
|
||||
rootView.getActiveView().splitRight()
|
||||
atom.rootView.getActiveView().splitRight()
|
||||
expect(count).toBe 2
|
||||
subscription.off()
|
||||
rootView.getActiveView().splitRight()
|
||||
atom.rootView.getActiveView().splitRight()
|
||||
expect(count).toBe 2
|
||||
|
||||
describe ".eachBuffer(callback)", ->
|
||||
beforeEach ->
|
||||
rootView.attachToDom()
|
||||
atom.rootView.attachToDom()
|
||||
|
||||
it "invokes the callback for existing buffer", ->
|
||||
count = 0
|
||||
@@ -461,9 +538,9 @@ describe "RootView", ->
|
||||
callback = (buffer) ->
|
||||
callbackBuffer = buffer
|
||||
count++
|
||||
rootView.eachBuffer(callback)
|
||||
atom.rootView.eachBuffer(callback)
|
||||
expect(count).toBe 1
|
||||
expect(callbackBuffer).toBe rootView.getActiveView().getBuffer()
|
||||
expect(callbackBuffer).toBe atom.rootView.getActiveView().getBuffer()
|
||||
|
||||
it "invokes the callback for new buffer", ->
|
||||
count = 0
|
||||
@@ -472,9 +549,9 @@ describe "RootView", ->
|
||||
callbackBuffer = buffer
|
||||
count++
|
||||
|
||||
rootView.eachBuffer(callback)
|
||||
atom.rootView.eachBuffer(callback)
|
||||
count = 0
|
||||
callbackBuffer = null
|
||||
rootView.openSync(require.resolve('./fixtures/sample.txt'))
|
||||
atom.rootView.openSync(require.resolve('./fixtures/sample.txt'))
|
||||
expect(count).toBe 1
|
||||
expect(callbackBuffer).toBe rootView.getActiveView().getBuffer()
|
||||
expect(callbackBuffer).toBe atom.rootView.getActiveView().getBuffer()
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
EditSession = require '../src/edit-session'
|
||||
Editor = require '../src/editor'
|
||||
|
||||
describe "Selection", ->
|
||||
[buffer, editSession, selection] = []
|
||||
[buffer, editor, selection] = []
|
||||
|
||||
beforeEach ->
|
||||
buffer = project.buildBufferSync('sample.js')
|
||||
editSession = new EditSession(buffer: buffer, tabLength: 2)
|
||||
selection = editSession.getSelection()
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
editor = new Editor(buffer: buffer, tabLength: 2)
|
||||
selection = editor.getSelection()
|
||||
|
||||
afterEach ->
|
||||
buffer.destroy()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{View, $$} = require 'atom'
|
||||
{View, $, $$} = require 'atom'
|
||||
|
||||
describe "SpacePen extensions", ->
|
||||
class TestView extends View
|
||||
@@ -23,21 +23,21 @@ describe "SpacePen extensions", ->
|
||||
expect(observeHandler).toHaveBeenCalledWith(undefined)
|
||||
observeHandler.reset()
|
||||
|
||||
config.set("foo.bar", "hello")
|
||||
atom.config.set("foo.bar", "hello")
|
||||
|
||||
expect(observeHandler).toHaveBeenCalledWith("hello", previous: undefined)
|
||||
observeHandler.reset()
|
||||
|
||||
view.unobserveConfig()
|
||||
|
||||
config.set("foo.bar", "goodbye")
|
||||
atom.config.set("foo.bar", "goodbye")
|
||||
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "unobserves when the view is removed", ->
|
||||
observeHandler.reset()
|
||||
parent.remove()
|
||||
config.set("foo.bar", "hello")
|
||||
atom.config.set("foo.bar", "hello")
|
||||
expect(observeHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe "View.subscribe(eventEmitter, eventName, callback)", ->
|
||||
@@ -51,3 +51,17 @@ describe "SpacePen extensions", ->
|
||||
it "subscribes to the given event emitter and unsubscribes when unsubscribe is called", ->
|
||||
emitter.trigger "foo"
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
|
||||
describe "tooltips", ->
|
||||
describe "replaceModifiers", ->
|
||||
replaceModifiers = $.fn.setTooltip.replaceModifiers
|
||||
|
||||
it "replaces single keystroke", ->
|
||||
expect(replaceModifiers('cmd-O')).toEqual '⌘⇧O'
|
||||
expect(replaceModifiers('cmd-shift-up')).toEqual '⌘⇧↑'
|
||||
expect(replaceModifiers('cmd-option-down')).toEqual '⌘⌥↓'
|
||||
expect(replaceModifiers('cmd-option-left')).toEqual '⌘⌥←'
|
||||
expect(replaceModifiers('cmd-option-right')).toEqual '⌘⌥→'
|
||||
|
||||
it "replaces multiple keystroke", ->
|
||||
expect(replaceModifiers('cmd-o ctrl-2')).toEqual '⌘O ⌃2'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
path = require 'path'
|
||||
fsUtils = require '../src/fs-utils'
|
||||
fs = require 'fs-plus'
|
||||
|
||||
{_} = require 'atom'
|
||||
|
||||
@@ -15,8 +15,8 @@ module.exports =
|
||||
# Returns nothing.
|
||||
generateEvilFiles: ->
|
||||
evilFilesPath = path.join(__dirname, 'fixtures', 'evil-files')
|
||||
fsUtils.remove(evilFilesPath) if fsUtils.exists(evilFilesPath)
|
||||
fsUtils.mkdirSync(evilFilesPath)
|
||||
fs.removeSync(evilFilesPath) if fs.existsSync(evilFilesPath)
|
||||
fs.mkdirSync(evilFilesPath)
|
||||
|
||||
if (@isWindows())
|
||||
filenames = [
|
||||
@@ -34,5 +34,4 @@ module.exports =
|
||||
]
|
||||
|
||||
for filename in filenames
|
||||
fd = fsUtils.writeFileSync(path.join(evilFilesPath, filename), 'evil file!', flag: 'w')
|
||||
|
||||
fd = fs.writeFileSync(path.join(evilFilesPath, filename), 'evil file!', flag: 'w')
|
||||
|
||||
+37
-53
@@ -1,6 +1,6 @@
|
||||
require '../src/window'
|
||||
window.setUpEnvironment('spec')
|
||||
window.restoreDimensions()
|
||||
atom.setUpEnvironment('spec')
|
||||
atom.restoreDimensions()
|
||||
|
||||
require '../vendor/jasmine-jquery'
|
||||
path = require 'path'
|
||||
@@ -9,7 +9,7 @@ Keymap = require '../src/keymap'
|
||||
Config = require '../src/config'
|
||||
{Point} = require 'telepath'
|
||||
Project = require '../src/project'
|
||||
Editor = require '../src/editor'
|
||||
EditorView = require '../src/editor-view'
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
pathwatcher = require 'pathwatcher'
|
||||
platform = require './spec-helper-platform'
|
||||
@@ -23,7 +23,7 @@ atom.themes.requireStylesheet '../static/jasmine'
|
||||
fixturePackagesPath = path.resolve(__dirname, './fixtures/packages')
|
||||
atom.packages.packageDirPaths.unshift(fixturePackagesPath)
|
||||
atom.keymap.loadBundledKeymaps()
|
||||
[bindingSetsToRestore, bindingSetsByFirstKeystrokeToRestore] = []
|
||||
keyBindingsToRestore = atom.keymap.getKeyBindings()
|
||||
|
||||
$(window).on 'core:close', -> window.close()
|
||||
$(window).on 'unload', ->
|
||||
@@ -39,7 +39,9 @@ specPackageName = null
|
||||
specPackagePath = null
|
||||
specProjectPath = null
|
||||
|
||||
if specDirectory = atom.getLoadSettings().specDirectory
|
||||
{specDirectory, resourcePath} = atom.getLoadSettings()
|
||||
|
||||
if specDirectory
|
||||
specPackagePath = path.resolve(specDirectory, '..')
|
||||
try
|
||||
specPackageName = fs.readObjectSync(path.join(specPackagePath, 'package.json'))?.name
|
||||
@@ -47,11 +49,9 @@ if specDirectory = atom.getLoadSettings().specDirectory
|
||||
|
||||
beforeEach ->
|
||||
$.fx.off = true
|
||||
if specProjectPath
|
||||
atom.project = new Project(specProjectPath)
|
||||
else
|
||||
atom.project = new Project(path.join(@specDirectory, 'fixtures'))
|
||||
window.project = atom.project
|
||||
projectPath = specProjectPath ? path.join(@specDirectory, 'fixtures')
|
||||
atom.project = atom.getWindowState().set('project', new Project(path: projectPath))
|
||||
atom.keymap.keyBindings = _.clone(keyBindingsToRestore)
|
||||
|
||||
window.resetTimeouts()
|
||||
atom.packages.packageStates = {}
|
||||
@@ -59,28 +59,22 @@ beforeEach ->
|
||||
atom.syntax.clearGrammarOverrides()
|
||||
atom.syntax.clearProperties()
|
||||
|
||||
if specPackageName
|
||||
spy = spyOn(atom.packages, 'resolvePackagePath').andCallFake (packageName) ->
|
||||
if packageName is specPackageName
|
||||
resolvePackagePath(specPackagePath)
|
||||
else
|
||||
resolvePackagePath(packageName)
|
||||
resolvePackagePath = _.bind(spy.originalValue, atom.packages)
|
||||
|
||||
# used to reset keymap after each spec
|
||||
bindingSetsToRestore = _.clone(keymap.bindingSets)
|
||||
bindingSetsByFirstKeystrokeToRestore = _.clone(keymap.bindingSetsByFirstKeystroke)
|
||||
spy = spyOn(atom.packages, 'resolvePackagePath').andCallFake (packageName) ->
|
||||
if specPackageName and packageName is specPackageName
|
||||
resolvePackagePath(specPackagePath)
|
||||
else
|
||||
resolvePackagePath(packageName)
|
||||
resolvePackagePath = _.bind(spy.originalValue, atom.packages)
|
||||
|
||||
# prevent specs from modifying Atom's menus
|
||||
spyOn(atom.menu, 'sendToBrowserProcess')
|
||||
|
||||
# reset config before each spec; don't load or save from/to `config.json`
|
||||
config = new Config
|
||||
resourcePath: window.resourcePath
|
||||
configDirPath: atom.getConfigDirPath()
|
||||
config.packageDirPaths.unshift(fixturePackagesPath)
|
||||
config = new Config({resourcePath, configDirPath: atom.getConfigDirPath()})
|
||||
spyOn(config, 'load')
|
||||
spyOn(config, 'save')
|
||||
config.setDefaults('core', RootView.configDefaults)
|
||||
config.setDefaults('editor', EditorView.configDefaults)
|
||||
config.set "editor.fontFamily", "Courier"
|
||||
config.set "editor.fontSize", 16
|
||||
config.set "editor.autoIndent", false
|
||||
@@ -88,10 +82,9 @@ beforeEach ->
|
||||
"package-with-broken-package-json", "package-with-broken-keymap"]
|
||||
config.save.reset()
|
||||
atom.config = config
|
||||
window.config = config
|
||||
|
||||
# make editor display updates synchronous
|
||||
spyOn(Editor.prototype, 'requestDisplayUpdate').andCallFake -> @updateDisplay()
|
||||
spyOn(EditorView.prototype, 'requestDisplayUpdate').andCallFake -> @updateDisplay()
|
||||
spyOn(RootView.prototype, 'setTitle').andCallFake (@title) ->
|
||||
spyOn(window, "setTimeout").andCallFake window.fakeSetTimeout
|
||||
spyOn(window, "clearTimeout").andCallFake window.fakeClearTimeout
|
||||
@@ -108,26 +101,20 @@ beforeEach ->
|
||||
addCustomMatchers(this)
|
||||
|
||||
afterEach ->
|
||||
keymap.bindingSets = bindingSetsToRestore
|
||||
keymap.bindingSetsByFirstKeystroke = bindingSetsByFirstKeystrokeToRestore
|
||||
atom.deactivatePackages()
|
||||
atom.packages.deactivatePackages()
|
||||
atom.menu.template = []
|
||||
|
||||
window.rootView?.remove?()
|
||||
atom.rootView?.remove?() if atom.rootView isnt window.rootView
|
||||
window.rootView = null
|
||||
atom.rootView?.remove?()
|
||||
atom.rootView = null
|
||||
|
||||
window.project?.destroy?()
|
||||
atom.project?.destroy?() if atom.project isnt window.project
|
||||
window.project = null
|
||||
atom.project?.destroy?()
|
||||
atom.project = null
|
||||
|
||||
$('#jasmine-content').empty() unless window.debugContent
|
||||
delete atom.windowState
|
||||
jasmine.unspy(atom, 'saveWindowState')
|
||||
ensureNoPathSubscriptions()
|
||||
syntax.off()
|
||||
atom.syntax.off()
|
||||
waits(0) # yield to ui thread to make screen update more frequently
|
||||
|
||||
ensureNoPathSubscriptions = ->
|
||||
@@ -166,7 +153,7 @@ addCustomMatchers = (spec) ->
|
||||
toExistOnDisk: (expected) ->
|
||||
notText = this.isNot and " not" or ""
|
||||
@message = -> return "Expected path '" + @actual + "'" + notText + " to exist."
|
||||
fs.exists(@actual)
|
||||
fs.existsSync(@actual)
|
||||
|
||||
window.keyIdentifierForKey = (key) ->
|
||||
if key.length > 1 # named key
|
||||
@@ -181,8 +168,8 @@ window.keydownEvent = (key, properties={}) ->
|
||||
|
||||
window.mouseEvent = (type, properties) ->
|
||||
if properties.point
|
||||
{point, editor} = properties
|
||||
{top, left} = @pagePixelPositionForPoint(editor, point)
|
||||
{point, editorView} = properties
|
||||
{top, left} = @pagePixelPositionForPoint(editorView, point)
|
||||
properties.pageX = left + 1
|
||||
properties.pageY = top + 1
|
||||
properties.originalEvent ?= {detail: 1}
|
||||
@@ -243,22 +230,22 @@ window.advanceClock = (delta=1) ->
|
||||
|
||||
callback() for callback in callbacks
|
||||
|
||||
window.pagePixelPositionForPoint = (editor, point) ->
|
||||
window.pagePixelPositionForPoint = (editorView, point) ->
|
||||
point = Point.fromObject point
|
||||
top = editor.renderedLines.offset().top + point.row * editor.lineHeight
|
||||
left = editor.renderedLines.offset().left + point.column * editor.charWidth - editor.renderedLines.scrollLeft()
|
||||
top = editorView.renderedLines.offset().top + point.row * editorView.lineHeight
|
||||
left = editorView.renderedLines.offset().left + point.column * editorView.charWidth - editorView.renderedLines.scrollLeft()
|
||||
{ top, left }
|
||||
|
||||
window.tokensText = (tokens) ->
|
||||
_.pluck(tokens, 'value').join('')
|
||||
|
||||
window.setEditorWidthInChars = (editor, widthInChars, charWidth=editor.charWidth) ->
|
||||
editor.width(charWidth * widthInChars + editor.gutter.outerWidth())
|
||||
$(window).trigger 'resize' # update width of editor's on-screen lines
|
||||
window.setEditorWidthInChars = (editorView, widthInChars, charWidth=editorView.charWidth) ->
|
||||
editorView.width(charWidth * widthInChars + editorView.gutter.outerWidth())
|
||||
$(window).trigger 'resize' # update width of editor view's on-screen lines
|
||||
|
||||
window.setEditorHeightInLines = (editor, heightInChars, charHeight=editor.lineHeight) ->
|
||||
editor.height(charHeight * heightInChars + editor.renderedLines.position().top)
|
||||
$(window).trigger 'resize' # update editor's on-screen lines
|
||||
window.setEditorHeightInLines = (editorView, heightInChars, charHeight=editorView.lineHeight) ->
|
||||
editorView.height(charHeight * heightInChars + editorView.renderedLines.position().top)
|
||||
$(window).trigger 'resize' # update editor view's on-screen lines
|
||||
|
||||
$.fn.resultOfTrigger = (type) ->
|
||||
event = $.Event(type)
|
||||
@@ -266,7 +253,7 @@ $.fn.resultOfTrigger = (type) ->
|
||||
event.result
|
||||
|
||||
$.fn.enableKeymap = ->
|
||||
@on 'keydown', (e) => window.keymap.handleKeyEvent(e)
|
||||
@on 'keydown', (e) => atom.keymap.handleKeyEvent(e)
|
||||
|
||||
$.fn.attachToDom = ->
|
||||
@appendTo($('#jasmine-content'))
|
||||
@@ -280,6 +267,3 @@ $.fn.textInput = (data) ->
|
||||
event.initTextEvent('textInput', true, true, window, data)
|
||||
event = $.event.fix(event)
|
||||
$(this).trigger(event)
|
||||
|
||||
unless fs.md5ForPath(require.resolve('./fixtures/sample.js')) == "dd38087d0d7e3e4802a6d3f9b9745f2b"
|
||||
throw new Error("Sample.js is modified")
|
||||
|
||||
+45
-44
@@ -1,54 +1,55 @@
|
||||
measure 'spec suite require time', ->
|
||||
{_, fs, Git} = require 'atom'
|
||||
path = require 'path'
|
||||
require './spec-helper'
|
||||
{_, fs, Git} = require 'atom'
|
||||
path = require 'path'
|
||||
require './spec-helper'
|
||||
|
||||
requireSpecs = (specDirectory, specType) ->
|
||||
for specFilePath in fs.listTreeSync(specDirectory) when /-spec\.coffee$/.test specFilePath
|
||||
require specFilePath
|
||||
requireSpecs = (specDirectory, specType) ->
|
||||
for specFilePath in fs.listTreeSync(specDirectory) when /-spec\.coffee$/.test specFilePath
|
||||
require specFilePath
|
||||
|
||||
# Set spec directory on spec for setting up the project in spec-helper
|
||||
setSpecDirectory(specDirectory)
|
||||
# Set spec directory on spec for setting up the project in spec-helper
|
||||
setSpecDirectory(specDirectory)
|
||||
|
||||
setSpecField = (name, value) ->
|
||||
specs = jasmine.getEnv().currentRunner().specs()
|
||||
return if specs.length is 0
|
||||
for index in [specs.length-1..0]
|
||||
break if specs[index][name]?
|
||||
specs[index][name] = value
|
||||
setSpecField = (name, value) ->
|
||||
specs = jasmine.getEnv().currentRunner().specs()
|
||||
return if specs.length is 0
|
||||
for index in [specs.length-1..0]
|
||||
break if specs[index][name]?
|
||||
specs[index][name] = value
|
||||
|
||||
setSpecType = (specType) ->
|
||||
setSpecField('specType', specType)
|
||||
setSpecType = (specType) ->
|
||||
setSpecField('specType', specType)
|
||||
|
||||
setSpecDirectory = (specDirectory) ->
|
||||
setSpecField('specDirectory', specDirectory)
|
||||
setSpecDirectory = (specDirectory) ->
|
||||
setSpecField('specDirectory', specDirectory)
|
||||
|
||||
runAllSpecs = ->
|
||||
# Only run core specs when resource path is the Atom repository
|
||||
if Git.exists(window.resourcePath)
|
||||
requireSpecs(path.join(window.resourcePath, 'spec'))
|
||||
setSpecType('core')
|
||||
runAllSpecs = ->
|
||||
{resourcePath} = atom.getLoadSettings()
|
||||
# Only run core specs when resource path is the Atom repository
|
||||
if Git.exists(resourcePath)
|
||||
requireSpecs(path.join(resourcePath, 'spec'))
|
||||
setSpecType('core')
|
||||
|
||||
fixturesPackagesPath = path.join(__dirname, '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'
|
||||
fixturesPackagesPath = path.join(__dirname, 'fixtures', 'packages')
|
||||
packagePaths = atom.packages.getAvailablePackageNames().map (packageName) ->
|
||||
atom.packages.resolvePackagePath(packageName)
|
||||
packagePaths = _.groupBy packagePaths, (packagePath) ->
|
||||
if packagePath.indexOf("#{fixturesPackagesPath}#{path.sep}") is 0
|
||||
'fixtures'
|
||||
else if packagePath.indexOf("#{resourcePath}#{path.sep}") is 0
|
||||
'bundled'
|
||||
else
|
||||
'user'
|
||||
|
||||
# Run bundled package specs
|
||||
requireSpecs(path.join(packagePath, 'spec')) for packagePath in packagePaths.bundled ? []
|
||||
setSpecType('bundled')
|
||||
# Run bundled package specs
|
||||
requireSpecs(path.join(packagePath, 'spec')) for packagePath in packagePaths.bundled ? []
|
||||
setSpecType('bundled')
|
||||
|
||||
# Run user package specs
|
||||
requireSpecs(path.join(packagePath, 'spec')) for packagePath in packagePaths.user ? []
|
||||
setSpecType('user')
|
||||
# Run user package specs
|
||||
requireSpecs(path.join(packagePath, 'spec')) for packagePath in packagePaths.user ? []
|
||||
setSpecType('user')
|
||||
|
||||
if specDirectory = atom.getLoadSettings().specDirectory
|
||||
requireSpecs(specDirectory)
|
||||
setSpecType('user')
|
||||
else
|
||||
runAllSpecs()
|
||||
if specDirectory = atom.getLoadSettings().specDirectory
|
||||
requireSpecs(specDirectory)
|
||||
setSpecType('user')
|
||||
else
|
||||
runAllSpecs()
|
||||
|
||||
+51
-51
@@ -5,60 +5,60 @@ TextMateGrammar = require '../src/text-mate-grammar'
|
||||
|
||||
describe "the `syntax` global", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-text', sync: true)
|
||||
atom.activatePackage('language-javascript', sync: true)
|
||||
atom.activatePackage('language-coffee-script', sync: true)
|
||||
atom.activatePackage('language-ruby', sync: true)
|
||||
atom.packages.activatePackage('language-text', sync: true)
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
atom.packages.activatePackage('language-coffee-script', sync: true)
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
|
||||
describe "serialization", ->
|
||||
it "remembers grammar overrides by path", ->
|
||||
filePath = '/foo/bar/file.js'
|
||||
expect(syntax.selectGrammar(filePath).name).not.toBe 'Ruby'
|
||||
syntax.setGrammarOverrideForPath(filePath, 'source.ruby')
|
||||
syntax2 = deserialize(syntax.serialize())
|
||||
syntax2.addGrammar(grammar) for grammar in syntax.grammars when grammar isnt syntax.nullGrammar
|
||||
expect(atom.syntax.selectGrammar(filePath).name).not.toBe 'Ruby'
|
||||
atom.syntax.setGrammarOverrideForPath(filePath, 'source.ruby')
|
||||
syntax2 = atom.deserializers.deserialize(atom.syntax.serialize())
|
||||
syntax2.addGrammar(grammar) for grammar in atom.syntax.grammars when grammar isnt atom.syntax.nullGrammar
|
||||
expect(syntax2.selectGrammar(filePath).name).toBe 'Ruby'
|
||||
|
||||
describe ".selectGrammar(filePath)", ->
|
||||
it "can use the filePath to load the correct grammar based on the grammar's filetype", ->
|
||||
atom.activatePackage('language-git', sync: true)
|
||||
atom.packages.activatePackage('language-git', sync: true)
|
||||
|
||||
expect(syntax.selectGrammar("file.js").name).toBe "JavaScript" # based on extension (.js)
|
||||
expect(syntax.selectGrammar(path.join(temp.dir, '.git', 'config')).name).toBe "Git Config" # based on end of the path (.git/config)
|
||||
expect(syntax.selectGrammar("Rakefile").name).toBe "Ruby" # based on the file's basename (Rakefile)
|
||||
expect(syntax.selectGrammar("curb").name).toBe "Null Grammar"
|
||||
expect(syntax.selectGrammar("/hu.git/config").name).toBe "Null Grammar"
|
||||
expect(atom.syntax.selectGrammar("file.js").name).toBe "JavaScript" # based on extension (.js)
|
||||
expect(atom.syntax.selectGrammar(path.join(temp.dir, '.git', 'config')).name).toBe "Git Config" # based on end of the path (.git/config)
|
||||
expect(atom.syntax.selectGrammar("Rakefile").name).toBe "Ruby" # based on the file's basename (Rakefile)
|
||||
expect(atom.syntax.selectGrammar("curb").name).toBe "Null Grammar"
|
||||
expect(atom.syntax.selectGrammar("/hu.git/config").name).toBe "Null Grammar"
|
||||
|
||||
it "uses the filePath's shebang line if the grammar cannot be determined by the extension or basename", ->
|
||||
filePath = require.resolve("./fixtures/shebang")
|
||||
expect(syntax.selectGrammar(filePath).name).toBe "Ruby"
|
||||
expect(atom.syntax.selectGrammar(filePath).name).toBe "Ruby"
|
||||
|
||||
it "uses the number of newlines in the first line regex to determine the number of lines to test against", ->
|
||||
atom.activatePackage('language-property-list', sync: true)
|
||||
atom.packages.activatePackage('language-property-list', sync: true)
|
||||
|
||||
fileContent = "first-line\n<html>"
|
||||
expect(syntax.selectGrammar("dummy.coffee", fileContent).name).toBe "CoffeeScript"
|
||||
expect(atom.syntax.selectGrammar("dummy.coffee", fileContent).name).toBe "CoffeeScript"
|
||||
|
||||
fileContent = '<?xml version="1.0" encoding="UTF-8"?>'
|
||||
expect(syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Null Grammar"
|
||||
expect(atom.syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Null Grammar"
|
||||
|
||||
fileContent += '\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
|
||||
expect(syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Property List (XML)"
|
||||
expect(atom.syntax.selectGrammar("grammar.tmLanguage", fileContent).name).toBe "Property List (XML)"
|
||||
|
||||
it "doesn't read the file when the file contents are specified", ->
|
||||
filePath = require.resolve("./fixtures/shebang")
|
||||
filePathContents = fs.read(filePath)
|
||||
filePathContents = fs.readFileSync(filePath, 'utf8')
|
||||
spyOn(fs, 'read').andCallThrough()
|
||||
expect(syntax.selectGrammar(filePath, filePathContents).name).toBe "Ruby"
|
||||
expect(atom.syntax.selectGrammar(filePath, filePathContents).name).toBe "Ruby"
|
||||
expect(fs.read).not.toHaveBeenCalled()
|
||||
|
||||
it "allows the default grammar to be overridden for a path", ->
|
||||
filePath = '/foo/bar/file.js'
|
||||
expect(syntax.selectGrammar(filePath).name).not.toBe 'Ruby'
|
||||
syntax.setGrammarOverrideForPath(filePath, 'source.ruby')
|
||||
expect(syntax.selectGrammar(filePath).name).toBe 'Ruby'
|
||||
syntax.clearGrammarOverrideForPath(filePath)
|
||||
expect(syntax.selectGrammar(filePath).name).not.toBe 'Ruby'
|
||||
expect(atom.syntax.selectGrammar(filePath).name).not.toBe 'Ruby'
|
||||
atom.syntax.setGrammarOverrideForPath(filePath, 'source.ruby')
|
||||
expect(atom.syntax.selectGrammar(filePath).name).toBe 'Ruby'
|
||||
atom.syntax.clearGrammarOverrideForPath(filePath)
|
||||
expect(atom.syntax.selectGrammar(filePath).name).not.toBe 'Ruby'
|
||||
|
||||
describe "when multiple grammars have matching fileTypes", ->
|
||||
it "selects the grammar with the longest fileType match", ->
|
||||
@@ -72,46 +72,46 @@ describe "the `syntax` global", ->
|
||||
scopeName: 'source2'
|
||||
fileTypes: ['test']
|
||||
|
||||
syntax.addGrammar(grammar1)
|
||||
syntax.addGrammar(grammar2)
|
||||
atom.syntax.addGrammar(grammar1)
|
||||
atom.syntax.addGrammar(grammar2)
|
||||
|
||||
expect(syntax.selectGrammar('more.test', '')).toBe grammar1
|
||||
expect(atom.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()
|
||||
expect(-> atom.syntax.selectGrammar(null, '#!/usr/bin/ruby')).not.toThrow()
|
||||
expect(-> atom.syntax.selectGrammar(null, '')).not.toThrow()
|
||||
expect(-> atom.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')
|
||||
syntax.removeGrammar(grammar)
|
||||
expect(syntax.selectGrammar('foo.js').name).not.toBe grammar.name
|
||||
grammar = atom.syntax.selectGrammar('foo.js')
|
||||
atom.syntax.removeGrammar(grammar)
|
||||
expect(atom.syntax.selectGrammar('foo.js').name).not.toBe grammar.name
|
||||
|
||||
describe ".getProperty(scopeDescriptor)", ->
|
||||
it "returns the property with the most specific scope selector", ->
|
||||
syntax.addProperties(".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42)
|
||||
syntax.addProperties(".source .string.quoted.double", foo: bar: baz: 22)
|
||||
syntax.addProperties(".source", foo: bar: baz: 11)
|
||||
atom.syntax.addProperties(".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42)
|
||||
atom.syntax.addProperties(".source .string.quoted.double", foo: bar: baz: 22)
|
||||
atom.syntax.addProperties(".source", foo: bar: baz: 11)
|
||||
|
||||
expect(syntax.getProperty([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42
|
||||
expect(syntax.getProperty([".source.js", ".string.quoted.double.js"], "foo.bar.baz")).toBe 22
|
||||
expect(syntax.getProperty([".source.js", ".variable.assignment.js"], "foo.bar.baz")).toBe 11
|
||||
expect(syntax.getProperty([".text"], "foo.bar.baz")).toBeUndefined()
|
||||
expect(atom.syntax.getProperty([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42
|
||||
expect(atom.syntax.getProperty([".source.js", ".string.quoted.double.js"], "foo.bar.baz")).toBe 22
|
||||
expect(atom.syntax.getProperty([".source.js", ".variable.assignment.js"], "foo.bar.baz")).toBe 11
|
||||
expect(atom.syntax.getProperty([".text"], "foo.bar.baz")).toBeUndefined()
|
||||
|
||||
it "favors the most recently added properties in the event of a specificity tie", ->
|
||||
syntax.addProperties(".source.coffee .string.quoted.single", foo: bar: baz: 42)
|
||||
syntax.addProperties(".source.coffee .string.quoted.double", foo: bar: baz: 22)
|
||||
atom.syntax.addProperties(".source.coffee .string.quoted.single", foo: bar: baz: 42)
|
||||
atom.syntax.addProperties(".source.coffee .string.quoted.double", foo: bar: baz: 22)
|
||||
|
||||
expect(syntax.getProperty([".source.coffee", ".string.quoted.single"], "foo.bar.baz")).toBe 42
|
||||
expect(syntax.getProperty([".source.coffee", ".string.quoted.single.double"], "foo.bar.baz")).toBe 22
|
||||
expect(atom.syntax.getProperty([".source.coffee", ".string.quoted.single"], "foo.bar.baz")).toBe 42
|
||||
expect(atom.syntax.getProperty([".source.coffee", ".string.quoted.single.double"], "foo.bar.baz")).toBe 22
|
||||
|
||||
describe ".removeProperties(name)", ->
|
||||
it "allows properties to be removed by name", ->
|
||||
syntax.addProperties("a", ".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42)
|
||||
syntax.addProperties("b", ".source .string.quoted.double", foo: bar: baz: 22)
|
||||
atom.syntax.addProperties("a", ".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42)
|
||||
atom.syntax.addProperties("b", ".source .string.quoted.double", foo: bar: baz: 22)
|
||||
|
||||
syntax.removeProperties("b")
|
||||
expect(syntax.getProperty([".source.js", ".string.quoted.double.js"], "foo.bar.baz")).toBeUndefined()
|
||||
expect(syntax.getProperty([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42
|
||||
atom.syntax.removeProperties("b")
|
||||
expect(atom.syntax.getProperty([".source.js", ".string.quoted.double.js"], "foo.bar.baz")).toBeUndefined()
|
||||
expect(atom.syntax.getProperty([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
{Site} = require 'telepath'
|
||||
|
||||
describe "TextBuffer replication", ->
|
||||
[buffer1, buffer2] = []
|
||||
|
||||
beforeEach ->
|
||||
buffer1 = project.buildBufferSync('sample.js')
|
||||
buffer1.insert([0, 0], 'changed\n')
|
||||
doc1 = buffer1.getState()
|
||||
doc2 = doc1.clone(new Site(2))
|
||||
doc1.connect(doc2)
|
||||
buffer2 = deserialize(doc2, {project})
|
||||
|
||||
waitsFor ->
|
||||
buffer1.loaded and buffer2.loaded
|
||||
|
||||
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
|
||||
+104
-134
@@ -9,41 +9,41 @@ describe 'TextBuffer', ->
|
||||
|
||||
beforeEach ->
|
||||
filePath = require.resolve('./fixtures/sample.js')
|
||||
fileContents = fs.read(filePath)
|
||||
buffer = project.bufferForPathSync(filePath)
|
||||
fileContents = fs.readFileSync(filePath, 'utf8')
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
|
||||
afterEach ->
|
||||
buffer?.release()
|
||||
buffer?.destroy()
|
||||
|
||||
describe 'constructor', ->
|
||||
beforeEach ->
|
||||
buffer.release()
|
||||
buffer.destroy()
|
||||
buffer = null
|
||||
|
||||
describe "when given a path", ->
|
||||
describe "when a file exists for the path", ->
|
||||
it "loads the contents of that file", ->
|
||||
filePath = require.resolve './fixtures/sample.txt'
|
||||
buffer = project.bufferForPathSync(filePath)
|
||||
expect(buffer.getText()).toBe fs.read(filePath)
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
expect(buffer.getText()).toBe fs.readFileSync(filePath, 'utf8')
|
||||
|
||||
it "does not allow the initial state of the buffer to be undone", ->
|
||||
filePath = require.resolve './fixtures/sample.txt'
|
||||
buffer = project.bufferForPathSync(filePath)
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
buffer.undo()
|
||||
expect(buffer.getText()).toBe fs.read(filePath)
|
||||
expect(buffer.getText()).toBe fs.readFileSync(filePath, 'utf8')
|
||||
|
||||
describe "when no file exists for the path", ->
|
||||
it "is modified and is initially empty", ->
|
||||
filePath = "does-not-exist.txt"
|
||||
expect(fs.exists(filePath)).toBeFalsy()
|
||||
buffer = project.bufferForPathSync(filePath)
|
||||
expect(fs.existsSync(filePath)).toBeFalsy()
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
expect(buffer.isModified()).toBeTruthy()
|
||||
expect(buffer.getText()).toBe ''
|
||||
|
||||
describe "when no path is given", ->
|
||||
it "creates an empty buffer", ->
|
||||
buffer = project.bufferForPathSync(null)
|
||||
buffer = atom.project.bufferForPathSync(null)
|
||||
expect(buffer .getText()).toBe ""
|
||||
|
||||
describe "path-changed event", ->
|
||||
@@ -52,15 +52,15 @@ describe 'TextBuffer', ->
|
||||
beforeEach ->
|
||||
filePath = path.join(__dirname, "fixtures", "atom-manipulate-me")
|
||||
newPath = "#{filePath}-i-moved"
|
||||
fs.writeSync(filePath, "")
|
||||
bufferToChange = project.bufferForPathSync(filePath)
|
||||
fs.writeFileSync(filePath, "")
|
||||
bufferToChange = atom.project.bufferForPathSync(filePath)
|
||||
eventHandler = jasmine.createSpy('eventHandler')
|
||||
bufferToChange.on 'path-changed', eventHandler
|
||||
|
||||
afterEach ->
|
||||
bufferToChange.destroy()
|
||||
fs.remove(filePath) if fs.exists(filePath)
|
||||
fs.remove(newPath) if fs.exists(newPath)
|
||||
fs.removeSync(filePath) if fs.existsSync(filePath)
|
||||
fs.removeSync(newPath) if fs.existsSync(newPath)
|
||||
|
||||
it "triggers a `path-changed` event when path is changed", ->
|
||||
bufferToChange.saveAs(newPath)
|
||||
@@ -69,8 +69,8 @@ describe 'TextBuffer', ->
|
||||
it "triggers a `path-changed` event when the file is moved", ->
|
||||
jasmine.unspy(window, "setTimeout")
|
||||
|
||||
fs.remove(newPath) if fs.exists(newPath)
|
||||
fs.move(filePath, newPath)
|
||||
fs.removeSync(newPath) if fs.existsSync(newPath)
|
||||
fs.moveSync(filePath, newPath)
|
||||
|
||||
waitsFor "buffer path change", ->
|
||||
eventHandler.callCount > 0
|
||||
@@ -84,8 +84,8 @@ describe 'TextBuffer', ->
|
||||
beforeEach ->
|
||||
buffer.release()
|
||||
filePath = temp.openSync('atom').path
|
||||
fs.writeSync(filePath, "first")
|
||||
buffer = project.bufferForPathSync(filePath).retain()
|
||||
fs.writeFileSync(filePath, "first")
|
||||
buffer = atom.project.bufferForPathSync(filePath).retain()
|
||||
|
||||
afterEach ->
|
||||
buffer.release()
|
||||
@@ -105,7 +105,7 @@ describe 'TextBuffer', ->
|
||||
it "changes the memory contents of the buffer to match the new disk contents and triggers a 'changed' event", ->
|
||||
changeHandler = jasmine.createSpy('changeHandler')
|
||||
buffer.on 'changed', changeHandler
|
||||
fs.writeSync(filePath, "second")
|
||||
fs.writeFileSync(filePath, "second")
|
||||
|
||||
expect(changeHandler.callCount).toBe 0
|
||||
waitsFor "file to trigger change event", ->
|
||||
@@ -125,7 +125,7 @@ describe 'TextBuffer', ->
|
||||
buffer.file.on 'contents-changed', fileChangeHandler
|
||||
|
||||
buffer.insert([0, 0], "a change")
|
||||
fs.writeSync(filePath, "second")
|
||||
fs.writeFileSync(filePath, "second")
|
||||
|
||||
expect(fileChangeHandler.callCount).toBe 0
|
||||
waitsFor "file to trigger 'contents-changed' event", ->
|
||||
@@ -140,7 +140,7 @@ describe 'TextBuffer', ->
|
||||
buffer.insert([0, 0], "a second change")
|
||||
|
||||
handler = jasmine.createSpy('fileChange')
|
||||
fs.writeSync(filePath, "a disk change")
|
||||
fs.writeFileSync(filePath, "a disk change")
|
||||
buffer.on 'contents-conflicted', handler
|
||||
|
||||
expect(handler.callCount).toBe 0
|
||||
@@ -155,8 +155,8 @@ describe 'TextBuffer', ->
|
||||
|
||||
beforeEach ->
|
||||
filePath = path.join(temp.dir, 'atom-file-to-delete.txt')
|
||||
fs.writeSync(filePath, 'delete me')
|
||||
bufferToDelete = project.bufferForPathSync(filePath)
|
||||
fs.writeFileSync(filePath, 'delete me')
|
||||
bufferToDelete = atom.project.bufferForPathSync(filePath)
|
||||
filePath = bufferToDelete.getPath() # symlinks may have been converted
|
||||
|
||||
expect(bufferToDelete.getPath()).toBe filePath
|
||||
@@ -164,7 +164,7 @@ describe 'TextBuffer', ->
|
||||
|
||||
removeHandler = jasmine.createSpy('removeHandler')
|
||||
bufferToDelete.file.on 'removed', removeHandler
|
||||
fs.remove(filePath)
|
||||
fs.removeSync(filePath)
|
||||
waitsFor "file to be removed", ->
|
||||
removeHandler.callCount > 0
|
||||
|
||||
@@ -177,10 +177,10 @@ describe 'TextBuffer', ->
|
||||
|
||||
it "resumes watching of the file when it is re-saved", ->
|
||||
bufferToDelete.save()
|
||||
expect(fs.exists(bufferToDelete.getPath())).toBeTruthy()
|
||||
expect(fs.existsSync(bufferToDelete.getPath())).toBeTruthy()
|
||||
expect(bufferToDelete.isInConflict()).toBeFalsy()
|
||||
|
||||
fs.writeSync(filePath, 'moo')
|
||||
fs.writeFileSync(filePath, 'moo')
|
||||
|
||||
changeHandler = jasmine.createSpy('changeHandler')
|
||||
bufferToDelete.on 'changed', changeHandler
|
||||
@@ -213,21 +213,21 @@ describe 'TextBuffer', ->
|
||||
it "reports the modified status changing to true after the underlying file is deleted", ->
|
||||
buffer.release()
|
||||
filePath = path.join(temp.dir, 'atom-tmp-file')
|
||||
fs.writeSync(filePath, 'delete me')
|
||||
buffer = project.bufferForPathSync(filePath)
|
||||
fs.writeFileSync(filePath, 'delete me')
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
modifiedHandler = jasmine.createSpy("modifiedHandler")
|
||||
buffer.on 'modified-status-changed', modifiedHandler
|
||||
|
||||
fs.remove(filePath)
|
||||
fs.removeSync(filePath)
|
||||
|
||||
waitsFor "modified status to change", -> modifiedHandler.callCount
|
||||
runs -> expect(buffer.isModified()).toBe true
|
||||
|
||||
it "reports the modified status changing to false after a modified buffer is saved", ->
|
||||
filePath = path.join(temp.dir, 'atom-tmp-file')
|
||||
fs.writeSync(filePath, '')
|
||||
fs.writeFileSync(filePath, '')
|
||||
buffer.release()
|
||||
buffer = project.bufferForPathSync(filePath)
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
modifiedHandler = jasmine.createSpy("modifiedHandler")
|
||||
buffer.on 'modified-status-changed', modifiedHandler
|
||||
|
||||
@@ -249,9 +249,9 @@ describe 'TextBuffer', ->
|
||||
|
||||
it "reports the modified status changing to false after a modified buffer is reloaded", ->
|
||||
filePath = path.join(temp.dir, 'atom-tmp-file')
|
||||
fs.writeSync(filePath, '')
|
||||
fs.writeFileSync(filePath, '')
|
||||
buffer.release()
|
||||
buffer = project.bufferForPathSync(filePath)
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
modifiedHandler = jasmine.createSpy("modifiedHandler")
|
||||
buffer.on 'modified-status-changed', modifiedHandler
|
||||
|
||||
@@ -272,10 +272,10 @@ describe 'TextBuffer', ->
|
||||
|
||||
it "reports the modified status changing to false after a buffer to a non-existent file is saved", ->
|
||||
filePath = path.join(temp.dir, 'atom-tmp-file')
|
||||
fs.remove(filePath) if fs.exists(filePath)
|
||||
expect(fs.exists(filePath)).toBeFalsy()
|
||||
fs.removeSync(filePath) if fs.existsSync(filePath)
|
||||
expect(fs.existsSync(filePath)).toBeFalsy()
|
||||
buffer.release()
|
||||
buffer = project.bufferForPathSync(filePath)
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
modifiedHandler = jasmine.createSpy("modifiedHandler")
|
||||
buffer.on 'modified-status-changed', modifiedHandler
|
||||
|
||||
@@ -285,7 +285,7 @@ describe 'TextBuffer', ->
|
||||
modifiedHandler.reset()
|
||||
|
||||
buffer.save()
|
||||
expect(fs.exists(filePath)).toBeTruthy()
|
||||
expect(fs.existsSync(filePath)).toBeTruthy()
|
||||
|
||||
expect(modifiedHandler).toHaveBeenCalledWith(false)
|
||||
expect(buffer.isModified()).toBe false
|
||||
@@ -298,12 +298,12 @@ describe 'TextBuffer', ->
|
||||
|
||||
it "returns false for an empty buffer with no path", ->
|
||||
buffer.release()
|
||||
buffer = project.bufferForPathSync(null)
|
||||
buffer = atom.project.bufferForPathSync(null)
|
||||
expect(buffer.isModified()).toBeFalsy()
|
||||
|
||||
it "returns true for a non-empty buffer with no path", ->
|
||||
buffer.release()
|
||||
buffer = project.bufferForPathSync(null)
|
||||
buffer = atom.project.bufferForPathSync(null)
|
||||
buffer.setText('a')
|
||||
expect(buffer.isModified()).toBeTruthy()
|
||||
buffer.setText('\n')
|
||||
@@ -311,8 +311,8 @@ describe 'TextBuffer', ->
|
||||
|
||||
it "returns false until the buffer is fully loaded", ->
|
||||
buffer.release()
|
||||
filePath = temp.openSync('atom').path
|
||||
buffer = new TextBuffer({project, filePath})
|
||||
buffer = new TextBuffer({filePath: temp.openSync('atom').path})
|
||||
atom.project.addBuffer(buffer)
|
||||
|
||||
expect(buffer.isModified()).toBeFalsy()
|
||||
|
||||
@@ -432,7 +432,7 @@ describe 'TextBuffer', ->
|
||||
expect(event.newText).toBe "foo\nbar"
|
||||
|
||||
it "allows a 'changed' event handler to safely undo the change", ->
|
||||
buffer.one 'changed', -> buffer.undo()
|
||||
buffer.once 'changed', -> buffer.undo()
|
||||
buffer.change([0, 0], "hello")
|
||||
expect(buffer.lineForRow(0)).toBe "var quicksort = function () {"
|
||||
|
||||
@@ -465,16 +465,16 @@ describe 'TextBuffer', ->
|
||||
|
||||
beforeEach ->
|
||||
filePath = path.join(temp.dir, 'temp.txt')
|
||||
fs.writeSync(filePath, "")
|
||||
saveBuffer = project.bufferForPathSync(filePath)
|
||||
fs.writeFileSync(filePath, "")
|
||||
saveBuffer = atom.project.bufferForPathSync(filePath)
|
||||
saveBuffer.setText("blah")
|
||||
|
||||
it "saves the contents of the buffer to the path", ->
|
||||
saveBuffer.setText 'Buffer contents!'
|
||||
saveBuffer.save()
|
||||
expect(fs.read(filePath)).toEqual 'Buffer contents!'
|
||||
expect(fs.readFileSync(filePath, 'utf8')).toEqual 'Buffer contents!'
|
||||
|
||||
it "fires will-be-saved and saved events around the call to fs.writeSync", ->
|
||||
it "fires will-be-saved and saved events around the call to fs.writeFileSync", ->
|
||||
events = []
|
||||
beforeSave1 = -> events.push('beforeSave1')
|
||||
beforeSave2 = -> events.push('beforeSave2')
|
||||
@@ -483,12 +483,12 @@ describe 'TextBuffer', ->
|
||||
|
||||
saveBuffer.on 'will-be-saved', beforeSave1
|
||||
saveBuffer.on 'will-be-saved', beforeSave2
|
||||
spyOn(fs, 'writeSync').andCallFake -> events.push 'fs.writeSync'
|
||||
spyOn(fs, 'writeFileSync').andCallFake -> events.push 'fs.writeFileSync'
|
||||
saveBuffer.on 'saved', afterSave1
|
||||
saveBuffer.on 'saved', afterSave2
|
||||
|
||||
saveBuffer.save()
|
||||
expect(events).toEqual ['beforeSave1', 'beforeSave2', 'fs.writeSync', 'afterSave1', 'afterSave2']
|
||||
expect(events).toEqual ['beforeSave1', 'beforeSave2', 'fs.writeFileSync', 'afterSave1', 'afterSave2']
|
||||
|
||||
it "fires will-reload and reloaded events when reloaded", ->
|
||||
events = []
|
||||
@@ -500,7 +500,7 @@ describe 'TextBuffer', ->
|
||||
|
||||
describe "when the buffer has no path", ->
|
||||
it "throws an exception", ->
|
||||
saveBuffer = project.bufferForPathSync(null)
|
||||
saveBuffer = atom.project.bufferForPathSync(null)
|
||||
saveBuffer.setText "hi"
|
||||
expect(-> saveBuffer.save()).toThrow()
|
||||
|
||||
@@ -522,67 +522,38 @@ describe 'TextBuffer', ->
|
||||
|
||||
it "saves the contents of the buffer to the path", ->
|
||||
filePath = path.join(temp.dir, 'temp.txt')
|
||||
fs.remove filePath if fs.exists(filePath)
|
||||
fs.removeSync filePath if fs.existsSync(filePath)
|
||||
|
||||
saveAsBuffer = project.bufferForPathSync(null).retain()
|
||||
saveAsBuffer = atom.project.bufferForPathSync(null).retain()
|
||||
eventHandler = jasmine.createSpy('eventHandler')
|
||||
saveAsBuffer.on 'path-changed', eventHandler
|
||||
|
||||
saveAsBuffer.setText 'Buffer contents!'
|
||||
saveAsBuffer.saveAs(filePath)
|
||||
expect(fs.read(filePath)).toEqual 'Buffer contents!'
|
||||
expect(fs.readFileSync(filePath, 'utf8')).toEqual 'Buffer contents!'
|
||||
|
||||
expect(eventHandler).toHaveBeenCalledWith(saveAsBuffer)
|
||||
|
||||
it "stops listening to events on previous path and begins listening to events on new path", ->
|
||||
originalPath = path.join(temp.dir, 'original.txt')
|
||||
newPath = path.join(temp.dir, 'new.txt')
|
||||
fs.writeSync(originalPath, "")
|
||||
fs.writeFileSync(originalPath, "")
|
||||
|
||||
saveAsBuffer = project.bufferForPathSync(originalPath).retain()
|
||||
saveAsBuffer = atom.project.bufferForPathSync(originalPath).retain()
|
||||
changeHandler = jasmine.createSpy('changeHandler')
|
||||
saveAsBuffer.on 'changed', changeHandler
|
||||
saveAsBuffer.saveAs(newPath)
|
||||
expect(changeHandler).not.toHaveBeenCalled()
|
||||
|
||||
fs.writeSync(originalPath, "should not trigger buffer event")
|
||||
fs.writeFileSync(originalPath, "should not trigger buffer event")
|
||||
waits 20
|
||||
runs ->
|
||||
expect(changeHandler).not.toHaveBeenCalled()
|
||||
fs.writeSync(newPath, "should trigger buffer event")
|
||||
fs.writeFileSync(newPath, "should trigger buffer event")
|
||||
|
||||
waitsFor ->
|
||||
changeHandler.callCount > 0
|
||||
|
||||
describe ".getRelativePath()", ->
|
||||
[filePath, newPath, bufferToChange, eventHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
filePath = path.join(__dirname, "fixtures", "atom-manipulate-me")
|
||||
newPath = "#{filePath}-i-moved"
|
||||
fs.writeSync(filePath, "")
|
||||
bufferToChange = project.bufferForPathSync(filePath)
|
||||
eventHandler = jasmine.createSpy('eventHandler')
|
||||
bufferToChange.on 'path-changed', eventHandler
|
||||
|
||||
afterEach ->
|
||||
bufferToChange.destroy()
|
||||
fs.remove(filePath) if fs.exists(filePath)
|
||||
fs.remove(newPath) if fs.exists(newPath)
|
||||
|
||||
it "updates when the text buffer's file is moved", ->
|
||||
expect(bufferToChange.getRelativePath()).toBe('atom-manipulate-me')
|
||||
|
||||
jasmine.unspy(window, "setTimeout")
|
||||
eventHandler.reset()
|
||||
fs.move(filePath, newPath)
|
||||
|
||||
waitsFor "buffer path change", ->
|
||||
eventHandler.callCount > 0
|
||||
|
||||
runs ->
|
||||
expect(bufferToChange.getRelativePath()).toBe('atom-manipulate-me-i-moved')
|
||||
|
||||
describe ".getTextInRange(range)", ->
|
||||
describe "when range is empty", ->
|
||||
it "returns an empty string", ->
|
||||
@@ -926,22 +897,28 @@ describe 'TextBuffer', ->
|
||||
expect(buffer.getText()).toBe "\ninitialtexthello\n1\n2\n"
|
||||
|
||||
describe "serialization", ->
|
||||
buffer2 = null
|
||||
[buffer2, project2] = []
|
||||
|
||||
beforeEach ->
|
||||
buffer.destroy()
|
||||
|
||||
filePath = temp.openSync('atom').path
|
||||
fs.writeFileSync(filePath, "words")
|
||||
buffer = atom.project.bufferForPathSync(filePath)
|
||||
|
||||
afterEach ->
|
||||
buffer2?.release()
|
||||
project2?.destroy()
|
||||
|
||||
describe "when the serialized buffer had no unsaved changes", ->
|
||||
it "loads the current contents of the file at the serialized path", ->
|
||||
expect(buffer.isModified()).toBeFalsy()
|
||||
|
||||
state = buffer.serialize()
|
||||
state.get('text').insert([0, 0], 'simulate divergence of on-disk contents from serialized contents')
|
||||
project2 = atom.replicate().get('project')
|
||||
buffer2 = project2.getBuffers()[0]
|
||||
|
||||
buffer2 = deserialize(state, {project})
|
||||
|
||||
waitsFor ->
|
||||
buffer2.cachedDiskContents
|
||||
waitsForPromise ->
|
||||
buffer2.load()
|
||||
|
||||
runs ->
|
||||
expect(buffer2.isModified()).toBeFalsy()
|
||||
@@ -949,60 +926,53 @@ describe 'TextBuffer', ->
|
||||
expect(buffer2.getText()).toBe(buffer.getText())
|
||||
|
||||
describe "when the serialized buffer had unsaved changes", ->
|
||||
it "restores the previous unsaved state of the buffer", ->
|
||||
previousText = buffer.getText()
|
||||
buffer.setText("abc")
|
||||
describe "when the disk contents were changed since serialization", ->
|
||||
it "loads the disk contents instead of the previous unsaved state", ->
|
||||
buffer.setText("BUFFER CHANGE")
|
||||
fs.writeFileSync(filePath, "DISK CHANGE")
|
||||
|
||||
state = buffer.serialize()
|
||||
expect(state.getObject('text')).toBe 'abc'
|
||||
project2 = atom.replicate().get('project')
|
||||
buffer2 = project2.getBuffers()[0]
|
||||
|
||||
buffer2 = deserialize(state, {project})
|
||||
waitsFor ->
|
||||
buffer2.cachedDiskContents
|
||||
|
||||
waitsFor ->
|
||||
buffer2.cachedDiskContents
|
||||
runs ->
|
||||
expect(buffer2.getPath()).toBe(buffer.getPath())
|
||||
expect(buffer2.getText()).toBe("DISK CHANGE")
|
||||
expect(buffer2.isModified()).toBeFalsy()
|
||||
|
||||
runs ->
|
||||
expect(buffer2.getPath()).toBe(buffer.getPath())
|
||||
expect(buffer2.getText()).toBe(buffer.getText())
|
||||
expect(buffer2.isModified()).toBeTruthy()
|
||||
buffer2.setText(previousText)
|
||||
expect(buffer2.isModified()).toBeFalsy()
|
||||
describe "when the disk contents are the same since serialization", ->
|
||||
it "restores the previous unsaved state of the buffer", ->
|
||||
previousText = buffer.getText()
|
||||
buffer.setText("abc")
|
||||
buffer.retain()
|
||||
|
||||
buffer.getState().serializeForPersistence()
|
||||
project2 = atom.replicate().get('project')
|
||||
buffer2 = project2.getBuffers()[0]
|
||||
|
||||
waitsForPromise ->
|
||||
buffer2.load()
|
||||
|
||||
runs ->
|
||||
expect(buffer2.getPath()).toBe(buffer.getPath())
|
||||
expect(buffer2.getText()).toBe(buffer.getText())
|
||||
expect(buffer2.isModified()).toBeTruthy()
|
||||
buffer2.setText(previousText)
|
||||
expect(buffer2.isModified()).toBeFalsy()
|
||||
|
||||
describe "when the serialized buffer was unsaved and had no path", ->
|
||||
it "restores the previous unsaved state of the buffer", ->
|
||||
buffer.release()
|
||||
|
||||
buffer = project.bufferForPathSync()
|
||||
buffer = atom.project.bufferForPathSync()
|
||||
buffer.setText("abc")
|
||||
|
||||
state = buffer.serialize()
|
||||
state = buffer.getState().clone()
|
||||
expect(state.get('path')).toBeUndefined()
|
||||
expect(state.getObject('text')).toBe 'abc'
|
||||
|
||||
buffer2 = deserialize(state)
|
||||
buffer2 = atom.project.addBuffer(new TextBuffer(state))
|
||||
expect(buffer2.getPath()).toBeUndefined()
|
||||
expect(buffer2.getText()).toBe("abc")
|
||||
|
||||
describe "when the buffer has remote markers", ->
|
||||
[buffer2, buffer3] = []
|
||||
|
||||
afterEach ->
|
||||
buffer2.destroy()
|
||||
buffer3.destroy()
|
||||
|
||||
it "does not include them in the serialized state", ->
|
||||
doc1 = buffer.getState()
|
||||
doc2 = doc1.clone(new Site(2))
|
||||
doc1.connect(doc2)
|
||||
buffer2 = deserialize(doc2, {project})
|
||||
|
||||
buffer.markPosition [1, 0]
|
||||
buffer2.markPosition [2, 0]
|
||||
expect(buffer.getMarkerCount()).toBe 2
|
||||
expect(buffer.getMarkers()[0].isRemote()).toBe false
|
||||
expect(buffer.getMarkers()[1].isRemote()).toBe true
|
||||
|
||||
buffer3 = deserialize(buffer.serialize(), {project})
|
||||
expect(buffer3.getMarkerCount()).toBe 1
|
||||
expect(buffer3.getMarkers()[0].isRemote()).toBe false
|
||||
expect(buffer3.getMarkers()[0].getRange()).toEqual [[1, 0], [1, 0]]
|
||||
|
||||
@@ -6,14 +6,14 @@ describe "TextMateGrammar", ->
|
||||
grammar = null
|
||||
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-text', sync: true)
|
||||
atom.activatePackage('language-javascript', sync: true)
|
||||
atom.activatePackage('language-coffee-script', sync: true)
|
||||
atom.activatePackage('language-ruby', sync: true)
|
||||
atom.activatePackage('language-html', sync: true)
|
||||
atom.activatePackage('language-php', sync: true)
|
||||
atom.activatePackage('language-python', sync: true)
|
||||
grammar = syntax.selectGrammar("hello.coffee")
|
||||
atom.packages.activatePackage('language-text', sync: true)
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
atom.packages.activatePackage('language-coffee-script', sync: true)
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
atom.packages.activatePackage('language-html', sync: true)
|
||||
atom.packages.activatePackage('language-php', sync: true)
|
||||
atom.packages.activatePackage('language-python', sync: true)
|
||||
grammar = atom.syntax.selectGrammar("hello.coffee")
|
||||
|
||||
describe "@loadSync(path)", ->
|
||||
it "loads grammars from plists", ->
|
||||
@@ -49,7 +49,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the line doesn't match any patterns", ->
|
||||
it "returns the entire line as a single simple token with the grammar's scope", ->
|
||||
textGrammar = syntax.selectGrammar('foo.txt')
|
||||
textGrammar = atom.syntax.selectGrammar('foo.txt')
|
||||
{tokens} = textGrammar.tokenizeLine("abc def")
|
||||
expect(tokens.length).toBe 1
|
||||
|
||||
@@ -126,20 +126,20 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the line matches no patterns", ->
|
||||
it "does not infinitely loop", ->
|
||||
grammar = syntax.selectGrammar("sample.txt")
|
||||
grammar = atom.syntax.selectGrammar("sample.txt")
|
||||
{tokens} = grammar.tokenizeLine('hoo')
|
||||
expect(tokens.length).toBe 1
|
||||
expect(tokens[0]).toEqual value: 'hoo', scopes: ["text.plain", "meta.paragraph.text"]
|
||||
|
||||
describe "when the line matches a pattern with a 'contentName'", ->
|
||||
it "creates tokens using the content of contentName as the token name", ->
|
||||
grammar = syntax.selectGrammar("sample.txt")
|
||||
grammar = atom.syntax.selectGrammar("sample.txt")
|
||||
{tokens} = grammar.tokenizeLine('ok, cool')
|
||||
expect(tokens[0]).toEqual value: 'ok, cool', scopes: ["text.plain", "meta.paragraph.text"]
|
||||
|
||||
describe "when the line matches a pattern with no `name` or `contentName`", ->
|
||||
it "creates tokens without adding a new scope", ->
|
||||
grammar = syntax.selectGrammar('foo.rb')
|
||||
grammar = atom.syntax.selectGrammar('foo.rb')
|
||||
{tokens} = grammar.tokenizeLine('%w|oh \\look|')
|
||||
expect(tokens.length).toBe 5
|
||||
expect(tokens[0]).toEqual value: '%w|', scopes: ["source.ruby", "string.quoted.other.literal.lower.ruby", "punctuation.definition.string.begin.ruby"]
|
||||
@@ -184,7 +184,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the end pattern contains a back reference", ->
|
||||
it "constructs the end rule based on its back-references to captures in the begin rule", ->
|
||||
grammar = syntax.selectGrammar('foo.rb')
|
||||
grammar = atom.syntax.selectGrammar('foo.rb')
|
||||
{tokens} = grammar.tokenizeLine('%w|oh|,')
|
||||
expect(tokens.length).toBe 4
|
||||
expect(tokens[0]).toEqual value: '%w|', scopes: ["source.ruby", "string.quoted.other.literal.lower.ruby", "punctuation.definition.string.begin.ruby"]
|
||||
@@ -193,7 +193,7 @@ describe "TextMateGrammar", ->
|
||||
expect(tokens[3]).toEqual value: ',', scopes: ["source.ruby", "punctuation.separator.object.ruby"]
|
||||
|
||||
it "allows the rule containing that end pattern to be pushed to the stack multiple times", ->
|
||||
grammar = syntax.selectGrammar('foo.rb')
|
||||
grammar = atom.syntax.selectGrammar('foo.rb')
|
||||
{tokens} = grammar.tokenizeLine('%Q+matz had some #{%Q-crazy ideas-} for ruby syntax+ # damn.')
|
||||
expect(tokens[0]).toEqual value: '%Q+', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby","punctuation.definition.string.begin.ruby"]
|
||||
expect(tokens[1]).toEqual value: 'matz had some ', scopes: ["source.ruby","string.quoted.other.literal.upper.ruby"]
|
||||
@@ -211,10 +211,10 @@ describe "TextMateGrammar", ->
|
||||
describe "when the pattern includes rules from another grammar", ->
|
||||
describe "when a grammar matching the desired scope is available", ->
|
||||
it "parses tokens inside the begin/end patterns based on the included grammar's rules", ->
|
||||
atom.activatePackage('language-html', sync: true)
|
||||
atom.activatePackage('language-ruby-on-rails', sync: true)
|
||||
atom.packages.activatePackage('language-html', sync: true)
|
||||
atom.packages.activatePackage('language-ruby-on-rails', sync: true)
|
||||
|
||||
grammar = syntax.grammarForScopeName('text.html.ruby')
|
||||
grammar = atom.syntax.grammarForScopeName('text.html.ruby')
|
||||
{tokens} = grammar.tokenizeLine("<div class='name'><%= User.find(2).full_name %></div>")
|
||||
|
||||
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"]
|
||||
@@ -242,17 +242,17 @@ describe "TextMateGrammar", ->
|
||||
expect(tokens[22]).toEqual value: '>', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.end.html"]
|
||||
|
||||
it "updates the grammar if the included grammar is updated later", ->
|
||||
atom.activatePackage('language-html', sync: true)
|
||||
atom.activatePackage('language-ruby-on-rails', sync: true)
|
||||
atom.packages.activatePackage('language-html', sync: true)
|
||||
atom.packages.activatePackage('language-ruby-on-rails', sync: true)
|
||||
|
||||
grammar = syntax.selectGrammar('foo.html.erb')
|
||||
grammar = atom.syntax.selectGrammar('foo.html.erb')
|
||||
grammarUpdatedHandler = jasmine.createSpy("grammarUpdatedHandler")
|
||||
grammar.on 'grammar-updated', grammarUpdatedHandler
|
||||
|
||||
{tokens} = grammar.tokenizeLine("<div class='name'><% <<-SQL select * from users;")
|
||||
expect(tokens[12].value).toBe " select * from users;"
|
||||
|
||||
atom.activatePackage('language-sql', sync: true)
|
||||
atom.packages.activatePackage('language-sql', sync: true)
|
||||
expect(grammarUpdatedHandler).toHaveBeenCalled()
|
||||
{tokens} = grammar.tokenizeLine("<div class='name'><% <<-SQL select * from users;")
|
||||
expect(tokens[12].value).toBe " "
|
||||
@@ -260,17 +260,17 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when a grammar matching the desired scope is unavailable", ->
|
||||
it "updates the grammar if a matching grammar is added later", ->
|
||||
atom.deactivatePackage('language-html')
|
||||
atom.activatePackage('language-ruby-on-rails', sync: true)
|
||||
atom.packages.deactivatePackage('language-html')
|
||||
atom.packages.activatePackage('language-ruby-on-rails', sync: true)
|
||||
|
||||
grammar = syntax.grammarForScopeName('text.html.ruby')
|
||||
grammar = atom.syntax.grammarForScopeName('text.html.ruby')
|
||||
{tokens} = grammar.tokenizeLine("<div class='name'><%= User.find(2).full_name %></div>")
|
||||
expect(tokens[0]).toEqual value: "<div class='name'>", scopes: ["text.html.ruby"]
|
||||
expect(tokens[1]).toEqual value: '<%=', scopes: ["text.html.ruby","source.ruby.rails.embedded.html","punctuation.section.embedded.ruby"]
|
||||
expect(tokens[2]).toEqual value: ' ', scopes: ["text.html.ruby","source.ruby.rails.embedded.html"]
|
||||
expect(tokens[3]).toEqual value: 'User', scopes: ["text.html.ruby","source.ruby.rails.embedded.html","support.class.ruby"]
|
||||
|
||||
atom.activatePackage('language-html', sync: true)
|
||||
atom.packages.activatePackage('language-html', sync: true)
|
||||
{tokens} = grammar.tokenizeLine("<div class='name'><%= User.find(2).full_name %></div>")
|
||||
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"]
|
||||
expect(tokens[1]).toEqual value: 'div', scopes: ["text.html.ruby","meta.tag.block.any.html","entity.name.tag.block.any.html"]
|
||||
@@ -308,21 +308,21 @@ describe "TextMateGrammar", ->
|
||||
expect(tokens[1].value).toBe " a singleLineComment"
|
||||
|
||||
it "does not loop infinitely (regression)", ->
|
||||
grammar = syntax.selectGrammar("hello.js")
|
||||
grammar = atom.syntax.selectGrammar("hello.js")
|
||||
{tokens, ruleStack} = grammar.tokenizeLine("// line comment")
|
||||
{tokens, ruleStack} = grammar.tokenizeLine(" // second line comment with a single leading space", ruleStack)
|
||||
|
||||
describe "when inside a C block", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-c', sync: true)
|
||||
atom.packages.activatePackage('language-c', sync: true)
|
||||
|
||||
it "correctly parses a method. (regression)", ->
|
||||
grammar = syntax.selectGrammar("hello.c")
|
||||
grammar = atom.syntax.selectGrammar("hello.c")
|
||||
{tokens, ruleStack} = grammar.tokenizeLine("if(1){m()}")
|
||||
expect(tokens[5]).toEqual value: "m", scopes: ["source.c", "meta.block.c", "meta.function-call.c", "support.function.any-method.c"]
|
||||
|
||||
it "correctly parses nested blocks. (regression)", ->
|
||||
grammar = syntax.selectGrammar("hello.c")
|
||||
grammar = atom.syntax.selectGrammar("hello.c")
|
||||
{tokens, ruleStack} = grammar.tokenizeLine("if(1){if(1){m()}}")
|
||||
expect(tokens[5]).toEqual value: "if", scopes: ["source.c", "meta.block.c", "keyword.control.c"]
|
||||
expect(tokens[10]).toEqual value: "m", scopes: ["source.c", "meta.block.c", "meta.block.c", "meta.function-call.c", "support.function.any-method.c"]
|
||||
@@ -330,8 +330,8 @@ describe "TextMateGrammar", ->
|
||||
describe "when the grammar can infinitely loop over a line", ->
|
||||
it "aborts tokenization", ->
|
||||
spyOn(console, 'error')
|
||||
atom.activatePackage("package-with-infinite-loop-grammar")
|
||||
grammar = syntax.selectGrammar("something.package-with-infinite-loop-grammar")
|
||||
atom.packages.activatePackage("package-with-infinite-loop-grammar")
|
||||
grammar = atom.syntax.selectGrammar("something.package-with-infinite-loop-grammar")
|
||||
{tokens} = grammar.tokenizeLine("abc")
|
||||
expect(tokens[0].value).toBe "a"
|
||||
expect(tokens[1].value).toBe "bc"
|
||||
@@ -339,14 +339,14 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when a grammar has a pattern that has back references in the match value", ->
|
||||
it "does not special handle the back references and instead allows oniguruma to resolve them", ->
|
||||
atom.activatePackage('language-sass', sync: true)
|
||||
grammar = syntax.selectGrammar("style.scss")
|
||||
atom.packages.activatePackage('language-sass', sync: true)
|
||||
grammar = atom.syntax.selectGrammar("style.scss")
|
||||
{tokens} = grammar.tokenizeLine("@mixin x() { -moz-selector: whatever; }")
|
||||
expect(tokens[9]).toEqual value: "-moz-selector", scopes: ["source.css.scss", "meta.property-list.scss", "meta.property-name.scss"]
|
||||
|
||||
describe "when a line has more tokens than `maxTokensPerLine`", ->
|
||||
it "creates a final token with the remaining text and resets the ruleStack to match the begining of the line", ->
|
||||
grammar = syntax.selectGrammar("hello.js")
|
||||
grammar = atom.syntax.selectGrammar("hello.js")
|
||||
spyOn(grammar, 'getMaxTokensPerLine').andCallFake -> 5
|
||||
originalRuleStack = [grammar.initialRule, grammar.initialRule, grammar.initialRule]
|
||||
{tokens, ruleStack} = grammar.tokenizeLine("one(two(three(four(five(_param_)))))", originalRuleStack)
|
||||
@@ -356,7 +356,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when a grammar has a capture with patterns", ->
|
||||
it "matches the patterns and includes the scope specified as the pattern's match name", ->
|
||||
grammar = syntax.selectGrammar("hello.php")
|
||||
grammar = atom.syntax.selectGrammar("hello.php")
|
||||
{tokens} = grammar.tokenizeLine("<?php public final function meth() {} ?>")
|
||||
|
||||
expect(tokens[2].value).toBe "public"
|
||||
@@ -402,7 +402,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the grammar has injections", ->
|
||||
it "correctly includes the injected patterns when tokenizing", ->
|
||||
grammar = syntax.selectGrammar("hello.php")
|
||||
grammar = atom.syntax.selectGrammar("hello.php")
|
||||
{tokens} = grammar.tokenizeLine("<div><?php function hello() {} ?></div>")
|
||||
|
||||
expect(tokens[3].value).toBe "<?php"
|
||||
@@ -428,15 +428,15 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the grammar's pattern name has a group number in it", ->
|
||||
it "replaces the group number with the matched captured text", ->
|
||||
atom.activatePackage('language-hyperlink', sync: true)
|
||||
grammar = syntax.grammarForScopeName("text.hyperlink")
|
||||
atom.packages.activatePackage('language-hyperlink', sync: true)
|
||||
grammar = atom.syntax.grammarForScopeName("text.hyperlink")
|
||||
{tokens} = grammar.tokenizeLine("https://github.com")
|
||||
expect(tokens[0].scopes).toEqual ["text.hyperlink", "markup.underline.link.https.hyperlink"]
|
||||
|
||||
describe "when the grammar has an injection selector", ->
|
||||
it "includes the grammar's patterns when the selector matches the current scope in other grammars", ->
|
||||
atom.activatePackage('language-hyperlink', sync: true)
|
||||
grammar = syntax.selectGrammar("text.js")
|
||||
atom.packages.activatePackage('language-hyperlink', sync: true)
|
||||
grammar = atom.syntax.selectGrammar("text.js")
|
||||
{tokens} = grammar.tokenizeLine("var i; // http://github.com")
|
||||
|
||||
expect(tokens[0].value).toBe "var"
|
||||
@@ -447,29 +447,29 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the grammar is added", ->
|
||||
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
|
||||
editSession = project.openSync('sample.js')
|
||||
editSession.setText("// http://github.com")
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor.setText("// http://github.com")
|
||||
|
||||
{tokens} = editSession.lineForScreenRow(0)
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " http://github.com"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
atom.activatePackage('language-hyperlink', sync: true)
|
||||
atom.packages.activatePackage('language-hyperlink', sync: true)
|
||||
|
||||
{tokens} = editSession.lineForScreenRow(0)
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "http://github.com"
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
|
||||
describe "when the grammar is updated", ->
|
||||
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
|
||||
editSession = project.openSync('sample.js')
|
||||
editSession.setText("// SELECT * FROM OCTOCATS")
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor.setText("// SELECT * FROM OCTOCATS")
|
||||
|
||||
{tokens} = editSession.lineForScreenRow(0)
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
syntax.addGrammar(new TextMateGrammar(
|
||||
atom.syntax.addGrammar(new TextMateGrammar(
|
||||
name: "test"
|
||||
scopeName: "source.test"
|
||||
repository: {}
|
||||
@@ -477,13 +477,13 @@ describe "TextMateGrammar", ->
|
||||
patterns: [ { include: "source.sql" } ]
|
||||
))
|
||||
|
||||
{tokens} = editSession.lineForScreenRow(0)
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
|
||||
atom.activatePackage('language-sql', sync: true)
|
||||
atom.packages.activatePackage('language-sql', sync: true)
|
||||
|
||||
{tokens} = editSession.lineForScreenRow(0)
|
||||
{tokens} = editor.lineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "SELECT"
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "keyword.other.DML.sql"]
|
||||
|
||||
@@ -514,8 +514,8 @@ describe "TextMateGrammar", ->
|
||||
lines = null
|
||||
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-todo', sync: true)
|
||||
grammar = syntax.selectGrammar('main.rb')
|
||||
atom.packages.activatePackage('language-todo', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('main.rb')
|
||||
lines = grammar.tokenizeLines "# TODO be nicer"
|
||||
|
||||
it "replaces the number with the capture group and translates the text", ->
|
||||
@@ -528,8 +528,8 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "Git commit messages", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-git', sync: true)
|
||||
grammar = syntax.selectGrammar('COMMIT_EDITMSG')
|
||||
atom.packages.activatePackage('language-git', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('COMMIT_EDITMSG')
|
||||
lines = grammar.tokenizeLines """
|
||||
longggggggggggggggggggggggggggggggggggggggggggggggg
|
||||
# Please enter the commit message for your changes. Lines starting
|
||||
@@ -547,8 +547,8 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "C++", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-c', sync: true)
|
||||
grammar = syntax.selectGrammar('includes.cc')
|
||||
atom.packages.activatePackage('language-c', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('includes.cc')
|
||||
lines = grammar.tokenizeLines """
|
||||
#include "a.h"
|
||||
#include "b.h"
|
||||
@@ -570,7 +570,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "Ruby", ->
|
||||
beforeEach ->
|
||||
grammar = syntax.selectGrammar('hello.rb')
|
||||
grammar = atom.syntax.selectGrammar('hello.rb')
|
||||
lines = grammar.tokenizeLines """
|
||||
a = {
|
||||
"b" => "c",
|
||||
@@ -585,9 +585,9 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "Objective-C", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-c', sync: true)
|
||||
atom.activatePackage('language-objective-c', sync: true)
|
||||
grammar = syntax.selectGrammar('function.mm')
|
||||
atom.packages.activatePackage('language-c', sync: true)
|
||||
atom.packages.activatePackage('language-objective-c', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('function.mm')
|
||||
lines = grammar.tokenizeLines """
|
||||
void test() {
|
||||
NSString *a = @"a\\nb";
|
||||
@@ -612,8 +612,8 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "Java", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-java', sync: true)
|
||||
grammar = syntax.selectGrammar('Function.java')
|
||||
atom.packages.activatePackage('language-java', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('Function.java')
|
||||
|
||||
it "correctly parses single line comments", ->
|
||||
lines = grammar.tokenizeLines """
|
||||
@@ -636,7 +636,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "HTML (Ruby - ERB)", ->
|
||||
beforeEach ->
|
||||
grammar = syntax.selectGrammar('page.erb')
|
||||
grammar = atom.syntax.selectGrammar('page.erb')
|
||||
lines = grammar.tokenizeLines '<% page_title "My Page" %>'
|
||||
|
||||
it "correctly parses strings inside tags", ->
|
||||
@@ -652,7 +652,7 @@ describe "TextMateGrammar", ->
|
||||
describe "Unicode support", ->
|
||||
describe "Surrogate pair characters", ->
|
||||
beforeEach ->
|
||||
grammar = syntax.selectGrammar('main.js')
|
||||
grammar = atom.syntax.selectGrammar('main.js')
|
||||
lines = grammar.tokenizeLines "'\uD835\uDF97'"
|
||||
|
||||
it "correctly parses JavaScript strings containing surrogate pair characters", ->
|
||||
@@ -664,8 +664,8 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "when the line contains unicode characters", ->
|
||||
it "correctly parses tokens starting after them", ->
|
||||
atom.activatePackage('language-json', sync: true)
|
||||
grammar = syntax.selectGrammar('package.json')
|
||||
atom.packages.activatePackage('language-json', sync: true)
|
||||
grammar = atom.syntax.selectGrammar('package.json')
|
||||
{tokens} = grammar.tokenizeLine '{"\u2026": 1}'
|
||||
|
||||
expect(tokens.length).toBe 8
|
||||
@@ -674,7 +674,7 @@ describe "TextMateGrammar", ->
|
||||
|
||||
describe "python", ->
|
||||
it "parses import blocks correctly", ->
|
||||
grammar = syntax.selectGrammar("file.py")
|
||||
grammar = atom.syntax.selectGrammar("file.py")
|
||||
lines = grammar.tokenizeLines "import a\nimport b"
|
||||
|
||||
line1 = lines[0]
|
||||
|
||||
@@ -6,16 +6,18 @@ AtomPackage = require '../src/atom-package'
|
||||
|
||||
describe "ThemeManager", ->
|
||||
themeManager = null
|
||||
resourcePath = atom.getLoadSettings().resourcePath
|
||||
configDirPath = atom.getConfigDirPath()
|
||||
|
||||
beforeEach ->
|
||||
themeManager = new ThemeManager(atom.packages)
|
||||
themeManager = new ThemeManager({packageManager: atom.packages, resourcePath, configDirPath})
|
||||
|
||||
afterEach ->
|
||||
themeManager.deactivateThemes()
|
||||
|
||||
describe "theme getters and setters", ->
|
||||
beforeEach ->
|
||||
atom.packages.loadPackages()
|
||||
atom.packages.loadPackages(sync: true)
|
||||
|
||||
it 'getLoadedThemes get all the loaded themes', ->
|
||||
themes = themeManager.getLoadedThemes()
|
||||
@@ -30,7 +32,7 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "getImportPaths()", ->
|
||||
it "returns the theme directories before the themes are loaded", ->
|
||||
config.set('core.themes', ['atom-dark-syntax', 'atom-dark-ui', 'atom-light-ui'])
|
||||
atom.config.set('core.themes', ['atom-dark-syntax', 'atom-dark-ui', 'atom-light-ui'])
|
||||
|
||||
paths = themeManager.getImportPaths()
|
||||
|
||||
@@ -40,7 +42,7 @@ describe "ThemeManager", ->
|
||||
expect(paths[1]).toContain 'atom-light-ui'
|
||||
|
||||
it "ignores themes that cannot be resolved to a directory", ->
|
||||
config.set('core.themes', ['definitely-not-a-theme'])
|
||||
atom.config.set('core.themes', ['definitely-not-a-theme'])
|
||||
expect(-> themeManager.getImportPaths()).not.toThrow()
|
||||
|
||||
describe "when the core.themes config value changes", ->
|
||||
@@ -49,24 +51,24 @@ describe "ThemeManager", ->
|
||||
spyOn(themeManager, 'getUserStylesheetPath').andCallFake -> null
|
||||
themeManager.activateThemes()
|
||||
|
||||
config.set('core.themes', [])
|
||||
atom.config.set('core.themes', [])
|
||||
expect($('style.theme').length).toBe 0
|
||||
expect(reloadHandler).toHaveBeenCalled()
|
||||
|
||||
config.set('core.themes', ['atom-dark-syntax'])
|
||||
atom.config.set('core.themes', ['atom-dark-syntax'])
|
||||
expect($('style.theme').length).toBe 1
|
||||
expect($('style.theme:eq(0)').attr('id')).toMatch /atom-dark-syntax/
|
||||
|
||||
config.set('core.themes', ['atom-light-syntax', 'atom-dark-syntax'])
|
||||
atom.config.set('core.themes', ['atom-light-syntax', 'atom-dark-syntax'])
|
||||
expect($('style.theme').length).toBe 2
|
||||
expect($('style.theme:eq(0)').attr('id')).toMatch /atom-dark-syntax/
|
||||
expect($('style.theme:eq(1)').attr('id')).toMatch /atom-light-syntax/
|
||||
|
||||
config.set('core.themes', [])
|
||||
atom.config.set('core.themes', [])
|
||||
expect($('style.theme').length).toBe 0
|
||||
|
||||
# atom-dark-ui has an directory path, the syntax ones dont.
|
||||
config.set('core.themes', ['atom-light-syntax', 'atom-dark-ui', 'atom-dark-syntax'])
|
||||
atom.config.set('core.themes', ['atom-light-syntax', 'atom-dark-ui', 'atom-dark-syntax'])
|
||||
importPaths = themeManager.getImportPaths()
|
||||
expect(importPaths.length).toBe 1
|
||||
expect(importPaths[0]).toContain 'atom-dark-ui'
|
||||
@@ -78,15 +80,15 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "requireStylesheet(path)", ->
|
||||
it "synchronously loads css at the given path and installs a style tag for it in the head", ->
|
||||
cssPath = project.resolve('css.css')
|
||||
cssPath = atom.project.resolve('css.css')
|
||||
lengthBefore = $('head style').length
|
||||
|
||||
themeManager.requireStylesheet(cssPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
|
||||
element = $('head style[id*="css.css"]')
|
||||
expect(element.attr('id')).toBe cssPath
|
||||
expect(element.text()).toBe fs.read(cssPath)
|
||||
expect(element.attr('id')).toBe themeManager.stringToId(cssPath)
|
||||
expect(element.text()).toBe fs.readFileSync(cssPath, 'utf8')
|
||||
|
||||
# doesn't append twice
|
||||
themeManager.requireStylesheet(cssPath)
|
||||
@@ -95,13 +97,13 @@ describe "ThemeManager", ->
|
||||
$('head style[id*="css.css"]').remove()
|
||||
|
||||
it "synchronously loads and parses less files at the given path and installs a style tag for it in the head", ->
|
||||
lessPath = project.resolve('sample.less')
|
||||
lessPath = atom.project.resolve('sample.less')
|
||||
lengthBefore = $('head style').length
|
||||
themeManager.requireStylesheet(lessPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
|
||||
element = $('head style[id*="sample.less"]')
|
||||
expect(element.attr('id')).toBe lessPath
|
||||
expect(element.attr('id')).toBe themeManager.stringToId(lessPath)
|
||||
expect(element.text()).toBe """
|
||||
#header {
|
||||
color: #4d926f;
|
||||
@@ -119,9 +121,9 @@ describe "ThemeManager", ->
|
||||
|
||||
it "supports requiring css and less stylesheets without an explicit extension", ->
|
||||
themeManager.requireStylesheet path.join(__dirname, 'fixtures', 'css')
|
||||
expect($('head style[id*="css.css"]').attr('id')).toBe project.resolve('css.css')
|
||||
expect($('head style[id*="css.css"]').attr('id')).toBe themeManager.stringToId(atom.project.resolve('css.css'))
|
||||
themeManager.requireStylesheet path.join(__dirname, 'fixtures', 'sample')
|
||||
expect($('head style[id*="sample.less"]').attr('id')).toBe project.resolve('sample.less')
|
||||
expect($('head style[id*="sample.less"]').attr('id')).toBe themeManager.stringToId(atom.project.resolve('sample.less'))
|
||||
|
||||
$('head style[id*="css.css"]').remove()
|
||||
$('head style[id*="sample.less"]').remove()
|
||||
@@ -138,16 +140,16 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "base stylesheet loading", ->
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
rootView.append $$ -> @div class: 'editor'
|
||||
rootView.attachToDom()
|
||||
atom.rootView = new RootView
|
||||
atom.rootView.append $$ -> @div class: 'editor'
|
||||
atom.rootView.attachToDom()
|
||||
themeManager.activateThemes()
|
||||
|
||||
it "loads the correct values from the theme's ui-variables file", ->
|
||||
config.set('core.themes', ['theme-with-ui-variables'])
|
||||
atom.config.set('core.themes', ['theme-with-ui-variables'])
|
||||
|
||||
# an override loaded in the base css
|
||||
expect(rootView.css("background-color")).toBe "rgb(0, 0, 255)"
|
||||
expect(atom.rootView.css("background-color")).toBe "rgb(0, 0, 255)"
|
||||
|
||||
# from within the theme itself
|
||||
expect($(".editor").css("padding-top")).toBe "150px"
|
||||
|
||||
@@ -5,7 +5,7 @@ describe "TokenizedBuffer", ->
|
||||
[tokenizedBuffer, buffer, changeHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-javascript', sync: true)
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
# enable async tokenization
|
||||
TokenizedBuffer.prototype.chunkSize = 5
|
||||
jasmine.unspy(TokenizedBuffer.prototype, 'tokenizeInBackground')
|
||||
@@ -20,15 +20,15 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "@deserialize(state)", ->
|
||||
it "constructs a tokenized buffer with the same buffer and tabLength setting", ->
|
||||
buffer = project.bufferForPathSync('sample.js')
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer1 = new TokenizedBuffer(buffer: buffer, tabLength: 4)
|
||||
tokenizedBuffer2 = deserialize(tokenizedBuffer1.serialize())
|
||||
tokenizedBuffer2 = atom.deserializers.deserialize(tokenizedBuffer1.serialize())
|
||||
expect(tokenizedBuffer2.buffer).toBe tokenizedBuffer1.buffer
|
||||
expect(tokenizedBuffer2.getTabLength()).toBe tokenizedBuffer1.getTabLength()
|
||||
|
||||
describe "when the buffer is destroyed", ->
|
||||
beforeEach ->
|
||||
buffer = project.bufferForPathSync('sample.js')
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
|
||||
@@ -40,7 +40,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "when the buffer contains soft-tabs", ->
|
||||
beforeEach ->
|
||||
buffer = project.bufferForPathSync('sample.js')
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler')
|
||||
@@ -319,8 +319,8 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "when the buffer contains hard-tabs", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-coffee-script', sync: true)
|
||||
buffer = project.bufferForPathSync('sample-with-tabs.coffee')
|
||||
atom.packages.activatePackage('language-coffee-script', sync: true)
|
||||
buffer = atom.project.bufferForPathSync('sample-with-tabs.coffee')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
startTokenizing(tokenizedBuffer)
|
||||
|
||||
@@ -349,8 +349,9 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "when the buffer contains surrogate pairs", ->
|
||||
beforeEach ->
|
||||
atom.activatePackage('language-javascript', sync: true)
|
||||
buffer = project.buildBufferSync 'sample-with-pairs.js', """
|
||||
atom.packages.activatePackage('language-javascript', sync: true)
|
||||
buffer = atom.project.bufferForPathSync 'sample-with-pairs.js'
|
||||
buffer.setText """
|
||||
'abc\uD835\uDF97def'
|
||||
//\uD835\uDF97xyz
|
||||
"""
|
||||
@@ -386,18 +387,19 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "when the grammar is updated because a grammar it includes is activated", ->
|
||||
it "retokenizes the buffer", ->
|
||||
atom.activatePackage('language-ruby-on-rails', sync: true)
|
||||
atom.activatePackage('language-ruby', sync: true)
|
||||
atom.packages.activatePackage('language-ruby-on-rails', sync: true)
|
||||
atom.packages.activatePackage('language-ruby', sync: true)
|
||||
|
||||
buffer = project.bufferForPathSync(null, "<div class='name'><%= User.find(2).full_name %></div>")
|
||||
buffer = atom.project.bufferForPathSync()
|
||||
buffer.setText "<div class='name'><%= User.find(2).full_name %></div>"
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
tokenizedBuffer.setGrammar(syntax.selectGrammar('test.erb'))
|
||||
tokenizedBuffer.setGrammar(atom.syntax.selectGrammar('test.erb'))
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
{tokens} = tokenizedBuffer.lineForScreenRow(0)
|
||||
expect(tokens[0]).toEqual value: "<div class='name'>", scopes: ["text.html.ruby"]
|
||||
|
||||
atom.activatePackage('language-html', sync: true)
|
||||
atom.packages.activatePackage('language-html', sync: true)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
{tokens} = tokenizedBuffer.lineForScreenRow(0)
|
||||
expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"]
|
||||
@@ -408,7 +410,7 @@ describe "TokenizedBuffer", ->
|
||||
buffer.release()
|
||||
|
||||
it "returns the correct token (regression)", ->
|
||||
buffer = project.bufferForPathSync('sample.js')
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenForPosition([1,0]).scopes).toEqual ["source.js"]
|
||||
@@ -417,7 +419,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe ".bufferRangeForScopeAtPosition(selector, position)", ->
|
||||
beforeEach ->
|
||||
buffer = project.bufferForPathSync('sample.js')
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
|
||||
+28
-27
@@ -8,11 +8,11 @@ describe "Window", ->
|
||||
beforeEach ->
|
||||
spyOn(atom, 'hide')
|
||||
atom.getLoadSettings() # Causes atom.loadSettings to be initialized
|
||||
atom.loadSettings.initialPath = project.getPath()
|
||||
project.destroy()
|
||||
atom.loadSettings.initialPath = atom.project.getPath()
|
||||
atom.project.destroy()
|
||||
windowEventHandler = new WindowEventHandler()
|
||||
window.deserializeEditorWindow()
|
||||
projectPath = project.getPath()
|
||||
atom.deserializeEditorWindow()
|
||||
projectPath = atom.project.getPath()
|
||||
|
||||
afterEach ->
|
||||
windowEventHandler.unsubscribe()
|
||||
@@ -59,48 +59,49 @@ describe "Window", ->
|
||||
|
||||
describe "when pane items are are modified", ->
|
||||
it "prompts user to save and and calls rootView.confirmClose", ->
|
||||
spyOn(rootView, 'confirmClose').andCallThrough()
|
||||
spyOn(atom, "confirmSync").andReturn(2)
|
||||
editSession = rootView.openSync("sample.js")
|
||||
editSession.insertText("I look different, I feel different.")
|
||||
spyOn(atom.rootView, 'confirmClose').andCallThrough()
|
||||
spyOn(atom, "confirm").andReturn(2)
|
||||
editor = atom.rootView.openSync("sample.js")
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(rootView.confirmClose).toHaveBeenCalled()
|
||||
expect(atom.confirmSync).toHaveBeenCalled()
|
||||
expect(atom.rootView.confirmClose).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "prompts user to save and handler returns true if don't save", ->
|
||||
spyOn(atom, "confirmSync").andReturn(2)
|
||||
editSession = rootView.openSync("sample.js")
|
||||
editSession.insertText("I look different, I feel different.")
|
||||
spyOn(atom, "confirm").andReturn(2)
|
||||
editor = atom.rootView.openSync("sample.js")
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.confirmSync).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "prompts user to save and handler returns false if dialog is canceled", ->
|
||||
spyOn(atom, "confirmSync").andReturn(1)
|
||||
editSession = rootView.openSync("sample.js")
|
||||
editSession.insertText("I look different, I feel different.")
|
||||
spyOn(atom, "confirm").andReturn(1)
|
||||
editor = atom.rootView.openSync("sample.js")
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.confirmSync).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
describe ".unloadEditorWindow()", ->
|
||||
it "saves the serialized state of the window so it can be deserialized after reload", ->
|
||||
rootViewState = rootView.serialize()
|
||||
syntaxState = syntax.serialize()
|
||||
rootViewState = atom.rootView.serialize()
|
||||
syntaxState = atom.syntax.serialize()
|
||||
|
||||
window.unloadEditorWindow()
|
||||
atom.unloadEditorWindow()
|
||||
|
||||
expect(atom.getWindowState().getObject('rootView')).toEqual rootViewState.toObject()
|
||||
expect(atom.getWindowState().getObject('syntax')).toEqual syntaxState
|
||||
expect(atom.saveWindowState).toHaveBeenCalled()
|
||||
|
||||
it "unsubscribes from all buffers", ->
|
||||
rootView.openSync('sample.js')
|
||||
buffer = rootView.getActivePaneItem().buffer
|
||||
rootView.getActivePane().splitRight()
|
||||
expect(window.rootView.find('.editor').length).toBe 2
|
||||
atom.rootView.openSync('sample.js')
|
||||
buffer = atom.rootView.getActivePaneItem().buffer
|
||||
pane = atom.rootView.getActivePane()
|
||||
pane.splitRight(pane.copyActiveItem())
|
||||
expect(atom.rootView.find('.editor').length).toBe 2
|
||||
|
||||
window.unloadEditorWindow()
|
||||
atom.unloadEditorWindow()
|
||||
|
||||
expect(buffer.subscriptionCount()).toBe 0
|
||||
expect(buffer.getSubscriptionCount()).toBe 0
|
||||
|
||||
describe "drag and drop", ->
|
||||
buildDragEvent = (type, files) ->
|
||||
|
||||
+26
-26
@@ -1,6 +1,6 @@
|
||||
TextMateGrammar = require './text-mate-grammar'
|
||||
Package = require './package'
|
||||
fsUtils = require './fs-utils'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
{$} = require './space-pen-extensions'
|
||||
@@ -89,7 +89,7 @@ class AtomPackage extends Package
|
||||
|
||||
@requireMainModule()
|
||||
if @mainModule?
|
||||
config.setDefaults(@name, @mainModule.configDefaults)
|
||||
atom.config.setDefaults(@name, @mainModule.configDefaults)
|
||||
@mainModule.activateConfig?()
|
||||
@configActivated = true
|
||||
|
||||
@@ -105,9 +105,9 @@ class AtomPackage extends Package
|
||||
atom.keymap.add(keymapPath, map) for [keymapPath, map] in @keymaps
|
||||
atom.contextMenu.add(menuPath, map['context-menu']) for [menuPath, map] in @menus
|
||||
atom.menu.add(map.menu) for [menuPath, map] in @menus when map.menu
|
||||
syntax.addGrammar(grammar) for grammar in @grammars
|
||||
atom.syntax.addGrammar(grammar) for grammar in @grammars
|
||||
for [scopedPropertiesPath, selector, properties] in @scopedProperties
|
||||
syntax.addProperties(scopedPropertiesPath, selector, properties)
|
||||
atom.syntax.addProperties(scopedPropertiesPath, selector, properties)
|
||||
|
||||
loadKeymaps: ->
|
||||
@keymaps = @getKeymapPaths().map (keymapPath) -> [keymapPath, CSON.readFileSync(keymapPath)]
|
||||
@@ -118,16 +118,16 @@ class AtomPackage extends Package
|
||||
getKeymapPaths: ->
|
||||
keymapsDirPath = path.join(@path, 'keymaps')
|
||||
if @metadata.keymaps
|
||||
@metadata.keymaps.map (name) -> fsUtils.resolve(keymapsDirPath, name, ['json', 'cson', ''])
|
||||
@metadata.keymaps.map (name) -> fs.resolve(keymapsDirPath, name, ['json', 'cson', ''])
|
||||
else
|
||||
fsUtils.listSync(keymapsDirPath, ['cson', 'json'])
|
||||
fs.listSync(keymapsDirPath, ['cson', 'json'])
|
||||
|
||||
getMenuPaths: ->
|
||||
menusDirPath = path.join(@path, 'menus')
|
||||
if @metadata.menus
|
||||
@metadata.menus.map (name) -> fsUtils.resolve(menusDirPath, name, ['json', 'cson', ''])
|
||||
@metadata.menus.map (name) -> fs.resolve(menusDirPath, name, ['json', 'cson', ''])
|
||||
else
|
||||
fsUtils.listSync(menusDirPath, ['cson', 'json'])
|
||||
fs.listSync(menusDirPath, ['cson', 'json'])
|
||||
|
||||
loadStylesheets: ->
|
||||
@stylesheets = @getStylesheetPaths().map (stylesheetPath) ->
|
||||
@@ -140,25 +140,25 @@ class AtomPackage extends Package
|
||||
stylesheetDirPath = @getStylesheetsPath()
|
||||
|
||||
if @metadata.stylesheetMain
|
||||
[fsUtils.resolve(@path, @metadata.stylesheetMain)]
|
||||
[fs.resolve(@path, @metadata.stylesheetMain)]
|
||||
else if @metadata.stylesheets
|
||||
@metadata.stylesheets.map (name) -> fsUtils.resolve(stylesheetDirPath, name, ['css', 'less', ''])
|
||||
else if indexStylesheet = fsUtils.resolve(@path, 'index', ['css', 'less'])
|
||||
@metadata.stylesheets.map (name) -> fs.resolve(stylesheetDirPath, name, ['css', 'less', ''])
|
||||
else if indexStylesheet = fs.resolve(@path, 'index', ['css', 'less'])
|
||||
[indexStylesheet]
|
||||
else
|
||||
fsUtils.listSync(stylesheetDirPath, ['css', 'less'])
|
||||
fs.listSync(stylesheetDirPath, ['css', 'less'])
|
||||
|
||||
loadGrammars: ->
|
||||
@grammars = []
|
||||
grammarsDirPath = path.join(@path, 'grammars')
|
||||
for grammarPath in fsUtils.listSync(grammarsDirPath, ['.json', '.cson'])
|
||||
for grammarPath in fs.listSync(grammarsDirPath, ['.json', '.cson'])
|
||||
@grammars.push(TextMateGrammar.loadSync(grammarPath))
|
||||
|
||||
loadScopedProperties: ->
|
||||
@scopedProperties = []
|
||||
scopedPropertiessDirPath = path.join(@path, 'scoped-properties')
|
||||
for scopedPropertiesPath in fsUtils.listSync(scopedPropertiessDirPath, ['.json', '.cson'])
|
||||
for selector, properties of fsUtils.readObjectSync(scopedPropertiesPath)
|
||||
for scopedPropertiesPath in fs.listSync(scopedPropertiessDirPath, ['.json', '.cson'])
|
||||
for selector, properties of fs.readObjectSync(scopedPropertiesPath)
|
||||
@scopedProperties.push([scopedPropertiesPath, selector, properties])
|
||||
|
||||
serialize: ->
|
||||
@@ -180,8 +180,8 @@ class AtomPackage extends Package
|
||||
@configActivated = false
|
||||
|
||||
deactivateResources: ->
|
||||
syntax.removeGrammar(grammar) for grammar in @grammars
|
||||
syntax.removeProperties(scopedPropertiesPath) for [scopedPropertiesPath] in @scopedProperties
|
||||
atom.syntax.removeGrammar(grammar) for grammar in @grammars
|
||||
atom.syntax.removeProperties(scopedPropertiesPath) for [scopedPropertiesPath] in @scopedProperties
|
||||
atom.keymap.remove(keymapPath) for [keymapPath] in @keymaps
|
||||
atom.themes.removeStylesheet(stylesheetPath) for [stylesheetPath] in @stylesheets
|
||||
@stylesheetsActivated = false
|
||||
@@ -198,7 +198,7 @@ class AtomPackage extends Package
|
||||
requireMainModule: ->
|
||||
return @mainModule if @mainModule?
|
||||
mainModulePath = @getMainModulePath()
|
||||
@mainModule = require(mainModulePath) if fsUtils.isFileSync(mainModulePath)
|
||||
@mainModule = require(mainModulePath) if fs.isFileSync(mainModulePath)
|
||||
|
||||
getMainModulePath: ->
|
||||
return @mainModulePath if @resolvedMainModulePath
|
||||
@@ -208,22 +208,22 @@ class AtomPackage extends Package
|
||||
path.join(@path, @metadata.main)
|
||||
else
|
||||
path.join(@path, 'index')
|
||||
@mainModulePath = fsUtils.resolveExtension(mainModulePath, ["", _.keys(require.extensions)...])
|
||||
@mainModulePath = fs.resolveExtension(mainModulePath, ["", _.keys(require.extensions)...])
|
||||
|
||||
registerDeferredDeserializers: ->
|
||||
for deserializerName in @metadata.deferredDeserializers ? []
|
||||
registerDeferredDeserializer deserializerName, =>
|
||||
atom.deserializers.addDeferred deserializerName, =>
|
||||
@activateStylesheets()
|
||||
@requireMainModule()
|
||||
|
||||
subscribeToActivationEvents: ->
|
||||
return unless @metadata.activationEvents?
|
||||
if _.isArray(@metadata.activationEvents)
|
||||
rootView.command(event, @handleActivationEvent) for event in @metadata.activationEvents
|
||||
atom.rootView.command(event, @handleActivationEvent) for event in @metadata.activationEvents
|
||||
else if _.isString(@metadata.activationEvents)
|
||||
rootView.command(@metadata.activationEvents, @handleActivationEvent)
|
||||
atom.rootView.command(@metadata.activationEvents, @handleActivationEvent)
|
||||
else
|
||||
rootView.command(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
|
||||
atom.rootView.command(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
|
||||
|
||||
handleActivationEvent: (event) =>
|
||||
bubblePathEventHandlers = @disableEventHandlersOnBubblePath(event)
|
||||
@@ -234,11 +234,11 @@ class AtomPackage extends Package
|
||||
|
||||
unsubscribeFromActivationEvents: ->
|
||||
if _.isArray(@metadata.activationEvents)
|
||||
rootView.off(event, @handleActivationEvent) for event in @metadata.activationEvents
|
||||
atom.rootView.off(event, @handleActivationEvent) for event in @metadata.activationEvents
|
||||
else if _.isString(@metadata.activationEvents)
|
||||
rootView.off(@metadata.activationEvents, @handleActivationEvent)
|
||||
atom.rootView.off(@metadata.activationEvents, @handleActivationEvent)
|
||||
else
|
||||
rootView.off(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
|
||||
atom.rootView.off(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
|
||||
|
||||
disableEventHandlersOnBubblePath: (event) ->
|
||||
bubblePathEventHandlers = []
|
||||
|
||||
+228
-84
@@ -1,38 +1,52 @@
|
||||
#TODO remove once all packages have been updated
|
||||
{Emitter} = require 'emissary'
|
||||
Emitter::one = (args...) -> @once(args...)
|
||||
Emitter::trigger = (args...) -> @emit(args...)
|
||||
Emitter::subscriptionCount = (args...) -> @getSubscriptionCount(args...)
|
||||
|
||||
fsUtils = require './fs-utils'
|
||||
{$} = require './space-pen-extensions'
|
||||
_ = require 'underscore-plus'
|
||||
Package = require './package'
|
||||
crypto = require 'crypto'
|
||||
ipc = require 'ipc'
|
||||
os = require 'os'
|
||||
path = require 'path'
|
||||
remote = require 'remote'
|
||||
shell = require 'shell'
|
||||
crypto = require 'crypto'
|
||||
path = require 'path'
|
||||
os = require 'os'
|
||||
dialog = remote.require 'dialog'
|
||||
app = remote.require 'app'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
{Document} = require 'telepath'
|
||||
DeserializerManager = require './deserializer-manager'
|
||||
fs = require 'fs-plus'
|
||||
{Subscriber} = require 'emissary'
|
||||
|
||||
{$} = require './space-pen-extensions'
|
||||
DeserializerManager = require './deserializer-manager'
|
||||
Package = require './package'
|
||||
SiteShim = require './site-shim'
|
||||
WindowEventHandler = require './window-event-handler'
|
||||
|
||||
# Public: Atom global for dealing with packages, themes, menus, and the window.
|
||||
#
|
||||
# An instance of this class is always available as the `atom` global.
|
||||
#
|
||||
# ## Useful properties available:
|
||||
#
|
||||
# * `atom.config` - A {Config} instance
|
||||
# * `atom.contextMenu` - A {ContextMenuManager} instance
|
||||
# * `atom.keymap` - A {Keymap} instance
|
||||
# * `atom.menu` - A {MenuManager} instance
|
||||
# * `atom.rootView` - A {RootView} instance
|
||||
# * `atom.packages` - A {PackageManager} instance
|
||||
# * `atom.pasteboard` - A {Pasteboard} instance
|
||||
# * `atom.project` - A {Project} instance
|
||||
# * `atom.syntax` - A {Syntax} instance
|
||||
# * `atom.themes` - A {ThemeManager} instance
|
||||
module.exports =
|
||||
class Atom
|
||||
Subscriber.includeInto(this)
|
||||
|
||||
# Private:
|
||||
constructor: ->
|
||||
@rootViewParentSelector = 'body'
|
||||
@deserializers = new DeserializerManager()
|
||||
|
||||
# Private: Initialize all the properties in this object.
|
||||
initialize: ->
|
||||
@unsubscribe()
|
||||
@setBodyPlatformClass()
|
||||
|
||||
{devMode, resourcePath} = atom.getLoadSettings()
|
||||
configDirPath = @getConfigDirPath()
|
||||
@@ -47,20 +61,25 @@ class Atom
|
||||
MenuManager = require './menu-manager'
|
||||
|
||||
@config = new Config({configDirPath, resourcePath})
|
||||
@keymap = new Keymap()
|
||||
@keymap = new Keymap({configDirPath, resourcePath})
|
||||
@packages = new PackageManager({devMode, configDirPath, resourcePath})
|
||||
|
||||
#TODO Remove once packages have been updated to not touch atom.packageStates directly
|
||||
@__defineGetter__ 'packageStates', => @packages.packageStates
|
||||
@__defineSetter__ 'packageStates', (packageStates) => @packages.packageStates = packageStates
|
||||
|
||||
@subscribe @packages, 'loaded', => @watchThemes()
|
||||
@themes = new ThemeManager(@packages)
|
||||
@subscribe @packages, 'activated', => @watchThemes()
|
||||
@themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath})
|
||||
@contextMenu = new ContextMenuManager(devMode)
|
||||
@menu = new MenuManager()
|
||||
@menu = new MenuManager({resourcePath})
|
||||
@pasteboard = new Pasteboard()
|
||||
@syntax = deserialize(@getWindowState('syntax')) ? new Syntax()
|
||||
@syntax = @deserializers.deserialize(@getWindowState('syntax')) ? new Syntax()
|
||||
|
||||
# Private: This method is called in any window needing a general environment, including specs
|
||||
setUpEnvironment: (@windowMode) ->
|
||||
@initialize()
|
||||
|
||||
# Private:
|
||||
setBodyPlatformClass: ->
|
||||
document.body.classList.add("platform-#{process.platform}")
|
||||
|
||||
# Public: Get the current window
|
||||
getCurrentWindow: ->
|
||||
remote.getCurrentWindow()
|
||||
|
||||
@@ -95,6 +114,7 @@ class Atom
|
||||
else
|
||||
browserWindow.center()
|
||||
|
||||
# Private:
|
||||
restoreDimensions: ->
|
||||
dimensions = @getWindowState().getObject('dimensions')
|
||||
unless dimensions?.width and dimensions?.height
|
||||
@@ -111,137 +131,240 @@ class Atom
|
||||
@loadSettings ?= _.deepClone(@getCurrentWindow().loadSettings)
|
||||
_.deepClone(@loadSettings)
|
||||
|
||||
# Private:
|
||||
deserializeProject: ->
|
||||
Project = require './project'
|
||||
state = @getWindowState()
|
||||
@project = deserialize(state.get('project'))
|
||||
unless @project?
|
||||
@project = new Project(@getLoadSettings().initialPath)
|
||||
state.set('project', @project.getState())
|
||||
@project = @getWindowState('project')
|
||||
unless @project instanceof Project
|
||||
@project = new Project(path: @getLoadSettings().initialPath)
|
||||
@setWindowState('project', @project)
|
||||
|
||||
# Private:
|
||||
deserializeRootView: ->
|
||||
RootView = require './root-view'
|
||||
state = @getWindowState()
|
||||
@rootView = deserialize(state.get('rootView'))
|
||||
@rootView = @deserializers.deserialize(state.get('rootView'))
|
||||
unless @rootView?
|
||||
@rootView = new RootView()
|
||||
state.set('rootView', @rootView.getState())
|
||||
$(@rootViewParentSelector).append(@rootView)
|
||||
|
||||
# Private:
|
||||
deserializePackageStates: ->
|
||||
state = @getWindowState()
|
||||
@packages.packageStates = state.getObject('packageStates') ? {}
|
||||
state.remove('packageStates')
|
||||
|
||||
#TODO Remove theses once packages have been migrated
|
||||
getPackageState: (args...) -> @packages.getPackageState(args...)
|
||||
setPackageState: (args...) -> @packages.setPackageState(args...)
|
||||
activatePackages: (args...) -> @packages.activatePackages(args...)
|
||||
activatePackage: (args...) -> @packages.activatePackage(args...)
|
||||
deactivatePackages: (args...) -> @packages.deactivatePackages(args...)
|
||||
deactivatePackage: (args...) -> @packages.deactivatePackage(args...)
|
||||
getActivePackage: (args...) -> @packages.getActivePackage(args...)
|
||||
isPackageActive: (args...) -> @packages.isPackageActive(args...)
|
||||
getActivePackages: (args...) -> @packages.getActivePackages(args...)
|
||||
loadPackages: (args...) -> @packages.loadPackages(args...)
|
||||
loadPackage: (args...) -> @packages.loadPackage(args...)
|
||||
unloadPackage: (args...) -> @packages.unloadPackage(args...)
|
||||
resolvePackagePath: (args...) -> @packages.resolvePackagePath(args...)
|
||||
isInternalPackage: (args...) -> @packages.isInternalPackage(args...)
|
||||
getLoadedPackage: (args...) -> @packages.getLoadedPackage(args...)
|
||||
isPackageLoaded: (args...) -> @packages.isPackageLoaded(args...)
|
||||
getLoadedPackages: (args...) -> @packages.getLoadedPackages(args...)
|
||||
isPackageDisabled: (args...) -> @packages.isPackageDisabled(args...)
|
||||
getAvailablePackagePaths: (args...) -> @packages.getAvailablePackagePaths(args...)
|
||||
getAvailablePackageNames: (args...) -> @packages.getAvailablePackageNames(args...)
|
||||
getAvailablePackageMetadata: (args...)-> @packages.getAvailablePackageMetadata(args...)
|
||||
# Private:
|
||||
deserializeEditorWindow: ->
|
||||
@deserializePackageStates()
|
||||
@deserializeProject()
|
||||
@deserializeRootView()
|
||||
|
||||
# Private: This method is only called when opening a real application window
|
||||
startEditorWindow: ->
|
||||
if process.platform is 'darwin'
|
||||
CommandInstaller = require './command-installer'
|
||||
CommandInstaller.installAtomCommand()
|
||||
CommandInstaller.installApmCommand()
|
||||
|
||||
@windowEventHandler = new WindowEventHandler
|
||||
@restoreDimensions()
|
||||
@config.load()
|
||||
@config.setDefaults('core', require('./root-view').configDefaults)
|
||||
@config.setDefaults('editor', require('./editor-view').configDefaults)
|
||||
@keymap.loadBundledKeymaps()
|
||||
@themes.loadBaseStylesheets()
|
||||
@packages.loadPackages()
|
||||
@deserializeEditorWindow()
|
||||
@packages.activate()
|
||||
@keymap.loadUserKeymap()
|
||||
@requireUserInitScript()
|
||||
@menu.update()
|
||||
|
||||
$(window).on 'unload', =>
|
||||
$(document.body).hide()
|
||||
@unloadEditorWindow()
|
||||
false
|
||||
|
||||
@displayWindow()
|
||||
|
||||
# Private:
|
||||
unloadEditorWindow: ->
|
||||
return if not @project and not @rootView
|
||||
|
||||
windowState = @getWindowState()
|
||||
windowState.set('project', @project)
|
||||
windowState.set('syntax', @syntax.serialize())
|
||||
windowState.set('rootView', @rootView.serialize())
|
||||
@packages.deactivatePackages()
|
||||
windowState.set('packageStates', @packages.packageStates)
|
||||
@saveWindowState()
|
||||
@rootView.remove()
|
||||
@project.destroy()
|
||||
@windowEventHandler?.unsubscribe()
|
||||
|
||||
# Set up the default event handlers and menus for a non-editor window.
|
||||
#
|
||||
# This can be used by packages to have a minimum level of keybindings and
|
||||
# menus available when not using the standard editor window.
|
||||
#
|
||||
# This should only be called after setUpEnvironment() has been called.
|
||||
setUpDefaultEvents: ->
|
||||
@windowEventHandler = new WindowEventHandler
|
||||
@keymap.loadBundledKeymaps()
|
||||
@menu.update()
|
||||
|
||||
# Private:
|
||||
loadThemes: ->
|
||||
@themes.load()
|
||||
|
||||
# Private:
|
||||
watchThemes: ->
|
||||
@themes.on 'reloaded', =>
|
||||
pack.reloadStylesheets?() for name, pack of @packages.getActivePackages()
|
||||
# Only reload stylesheets from non-theme packages
|
||||
for pack in @packages.getActivePackages() when pack.getType() isnt 'theme'
|
||||
pack.reloadStylesheets?()
|
||||
null
|
||||
|
||||
# Public: Open a new Atom window using the given options.
|
||||
#
|
||||
# Calling this method without an options parameter will open a prompt to pick
|
||||
# a file/folder to open in the new window.
|
||||
#
|
||||
# * options
|
||||
# * pathsToOpen: A string array of paths to open
|
||||
open: (options) ->
|
||||
ipc.sendChannel('open', options)
|
||||
|
||||
confirm: (message, detailedMessage, buttonLabelsAndCallbacks...) ->
|
||||
buttons = []
|
||||
callbacks = []
|
||||
while buttonLabelsAndCallbacks.length
|
||||
do =>
|
||||
buttons.push buttonLabelsAndCallbacks.shift()
|
||||
callbacks.push buttonLabelsAndCallbacks.shift()
|
||||
# Public: Open a confirm dialog.
|
||||
#
|
||||
# ## Example:
|
||||
# ```coffeescript
|
||||
# atom.confirm
|
||||
# message: 'How you feeling?'
|
||||
# detailedMessage: 'Be honest.'
|
||||
# buttons:
|
||||
# Good: -> window.alert('good to hear')
|
||||
# Bad: -> window.alert('bummer')
|
||||
# ```
|
||||
#
|
||||
# * options:
|
||||
# + message: The string message to display.
|
||||
# + detailedMessage: The string detailed message to display.
|
||||
# + buttons: Either an array of strings or an object where the values
|
||||
# are callbacks to invoke when clicked.
|
||||
#
|
||||
# Returns the chosen index if buttons was an array or the return of the
|
||||
# callback if buttons was an object.
|
||||
confirm: ({message, detailedMessage, buttons}={}) ->
|
||||
buttons ?= {}
|
||||
if _.isArray(buttons)
|
||||
buttonLabels = buttons
|
||||
else
|
||||
buttonLabels = Object.keys(buttons)
|
||||
|
||||
chosen = @confirmSync(message, detailedMessage, buttons)
|
||||
callbacks[chosen]?()
|
||||
|
||||
confirmSync: (message, detailedMessage, buttons, browserWindow=@getCurrentWindow()) ->
|
||||
dialog.showMessageBox browserWindow,
|
||||
chosen = dialog.showMessageBox @getCurrentWindow(),
|
||||
type: 'info'
|
||||
message: message
|
||||
detail: detailedMessage
|
||||
buttons: buttons
|
||||
buttons: buttonLabels
|
||||
|
||||
if _.isArray(buttons)
|
||||
chosen
|
||||
else
|
||||
callback = buttons[buttonLabels[chosen]]
|
||||
callback?()
|
||||
|
||||
# Private:
|
||||
showSaveDialog: (callback) ->
|
||||
callback(showSaveDialogSync())
|
||||
|
||||
# Private:
|
||||
showSaveDialogSync: (defaultPath) ->
|
||||
defaultPath ?= project?.getPath()
|
||||
defaultPath ?= @project?.getPath()
|
||||
currentWindow = @getCurrentWindow()
|
||||
dialog.showSaveDialog currentWindow, {title: 'Save File', defaultPath}
|
||||
|
||||
# Public: Open the dev tools for the current window.
|
||||
openDevTools: ->
|
||||
@getCurrentWindow().openDevTools()
|
||||
|
||||
# Public: Toggle the visibility of the dev tools for the current window.
|
||||
toggleDevTools: ->
|
||||
@getCurrentWindow().toggleDevTools()
|
||||
|
||||
# Public: Reload the current window.
|
||||
reload: ->
|
||||
@getCurrentWindow().restart()
|
||||
|
||||
# Public: Focus the current window.
|
||||
focus: ->
|
||||
@getCurrentWindow().focus()
|
||||
$(window).focus()
|
||||
|
||||
# Public: Show the current window.
|
||||
show: ->
|
||||
@getCurrentWindow().show()
|
||||
|
||||
# Public: Hide the current window.
|
||||
hide: ->
|
||||
@getCurrentWindow().hide()
|
||||
|
||||
# Private: Schedule the window to be shown and focused on the next tick.
|
||||
#
|
||||
# This is done in a next tick to prevent a white flicker from occurring
|
||||
# if called synchronously.
|
||||
displayWindow: ->
|
||||
setImmediate =>
|
||||
@show()
|
||||
@focus()
|
||||
@setFullScreen(true) if @rootView.getState().get('fullScreen')
|
||||
|
||||
# Public: Close the current window.
|
||||
close: ->
|
||||
@getCurrentWindow().close()
|
||||
|
||||
# Private:
|
||||
exit: (status) -> app.exit(status)
|
||||
|
||||
# Public: Is the current window in development mode?
|
||||
inDevMode: ->
|
||||
@getLoadSettings().devMode
|
||||
|
||||
# Public: Is the current window running specs?
|
||||
inSpecMode: ->
|
||||
@getLoadSettings().isSpec
|
||||
|
||||
# Public: Toggle the full screen state of the current window.
|
||||
toggleFullScreen: ->
|
||||
@setFullScreen(!@isFullScreen())
|
||||
|
||||
# Public: Set the full screen state of the current window.
|
||||
setFullScreen: (fullScreen=false) ->
|
||||
@getCurrentWindow().setFullScreen(fullScreen)
|
||||
|
||||
# Public: Is the current window in full screen mode?
|
||||
isFullScreen: ->
|
||||
@getCurrentWindow().isFullScreen()
|
||||
|
||||
getHomeDirPath: ->
|
||||
process.env[if process.platform is 'win32' then 'USERPROFILE' else 'HOME']
|
||||
|
||||
getTempDirPath: ->
|
||||
if process.platform is 'win32' then os.tmpdir() else '/tmp'
|
||||
# Public: Get the version of the Atom application.
|
||||
getVersion: ->
|
||||
app.getVersion()
|
||||
|
||||
# Public: Get the directory path to Atom's configuration area.
|
||||
#
|
||||
# Returns the absolute path to ~/.atom
|
||||
getConfigDirPath: ->
|
||||
@configDirPath ?= fsUtils.absolute('~/.atom')
|
||||
@configDirPath ?= fs.absolute('~/.atom')
|
||||
|
||||
# Public: Get the directory path to Atom's storage area.
|
||||
#
|
||||
# Returns the absoluste path to ~/.atom/storage
|
||||
getStorageDirPath: ->
|
||||
@storageDirPath ?= path.join(@getConfigDirPath(), 'storage')
|
||||
|
||||
# Private:
|
||||
getWindowStatePath: ->
|
||||
switch @windowMode
|
||||
when 'spec'
|
||||
@@ -253,20 +376,22 @@ class Atom
|
||||
filename = "editor-#{sha1}"
|
||||
|
||||
if filename
|
||||
path.join(@config.userStoragePath, filename)
|
||||
path.join(@getStorageDirPath(), filename)
|
||||
else
|
||||
null
|
||||
|
||||
# Public: Set the window state of the given keypath to the value.
|
||||
setWindowState: (keyPath, value) ->
|
||||
windowState = @getWindowState()
|
||||
windowState.set(keyPath, value)
|
||||
windowState
|
||||
|
||||
# Private:
|
||||
loadWindowState: ->
|
||||
if windowStatePath = @getWindowStatePath()
|
||||
if fsUtils.exists(windowStatePath)
|
||||
if fs.existsSync(windowStatePath)
|
||||
try
|
||||
documentStateJson = fsUtils.read(windowStatePath)
|
||||
documentStateJson = fs.readFileSync(windowStatePath, 'utf8')
|
||||
catch error
|
||||
console.warn "Error reading window state: #{windowStatePath}", error.stack, error
|
||||
else
|
||||
@@ -277,18 +402,25 @@ class Atom
|
||||
catch error
|
||||
console.warn "Error parsing window state: #{windowStatePath}", error.stack, error
|
||||
|
||||
doc = Document.deserialize(state: documentState) if documentState?
|
||||
doc = Document.deserialize(documentState) if documentState?
|
||||
doc ?= Document.create()
|
||||
@site = doc.site # TODO: Remove this when everything is using telepath models
|
||||
doc.registerModelClasses(require('./text-buffer'), require('./project'))
|
||||
# TODO: Remove this when everything is using telepath models
|
||||
if @site?
|
||||
@site.setRootDocument(doc)
|
||||
else
|
||||
@site = new SiteShim(doc)
|
||||
doc
|
||||
|
||||
# Private:
|
||||
saveWindowState: ->
|
||||
windowState = @getWindowState()
|
||||
if windowStatePath = @getWindowStatePath()
|
||||
windowState.saveSync(path: windowStatePath)
|
||||
windowState.saveSync(windowStatePath)
|
||||
else
|
||||
@getCurrentWindow().loadSettings.windowState = JSON.stringify(windowState.serialize())
|
||||
@getCurrentWindow().loadSettings.windowState = JSON.stringify(windowState.serializeForPersistence())
|
||||
|
||||
# Public: Get the window state for the key path.
|
||||
getWindowState: (keyPath) ->
|
||||
@windowState ?= @loadWindowState()
|
||||
if keyPath
|
||||
@@ -296,23 +428,35 @@ class Atom
|
||||
else
|
||||
@windowState
|
||||
|
||||
# Private: Returns a replicated copy of the current state.
|
||||
replicate: ->
|
||||
@getWindowState().replicate()
|
||||
|
||||
# Private:
|
||||
crashMainProcess: ->
|
||||
remote.process.crash()
|
||||
|
||||
# Private:
|
||||
crashRenderProcess: ->
|
||||
process.crash()
|
||||
|
||||
# Public: Visually and audibly trigger a beep.
|
||||
beep: ->
|
||||
shell.beep() if @config.get('core.audioBeep')
|
||||
@rootView.trigger 'beep'
|
||||
|
||||
# Private:
|
||||
requireUserInitScript: ->
|
||||
userInitScriptPath = path.join(@config.configDirPath, "user.coffee")
|
||||
try
|
||||
require userInitScriptPath if fsUtils.isFileSync(userInitScriptPath)
|
||||
catch error
|
||||
console.error "Failed to load `#{userInitScriptPath}`", error.stack, error
|
||||
if userInitScriptPath = fs.resolve(@getConfigDirPath(), 'user', ['js', 'coffee'])
|
||||
try
|
||||
require userInitScriptPath
|
||||
catch error
|
||||
console.error "Failed to load `#{userInitScriptPath}`", error.stack, error
|
||||
|
||||
# Public: Require the module with the given globals.
|
||||
#
|
||||
# The globals will be set on the `window` object and removed after the
|
||||
# require completes.
|
||||
requireWithGlobals: (id, globals={}) ->
|
||||
existingGlobals = {}
|
||||
for key, value of globals
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
{$} = require './space-pen-extensions'
|
||||
_ = require 'underscore-plus'
|
||||
fsUtils = require './fs-utils'
|
||||
|
||||
{specificity} = require 'clear-cut'
|
||||
PEG = require 'pegjs'
|
||||
|
||||
### Internal ###
|
||||
|
||||
module.exports =
|
||||
class BindingSet
|
||||
|
||||
@parser: null
|
||||
|
||||
selector: null
|
||||
commandsByKeystrokes: null
|
||||
parser: null
|
||||
name: null
|
||||
|
||||
constructor: (selector, commandsByKeystrokes, @index, @name) ->
|
||||
BindingSet.parser ?= PEG.buildParser(fsUtils.read(require.resolve './keystroke-pattern.pegjs'))
|
||||
@specificity = specificity(selector)
|
||||
@selector = selector.replace(/!important/g, '')
|
||||
@commandsByKeystrokes = @normalizeCommandsByKeystrokes(commandsByKeystrokes)
|
||||
|
||||
# Private:
|
||||
getName: ->
|
||||
@name
|
||||
|
||||
# Private:
|
||||
getSelector: ->
|
||||
@selector
|
||||
|
||||
# Private:
|
||||
getCommandsByKeystrokes: ->
|
||||
@commandsByKeystrokes
|
||||
|
||||
commandForEvent: (event) ->
|
||||
for keystrokes, command of @commandsByKeystrokes
|
||||
return command if event.keystrokes == keystrokes
|
||||
null
|
||||
|
||||
matchesKeystrokePrefix: (event) ->
|
||||
eventKeystrokes = event.keystrokes.split(' ')
|
||||
for keystrokes, command of @commandsByKeystrokes
|
||||
bindingKeystrokes = keystrokes.split(' ')
|
||||
continue unless eventKeystrokes.length < bindingKeystrokes.length
|
||||
return true if _.isEqual(eventKeystrokes, bindingKeystrokes[0...eventKeystrokes.length])
|
||||
false
|
||||
|
||||
normalizeCommandsByKeystrokes: (commandsByKeystrokes) ->
|
||||
normalizedCommandsByKeystrokes = {}
|
||||
for keystrokes, command of commandsByKeystrokes
|
||||
normalizedCommandsByKeystrokes[@normalizeKeystrokes(keystrokes)] = command
|
||||
normalizedCommandsByKeystrokes
|
||||
|
||||
normalizeKeystrokes: (keystrokes) ->
|
||||
normalizedKeystrokes = keystrokes.split(/\s+/).map (keystroke) =>
|
||||
@normalizeKeystroke(keystroke)
|
||||
normalizedKeystrokes.join(' ')
|
||||
|
||||
normalizeKeystroke: (keystroke) ->
|
||||
keys = BindingSet.parser.parse(keystroke)
|
||||
modifiers = keys[0...-1]
|
||||
modifiers.sort()
|
||||
[modifiers..., _.last(keys)].join('-')
|
||||
@@ -22,7 +22,7 @@ class ApplicationMenu
|
||||
# The Object which describes the menu to display.
|
||||
# * keystrokesByCommand:
|
||||
# An Object where the keys are commands and the values are Arrays containing
|
||||
# the keystrokes.
|
||||
# the keystroke.
|
||||
update: (template, keystrokesByCommand) ->
|
||||
@translateTemplate(template, keystrokesByCommand)
|
||||
@substituteVersion(template)
|
||||
@@ -97,14 +97,14 @@ class ApplicationMenu
|
||||
]
|
||||
]
|
||||
|
||||
# Private: Combines a menu template with the appropriate keystrokes.
|
||||
# Private: Combines a menu template with the appropriate keystroke.
|
||||
#
|
||||
# * template:
|
||||
# An Object conforming to atom-shell's menu api but lacking accelerator and
|
||||
# click properties.
|
||||
# * keystrokesByCommand:
|
||||
# An Object where the keys are commands and the values are Arrays containing
|
||||
# the keystrokes.
|
||||
# the keystroke.
|
||||
#
|
||||
# Returns a complete menu configuration object for atom-shell's menu API.
|
||||
translateTemplate: (template, keystrokesByCommand) ->
|
||||
@@ -123,21 +123,21 @@ class ApplicationMenu
|
||||
# The name of the command.
|
||||
# * keystrokesByCommand:
|
||||
# An Object where the keys are commands and the values are Arrays containing
|
||||
# the keystrokes.
|
||||
# the keystroke.
|
||||
#
|
||||
# Returns a String containing the keystroke in a format that can be interpreted
|
||||
# by atom shell to provide nice icons where available.
|
||||
acceleratorForCommand: (command, keystrokesByCommand) ->
|
||||
keystroke = keystrokesByCommand[command]?[0]
|
||||
return null unless keystroke
|
||||
firstKeystroke = keystrokesByCommand[command]?[0]
|
||||
return null unless firstKeystroke
|
||||
|
||||
modifiers = keystroke.split('-')
|
||||
modifiers = firstKeystroke.split('-')
|
||||
key = modifiers.pop()
|
||||
|
||||
modifiers.push("Shift") if key != key.toLowerCase()
|
||||
modifiers = modifiers.map (modifier) ->
|
||||
modifier.replace(/shift/ig, "Shift")
|
||||
.replace(/meta/ig, "Command")
|
||||
.replace(/cmd/ig, "Command")
|
||||
.replace(/ctrl/ig, "Ctrl")
|
||||
.replace(/alt/ig, "Alt")
|
||||
|
||||
|
||||
@@ -146,6 +146,9 @@ class AtomApplication
|
||||
else
|
||||
@openPath(pathToOpen: "atom://config")
|
||||
|
||||
app.on 'window-all-closed', ->
|
||||
app.quit() if process.platform is 'win32'
|
||||
|
||||
app.on 'will-quit', =>
|
||||
fs.unlinkSync socketPath if fs.existsSync(socketPath) # Clean the socket file when quit normally.
|
||||
|
||||
@@ -288,9 +291,9 @@ class AtomApplication
|
||||
openUrl: ({urlToOpen, devMode}) ->
|
||||
unless @packages?
|
||||
PackageManager = require '../package-manager'
|
||||
fsUtils = require '../fs-utils'
|
||||
fs = require 'fs-plus'
|
||||
@packages = new PackageManager
|
||||
configDirPath: fsUtils.absolute('~/.atom')
|
||||
configDirPath: fs.absolute('~/.atom')
|
||||
devMode: devMode
|
||||
resourcePath: @resourcePath
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ _ = require 'underscore-plus'
|
||||
# Private:
|
||||
module.exports =
|
||||
class AtomWindow
|
||||
@iconPath: path.resolve(__dirname, '..', '..', 'atom.png')
|
||||
|
||||
browserWindow: null
|
||||
loaded: null
|
||||
isSpec: null
|
||||
@@ -19,7 +21,7 @@ class AtomWindow
|
||||
global.atomApplication.addWindow(this)
|
||||
|
||||
@setupNodePath(@resourcePath)
|
||||
@browserWindow = new BrowserWindow show: false, title: 'Atom'
|
||||
@browserWindow = new BrowserWindow show: false, title: 'Atom', icon: @constructor.iconPath
|
||||
@browserWindow.restart = _.wrap _.bind(@browserWindow.restart, @browserWindow), (restart) =>
|
||||
@setupNodePath(@resourcePath)
|
||||
restart()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
Menu = require 'menu'
|
||||
|
||||
# Private:
|
||||
module.exports =
|
||||
class ContextMenu
|
||||
constructor: (template, browserWindow) ->
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
startTime = new Date().getTime()
|
||||
startTime = Date.now()
|
||||
|
||||
autoUpdater = require 'auto-updater'
|
||||
crashReporter = require 'crash-reporter'
|
||||
@@ -8,11 +8,17 @@ fs = require 'fs'
|
||||
module = require 'module'
|
||||
path = require 'path'
|
||||
optimist = require 'optimist'
|
||||
nslog = require 'nslog'
|
||||
# TODO: NSLog is missing .lib on windows
|
||||
nslog = require 'nslog' unless process.platform is 'win32'
|
||||
dialog = require 'dialog'
|
||||
|
||||
console.log = (args...) ->
|
||||
nslog(args.map((arg) -> JSON.stringify(arg)).join(" "))
|
||||
# TODO: Make NSLog work as expected
|
||||
output = args.map((arg) -> JSON.stringify(arg)).join(" ")
|
||||
if process.platform == 'darwin'
|
||||
nslog(output)
|
||||
else
|
||||
fs.writeFileSync('debug.log', output, flag: 'a')
|
||||
|
||||
process.on 'uncaughtException', (error={}) ->
|
||||
nslog(error.message) if error.message?
|
||||
@@ -56,7 +62,7 @@ delegate.browserMainParts.preMainMessageLoopRun = ->
|
||||
AtomApplication = require './atom-application'
|
||||
|
||||
AtomApplication.open(args)
|
||||
console.log("App load time: #{new Date().getTime() - startTime}ms")
|
||||
console.log("App load time: #{Date.now() - startTime}ms") unless args.test
|
||||
|
||||
global.devResourcePath = path.join(app.getHomeDir(), 'github', 'atom')
|
||||
|
||||
|
||||
@@ -1,23 +1,46 @@
|
||||
BufferedProcess = require './buffered-process'
|
||||
path = require 'path'
|
||||
|
||||
# Public: Like BufferedProcess, but accepts a node script instead of an
|
||||
# executable, on Unix which allows running scripts and executables, this seems
|
||||
# unnecessary, but on Windows we have to separate scripts from executables since
|
||||
# it doesn't support shebang strings.
|
||||
# Public: Like {BufferedProcess}, but accepts a Node script instead of an
|
||||
# executable.
|
||||
#
|
||||
# This may seem unnecessary but on Windows we have to have separate executables
|
||||
# for each script without this since Windows doesn't support shebang strings.
|
||||
module.exports =
|
||||
class BufferedNodeProcess extends BufferedProcess
|
||||
# Executes the given Node script.
|
||||
#
|
||||
# * options
|
||||
# + command:
|
||||
# The path to the Javascript script to execute.
|
||||
# + args:
|
||||
# The array of arguments to pass to the script (optional).
|
||||
# + options:
|
||||
# The options Object to pass to Node's `ChildProcess.spawn` (optional).
|
||||
# + stdout:
|
||||
# The callback that receives a single argument which contains the
|
||||
# standard output of the script. The callback is called as data is
|
||||
# received but it's buffered to ensure only complete lines are passed
|
||||
# until the source stream closes. After the source stream has closed
|
||||
# all remaining data is sent in a final call (optional).
|
||||
# + stderr:
|
||||
# The callback that receives a single argument which contains the
|
||||
# standard error of the script. The callback is called as data is
|
||||
# received but it's buffered to ensure only complete lines are passed
|
||||
# until the source stream closes. After the source stream has closed
|
||||
# all remaining data is sent in a final call (optional).
|
||||
# + exit:
|
||||
# The callback which receives a single argument containing the exit
|
||||
# status (optional).
|
||||
constructor: ({command, args, options, stdout, stderr, exit}) ->
|
||||
node =
|
||||
if process.platform is 'darwin'
|
||||
# On OS X we use the helper process to run script, because it doesn't
|
||||
# create an icon on the Dock.
|
||||
# Use a helper to prevent an icon from appearing on the Dock
|
||||
path.resolve(process.resourcesPath, '..', 'Frameworks',
|
||||
'Atom Helper.app', 'Contents', 'MacOS', 'Atom Helper')
|
||||
else
|
||||
process.execPath
|
||||
|
||||
# Tell atom-shell to run like upstream node.
|
||||
options ?= {}
|
||||
options.env ?= Object.create(process.env)
|
||||
options.env['ATOM_SHELL_INTERNAL_RUN_AS_NODE'] = 1
|
||||
|
||||
@@ -1,28 +1,35 @@
|
||||
ChildProcess = require 'child_process'
|
||||
path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
# Public: A wrapper which provides buffering for ChildProcess.
|
||||
# Public: A wrapper which provides line buffering for Node's ChildProcess.
|
||||
module.exports =
|
||||
class BufferedProcess
|
||||
process: null
|
||||
killed: false
|
||||
|
||||
# Executes the given command.
|
||||
# Executes the given executable.
|
||||
#
|
||||
# * options
|
||||
# + command:
|
||||
# The command to execute.
|
||||
# The path to the executable to execute.
|
||||
# + args:
|
||||
# The arguments for the given command.
|
||||
# The array of arguments to pass to the script (optional).
|
||||
# + options:
|
||||
# The options to pass to ChildProcess.
|
||||
# The options Object to pass to Node's `ChildProcess.spawn` (optional).
|
||||
# + stdout:
|
||||
# The callback to receive stdout data.
|
||||
# The callback that receives a single argument which contains the
|
||||
# standard output of the script. The callback is called as data is
|
||||
# received but it's buffered to ensure only complete lines are passed
|
||||
# until the source stream closes. After the source stream has closed
|
||||
# all remaining data is sent in a final call (optional).
|
||||
# + stderr:
|
||||
# The callback to receive stderr data.
|
||||
# The callback that receives a single argument which contains the
|
||||
# standard error of the script. The callback is called as data is
|
||||
# received but it's buffered to ensure only complete lines are passed
|
||||
# until the source stream closes. After the source stream has closed
|
||||
# all remaining data is sent in a final call (optional).
|
||||
# + exit:
|
||||
# The callback to receive exit status.
|
||||
# The callback which receives a single argument containing the exit
|
||||
# status (optional).
|
||||
constructor: ({command, args, options, stdout, stderr, exit}={}) ->
|
||||
options ?= {}
|
||||
@process = ChildProcess.spawn(command, args, options)
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
path = require 'path'
|
||||
fs = require 'fs'
|
||||
_ = require 'underscore-plus'
|
||||
async = require 'async'
|
||||
fs = require 'fs-plus'
|
||||
mkdirp = require 'mkdirp'
|
||||
fsUtils = require './fs-utils'
|
||||
|
||||
symlinkCommand = (sourcePath, destinationPath, callback) ->
|
||||
mkdirp path.dirname(destinationPath), (error) ->
|
||||
@@ -26,7 +25,7 @@ unlinkCommand = (destinationPath, callback) ->
|
||||
module.exports =
|
||||
findInstallDirectory: (callback) ->
|
||||
directories = ['/opt/boxen', '/opt/github', '/usr/local']
|
||||
async.detect(directories, fsUtils.isDirectory, callback)
|
||||
async.detect(directories, fs.isDirectory, callback)
|
||||
|
||||
install: (commandPath, commandName, callback) ->
|
||||
if not commandName? or _.isFunction(commandName)
|
||||
@@ -48,3 +47,13 @@ module.exports =
|
||||
symlinkCommand(commandPath, destinationPath, installCallback)
|
||||
else
|
||||
installCallback(new Error("No destination directory exists to install"))
|
||||
|
||||
installAtomCommand: (callback) ->
|
||||
{resourcePath} = atom.getLoadSettings()
|
||||
commandPath = path.join(resourcePath, 'atom.sh')
|
||||
@install(commandPath, callback)
|
||||
|
||||
installApmCommand: (callback) ->
|
||||
{resourcePath} = atom.getLoadSettings()
|
||||
commandPath = path.join(resourcePath, 'node_modules', '.bin', 'apm')
|
||||
@install(commandPath, callback)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
module.exports =
|
||||
observeConfig: (keyPath, args...) ->
|
||||
@configSubscriptions ?= {}
|
||||
@configSubscriptions[keyPath] = config.observe(keyPath, args...)
|
||||
@configSubscriptions[keyPath] = atom.config.observe(keyPath, args...)
|
||||
|
||||
unobserveConfig: ->
|
||||
if @configSubscriptions?
|
||||
subscription.cancel() for keyPath, subscription of @configSubscriptions
|
||||
subscription.off() for keyPath, subscription of @configSubscriptions
|
||||
@configSubscriptions = null
|
||||
|
||||
+33
-30
@@ -1,8 +1,7 @@
|
||||
fsUtils = require './fs-utils'
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
{Emitter} = require 'emissary'
|
||||
CSON = require 'season'
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
async = require 'async'
|
||||
pathWatcher = require 'pathwatcher'
|
||||
@@ -34,43 +33,27 @@ class Config
|
||||
|
||||
# Private: Created during initialization, available as `global.config`
|
||||
constructor: ({@configDirPath, @resourcePath}={}) ->
|
||||
@bundledKeymapsDirPath = path.join(@resourcePath, "keymaps")
|
||||
@bundledMenusDirPath = path.join(resourcePath, "menus")
|
||||
@nodeModulesDirPath = path.join(@resourcePath, "node_modules")
|
||||
@bundledPackageDirPaths = [@nodeModulesDirPath]
|
||||
@lessSearchPaths = [
|
||||
path.join(@resourcePath, 'static', 'variables')
|
||||
path.join(@resourcePath, 'static')
|
||||
]
|
||||
@packageDirPaths = [path.join(@configDirPath, "packages")]
|
||||
if atom.getLoadSettings().devMode
|
||||
@packageDirPaths.unshift(path.join(@configDirPath, "dev", "packages"))
|
||||
@userPackageDirPaths = _.clone(@packageDirPaths)
|
||||
@userStoragePath = path.join(@configDirPath, "storage")
|
||||
|
||||
@defaultSettings =
|
||||
core: _.clone(require('./root-view').configDefaults)
|
||||
editor: _.clone(require('./editor').configDefaults)
|
||||
@defaultSettings = {}
|
||||
@settings = {}
|
||||
@configFilePath = fsUtils.resolve(@configDirPath, 'config', ['json', 'cson'])
|
||||
@configFilePath = fs.resolve(@configDirPath, 'config', ['json', 'cson'])
|
||||
@configFilePath ?= path.join(@configDirPath, 'config.cson')
|
||||
|
||||
# Private:
|
||||
initializeConfigDirectory: (done) ->
|
||||
return if fsUtils.exists(@configDirPath)
|
||||
return if fs.existsSync(@configDirPath)
|
||||
|
||||
fsUtils.makeTree(@configDirPath)
|
||||
fs.makeTreeSync(@configDirPath)
|
||||
|
||||
queue = async.queue ({sourcePath, destinationPath}, callback) =>
|
||||
fsUtils.copy(sourcePath, destinationPath, callback)
|
||||
fs.copy(sourcePath, destinationPath, callback)
|
||||
queue.drain = done
|
||||
|
||||
templateConfigDirPath = fsUtils.resolve(@resourcePath, 'dot-atom')
|
||||
templateConfigDirPath = fs.resolve(@resourcePath, 'dot-atom')
|
||||
onConfigDirFile = (sourcePath) =>
|
||||
relativePath = sourcePath.substring(templateConfigDirPath.length + 1)
|
||||
destinationPath = path.join(@configDirPath, relativePath)
|
||||
queue.push({sourcePath, destinationPath})
|
||||
fsUtils.traverseTree(templateConfigDirPath, onConfigDirFile, (path) -> true)
|
||||
fs.traverseTree(templateConfigDirPath, onConfigDirFile, (path) -> true)
|
||||
|
||||
# Private:
|
||||
load: ->
|
||||
@@ -80,8 +63,8 @@ class Config
|
||||
|
||||
# Private:
|
||||
loadUserConfig: ->
|
||||
if !fsUtils.exists(@configFilePath)
|
||||
fsUtils.makeTree(path.dirname(@configFilePath))
|
||||
unless fs.existsSync(@configFilePath)
|
||||
fs.makeTreeSync(path.dirname(@configFilePath))
|
||||
CSON.writeFileSync(@configFilePath, {})
|
||||
|
||||
try
|
||||
@@ -164,6 +147,15 @@ class Config
|
||||
@update()
|
||||
value
|
||||
|
||||
# Public: Toggle the value at the key path.
|
||||
#
|
||||
# The new value will be `true` if the value is currently falsy and will be
|
||||
# `false` if the value is currently truthy.
|
||||
#
|
||||
# Returns the new value.
|
||||
toggle: (keyPath) ->
|
||||
@set(keyPath, !@get(keyPath))
|
||||
|
||||
# Public: Push the value to the array at the key path.
|
||||
#
|
||||
# keyPath - The {String} key path.
|
||||
@@ -176,6 +168,18 @@ class Config
|
||||
@set(keyPath, arrayValue)
|
||||
result
|
||||
|
||||
# Public: Add the value to the beginning of the array at the key path.
|
||||
#
|
||||
# keyPath - The {String} key path.
|
||||
# value - The value to shift onto the array.
|
||||
#
|
||||
# Returns the new array length of the setting.
|
||||
unshiftAtKeyPath: (keyPath, value) ->
|
||||
arrayValue = @get(keyPath) ? []
|
||||
result = arrayValue.unshift(value)
|
||||
@set(keyPath, arrayValue)
|
||||
result
|
||||
|
||||
# Public: Remove the value from the array at the key path.
|
||||
#
|
||||
# keyPath - The {String} key path.
|
||||
@@ -211,12 +215,11 @@ class Config
|
||||
previousValue = _.clone(value)
|
||||
callback(value, {previous})
|
||||
|
||||
subscription = { cancel: => @off 'updated', updateCallback }
|
||||
@on "updated.#{keyPath.replace(/\./, '-')}", updateCallback
|
||||
eventName = "updated.#{keyPath.replace(/\./, '-')}"
|
||||
subscription = @on eventName, updateCallback
|
||||
callback(value) if options.callNow ? true
|
||||
subscription
|
||||
|
||||
|
||||
# Public: Unobserve all callbacks on a given key
|
||||
#
|
||||
# keyPath - The {String} name of the key to unobserve
|
||||
|
||||
@@ -9,14 +9,14 @@ class CursorView extends View
|
||||
@div class: 'cursor idle', => @raw ' '
|
||||
|
||||
blinkPeriod: 800
|
||||
editor: null
|
||||
editorView: null
|
||||
visible: true
|
||||
|
||||
needsUpdate: true
|
||||
needsRemoval: false
|
||||
shouldPauseBlinking: false
|
||||
|
||||
initialize: (@cursor, @editor) ->
|
||||
initialize: (@cursor, @editorView) ->
|
||||
@cursor.on 'moved.cursor-view', =>
|
||||
@needsUpdate = true
|
||||
@shouldPauseBlinking = true
|
||||
@@ -25,7 +25,7 @@ class CursorView extends View
|
||||
@needsUpdate = true
|
||||
|
||||
@cursor.on 'autoscrolled.cursor-view', =>
|
||||
@editor.requestDisplayUpdate()
|
||||
@editorView.requestDisplayUpdate()
|
||||
|
||||
@cursor.on 'destroyed.cursor-view', =>
|
||||
@needsRemoval = true
|
||||
@@ -34,7 +34,7 @@ class CursorView extends View
|
||||
@addClass("site-#{@cursor.marker.getOriginSiteId()}")
|
||||
|
||||
beforeRemove: ->
|
||||
@editor.removeCursorView(this)
|
||||
@editorView.removeCursorView(this)
|
||||
@cursor.off('.cursor-view')
|
||||
@stopBlinking()
|
||||
|
||||
@@ -52,7 +52,7 @@ class CursorView extends View
|
||||
else if !@startBlinkingTimeout
|
||||
@startBlinking()
|
||||
|
||||
@setVisible(@cursor.isVisible() and not @editor.isFoldedAtScreenRow(screenPosition.row))
|
||||
@setVisible(@cursor.isVisible() and not @editorView.isFoldedAtScreenRow(screenPosition.row))
|
||||
|
||||
# Override for speed. The base function checks the computedStyle
|
||||
isHidden: ->
|
||||
@@ -69,7 +69,7 @@ class CursorView extends View
|
||||
@cursor.clearAutoscroll()
|
||||
|
||||
getPixelPosition: ->
|
||||
@editor.pixelPositionForScreenPosition(@getScreenPosition())
|
||||
@editorView.pixelPositionForScreenPosition(@getScreenPosition())
|
||||
|
||||
setVisible: (visible) ->
|
||||
unless @visible == visible
|
||||
|
||||
+46
-34
@@ -5,7 +5,7 @@ _ = require 'underscore-plus'
|
||||
# Public: The `Cursor` class represents the little blinking line identifying
|
||||
# where text can be inserted.
|
||||
#
|
||||
# Cursors belong to {EditSession}s and have some metadata attached in the form
|
||||
# Cursors belong to {Editor}s and have some metadata attached in the form
|
||||
# of a {StringMarker}.
|
||||
module.exports =
|
||||
class Cursor
|
||||
@@ -17,8 +17,8 @@ class Cursor
|
||||
visible: true
|
||||
needsAutoscroll: null
|
||||
|
||||
# Private: Instantiated by an {EditSession}
|
||||
constructor: ({@editSession, @marker}) ->
|
||||
# Private: Instantiated by an {Editor}
|
||||
constructor: ({@editor, @marker}) ->
|
||||
@updateVisibility()
|
||||
@marker.on 'changed', (e) =>
|
||||
@updateVisibility()
|
||||
@@ -37,10 +37,10 @@ class Cursor
|
||||
textChanged: textChanged
|
||||
|
||||
@emit 'moved', movedEvent
|
||||
@editSession.emit 'cursor-moved', movedEvent
|
||||
@editor.emit 'cursor-moved', movedEvent
|
||||
@marker.on 'destroyed', =>
|
||||
@destroyed = true
|
||||
@editSession.removeCursor(this)
|
||||
@editor.removeCursor(this)
|
||||
@emit 'destroyed'
|
||||
@needsAutoscroll = true
|
||||
|
||||
@@ -62,7 +62,7 @@ class Cursor
|
||||
# An {Array} of two numbers: the screen row, and the screen column.
|
||||
# * options:
|
||||
# + autoscroll:
|
||||
# A Boolean which, if `true`, scrolls the {EditSession} to wherever the
|
||||
# A Boolean which, if `true`, scrolls the {Editor} to wherever the
|
||||
# cursor moves to.
|
||||
setScreenPosition: (screenPosition, options={}) ->
|
||||
@changePosition options, =>
|
||||
@@ -78,7 +78,7 @@ class Cursor
|
||||
# An {Array} of two numbers: the buffer row, and the buffer column.
|
||||
# * options:
|
||||
# + autoscroll:
|
||||
# A Boolean which, if `true`, scrolls the {EditSession} to wherever the
|
||||
# A Boolean which, if `true`, scrolls the {Editor} to wherever the
|
||||
# cursor moves to.
|
||||
setBufferPosition: (bufferPosition, options={}) ->
|
||||
@changePosition options, =>
|
||||
@@ -111,20 +111,20 @@ class Cursor
|
||||
# Returns a RegExp.
|
||||
wordRegExp: ({includeNonWordCharacters}={})->
|
||||
includeNonWordCharacters ?= true
|
||||
nonWordCharacters = config.get('editor.nonWordCharacters')
|
||||
nonWordCharacters = atom.config.get('editor.nonWordCharacters')
|
||||
segments = ["^[\t ]*$"]
|
||||
segments.push("[^\\s#{_.escapeRegExp(nonWordCharacters)}]+")
|
||||
if includeNonWordCharacters
|
||||
segments.push("[#{_.escapeRegExp(nonWordCharacters)}]+")
|
||||
new RegExp(segments.join("|"), "g")
|
||||
|
||||
# Public: Identifies if this cursor is the last in the {EditSession}.
|
||||
# Public: Identifies if this cursor is the last in the {Editor}.
|
||||
#
|
||||
# "Last" is defined as the most recently added cursor.
|
||||
#
|
||||
# Returns a Boolean.
|
||||
isLastCursor: ->
|
||||
this == @editSession.getCursor()
|
||||
this == @editor.getCursor()
|
||||
|
||||
# Public: Identifies if the cursor is surrounded by whitespace.
|
||||
#
|
||||
@@ -135,7 +135,7 @@ class Cursor
|
||||
isSurroundedByWhitespace: ->
|
||||
{row, column} = @getBufferPosition()
|
||||
range = [[row, Math.min(0, column - 1)], [row, Math.max(0, column + 1)]]
|
||||
/^\s+$/.test @editSession.getTextInBufferRange(range)
|
||||
/^\s+$/.test @editor.getTextInBufferRange(range)
|
||||
|
||||
# Public: Returns whether the cursor is currently between a word and non-word
|
||||
# character. The non-word characters are defined by the
|
||||
@@ -150,17 +150,17 @@ class Cursor
|
||||
|
||||
{row, column} = @getBufferPosition()
|
||||
range = [[row, column - 1], [row, column + 1]]
|
||||
[before, after] = @editSession.getTextInBufferRange(range)
|
||||
[before, after] = @editor.getTextInBufferRange(range)
|
||||
return false if /\s/.test(before) or /\s/.test(after)
|
||||
|
||||
nonWordCharacters = config.get('editor.nonWordCharacters').split('')
|
||||
nonWordCharacters = atom.config.get('editor.nonWordCharacters').split('')
|
||||
_.contains(nonWordCharacters, before) isnt _.contains(nonWordCharacters, after)
|
||||
|
||||
# Public: Returns whether this cursor is between a word's start and end.
|
||||
isInsideWord: ->
|
||||
{row, column} = @getBufferPosition()
|
||||
range = [[row, column], [row, Infinity]]
|
||||
@editSession.getTextInBufferRange(range).search(@wordRegExp()) == 0
|
||||
@editor.getTextInBufferRange(range).search(@wordRegExp()) == 0
|
||||
|
||||
# Public: Prevents this cursor from causing scrolling.
|
||||
clearAutoscroll: ->
|
||||
@@ -189,7 +189,7 @@ class Cursor
|
||||
# Public: Returns the cursor's current buffer row of text excluding its line
|
||||
# ending.
|
||||
getCurrentBufferLine: ->
|
||||
@editSession.lineForBufferRow(@getBufferRow())
|
||||
@editor.lineForBufferRow(@getBufferRow())
|
||||
|
||||
# Public: Moves the cursor up one screen row.
|
||||
moveUp: (rowCount = 1, {moveToEndOfSelection}={}) ->
|
||||
@@ -248,7 +248,7 @@ class Cursor
|
||||
|
||||
# Public: Moves the cursor to the bottom of the buffer.
|
||||
moveToBottom: ->
|
||||
@setBufferPosition(@editSession.getEofBufferPosition())
|
||||
@setBufferPosition(@editor.getEofBufferPosition())
|
||||
|
||||
# Public: Moves the cursor to the beginning of the screen line.
|
||||
moveToBeginningOfLine: ->
|
||||
@@ -258,7 +258,7 @@ class Cursor
|
||||
# line.
|
||||
moveToFirstCharacterOfLine: ->
|
||||
{row, column} = @getScreenPosition()
|
||||
screenline = @editSession.lineForScreenRow(row)
|
||||
screenline = @editor.lineForScreenRow(row)
|
||||
|
||||
goalColumn = screenline.text.search(/\S/)
|
||||
return if goalColumn == -1
|
||||
@@ -272,7 +272,7 @@ class Cursor
|
||||
position = @getBufferPosition()
|
||||
scanRange = @getCurrentLineBufferRange()
|
||||
endOfLeadingWhitespace = null
|
||||
@editSession.scanInBufferRange /^[ \t]*/, scanRange, ({range}) =>
|
||||
@editor.scanInBufferRange /^[ \t]*/, scanRange, ({range}) =>
|
||||
endOfLeadingWhitespace = range.end
|
||||
|
||||
@setBufferPosition(endOfLeadingWhitespace) if endOfLeadingWhitespace.isGreaterThan(position)
|
||||
@@ -318,11 +318,11 @@ class Cursor
|
||||
getBeginningOfCurrentWordBufferPosition: (options = {}) ->
|
||||
allowPrevious = options.allowPrevious ? true
|
||||
currentBufferPosition = @getBufferPosition()
|
||||
previousNonBlankRow = @editSession.buffer.previousNonBlankRow(currentBufferPosition.row)
|
||||
previousNonBlankRow = @editor.buffer.previousNonBlankRow(currentBufferPosition.row)
|
||||
scanRange = [[previousNonBlankRow, 0], currentBufferPosition]
|
||||
|
||||
beginningOfWordPosition = null
|
||||
@editSession.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) =>
|
||||
@editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) =>
|
||||
if range.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious
|
||||
beginningOfWordPosition = range.start
|
||||
if not beginningOfWordPosition?.isEqual(currentBufferPosition)
|
||||
@@ -334,11 +334,11 @@ class Cursor
|
||||
# the current word, or the previous word.
|
||||
getPreviousWordBoundaryBufferPosition: (options = {}) ->
|
||||
currentBufferPosition = @getBufferPosition()
|
||||
previousNonBlankRow = @editSession.buffer.previousNonBlankRow(currentBufferPosition.row)
|
||||
previousNonBlankRow = @editor.buffer.previousNonBlankRow(currentBufferPosition.row)
|
||||
scanRange = [[previousNonBlankRow, 0], currentBufferPosition]
|
||||
|
||||
beginningOfWordPosition = null
|
||||
@editSession.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
|
||||
@editor.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
|
||||
if range.start.row < currentBufferPosition.row and currentBufferPosition.column > 0
|
||||
# force it to stop at the beginning of each line
|
||||
beginningOfWordPosition = new Point(currentBufferPosition.row, 0)
|
||||
@@ -356,10 +356,10 @@ class Cursor
|
||||
# the current word, or the previous word.
|
||||
getMoveNextWordBoundaryBufferPosition: (options = {}) ->
|
||||
currentBufferPosition = @getBufferPosition()
|
||||
scanRange = [currentBufferPosition, @editSession.getEofBufferPosition()]
|
||||
scanRange = [currentBufferPosition, @editor.getEofBufferPosition()]
|
||||
|
||||
endOfWordPosition = null
|
||||
@editSession.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
|
||||
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
|
||||
if range.start.row > currentBufferPosition.row
|
||||
# force it to stop at the beginning of each line
|
||||
endOfWordPosition = new Point(range.start.row, 0)
|
||||
@@ -386,10 +386,10 @@ class Cursor
|
||||
getEndOfCurrentWordBufferPosition: (options = {}) ->
|
||||
allowNext = options.allowNext ? true
|
||||
currentBufferPosition = @getBufferPosition()
|
||||
scanRange = [currentBufferPosition, @editSession.getEofBufferPosition()]
|
||||
scanRange = [currentBufferPosition, @editor.getEofBufferPosition()]
|
||||
|
||||
endOfWordPosition = null
|
||||
@editSession.scanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) =>
|
||||
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) =>
|
||||
if range.start.isLessThanOrEqual(currentBufferPosition) or allowNext
|
||||
endOfWordPosition = range.end
|
||||
if not endOfWordPosition?.isEqual(currentBufferPosition)
|
||||
@@ -407,10 +407,10 @@ class Cursor
|
||||
getBeginningOfNextWordBufferPosition: (options = {}) ->
|
||||
currentBufferPosition = @getBufferPosition()
|
||||
start = if @isInsideWord() then @getEndOfCurrentWordBufferPosition() else currentBufferPosition
|
||||
scanRange = [start, @editSession.getEofBufferPosition()]
|
||||
scanRange = [start, @editor.getEofBufferPosition()]
|
||||
|
||||
beginningOfNextWordPosition = null
|
||||
@editSession.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
|
||||
@editor.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) =>
|
||||
beginningOfNextWordPosition = range.start
|
||||
stop()
|
||||
|
||||
@@ -432,7 +432,7 @@ class Cursor
|
||||
# + includeNewline:
|
||||
# A boolean which controls whether the Range should include the newline.
|
||||
getCurrentLineBufferRange: (options) ->
|
||||
@editSession.bufferRangeForBufferRow(@getBufferRow(), options)
|
||||
@editor.bufferRangeForBufferRow(@getBufferRow(), options)
|
||||
|
||||
# Public: Retrieves the range for the current paragraph.
|
||||
#
|
||||
@@ -440,11 +440,11 @@ class Cursor
|
||||
#
|
||||
# Returns a {Range}.
|
||||
getCurrentParagraphBufferRange: ->
|
||||
@editSession.languageMode.rowRangeForParagraphAtBufferRow(@getBufferRow())
|
||||
@editor.languageMode.rowRangeForParagraphAtBufferRow(@getBufferRow())
|
||||
|
||||
# Public: Returns the characters preceeding the cursor in the current word.
|
||||
getCurrentWordPrefix: ->
|
||||
@editSession.getTextInBufferRange([@getBeginningOfCurrentWordBufferPosition(), @getBufferPosition()])
|
||||
@editor.getTextInBufferRange([@getBeginningOfCurrentWordBufferPosition(), @getBufferPosition()])
|
||||
|
||||
# Public: Returns whether the cursor is at the start of a line.
|
||||
isAtBeginningOfLine: ->
|
||||
@@ -452,8 +452,8 @@ class Cursor
|
||||
|
||||
# Public: Returns the indentation level of the current line.
|
||||
getIndentLevel: ->
|
||||
if @editSession.getSoftTabs()
|
||||
@getBufferColumn() / @editSession.getTabLength()
|
||||
if @editor.getSoftTabs()
|
||||
@getBufferColumn() / @editor.getTabLength()
|
||||
else
|
||||
@getBufferColumn()
|
||||
|
||||
@@ -465,4 +465,16 @@ class Cursor
|
||||
#
|
||||
# Returns an {Array} of {String}s.
|
||||
getScopes: ->
|
||||
@editSession.scopesForBufferPosition(@getBufferPosition())
|
||||
@editor.scopesForBufferPosition(@getBufferPosition())
|
||||
|
||||
# Public: Returns true if this cursor has no non-whitespace characters before
|
||||
# its current position.
|
||||
hasPrecedingCharactersOnLine: ->
|
||||
bufferPosition = @getBufferPosition()
|
||||
line = @editor.lineForBufferRow(bufferPosition.row)
|
||||
firstCharacterColumn = line.search(/\S/)
|
||||
|
||||
if firstCharacterColumn is -1
|
||||
false
|
||||
else
|
||||
bufferPosition.column > firstCharacterColumn
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
{Document} = require 'telepath'
|
||||
|
||||
# Public: Manages the deserializers used for serialized state
|
||||
#
|
||||
# Should be accessed via `atom.deserializers`
|
||||
module.exports =
|
||||
class DeserializerManager
|
||||
constructor: ->
|
||||
@deserializers = {}
|
||||
@deferredDeserializers = {}
|
||||
|
||||
# Public: Add a deserializer.
|
||||
# Public: Register the given class(es) as deserializers.
|
||||
add: (klasses...) ->
|
||||
@deserializers[klass.name] = klass for klass in klasses
|
||||
|
||||
# Public: Add a deferred deserializer.
|
||||
# Public: Add a deferred deserializer for the given class name.
|
||||
addDeferred: (name, fn) ->
|
||||
@deferredDeserializers[name] = fn
|
||||
|
||||
# Public: Remove a deserializer.
|
||||
# Public: Remove the given class(es) as deserializers.
|
||||
remove: (klasses...) ->
|
||||
delete @deserializers[klass.name] for klass in klasses
|
||||
|
||||
@@ -32,7 +34,7 @@ class DeserializerManager
|
||||
else
|
||||
console.warn "No deserializer found for", state
|
||||
|
||||
# Public: Get the deserializer for the state.
|
||||
# Private: Get the deserializer for the state.
|
||||
get: (state) ->
|
||||
return unless state?
|
||||
|
||||
|
||||
+19
-12
@@ -1,6 +1,5 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
fsUtils = require './fs-utils'
|
||||
fs = require 'fs-plus'
|
||||
pathWatcher = require 'pathwatcher'
|
||||
File = require './file'
|
||||
{Emitter} = require 'emissary'
|
||||
@@ -13,31 +12,35 @@ class Directory
|
||||
path: null
|
||||
realPath: null
|
||||
|
||||
# Public: Configures an new Directory instance, no files are accessed.
|
||||
# Public: Configures a new Directory instance, no files are accessed.
|
||||
#
|
||||
# * path:
|
||||
# A {String} representing the file directory
|
||||
# A String containing the absolute path to the directory.
|
||||
# + symlink:
|
||||
# A {Boolean} indicating if the path is a symlink
|
||||
# A Boolean indicating if the path is a symlink (defaults to false).
|
||||
constructor: (@path, @symlink=false) ->
|
||||
@on 'first-contents-changed-subscription-will-be-added', =>
|
||||
# Triggered by emissary, when a new contents-changed listener attaches
|
||||
@subscribeToNativeChangeEvents()
|
||||
|
||||
@on 'last-contents-changed-subscription-removed', =>
|
||||
# Triggered by emissary, when the last contents-changed listener detaches
|
||||
@unsubscribeFromNativeChangeEvents()
|
||||
|
||||
# Public: Returns the basename of the directory.
|
||||
getBaseName: ->
|
||||
path.basename(@path)
|
||||
|
||||
# Public: Returns the directory's path.
|
||||
# Public: Returns the directory's symbolic path.
|
||||
#
|
||||
# FIXME what is the difference between real path and path?
|
||||
# This may include unfollowed symlinks or relative directory entries. Or it
|
||||
# may be fully resolved, it depends on what you give it.
|
||||
getPath: -> @path
|
||||
|
||||
# Public: Returns this directory's real path.
|
||||
# Public: Returns this directory's completely resolved path.
|
||||
#
|
||||
# FIXME what is the difference between real path and path?
|
||||
# All relative directory entries are removed and symlinks are resolved to
|
||||
# their final destination.
|
||||
getRealPath: ->
|
||||
unless @realPath?
|
||||
try
|
||||
@@ -46,7 +49,8 @@ class Directory
|
||||
@realPath = @path
|
||||
@realPath
|
||||
|
||||
# Public: Returns whether the given path is inside this directory.
|
||||
# Public: Returns whether the given path (real or symbolic) is inside this
|
||||
# directory.
|
||||
contains: (pathToCheck) ->
|
||||
return false unless pathToCheck
|
||||
|
||||
@@ -61,6 +65,9 @@ class Directory
|
||||
relativize: (fullPath) ->
|
||||
return fullPath unless fullPath
|
||||
|
||||
# Normalize forward slashes to back slashes on windows
|
||||
fullPath = fullPath.replace(/\//g, '\\') if process.platform is 'win32'
|
||||
|
||||
if fullPath is @getPath()
|
||||
''
|
||||
else if fullPath.indexOf(path.join(@getPath(), path.sep)) is 0
|
||||
@@ -76,11 +83,11 @@ class Directory
|
||||
#
|
||||
# Note: It follows symlinks.
|
||||
#
|
||||
# Returns an {Array} of {Files}.
|
||||
# Returns an Array of {Files}.
|
||||
getEntries: ->
|
||||
directories = []
|
||||
files = []
|
||||
for entryPath in fsUtils.listSync(@path)
|
||||
for entryPath in fs.listSync(@path)
|
||||
try
|
||||
stat = fs.lstatSync(entryPath)
|
||||
symlink = stat.isSymbolicLink()
|
||||
|
||||
+17
-14
@@ -18,7 +18,7 @@ class DisplayBuffer
|
||||
_.extend @prototype, ConfigObserver
|
||||
|
||||
@acceptsDocuments: true
|
||||
registerDeserializer(this)
|
||||
atom.deserializers.add(this)
|
||||
@version: 2
|
||||
|
||||
@deserialize: (state) -> new this(state)
|
||||
@@ -27,18 +27,18 @@ class DisplayBuffer
|
||||
if optionsOrState instanceof telepath.Document
|
||||
@state = optionsOrState
|
||||
@id = @state.get('id')
|
||||
@tokenizedBuffer = deserialize(@state.get('tokenizedBuffer'))
|
||||
@tokenizedBuffer = atom.deserializers.deserialize(@state.get('tokenizedBuffer'))
|
||||
@buffer = @tokenizedBuffer.buffer
|
||||
else
|
||||
{@buffer, softWrap, editorWidthInChars} = optionsOrState
|
||||
@id = guid.create().toString()
|
||||
@tokenizedBuffer = new TokenizedBuffer(optionsOrState)
|
||||
@state = site.createDocument
|
||||
@state = atom.site.createDocument
|
||||
deserializer: @constructor.name
|
||||
version: @constructor.version
|
||||
id: @id
|
||||
tokenizedBuffer: @tokenizedBuffer.getState()
|
||||
softWrap: softWrap ? config.get('editor.softWrap') ? false
|
||||
softWrap: softWrap ? atom.config.get('editor.softWrap') ? false
|
||||
editorWidthInChars: editorWidthInChars
|
||||
|
||||
@markers = {}
|
||||
@@ -50,14 +50,13 @@ class DisplayBuffer
|
||||
@subscribe @buffer, 'markers-updated', @handleBufferMarkersUpdated
|
||||
@subscribe @buffer, 'marker-created', @handleBufferMarkerCreated
|
||||
|
||||
@subscribe @state, 'changed', ({key, newValue}) =>
|
||||
switch key
|
||||
when 'softWrap'
|
||||
@emit 'soft-wrap-changed', newValue
|
||||
@updateWrappedScreenLines()
|
||||
@subscribe @state, 'changed', ({newValues}) =>
|
||||
if newValues.softWrap?
|
||||
@emit 'soft-wrap-changed', newValues.softWrap
|
||||
@updateWrappedScreenLines()
|
||||
|
||||
@observeConfig 'editor.preferredLineLength', callNow: false, =>
|
||||
@updateWrappedScreenLines() if @getSoftWrap() and config.get('editor.softWrapAtPreferredLineLength')
|
||||
@updateWrappedScreenLines() if @getSoftWrap() and atom.config.get('editor.softWrapAtPreferredLineLength')
|
||||
|
||||
@observeConfig 'editor.softWrapAtPreferredLineLength', callNow: false, =>
|
||||
@updateWrappedScreenLines() if @getSoftWrap()
|
||||
@@ -107,13 +106,15 @@ class DisplayBuffer
|
||||
#
|
||||
# editorWidthInChars - A {Number} of characters.
|
||||
setEditorWidthInChars: (editorWidthInChars) ->
|
||||
previousWidthInChars = @state.get('editorWidthInChars')
|
||||
@state.set('editorWidthInChars', editorWidthInChars)
|
||||
@updateWrappedScreenLines() if @getSoftWrap()
|
||||
if editorWidthInChars isnt previousWidthInChars and @getSoftWrap()
|
||||
@updateWrappedScreenLines()
|
||||
|
||||
getSoftWrapColumn: ->
|
||||
editorWidthInChars = @state.get('editorWidthInChars')
|
||||
if config.get('editor.softWrapAtPreferredLineLength')
|
||||
Math.min(editorWidthInChars, config.getPositiveInt('editor.preferredLineLength', editorWidthInChars))
|
||||
if atom.config.get('editor.softWrapAtPreferredLineLength')
|
||||
Math.min(editorWidthInChars, atom.config.getPositiveInt('editor.preferredLineLength', editorWidthInChars))
|
||||
else
|
||||
editorWidthInChars
|
||||
|
||||
@@ -364,7 +365,9 @@ class DisplayBuffer
|
||||
setTabLength: (tabLength) ->
|
||||
@tokenizedBuffer.setTabLength(tabLength)
|
||||
|
||||
# Retrieves the grammar for the buffer.
|
||||
# Get the grammar for this buffer.
|
||||
#
|
||||
# Returns the current {TextMateGrammar} or the {NullGrammar}.
|
||||
getGrammar: ->
|
||||
@tokenizedBuffer.grammar
|
||||
|
||||
|
||||
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+1317
-1715
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+30
-14
@@ -1,14 +1,15 @@
|
||||
crypto = require 'crypto'
|
||||
path = require 'path'
|
||||
pathWatcher = require 'pathwatcher'
|
||||
Q = require 'q'
|
||||
{Emitter} = require 'emissary'
|
||||
_ = require 'underscore-plus'
|
||||
fsUtils = require './fs-utils'
|
||||
fs = require 'fs-plus'
|
||||
|
||||
# Public: Represents an individual file in the editor.
|
||||
# Public: Represents an individual file.
|
||||
#
|
||||
# This class shouldn't be created directly, instead you should create a
|
||||
# {Directory} and access the {File} objects that it creates.
|
||||
# You should probably create a {Directory} and access the {File} objects that
|
||||
# it creates, rather than instantiating the {File} class directly.
|
||||
module.exports =
|
||||
class File
|
||||
Emitter.includeInto(this)
|
||||
@@ -16,26 +17,29 @@ class File
|
||||
path: null
|
||||
cachedContents: null
|
||||
|
||||
# Private: Creates a new file.
|
||||
# Public: Creates a new file.
|
||||
#
|
||||
# * path:
|
||||
# A String representing the file path
|
||||
# A String containing the absolute path to the file
|
||||
# * symlink:
|
||||
# A Boolean indicating if the path is a symlink (default: false)
|
||||
constructor: (@path, @symlink=false) ->
|
||||
throw new Error("#{@path} is a directory") if fsUtils.isDirectorySync(@path)
|
||||
throw new Error("#{@path} is a directory") if fs.isDirectorySync(@path)
|
||||
|
||||
@handleEventSubscriptions()
|
||||
|
||||
# Private: Subscribes to file system notifications when necessary.
|
||||
handleEventSubscriptions: ->
|
||||
eventNames = ['contents-changed', 'moved', 'removed']
|
||||
|
||||
subscriptionsAdded = eventNames.map (eventName) -> "first-#{eventName}-subscription-will-be-added"
|
||||
@on subscriptionsAdded.join(' '), =>
|
||||
# Only subscribe when a listener of eventName attaches (triggered by emissary)
|
||||
@subscribeToNativeChangeEvents() if @exists()
|
||||
|
||||
subscriptionsRemoved = eventNames.map (eventName) -> "last-#{eventName}-subscription-removed"
|
||||
@on subscriptionsRemoved.join(' '), =>
|
||||
# Detach when the last listener of eventName detaches (triggered by emissary)
|
||||
subscriptionsEmpty = _.every eventNames, (eventName) => @getSubscriptionCount(eventName) is 0
|
||||
@unsubscribeFromNativeChangeEvents() if subscriptionsEmpty
|
||||
|
||||
@@ -53,18 +57,21 @@ class File
|
||||
write: (text) ->
|
||||
previouslyExisted = @exists()
|
||||
@cachedContents = text
|
||||
fsUtils.writeSync(@getPath(), text)
|
||||
@subscribeToNativeChangeEvents() if not previouslyExisted and @subscriptionCount() > 0
|
||||
fs.writeFileSync(@getPath(), text)
|
||||
@subscribeToNativeChangeEvents() if not previouslyExisted and @hasSubscriptions()
|
||||
|
||||
# Private: Deprecated
|
||||
readSync: (flushCache) ->
|
||||
if not @exists()
|
||||
@cachedContents = null
|
||||
else if not @cachedContents? or flushCache
|
||||
@cachedContents = fsUtils.read(@getPath())
|
||||
@cachedContents = fs.readFileSync(@getPath(), 'utf8')
|
||||
else
|
||||
@cachedContents
|
||||
|
||||
@setDigest(@cachedContents)
|
||||
@cachedContents
|
||||
|
||||
# Public: Reads the contents of the file.
|
||||
#
|
||||
# * flushCache:
|
||||
@@ -76,14 +83,14 @@ class File
|
||||
if not @exists()
|
||||
promise = Q(null)
|
||||
else if not @cachedContents? or flushCache
|
||||
if fsUtils.statSyncNoException(@getPath()).size >= 1048576 # 1MB
|
||||
if fs.getSizeSync(@getPath()) >= 1048576 # 1MB
|
||||
throw new Error("Atom can only handle files < 1MB, for now.")
|
||||
|
||||
deferred = Q.defer()
|
||||
promise = deferred.promise
|
||||
content = []
|
||||
bytesRead = 0
|
||||
readStream = fsUtils.createReadStream @getPath(), encoding: 'utf8'
|
||||
readStream = fs.createReadStream @getPath(), encoding: 'utf8'
|
||||
readStream.on 'data', (chunk) ->
|
||||
content.push(chunk)
|
||||
bytesRead += chunk.length
|
||||
@@ -98,11 +105,20 @@ class File
|
||||
promise = Q(@cachedContents)
|
||||
|
||||
promise.then (contents) =>
|
||||
@setDigest(contents)
|
||||
@cachedContents = contents
|
||||
|
||||
# Public: Returns whether a file exists.
|
||||
# Public: Returns whether the file exists.
|
||||
exists: ->
|
||||
fsUtils.exists(@getPath())
|
||||
fs.existsSync(@getPath())
|
||||
|
||||
# Private:
|
||||
setDigest: (contents) ->
|
||||
@digest = crypto.createHash('sha1').update(contents ? '').digest('hex')
|
||||
|
||||
# Public: Get the SHA-1 digest of this file
|
||||
getDigest: ->
|
||||
@digest ? @setDigest(@readSync())
|
||||
|
||||
# Private:
|
||||
handleNativeChangeEvent: (eventType, path) ->
|
||||
|
||||
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