Comparar commits
1816 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| c77a6b10de | |||
| f86280a77b | |||
| 65ae582d96 | |||
| 5694f9c703 | |||
| 9cfa46ea37 | |||
| 11d5dfee3a | |||
| 864f61c430 | |||
| ca164a0b0b | |||
| e011becc1a | |||
| 25f5717ccf | |||
| 3f0640f4c3 | |||
| c1ed25d5dc | |||
| f731769afd | |||
| c466cef7d1 | |||
| b4cb92af99 | |||
| b2e86c80c4 | |||
| b77ea04056 | |||
| 7cdaaf2f78 | |||
| 6caf60bd6b | |||
| 19c7086200 | |||
| 1519dda294 | |||
| a7efca8bb4 | |||
| dbe15f7dda | |||
| c81a6737ad | |||
| 5b0d974b43 | |||
| 2b73dff0f4 | |||
| adf0ff0a67 | |||
| c79ef0473d | |||
| d51b955e09 | |||
| 774d7ec0af | |||
| d6f4b00e16 | |||
| 9d81df8670 | |||
| 14a430b939 | |||
| 07de4a70a1 | |||
| eb84ac829b | |||
| b8db56a77b | |||
| 1036f16d1e | |||
| 5ed1cfc259 | |||
| ce5c29fb47 | |||
| f0fd48202c | |||
| d5eb8c21b2 | |||
| 76b9982e04 | |||
| ca7f11f7d0 | |||
| 1c1ace90db | |||
| bdd605e85b | |||
| e1b4b921ba | |||
| ab1ede5fe6 | |||
| 5e6d91d66c | |||
| dbd271f70a | |||
| 527ada47f9 | |||
| afb70d0a95 | |||
| b5f910ad06 | |||
| e412371b88 | |||
| 7b4bc16531 | |||
| e6df30e94c | |||
| d9ba9262bf | |||
| c4be32a5dd | |||
| 0a32f6b5f0 | |||
| f9fe5efbb0 | |||
| 87e723e33b | |||
| 3f0302b256 | |||
| 88d024e73b | |||
| 47a2e57633 | |||
| e7acbb314e | |||
| d6a4c70929 | |||
| 839cad0c2a | |||
| d3845db403 | |||
| 3d68bdf126 | |||
| 0d190d2cd4 | |||
| 4afae028ec | |||
| 5165f0df88 | |||
| 5b7b3501a6 | |||
| c3937d0c4c | |||
| 6e27208c5c | |||
| 91342db0ba | |||
| 8f7123ae12 | |||
| 3a22b3d4b8 | |||
| 919ca82ccd | |||
| d35baac054 | |||
| 9aa2df7cee | |||
| 41ea18a8f1 | |||
| 5cf37fd13e | |||
| 3e12695914 | |||
| ed1c8897ec | |||
| 75eb0182e9 | |||
| 554165ca48 | |||
| 5a5cb869e2 | |||
| a649d75ab8 | |||
| 255cdbb60a | |||
| bd4e56fd08 | |||
| 5246d784db | |||
| e945b83318 | |||
| 38bd996996 | |||
| 8f3a72e11b | |||
| ddc62efb44 | |||
| 8f08e497a0 | |||
| 8048f8af5f | |||
| 4fc4e36902 | |||
| c66d3fdba0 | |||
| 9b49c2f987 | |||
| bceed13606 | |||
| d99c9edc43 | |||
| 31306a3243 | |||
| 67de17e0c3 | |||
| 3371252656 | |||
| a7e9037e5b | |||
| b3e376ce7c | |||
| d24b664873 | |||
| b8394830a0 | |||
| 7d76105530 | |||
| bbe399196f | |||
| 8c6cdf7358 | |||
| f71bb9349b | |||
| 6bb260140b | |||
| 88aec85f92 | |||
| 61cdee9743 | |||
| 15c3efa6e4 | |||
| a1835efb4e | |||
| 5e0f132d33 | |||
| 2c7c4c95f2 | |||
| 81f115d3db | |||
| c4f872acff | |||
| b3dbb18889 | |||
| 02a278d80c | |||
| c7954a4c5e | |||
| 61fff23be2 | |||
| 7070ed8ae4 | |||
| 101b18e408 | |||
| f0bd3b1c20 | |||
| 921bf8501f | |||
| 594a2d201b | |||
| cb0ba55871 | |||
| 27bcf046b1 | |||
| 44f19610b6 | |||
| a1d2e253ea | |||
| a961a8f644 | |||
| 86b7fec0bb | |||
| 6060e0d8a9 | |||
| c160601a9d | |||
| 56a1ecf6c1 | |||
| 648441ee5c | |||
| 03f0e084e1 | |||
| ab57dc840f | |||
| a308903735 | |||
| b6cb604330 | |||
| fb557e9b90 | |||
| 3180ab067c | |||
| 4b7d982eb4 | |||
| fbabc6f455 | |||
| fa759d8128 | |||
| e9ed45671f | |||
| 8cc871f326 | |||
| 3651adbefd | |||
| ff70ded25b | |||
| 244c06b524 | |||
| e7309b254c | |||
| 0cdce9c665 | |||
| 7a10bf1c33 | |||
| a6d5a4ab5d | |||
| 68f3c98872 | |||
| 0b8c0cc431 | |||
| d9eaf8d334 | |||
| 8d29ec4116 | |||
| 806ff4e141 | |||
| 4b0ddbf2ab | |||
| 1ea54f8c92 | |||
| 31cb4c58c2 | |||
| b9bffc32b0 | |||
| b267a781da | |||
| 6f7209c68b | |||
| fb6782ef31 | |||
| fae8aafc17 | |||
| ba38dddf4d | |||
| 0a4600409a | |||
| 706e4476d5 | |||
| c113a0a217 | |||
| ef05278537 | |||
| 1254425ba8 | |||
| e3641286e7 | |||
| 3a03f470c7 | |||
| 4ea44d9612 | |||
| 15c2540c29 | |||
| 112f90979c | |||
| cbe73fd916 | |||
| 3ea28c8ed6 | |||
| 5a219a9087 | |||
| 11eae4d5db | |||
| fe4016a49d | |||
| 0b786427f4 | |||
| 4900dc6bcd | |||
| 31b06b2e47 | |||
| cf6d83a896 | |||
| 637410a8c5 | |||
| 084e4ab830 | |||
| 5a53b34697 | |||
| 8f3b76d3c9 | |||
| f5df111b5c | |||
| 1d31a10dc4 | |||
| db61de6cf3 | |||
| c75d36ef93 | |||
| bb2527bbb8 | |||
| eb31c6c85f | |||
| 546f81f2d0 | |||
| 7bb6cd99eb | |||
| 3dbdaee25c | |||
| b458de0f2d | |||
| 240747ed76 | |||
| 8b5fd64dd4 | |||
| 1c514bdc6b | |||
| f4d9abc789 | |||
| e5197f6f0f | |||
| 8f57246571 | |||
| 7658107e3b | |||
| c33227830e | |||
| 43f2153c86 | |||
| 084d84660a | |||
| 4120b34d22 | |||
| c4a0bce92f | |||
| 858c18114b | |||
| 91f9302fc4 | |||
| 4f3bf04668 | |||
| 11c9417e8d | |||
| db141d9d7f | |||
| 5624596c88 | |||
| cda7ca047c | |||
| ac25ad3985 | |||
| 1bf4e7ab96 | |||
| 49587ace05 | |||
| 54fb06d4b0 | |||
| 8f81b5dc63 | |||
| d5e1649607 | |||
| e2c1f6c0e2 | |||
| 8ec7d1273c | |||
| 7ffc5cc02b | |||
| aac27db552 | |||
| 03acb1c40a | |||
| 5eb332042e | |||
| 66943c4820 | |||
| 4ed9e34893 | |||
| 4f4da78e54 | |||
| 832dfd057e | |||
| dd74280146 | |||
| a7d1a68fb8 | |||
| 6b8b454625 | |||
| 5c5b7df70d | |||
| 7d3abda86f | |||
| 7f824ffbb3 | |||
| 0b7475a4c1 | |||
| cf62d3212c | |||
| b260cbb43a | |||
| 1367f08de8 | |||
| 27b02a7981 | |||
| e62ae02c00 | |||
| 233402dd33 | |||
| 69821b7a6b | |||
| efcffe5205 | |||
| 802e651b6e | |||
| 0e8b951050 | |||
| 8ef97bd78e | |||
| 6749988de1 | |||
| 4a26f8f28b | |||
| a4ee412e96 | |||
| 4f655d7de5 | |||
| 33c02ef3e1 | |||
| aa4749f175 | |||
| 6ace49b0e8 | |||
| 9cf642e910 | |||
| 2990f36595 | |||
| 032fc86a4d | |||
| 868cbb680c | |||
| 98d2aaf10e | |||
| f7dead17c0 | |||
| f3168b2672 | |||
| e8c62b501f | |||
| 6ff2b2a01e | |||
| 960285a270 | |||
| 17cd979299 | |||
| b5846f9755 | |||
| dd39ffdf3b | |||
| 368b871270 | |||
| 9c06b4d344 | |||
| f48241bc5e | |||
| a7b94e1709 | |||
| 3d2dedb49f | |||
| dd5ab8098e | |||
| f5b833c49c | |||
| ba032be402 | |||
| a96a448063 | |||
| faeed9f604 | |||
| 0a8f2422d3 | |||
| 24dbe19a82 | |||
| 7d8901072c | |||
| 848a4e2052 | |||
| e8090b16f3 | |||
| 950964eee3 | |||
| b403f76d81 | |||
| 8f2cdae0eb | |||
| fa4b3c8dea | |||
| e76bbc0f13 | |||
| c9a377d4ac | |||
| c7e308bca0 | |||
| 5193e6e46b | |||
| 66628e8c52 | |||
| bec65cf2ac | |||
| 606b249430 | |||
| 8bc1b7e955 | |||
| 2ceccf5239 | |||
| ad8bd98c9a | |||
| 6c4e138ec0 | |||
| 9ad1021c2d | |||
| c03dec2783 | |||
| 9d92a27ccf | |||
| b652dc8fd8 | |||
| c1929540a3 | |||
| 1601423f30 | |||
| 1de2c14e50 | |||
| c1907054b7 | |||
| 5f524c1181 | |||
| b897603b66 | |||
| 0e5c530006 | |||
| 4a60b5787a | |||
| 88cc69f87a | |||
| 42e91defb9 | |||
| aeb5a84696 | |||
| 228836447c | |||
| 832cc40a7f | |||
| d89b4534f1 | |||
| 725f6f7cf2 | |||
| b760d3e4fc | |||
| 1d634e471e | |||
| babbdbf9e5 | |||
| e9d820e8a8 | |||
| 16e05347a7 | |||
| 2574ee5936 | |||
| 283966dbb9 | |||
| 38c25feee0 | |||
| f8727ffa6f | |||
| 1f31918373 | |||
| 104aa5efc7 | |||
| 7fe0f5b445 | |||
| a15bb260c0 | |||
| 6b833043db | |||
| c1260d68bb | |||
| b30a2c9442 | |||
| 3eb99c9391 | |||
| 08748b5bad | |||
| 10636b96bc | |||
| b5ac7c89a3 | |||
| 01298bb470 | |||
| 752aa9a8e9 | |||
| e4639281f8 | |||
| f53d489abb | |||
| 628c2f82bd | |||
| df8a6437a5 | |||
| 1f768a21f0 | |||
| b13385b281 | |||
| 68d74e7de0 | |||
| c730e3c67e | |||
| 22496ceeb1 | |||
| a03f2f46ee | |||
| afec8f1ca0 | |||
| 43e6fb73f1 | |||
| a271e52a4e | |||
| 274ca33959 | |||
| 10d6ec156f | |||
| 4e27e765d0 | |||
| 168cda4f75 | |||
| fdccc0bcc2 | |||
| d566726b9f | |||
| e9f2a536ed | |||
| 2532527a6a | |||
| f10076c87d | |||
| 083f65ed5d | |||
| f59a8f1e68 | |||
| bef554709f | |||
| e5379515b9 | |||
| a0ff6f5325 | |||
| dd4b6a6d28 | |||
| 51ee591282 | |||
| 19a5269a5f | |||
| 9b6fa967be | |||
| 201e00aa83 | |||
| f02d956362 | |||
| 798739f837 | |||
| 216d561c79 | |||
| 6607f99c6c | |||
| 3a42346e5e | |||
| ae9f79bfc4 | |||
| 5a9a3c62e1 | |||
| d678f367db | |||
| febfb120c8 | |||
| addbe80e8a | |||
| b96abfffb7 | |||
| a6f2e926fe | |||
| 550a4ce906 | |||
| 1a56b487a1 | |||
| 4fa9c64c2b | |||
| 56e5fb7a63 | |||
| efa72bcb1c | |||
| 033db8997b | |||
| 64a487eebb | |||
| ddc677fb30 | |||
| fe6a007774 | |||
| 48d90e3dfc | |||
| f457b41a81 | |||
| 93388c2048 | |||
| 66f3f2d883 | |||
| 3657dc0bf4 | |||
| 274288161d | |||
| 0d03e388f1 | |||
| 507106d35b | |||
| eeba559ec7 | |||
| 14bfa90004 | |||
| 0ec6cbe141 | |||
| e952ab2e02 | |||
| 5c2eb053d8 | |||
| aee552476a | |||
| 355abef2cf | |||
| cec62c56a6 | |||
| 35ea4e6de4 | |||
| 8c266957f1 | |||
| f1f93f2f70 | |||
| de773e4f75 | |||
| 205e10fd09 | |||
| ca4dd5a29a | |||
| 2517765821 | |||
| 4b9871fa13 | |||
| 28dd7d4acd | |||
| e3eb51c135 | |||
| 9ec38ddb0d | |||
| 7a4dc0b9a4 | |||
| 6b4ce5f205 | |||
| 9a3f8022ad | |||
| a2a625a7bb | |||
| 495b1571ca | |||
| c862ccbc56 | |||
| 59709a92ba | |||
| 022f5ca219 | |||
| 96ebb9bf03 | |||
| 95b24fb933 | |||
| 9bdc78df2e | |||
| 241731f9c8 | |||
| 6997adece9 | |||
| d0a917ed14 | |||
| 7fc2e0b540 | |||
| 1c48f60e42 | |||
| 6b10fcc2f8 | |||
| cf27826156 | |||
| 5e38add177 | |||
| b4af0a79d0 | |||
| 616b9e4b7d | |||
| 985662b8f0 | |||
| cfdea7e73f | |||
| 48135a1e8d | |||
| 81233a696b | |||
| 84bb624b5b | |||
| 171631d20f | |||
| 486a7937b5 | |||
| 1162af61ed | |||
| a931aaff53 | |||
| 0fd8c5441c | |||
| f5551929d8 | |||
| 4a501a7c31 | |||
| 44413912bc | |||
| d0c61eb2be | |||
| 5c9a5cdc85 | |||
| 2b0ef68255 | |||
| f60f9b9f4f | |||
| 4f10500102 | |||
| 096afcf6f3 | |||
| 2204571ef5 | |||
| cbad8a56ec | |||
| c4be3069f7 | |||
| accee294dc | |||
| 1239ab540f | |||
| 5a0d7e716b | |||
| af9355e4e6 | |||
| 7738e74df0 | |||
| 3a433f734c | |||
| b0ad5e2f69 | |||
| 0c960552f0 | |||
| 8772e96935 | |||
| e24196c0ef | |||
| b879221a96 | |||
| 97a353b31a | |||
| 57e2cf80f4 | |||
| 669586c11b | |||
| 5b79bb7f66 | |||
| fd2ed9a1bc | |||
| 95bf08dfa0 | |||
| 3d3b72a954 | |||
| 724babdcb6 | |||
| bad2cebd6e | |||
| 565b611c18 | |||
| 1b8f23722b | |||
| ab02d5f25f | |||
| e472d7b038 | |||
| ba83b0ede0 | |||
| 96e6ddac2e | |||
| 08bd03b706 | |||
| 2f42f23ec6 | |||
| 5a8ca1ae66 | |||
| 9898cbc52e | |||
| 53cc5c9856 | |||
| 148a9f0248 | |||
| 48e2302ccb | |||
| 6327094696 | |||
| c4fdb54650 | |||
| a55c329226 | |||
| e365e51a2b | |||
| c44fd62eb1 | |||
| 9c2d321327 | |||
| 70e5880b1d | |||
| 61d9ff4ba4 | |||
| e4c1bf10f5 | |||
| c2858fcae2 | |||
| fddd411279 | |||
| 2dda577d7c | |||
| 958bc638d7 | |||
| 33ed403818 | |||
| 3c69fd2d49 | |||
| a134a60ce8 | |||
| 9c49a2d970 | |||
| 8cd9160ed5 | |||
| 8ad13d3045 | |||
| cc8ba2d679 | |||
| da36d5f40f | |||
| 3bd7bfcbf1 | |||
| 9904a9a197 | |||
| 3e94c0be22 | |||
| db0cce6234 | |||
| f3d4cbbd8f | |||
| 81df4ed238 | |||
| 7b756ec3f3 | |||
| 7250a9e407 | |||
| 1a02b2ec98 | |||
| 0d95718d1f | |||
| 1da23e8d04 | |||
| 43792d019a | |||
| ec0b4a2dc6 | |||
| 94c860e936 | |||
| 1aeb32eb81 | |||
| 1083136dec | |||
| e450e61248 | |||
| fd7951b2c6 | |||
| 743e79b659 | |||
| 3d10c3856b | |||
| d5df83e872 | |||
| 248948b217 | |||
| 7d3553659a | |||
| 28dad3a01a | |||
| be872c2fdd | |||
| 526d87c355 | |||
| cbeb0187da | |||
| 7c614c6c79 | |||
| f4f0b4be72 | |||
| 3e27beeafa | |||
| 4a6b70d261 | |||
| 5917f71685 | |||
| fb42c499b1 | |||
| 1583271e2f | |||
| 020e22a13d | |||
| a37a7e0a76 | |||
| d1b4d0f9f2 | |||
| fb111dd0f4 | |||
| 1392deb29c | |||
| 2dafc5eaa1 | |||
| a407b0107a | |||
| 36c3c38047 | |||
| e56317bd64 | |||
| da49751f24 | |||
| 762559d2b7 | |||
| 2a0070ef10 | |||
| 7c7de4436e | |||
| 87afa50477 | |||
| 2e71e2fa4b | |||
| b537ba8864 | |||
| c156119882 | |||
| fea958c8e4 | |||
| 8e0a695f7c | |||
| 47f99cd74e | |||
| 0eb5fd4c3b | |||
| 0c01de350d | |||
| 77641f138b | |||
| f24843a928 | |||
| 03ab77c60b | |||
| ec0ec3bb16 | |||
| 690fc7180a | |||
| 02553bf8e2 | |||
| 81646532b4 | |||
| 1e7c80aebf | |||
| 27fe5b784c | |||
| 9b2901583e | |||
| 408665d7d2 | |||
| 6ec58e1163 | |||
| 541ffa5057 | |||
| 0ea683e0b5 | |||
| f12004d27b | |||
| 35d268fcf8 | |||
| 20c3ca21e3 | |||
| 37d2e00f4e | |||
| 5bf9bbe764 | |||
| e37fc316fb | |||
| 9128041c20 | |||
| 020c5e795a | |||
| be3a6bc6d0 | |||
| 142bb4b615 | |||
| ddf7bb5303 | |||
| 442342937f | |||
| a967e52f68 | |||
| c475e89a3e | |||
| 58da88b71a | |||
| 71328f21bd | |||
| a1b0f6c25d | |||
| fdccc2dae4 | |||
| 1246f8ed20 | |||
| fb56817895 | |||
| b6d8d5d100 | |||
| 8eb23dbe5c | |||
| 34f624c895 | |||
| d554b7fd03 | |||
| a61dff6e21 | |||
| 47b820ce7d | |||
| 33557b87eb | |||
| aed0c16acc | |||
| b4c8cb4bf9 | |||
| 8988d55d25 | |||
| 5824b127ed | |||
| d6ec73886f | |||
| 411cf579f4 | |||
| ad0ed7e634 | |||
| 7c00e02b1a | |||
| e6040972d8 | |||
| ac2f723aba | |||
| ecfb505144 | |||
| 326d1dce14 | |||
| c2f87fb73b | |||
| 1245835e96 | |||
| e174bdfa40 | |||
| 3d8bc89c42 | |||
| 5fc6524493 | |||
| 6e6c93e6bb | |||
| fd48ad1c67 | |||
| 5163166990 | |||
| 54b60a5253 | |||
| ff1d07bec9 | |||
| 6d56283448 | |||
| 6dfbd5733d | |||
| e56fa3ec4f | |||
| fe0d714710 | |||
| 7c1a7f52a5 | |||
| 590c6be33d | |||
| f1ca7fdbfd | |||
| a8b135b4bc | |||
| 1afdd62a5d | |||
| d47e8fc583 | |||
| 3644a36d67 | |||
| bb98b3ef38 | |||
| 356a7b014a | |||
| 1ea0ef4ea2 | |||
| f37ca3e19c | |||
| 8afd90b9f8 | |||
| 613e05f4ac | |||
| 068eeca862 | |||
| 0502afb0c3 | |||
| e1491ca154 | |||
| e9a975fd92 | |||
| 1459123b15 | |||
| 9996eb825f | |||
| 10392942d5 | |||
| c452d05f44 | |||
| 1043f75c72 | |||
| ec69bb5ffe | |||
| 41cc0ecab4 | |||
| 67f8ddc314 | |||
| 10d626d63a | |||
| 2ec526508b | |||
| 6017ff585f | |||
| e6d656830f | |||
| 1a15c3394e | |||
| b328a69f50 | |||
| e9ee6dca25 | |||
| a2f73fd6b0 | |||
| 9f09173d0f | |||
| 1e052c1113 | |||
| b72bb4ab65 | |||
| 24f751ec83 | |||
| d72e3d9c81 | |||
| 0aeb32ad70 | |||
| 3393583b72 | |||
| 6fcd905d4f | |||
| 993534337c | |||
| 03814246ef | |||
| 053965602c | |||
| b45c1c7548 | |||
| e96d821653 | |||
| e3448b0a46 | |||
| 63a80a4d4d | |||
| d07eea405f | |||
| 87d008c337 | |||
| 4a55c90c63 | |||
| 7bce6cb10a | |||
| 32499351dc | |||
| 2d7dda066c | |||
| 837f3c553f | |||
| 96e3c63291 | |||
| d5da396c0f | |||
| 4b089b62a6 | |||
| be8514b864 | |||
| e826b66dd2 | |||
| 83d6fc2374 | |||
| ecdaf48308 | |||
| a79885eafb | |||
| a3c9a65838 | |||
| 8ca5d6db35 | |||
| 980c17e50c | |||
| c9bf8f04b8 | |||
| 6613b9fd4d | |||
| 3168751c97 | |||
| 6bc471cef1 | |||
| 4b3240eeee | |||
| 1f9de98eb2 | |||
| 7eea30fa7f | |||
| 63e51af87d | |||
| bbc6433a75 | |||
| d4ae836225 | |||
| 789f29e0b5 | |||
| c584c158d7 | |||
| d63c4f88fd | |||
| db327b92f3 | |||
| cd01e6f2ad | |||
| b1be4e9183 | |||
| 009a142273 | |||
| f0f00fd317 | |||
| f5ea098417 | |||
| 82081aa5e9 | |||
| 33adaab8b1 | |||
| e21a1339a2 | |||
| b90849fa66 | |||
| 23d3405ae7 | |||
| d39c422da9 | |||
| 053c0f703a | |||
| 1c91bd3100 | |||
| 0ffb5da515 | |||
| b7db7f6c99 | |||
| cae9782e24 | |||
| 5dbab7bb89 | |||
| ceab8e8166 | |||
| 97cb440a7c | |||
| fcdb42dbe4 | |||
| 11a91fb974 | |||
| 1d426e8fd2 | |||
| 6f0094e6ce | |||
| 915cad39e8 | |||
| d8f0743888 | |||
| 7685b27218 | |||
| 3aab0b7f51 | |||
| 2f5735e264 | |||
| 56bea31b5e | |||
| 52d5e205c7 | |||
| 0f5f6c8657 | |||
| 7457be2805 | |||
| 86842658c0 | |||
| d0276e60b5 | |||
| 18d6a56e82 | |||
| 59108322e7 | |||
| 665088ce27 | |||
| 79399a3c5a | |||
| 4659b95d36 | |||
| bdf73cd198 | |||
| 6e422d5428 | |||
| e464c4f047 | |||
| 4610c4446a | |||
| 156ca14ea6 | |||
| cebb27154d | |||
| afe6685ce8 | |||
| 148e143255 | |||
| 96d1c84f62 | |||
| f40ecccdcc | |||
| 03b9713547 | |||
| 9fb364026e | |||
| 1aa64a9d80 | |||
| 2ea09aa6b6 | |||
| 76ce150448 | |||
| 1d01ff0797 | |||
| a1d78a1062 | |||
| 3ac41a544a | |||
| 3e24d045e5 | |||
| 143738033b | |||
| f153cbb41f | |||
| f1ed5dedd7 | |||
| e58c2d8368 | |||
| bcf1781040 | |||
| 0b0dbbe4ee | |||
| c9e297a511 | |||
| 18082f7cdc | |||
| ca016ce116 | |||
| bb59e46267 | |||
| 86c4b99eab | |||
| a1e1a00f9f | |||
| 53996e7bb0 | |||
| f88c71e1a1 | |||
| 2cccbff509 | |||
| b173fcdd59 | |||
| 7b6c62ab5e | |||
| 809e57cb84 | |||
| de16ed6250 | |||
| 606160d312 | |||
| 78e990d1f9 | |||
| d47cc6fa0b | |||
| 8f3951fbd8 | |||
| e09486d425 | |||
| 662f252912 | |||
| 784d22368e | |||
| a40660d125 | |||
| af632df48b | |||
| 4a35c9a1af | |||
| f90f48e3f0 | |||
| 9c6a3c3d03 | |||
| 4bccb81656 | |||
| 870e80446f | |||
| 4165a69fed | |||
| 93d9a00819 | |||
| a3789411de | |||
| 2b6b40bda7 | |||
| 61ae185494 | |||
| 0eae68973b | |||
| 6f2695388c | |||
| 0c12f712c9 | |||
| 727d1cf4aa | |||
| 9324b538f7 | |||
| 5aff4a042b | |||
| e36bc5bc9f | |||
| 1a88937f5a | |||
| 5737593957 | |||
| d61d8bea86 | |||
| 062974882b | |||
| 8415ddcb49 | |||
| 64f392a380 | |||
| fce09f9bfe | |||
| f04ce025f4 | |||
| 57844c2ca4 | |||
| 91433cfe90 | |||
| 86423698b1 | |||
| 7dda8e1b80 | |||
| 70cd6a6bc3 | |||
| 6e9217be5f | |||
| 3d8c916a72 | |||
| cae9c96622 | |||
| aaf283ceaa | |||
| 4bac60ad94 | |||
| cc2e87b784 | |||
| 363fb0d66e | |||
| 2d2295dcc6 | |||
| bf3daeccc5 | |||
| 63d5caebbc | |||
| 3ac3a89fa5 | |||
| af6d7204f9 | |||
| 642671161d | |||
| b2fdc25c83 | |||
| 893a3f63dd | |||
| 271b104120 | |||
| 247eec44d5 | |||
| d064c27faf | |||
| 8c6ed69176 | |||
| 56222a072a | |||
| 1021979d98 | |||
| fcc4376cdd | |||
| afbf011103 | |||
| e858f74a17 | |||
| 58dc45d496 | |||
| 3029dc8d2a | |||
| 11d7562c12 | |||
| b8b95d537b | |||
| 9ec342a545 | |||
| cf60b1fde3 | |||
| 556cdfd300 | |||
| c566e7f1f3 | |||
| 366312dee8 | |||
| 1acc6bfadf | |||
| 9fd21ce2e1 | |||
| 5525e046e9 | |||
| 68e1e36347 | |||
| 199a0f7089 | |||
| 65564e2012 | |||
| d612a71af5 | |||
| 477624387f | |||
| bf686101dc | |||
| b348ef633c | |||
| 68022837b4 | |||
| 537981ff62 | |||
| 5bc339afae | |||
| 4e9871a26a | |||
| e334d2b640 | |||
| c7efaef1be | |||
| ad751a1a2a | |||
| fad21c6d3e | |||
| 373a7a6d04 | |||
| 0742909953 | |||
| 25fcd25c32 | |||
| 0fe4258a44 | |||
| 0727c8efab | |||
| 7198740b26 | |||
| 634a12470b | |||
| 940145be7a | |||
| fca03552e2 | |||
| f42e880804 | |||
| 341c474dfa | |||
| c3a1831959 | |||
| dde1a0b2eb | |||
| ecfddc1523 | |||
| 13b59213eb | |||
| c0bb3b07c6 | |||
| c804c5ada3 | |||
| 0816e923f9 | |||
| 5d30e30a63 | |||
| 89fdffdfdc | |||
| 8f089a5ce1 | |||
| f3bc2310f0 | |||
| bd9cabea75 | |||
| 2520b3f85a | |||
| 22b967adf5 | |||
| cb514d10d5 | |||
| 9e3b67b4b9 | |||
| 49556e2aa8 | |||
| 94e70d3f25 | |||
| 05c47bd3c0 | |||
| 5b6fe70b7e | |||
| 424bf706e9 | |||
| b1feb9cf2f | |||
| 6948183270 | |||
| 4aa612e70c | |||
| d146faae1d | |||
| aa02bc1aaf | |||
| f9eb8e3466 | |||
| 18e13cfc3b | |||
| c46238f795 | |||
| 50e045212b | |||
| d5c831c64b | |||
| 75b355d632 | |||
| dcf4394741 | |||
| 36a6ecdd9a | |||
| d8dfea00d0 | |||
| 9784d49667 | |||
| cb76c698e7 | |||
| 9d2957a7c9 | |||
| 92a722495b | |||
| 30a2e5d902 | |||
| 409d5c7783 | |||
| 9f567051fb | |||
| 624c7baf44 | |||
| 2671d8bd46 | |||
| 8b928d9320 | |||
| 27ddcfcbcb | |||
| 48798dead9 | |||
| 0adae86b4d | |||
| 92283cabfa | |||
| a4774ada84 | |||
| e6bcf7ec6e | |||
| 27ef1f8749 | |||
| 8ee1c2b591 | |||
| f6f659aec6 | |||
| 61372241f7 | |||
| d398d89de9 | |||
| af4a2340c4 | |||
| 2d9791d61a | |||
| f874ec7cb6 | |||
| 9e4baad701 | |||
| 59b711447c | |||
| 2bb4866e1a | |||
| 72ceaf772d | |||
| 2da9ee0cee | |||
| 9c070cd71d | |||
| 6f068f14fd | |||
| 67d8db8b9a | |||
| 03b9a25897 | |||
| 645fc1511a | |||
| ae1d3b1e80 | |||
| 627a839779 | |||
| 5151faea3d | |||
| eb1bed118f | |||
| fd6476e4d1 | |||
| d62b3d5236 | |||
| bb6aa5eedb | |||
| e9f4820dd4 | |||
| 6bd73f2647 | |||
| d816eca300 | |||
| 03d4fdb890 | |||
| 4aa6e78a4e | |||
| 7ecb0dcd4b | |||
| 1cdec9386c | |||
| ba8f6bccec | |||
| 22c8746ed6 | |||
| 96503f74e4 | |||
| 7fb3396f65 | |||
| 66fa40b29e | |||
| b5aefc6f5e | |||
| cf590685b9 | |||
| a604bfcdf1 | |||
| c389bccfa0 | |||
| ce02dcf5a5 | |||
| d3d38c0312 | |||
| 95d097dd7b | |||
| 47f3b562b5 | |||
| 5eb12e671b | |||
| 023cb2ea55 | |||
| 98ea42d808 | |||
| 4e3a46ee71 | |||
| 9ebc5fa30e | |||
| 810ca750cd | |||
| 424652e27c | |||
| 831beceaac | |||
| 2daf026967 | |||
| dbd6a98bbe | |||
| a4ffdeb842 | |||
| 9b6194c5a4 | |||
| 8e4573677d | |||
| 61e9befbe7 | |||
| 0f50c40a93 | |||
| 907f8c34ac | |||
| c178d4c874 | |||
| 233fd44b01 | |||
| ab88edaa02 | |||
| 50c25d9e44 | |||
| 9be593d390 | |||
| 318c43931c | |||
| 244fc33f49 | |||
| f83f5263a9 | |||
| 3266dde2e3 | |||
| 3428ee335a | |||
| db6b4d7b81 | |||
| 1c195987c1 | |||
| 4913714a61 | |||
| 8e2856465c | |||
| 868c7a3908 | |||
| afaf91ebde | |||
| c92fe1452b | |||
| 2bde128fed | |||
| 89461505b5 | |||
| 85df5e61e5 | |||
| 5e477ff984 | |||
| 2baf9eda61 | |||
| 7d6d8175b9 | |||
| 0fdda032cc | |||
| c82d0f8c1c | |||
| 65ff108615 | |||
| 075611eb65 | |||
| b82537b020 | |||
| 06fee4636a | |||
| 5fe1af3239 | |||
| bf6cc3d384 | |||
| b4a4ba91b0 | |||
| 271776944a | |||
| b1d5a3d75f | |||
| bd68790a10 | |||
| f0a7663447 | |||
| db627d65c7 | |||
| a9f581797a | |||
| 763291230d | |||
| 55a77a34bd | |||
| f4f975c6b7 | |||
| 2d26382510 | |||
| c5cd56a99e | |||
| 24943dd526 | |||
| 8b32e30ee7 | |||
| 72c99be0f2 | |||
| 10ac1f6ae8 | |||
| 524170534c | |||
| d542f35336 | |||
| 697ba53c6b | |||
| 148a8bad2e | |||
| 7a344b91aa | |||
| 5783350484 | |||
| 9fce23d5ac | |||
| 7c17c99b9e | |||
| ff560bc3a3 | |||
| 61f01f4713 | |||
| 2e8c0234bb | |||
| 8e62d772b3 | |||
| 532cfb5775 | |||
| f912eb805d | |||
| 6838ff8ea2 | |||
| 8aa2c40bbf | |||
| c2d603769e | |||
| 3710d85e63 | |||
| 12a72302d9 | |||
| 2c70f0dc27 | |||
| 6165c65a51 | |||
| 3ea3f64214 | |||
| 08601d32a3 | |||
| 0ae1f6858c | |||
| f2e5b480e1 | |||
| 19ab197bc1 | |||
| 8bf1464b64 | |||
| 0ee3421cbc | |||
| a18536e8a4 | |||
| cc2e1eecd3 | |||
| 87b95128f9 | |||
| 344a56b47a | |||
| da8d055817 | |||
| c07272633f | |||
| b26e0d42d5 | |||
| 8ee4e927a1 | |||
| 7eaa3cb2b6 | |||
| 696928540c | |||
| b33f36d30a | |||
| 179b392a49 | |||
| 857f19c382 | |||
| 8a333208a2 | |||
| ff23f62c3e | |||
| dcdc9d6b90 | |||
| 3d28f957c7 | |||
| 2eb2aa5bef | |||
| 371820fd59 | |||
| 596c584ef1 | |||
| 27a1577021 | |||
| 8f2745c248 | |||
| 1c964d05f8 | |||
| 2dcbf7f751 | |||
| 067f73da38 | |||
| ae324c13a6 | |||
| 9a488adbb9 | |||
| 4c0f1efec6 | |||
| 8974cb5f34 | |||
| cb303a3149 | |||
| c44628a5b1 | |||
| fb1effc654 | |||
| 5ab213b854 | |||
| bbf738635c | |||
| 705d3e9fbc | |||
| 6bd3728548 | |||
| 7d997f6d9a | |||
| e919eb3eba | |||
| c2b2096c8c | |||
| 46638d2ea1 | |||
| fdb08517d3 | |||
| f272962562 | |||
| 9e661d8a99 | |||
| 43c0b79273 | |||
| 96e96c3c7f | |||
| 15c51b4417 | |||
| 6c93ca0e42 | |||
| a3cb98a3d3 | |||
| 31ecd39f08 | |||
| 4d986b8417 | |||
| 2d14c06089 | |||
| 7746ad0b13 | |||
| 1cbaf8d1f5 | |||
| cb41e2c41a | |||
| 2fd71f2fa8 | |||
| 37d67e8a83 | |||
| 2f8e67d679 | |||
| 22dc165a25 | |||
| ebfe50aafc | |||
| de58936ed1 | |||
| eba2abd5d9 | |||
| 864ab0c095 | |||
| de3d424f51 | |||
| 68c2c18d83 | |||
| 54907455fe | |||
| ba8ab21a0d | |||
| f272d9abde | |||
| 972f194388 | |||
| adb2f31e7f | |||
| ccfd239ad1 | |||
| 6fc56b9372 | |||
| 9072a6da7a | |||
| d39547097c | |||
| 3b2f58fa22 | |||
| cbddc4eb6f | |||
| 96a0c1039a | |||
| 1390bef626 | |||
| e70a2ab3de | |||
| e0d1bdc072 | |||
| 79c60d3884 | |||
| 1e85bc66b4 | |||
| 0d633a1e27 | |||
| 0759f6a1fd | |||
| 99b5675c4e | |||
| 4f7957183c | |||
| 2de18d3c4d | |||
| fecf13e2df | |||
| 31e44d50e4 | |||
| c28aeeac4a | |||
| 5649de6cd1 | |||
| 26a649abda | |||
| 3b02ae1ce9 | |||
| 13278c3d40 | |||
| 17760a458a | |||
| 282814fe59 | |||
| 35fb02fce9 | |||
| 7286c339c8 | |||
| b956fa4d1f | |||
| 8cb66895f0 | |||
| 3a9584b840 | |||
| 5d20a518ae | |||
| bd3dc453ef | |||
| 151d22c325 | |||
| d6433a67c8 | |||
| be561f0e5e | |||
| 1f6d90319b | |||
| eca63364a6 | |||
| 38f25160d6 | |||
| 28ce023164 | |||
| 10b3d56682 | |||
| 945011f9e3 | |||
| d7c6cd68b9 | |||
| ed4c3fde2d | |||
| c5db1c33a0 | |||
| d719ea74dd | |||
| 996defaadb | |||
| 86cd7f7a01 | |||
| a79090c65a | |||
| 86c720af81 | |||
| a9becd5cc8 | |||
| 1e327699b8 | |||
| 0ba9425bb6 | |||
| 7d19b839d9 | |||
| e1a9f0a533 | |||
| f6d0051446 | |||
| 126df1fa03 | |||
| 0a72d9539f | |||
| 60ecbe054e | |||
| 5a3fa18863 | |||
| bea4f2abde | |||
| 3fc2ca3d77 | |||
| 29fcc4727a | |||
| ddb973b03a | |||
| c2b05f9956 | |||
| 7fd452102f | |||
| c80d3eea47 | |||
| 8421edbb72 | |||
| 03587f54be | |||
| dac7760018 | |||
| bd28d5d7b1 | |||
| 34df19b204 | |||
| 6e824dadf2 | |||
| c50cfc1714 | |||
| d0fcd573bd | |||
| 79089ced97 | |||
| aa6811a088 | |||
| dd27a35039 | |||
| f3b696c5ec | |||
| 8405041f64 | |||
| a372993c44 | |||
| 909e0d7312 | |||
| e8503f7ddc | |||
| 42a6caf7dc | |||
| eaffb12ffb | |||
| 105e0d3c82 | |||
| 534a36c7e1 | |||
| b5b8f757b2 | |||
| 10e3dfbfdb | |||
| bfdcbbe9f1 | |||
| 5d49839236 | |||
| e6508237e7 | |||
| 2d4797c84e | |||
| 392516e60f | |||
| d09a2d0772 | |||
| 2bd5c10f09 | |||
| 5c910113a3 | |||
| c591a2e098 | |||
| b5acc9b093 | |||
| 5b6f8ff0f6 | |||
| d530d0a4d5 | |||
| 211ce4b2d3 | |||
| 1800438a77 | |||
| 1eda9a839a | |||
| e3cc400ac2 | |||
| b9b3d021cc | |||
| 6e202e18d8 | |||
| 750b57859a | |||
| 7ba520fea1 | |||
| da85a07dae | |||
| 01851523a0 | |||
| eda744f07f | |||
| 4acfe11af1 | |||
| 85824c5df6 | |||
| 20a0c27111 | |||
| 76200884d5 | |||
| ce4ce55b29 | |||
| 66f6e38788 | |||
| d97a0a3324 | |||
| 63f5a0a324 | |||
| f0837bb98b | |||
| 0f98a72876 | |||
| 1dd59204e9 | |||
| decae725a6 | |||
| 6c92d45ff3 | |||
| 272dd4a076 | |||
| 626ebe380e | |||
| f86ce48d65 | |||
| ef92cca9db | |||
| aa71810bfb | |||
| 22c47ac648 | |||
| e592528937 | |||
| 1fdb49f314 | |||
| aff37d1ae4 | |||
| a0234d0cc6 | |||
| 8a77acb51e | |||
| 690bbbd14c | |||
| 9c9bb2f192 | |||
| 23a40fe051 | |||
| 5d9cd662fc | |||
| 6c01589d3b | |||
| 820dde8ad2 | |||
| 3de4d63f44 | |||
| e1df82e0c3 | |||
| c1985b4a10 | |||
| 1ca9ce4db3 | |||
| 636705bd70 | |||
| 153dcd8ad3 | |||
| f2c2fe11b4 | |||
| 7a2c4fa5cc | |||
| 923816ca8e | |||
| 23d0b7b89e | |||
| a6b35f3a22 | |||
| 87fb39c8a5 | |||
| 4d188a79fb | |||
| a02b807dad | |||
| e0fa9d0213 | |||
| 35ad8abf4c | |||
| 372ca374cc | |||
| 75b6e94be1 | |||
| f912d0ef88 | |||
| 1bf8a01d0b | |||
| 5e8213d45f | |||
| 8b093122ac | |||
| bc14ad5b15 | |||
| fc543fc5a6 | |||
| 2e3aec81e7 | |||
| 9b04578d3e | |||
| 6654d32229 | |||
| 7c2c9448a3 | |||
| 6531128647 | |||
| fd1b895ff9 | |||
| f4e88bbcc0 | |||
| 7993f98166 | |||
| e11b775a31 | |||
| b999494154 | |||
| 6a7ed1a948 | |||
| d0889cca31 | |||
| 8b673c7adf | |||
| f1b0390b9b | |||
| b43d767a26 | |||
| bd4f642045 | |||
| 3bdfc7d785 | |||
| 4aa73968f0 | |||
| 06bc09951d | |||
| 9d59043788 | |||
| 33f0ca3042 | |||
| e1bc62dae3 | |||
| 77dbe9ccac | |||
| 150d8c2119 | |||
| b16f6adf76 | |||
| 834dd963ef | |||
| 01e0f9c2b7 | |||
| b4c0bb1f18 | |||
| f70fc683d1 | |||
| 956d4bd5bf | |||
| 8b45f89a75 | |||
| def6b67c8f | |||
| e8136853db | |||
| 084b41e33b | |||
| 6242bd7eb7 | |||
| 5fbe298f9c | |||
| b1c2c645df | |||
| d2a6aa5462 | |||
| 71abe66b8f | |||
| 01eb6e31ba | |||
| 071e6f885f | |||
| 5fcde96f88 | |||
| a1ad10b3ee | |||
| 6d26732208 | |||
| 20a396f190 | |||
| 4b7e5ca9fc | |||
| 8522707bd2 | |||
| a1505736d7 | |||
| 5cd17028d6 | |||
| 3089dd8688 | |||
| 044c45225b | |||
| 0c1ade82b2 | |||
| 7fdf5ed7dc | |||
| 6fb956688a | |||
| 28b362f8ff | |||
| 7035ccdcd2 | |||
| ba5ac8d872 | |||
| ea16ecc81f | |||
| 0eac9d3e6f | |||
| 68a8b6d437 | |||
| e136ba9f3e | |||
| 9ee6bea772 | |||
| 79bdde4f08 | |||
| 2a5f0e3c9f | |||
| 3c42f3de47 | |||
| 7cd15d408e | |||
| 5228b778dc | |||
| 49eaf7afeb | |||
| b2f02232f2 | |||
| 2f2d7da20b | |||
| 168043d422 | |||
| 101e1f6b50 | |||
| 323b6cec9d | |||
| 019a04c488 | |||
| e566bbc5e7 | |||
| d2140672fd | |||
| 667df80d35 | |||
| 0d8ad2d8b0 | |||
| 1796ff6871 | |||
| 49c54ee69a | |||
| d8e314b0a0 | |||
| 65acfe61ce | |||
| 2f067de10a | |||
| acecb50f7e | |||
| 9b27932d2c | |||
| 4aa8c0a76a | |||
| 246e43217a | |||
| 6925a5f3a4 | |||
| 81a92a9f13 | |||
| 8440d66d33 | |||
| 63ce978afe | |||
| b4e6386496 | |||
| f524a96add | |||
| 1d0f3bb831 | |||
| 2e0ef27570 | |||
| ebbb8fcef1 | |||
| 1d89d3dbe9 | |||
| 18512f5b99 | |||
| 5d4906b4d1 | |||
| 5e83a0eea6 | |||
| 3f43856145 | |||
| bb16ca6394 | |||
| 3f83aaf32d | |||
| e8ec565273 | |||
| 8ab6460571 | |||
| 19f7abf0f8 | |||
| ce2ad727c6 | |||
| fe213d1e13 | |||
| 35b78af446 | |||
| a72d4ccded | |||
| 1babf4ba55 | |||
| 18ab2477a8 | |||
| 9d56392cbc | |||
| 7cc68f59a7 | |||
| d6a9a0b15f | |||
| 766471f4b9 | |||
| 143a6a9cca | |||
| 7ad41a3ff2 | |||
| 12fadffb00 | |||
| ef97128fe6 | |||
| 1b0bc366e4 | |||
| 89c87f99e8 | |||
| 0e0b6a4978 | |||
| 7fe3caf841 | |||
| 2799892f1d | |||
| 7ecd839310 | |||
| 11a2628ed2 | |||
| a24b9cf9b9 | |||
| 705f77f5ba | |||
| c71cbf2e09 | |||
| 33aba7b721 | |||
| 58f4e5bf76 | |||
| 2095c7546a | |||
| 6005444fda | |||
| 658ccaf78f | |||
| 3112b2b071 | |||
| 32f746b918 | |||
| 5145a0260d | |||
| acc782b754 | |||
| e6508e6978 | |||
| 4e45fbbc04 | |||
| 81c2d70fbb | |||
| d2ec20bd10 | |||
| 6e3a7f8fa0 | |||
| 8e83acbafd | |||
| 40a8522460 | |||
| dfa9ca733e | |||
| 4681098e8c | |||
| 4f81d53d11 | |||
| dc5dc608ba | |||
| 83dc1c76ef | |||
| d660e9a066 | |||
| abcaaa3264 | |||
| 7f2b871885 | |||
| e51dd05989 | |||
| 51da7732f4 | |||
| 1b7dc2f147 | |||
| 63aedb65d9 | |||
| 9c503fe8d6 | |||
| 83e59bf5b0 | |||
| 4ef5c374ef | |||
| 764b31fdcd | |||
| 7fe2b41209 | |||
| 25a14bf44f | |||
| bde3606757 | |||
| 84f7af7e58 | |||
| bf0a448bff | |||
| be148574e3 | |||
| 235e00c19a | |||
| 87035eb4a9 | |||
| e67a9c9664 | |||
| 719dfd91ab | |||
| 5f97c46e5f | |||
| 739d171462 | |||
| f105602da5 | |||
| 01f4ebde54 | |||
| 97f3d1662e | |||
| aa04589dd2 | |||
| 9473039a0b | |||
| 574cc098c6 | |||
| f17c490768 | |||
| 6d246f5244 | |||
| bec30ae833 | |||
| feb501c76f | |||
| b32f4ad80a | |||
| 4b12228b15 | |||
| afd576697e | |||
| 6a24360ffd | |||
| 5faa69b66c | |||
| 833498011c | |||
| 4f6cc659c4 | |||
| 5cef77e52c | |||
| 712ab734dd | |||
| 0f0a57af1c | |||
| ce5eef2605 | |||
| 6d6960badb | |||
| 54269aa92a | |||
| ec65a71d6d | |||
| 761fcde654 | |||
| 3346ddac38 | |||
| f20c55f849 | |||
| 37c5c35a12 | |||
| e541ccb197 | |||
| 1f7027d825 | |||
| 0d9e7a5e08 | |||
| c4ac96e669 | |||
| 74f65b978e | |||
| eab874b3ef | |||
| 54d9361dad | |||
| e8cd130ad6 | |||
| 2f70f4ba25 | |||
| 1548167b94 | |||
| 91a320127a | |||
| 7a0a4dc3b3 | |||
| ba9c006cb4 | |||
| 2f92160e9c | |||
| 8f0063465c | |||
| 0c8bb089b9 | |||
| 746b99508b | |||
| 05ce497f29 | |||
| 5bfb382209 | |||
| 32efa7b112 | |||
| e4fcd589db | |||
| 8da5a99196 | |||
| d5cb79027a | |||
| fea1a001ee | |||
| 3ae6f0a93d | |||
| 28d7db2371 | |||
| 02b582a60d | |||
| 83a40e1b33 | |||
| ca0a875e6b | |||
| 9932d4789e | |||
| 2c68dfd8cf | |||
| 64f66fad4d | |||
| 5a8e3100b9 | |||
| 87dedc3dd2 | |||
| 69b442c2a9 | |||
| 77bf358d7a | |||
| 9ed7254277 | |||
| 1a08d99079 | |||
| f852dcfd20 | |||
| 04a4f4d5bb | |||
| 2e42dd3308 | |||
| e9cf8d4647 | |||
| 216351803b | |||
| d3eb969095 | |||
| 77c405626f | |||
| e0af2122d2 | |||
| 1bd1121d50 | |||
| 54e2a2af9d | |||
| 90a0b31cf8 | |||
| 1b257df5cf | |||
| b73acc88a7 | |||
| c6bb6b48b6 | |||
| cb95c3050d | |||
| 3434a68ac0 | |||
| 9a941b97cb | |||
| 9074219145 | |||
| e8d9512f02 | |||
| 764e4ac7dd | |||
| 9350b9dc7b | |||
| fb4e16250a | |||
| 32a1d0c134 | |||
| d471d60bfa | |||
| 75d6aab811 | |||
| a80fcff81c | |||
| 42bce0d438 | |||
| 168e1d7c58 | |||
| bdf0f7d10a | |||
| d21060fd87 | |||
| ddc7ebb576 | |||
| 4ad45a1955 | |||
| 6c5b7cbd1e | |||
| b1492037d5 | |||
| 7a5537d01e | |||
| 91fbb10fd3 | |||
| 684687e412 | |||
| 4612d42fb3 | |||
| 026a0672df | |||
| fe4b60d6b0 | |||
| 040d76e3bf | |||
| 1885d50374 | |||
| f9e508e958 | |||
| de695e31d2 | |||
| 4eabb5bec6 | |||
| f3ec868af6 | |||
| 4e96d24d78 | |||
| b5c3c01cc1 | |||
| 13aa29b485 | |||
| be5342fb51 | |||
| 50dc82d074 | |||
| 4358ff9183 | |||
| 336f0382b5 | |||
| 0307c0e6c9 | |||
| fc1e62471f | |||
| 137e9aacab | |||
| 4ddc6c1c1d | |||
| ef12913422 | |||
| 29c36836fe | |||
| 5c2614544d | |||
| 4c1d85e2bf | |||
| 2dd04c449c | |||
| 55f4a7b63e | |||
| a651254e38 | |||
| 1131952a54 | |||
| 189ebc4476 | |||
| 0688ddf083 | |||
| de2e59ddcb | |||
| 41632f2d95 | |||
| d21e8f0aa8 | |||
| 5521d3765a | |||
| d8b4e00fa8 | |||
| e1906ec9ba | |||
| 35fce94025 | |||
| 4708ffa8f6 | |||
| 8a858a1836 | |||
| f8ce0be095 | |||
| cb17078c48 | |||
| 28ed117db8 | |||
| 67d505fc7e | |||
| dba2ab55e0 | |||
| 119bf1a879 | |||
| 30dd202f33 | |||
| 4a4285b18f | |||
| 37b5a00df3 | |||
| 45fff99967 | |||
| c319329b23 | |||
| e280aa7f40 | |||
| f06a976052 | |||
| 77b8b51c16 | |||
| 3b86bb528c | |||
| d55acdd67b | |||
| de8d0197ff | |||
| 1fb9697229 | |||
| 5f8fe698ba | |||
| ed41ba49f9 | |||
| b500b23a69 | |||
| 27099392f4 | |||
| 19e63996ac | |||
| 87a4f3fc20 | |||
| 1dfed85f37 | |||
| cac2599b55 | |||
| d765e7f115 | |||
| 552af032f4 | |||
| 9bd82b201c | |||
| 662e8b8a11 | |||
| eda4e53b2c | |||
| b8cbc3b5af | |||
| f6309ec489 | |||
| be30cce41a | |||
| a6efeb7a19 | |||
| bc3791f738 | |||
| a8cc73c5e6 | |||
| 2a8dce75cd | |||
| e7200fb938 | |||
| 8c60207fde | |||
| 1ca86d36fd | |||
| 6f971901d7 | |||
| 6f353b6c54 | |||
| a7ebdbb361 | |||
| d84a0337b4 | |||
| 0d1f980940 | |||
| cf382ea2f8 | |||
| a674950ce1 | |||
| 2b7bd8a9d7 | |||
| d98fef52f2 | |||
| c2d1a10684 | |||
| 2580c3e29f | |||
| d2a340c8d0 | |||
| 8ec01b1583 | |||
| 1e3dc14729 | |||
| 253593431d | |||
| 730fbec9ce | |||
| ec70b71511 | |||
| 0b33709c69 | |||
| 7db3f7fefb | |||
| 9bb81b3db9 | |||
| dc1ee10aa4 | |||
| 18214be4f7 | |||
| 1dd4e5bb87 | |||
| 5c17e20a23 | |||
| b040aed702 | |||
| b68b075b1e | |||
| df5f2c4f81 | |||
| 281eb99d88 | |||
| b03694efff | |||
| 2d9a805d8f | |||
| ee110dfd91 | |||
| 6a0fee406e | |||
| b8d2473c6a | |||
| ec4469aebb | |||
| 209adda958 | |||
| 1f74ae1206 | |||
| 04b0d6d930 | |||
| 5b6fcca58a | |||
| cfb785705b | |||
| 80460850d4 | |||
| 116b920703 | |||
| 89b683d9d3 | |||
| 5ac8611a2c | |||
| 52051837c9 | |||
| 1ed3767f0c | |||
| 7f0e6fd86c | |||
| 03610f01d4 | |||
| 5f548627b3 | |||
| 80497be71b | |||
| 952b5e84f7 | |||
| 25eb38ff60 | |||
| b4517c5393 | |||
| 67a1112ced | |||
| 6f38a40b94 | |||
| 5a20066d2e | |||
| 37be8fab30 | |||
| 91fffc08b9 | |||
| 458dea9780 | |||
| ea43384244 | |||
| 65a8c8d946 | |||
| 0e93a11850 | |||
| ce34f758bd | |||
| 0eaf3b6336 | |||
| 4700eeb7e2 | |||
| d7d2a6f1f0 | |||
| 878831bfc7 | |||
| 324ae3fe3a | |||
| bc22130938 | |||
| 461b95b1ac | |||
| d14396e2d3 | |||
| 1e0fd948a5 | |||
| 9d9e33f245 | |||
| f612a85599 | |||
| 136e7d3491 | |||
| 00b913d363 | |||
| 3fd11b7aaf | |||
| c1cf4efb31 | |||
| 4c65d711b0 | |||
| 090f737647 | |||
| 224294d00f | |||
| 8c145168ef | |||
| 4660ed5745 | |||
| b5a799c997 | |||
| 53fb36d0e2 | |||
| 99f6b89b6e | |||
| ff42526199 | |||
| dea7fafee7 | |||
| 357f158f8a | |||
| c18fa805f9 | |||
| 5fd0850f5b | |||
| 66225736fe | |||
| a87036b538 | |||
| 62b8ef7bb3 | |||
| 546351dd46 | |||
| 69f0572f2a | |||
| a6ffaff2a2 | |||
| 4e62540a9b | |||
| bb03d6b83d | |||
| 8aaa34b8fa | |||
| 03a60561b7 | |||
| 761e96429d | |||
| 3e685d91a5 | |||
| 668d3d3672 | |||
| 23308625f6 | |||
| 91a1ce84d0 | |||
| a8f57ed276 | |||
| 76903a0b91 | |||
| d316b14c4d | |||
| 523f965791 | |||
| 9f2d6fd8b5 | |||
| b176a246b3 | |||
| fac844c255 | |||
| 59bd424e89 | |||
| 12bc2a0a39 | |||
| dca5fadeec | |||
| 0872db3780 | |||
| 6134d95bfd | |||
| 1bf98f5b8d | |||
| f468420a29 | |||
| 6f8cb24e5a | |||
| b8d8ae68a3 | |||
| 63eb032472 | |||
| 69396922db | |||
| 99774d7417 | |||
| e67fb86084 | |||
| 98a25ca5e3 | |||
| 245ad7a356 | |||
| b042dffc2c | |||
| ef263580df |
+2
-1
@@ -1,5 +1,7 @@
|
||||
*.swp
|
||||
*~
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
.project
|
||||
.svn
|
||||
.nvm-version
|
||||
@@ -11,4 +13,3 @@ debug.log
|
||||
docs/output
|
||||
docs/includes
|
||||
spec/fixtures/evil-files/
|
||||
/apm
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[submodule "vendor/apm"]
|
||||
path = vendor/apm
|
||||
url = https://github.com/atom/apm.git
|
||||
@@ -0,0 +1,2 @@
|
||||
ca =
|
||||
cache = ~/.atom/.npm
|
||||
+11
-9
@@ -10,12 +10,12 @@ to propose changes to this document in a pull request.
|
||||
that behavior such as Emacs, vi, Xcode, etc.
|
||||
* Check the Console app for stack traces to include if reporting a crash.
|
||||
* Check the Dev tools (`alt-cmd-i`) for errors and stack traces to include.
|
||||
|
||||
|
||||
### Package Repositories
|
||||
|
||||
This is the repository for the core Atom editor only. Atom comes bundled with
|
||||
many packages and themes that are stored in other repos under the
|
||||
[atom org](https://github.com/atom) such as [tabs](https://github.com/atom/tabs),
|
||||
[atom organization](https://github.com/atom) such as [tabs](https://github.com/atom/tabs),
|
||||
[find-and-replace](https://github.com/atom/find-and-replace),
|
||||
[language-javascript](https://github.com/atom/language-javascript),
|
||||
and [atom-light-ui](http://github.com/atom/atom-light-ui).
|
||||
@@ -30,7 +30,7 @@ in the proper package's repository.
|
||||
* Follow the [CoffeeScript](#coffeescript-styleguide),
|
||||
[JavaScript](https://github.com/styleguide/javascript),
|
||||
and [CSS](https://github.com/styleguide/css) styleguides
|
||||
* Include thoughtfully worded [Jasmine](http://pivotal.github.com/jasmine)
|
||||
* Include thoughtfully worded [Jasmine](http://jasmine.github.io/)
|
||||
specs
|
||||
* Avoid placing files in `vendor`. 3rd-party packages should be added as a
|
||||
`package.json` dependency.
|
||||
@@ -52,10 +52,11 @@ in the proper package's repository.
|
||||
* Use the present tense
|
||||
* Reference issues and pull requests liberally
|
||||
* Consider starting the commit message with an applicable emoji:
|
||||
* :lipstick: when improving the format/structure of the code
|
||||
* :racehorse: when improving performance
|
||||
* :non-potable_water: when plugging memory leaks
|
||||
* :memo: when writing docs
|
||||
* :lipstick: `:lipstick:` when improving the format/structure of the code
|
||||
* :racehorse: `:racehorse:` when improving performance
|
||||
* :non-potable_water: `:non-potable_water:` when plugging memory leaks
|
||||
* :memo: `:memo:` when writing docs
|
||||
* :penguin: `:penguin:` when fixing something on Linux
|
||||
|
||||
## CoffeeScript Styleguide
|
||||
|
||||
@@ -66,8 +67,9 @@ in the proper package's repository.
|
||||
|
||||
* Use [TomDoc](http://tomdoc.org).
|
||||
* Use [Markdown](https://daringfireball.net/projects/markdown).
|
||||
* Reference classes with `{ClassName}` style notation.
|
||||
* Reference methods with `{ClassName.methodName}` style notation.
|
||||
* Reference classes with `{ClassName}`.
|
||||
* Reference instance methods with `{ClassName::methodName}`.
|
||||
* Reference class methods with `{ClassName.methodName}`.
|
||||
* Delegate to comments elsewhere with `{Delegates to: ClassName.methodName}`
|
||||
style notation.
|
||||
|
||||
|
||||
+17
-10
@@ -1,13 +1,20 @@
|
||||
Copyright 2013 GitHub Inc.
|
||||
Copyright (c) 2014 GitHub Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
+9
-5
@@ -1,8 +1,8 @@
|
||||
# Atom — The hackable editor
|
||||

|
||||
|
||||

|
||||
Atom is a hackable text editor for the 21st century, built on [atom-shell](http://github.com/atom/atom-shell), and based on everything we love about our favorite editors. We designed it to be deeply customizable, but still approachable using the default configuration.
|
||||
|
||||
Check out our [guides and API documentation](https://www.atom.io/docs/latest/).
|
||||
Visit [atom.io](https://atom.io) to learn more.
|
||||
|
||||
## Installing
|
||||
|
||||
@@ -12,6 +12,10 @@ Atom will automatically update when a new release is available.
|
||||
|
||||
## Building
|
||||
|
||||
Follow the instructions in the [build docs][building].
|
||||
* [Linux](docs/build-instructions/linux.md)
|
||||
* [OS X](docs/build-instructions/os-x.md)
|
||||
* [FreeBSD](docs/build-instructions/freebsd.md)
|
||||
* [Windows](docs/build-instructions/windows.md)
|
||||
|
||||
[building]: https://github.com/atom/atom/blob/master/docs/building-atom.md
|
||||
## Developing
|
||||
Check out the [guides](https://atom.io/docs/latest) and the [API reference](https://atom.io/docs/api).
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
This folder is where [apm](https://github.com/atom/apm) is installed to so that
|
||||
it is bundled with Atom.
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "atom-bundled-apm",
|
||||
"description": "Atom's bundled APM",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/atom/atom.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"atom-package-manager": "0.52.0"
|
||||
}
|
||||
}
|
||||
+66
-16
@@ -1,22 +1,28 @@
|
||||
#!/bin/sh
|
||||
ATOM_PATH=${ATOM_PATH-/Applications/Atom.app}
|
||||
ATOM_BINARY=$ATOM_PATH/Contents/MacOS/Atom
|
||||
#!/bin/bash
|
||||
|
||||
if [ ! -d $ATOM_PATH ]; then sleep 5; fi # Wait for Atom to reappear, Sparkle may be replacing it.
|
||||
|
||||
if [ ! -d $ATOM_PATH ]; then
|
||||
echo "Atom application not found at '$ATOM_PATH'" >&2
|
||||
if [ "$(uname)" == 'Darwin' ]; then
|
||||
OS='Mac'
|
||||
elif [ "$(expr substr $(uname -s) 1 5)" == 'Linux' ]; then
|
||||
OS='Linux'
|
||||
elif [ "$(expr substr $(uname -s) 1 10)" == 'MINGW32_NT' ]; then
|
||||
OS='Cygwin'
|
||||
else
|
||||
echo "Your platform ($(uname -a)) is not supported."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
while getopts ":wtfvhs-:" opt; do
|
||||
while getopts ":wtfvh-:" opt; do
|
||||
case "$opt" in
|
||||
-)
|
||||
case "${OPTARG}" in
|
||||
wait)
|
||||
WAIT=1
|
||||
;;
|
||||
help|version|foreground|test)
|
||||
help|version)
|
||||
REDIRECT_STDERR=1
|
||||
EXPECT_OUTPUT=1
|
||||
;;
|
||||
foreground|test)
|
||||
EXPECT_OUTPUT=1
|
||||
;;
|
||||
esac
|
||||
@@ -24,25 +30,69 @@ while getopts ":wtfvhs-:" opt; do
|
||||
w)
|
||||
WAIT=1
|
||||
;;
|
||||
h|v|f|t)
|
||||
h|v)
|
||||
REDIRECT_STDERR=1
|
||||
EXPECT_OUTPUT=1
|
||||
;;
|
||||
f|t)
|
||||
EXPECT_OUTPUT=1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ $EXPECT_OUTPUT ]; then
|
||||
$ATOM_BINARY --executed-from="$(pwd)" --pid=$$ $@
|
||||
exit $?
|
||||
else
|
||||
open -a $ATOM_PATH -n --args --executed-from="$(pwd)" --pid=$$ $@
|
||||
if [ $REDIRECT_STDERR ]; then
|
||||
exec 2> /dev/null
|
||||
fi
|
||||
|
||||
# Used to exit process when atom is used as $EDITOR
|
||||
if [ $OS == 'Mac' ]; then
|
||||
ATOM_PATH=${ATOM_PATH:-/Applications} # Set ATOM_PATH unless it is already set
|
||||
ATOM_APP_NAME=Atom.app
|
||||
|
||||
# If ATOM_PATH isn't a executable file, use spotlight to search for Atom
|
||||
if [ ! -x "$ATOM_PATH/$ATOM_APP_NAME" ]; then
|
||||
ATOM_PATH=$(mdfind "kMDItemCFBundleIdentifier == 'com.github.atom'" | head -1 | xargs dirname)
|
||||
fi
|
||||
|
||||
# Exit if Atom can't be found
|
||||
if [ -z "$ATOM_PATH" ]; then
|
||||
echo "Cannot locate Atom.app, it is usually located in /Applications. Set the ATOM_PATH environment variable to the directory containing Atom.app."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $EXPECT_OUTPUT ]; then
|
||||
"$ATOM_PATH/$ATOM_APP_NAME/Contents/MacOS/Atom" --executed-from="$(pwd)" --pid=$$ "$@"
|
||||
exit $?
|
||||
else
|
||||
open -a "$ATOM_PATH/$ATOM_APP_NAME" -n --args --executed-from="$(pwd)" --pid=$$ "$@"
|
||||
fi
|
||||
elif [ $OS == 'Linux' ]; then
|
||||
SCRIPT=$(readlink -f "$0")
|
||||
USR_DIRECTORY=$(readlink -f $(dirname $SCRIPT)/..)
|
||||
ATOM_PATH="$USR_DIRECTORY/share/atom/atom"
|
||||
|
||||
[ -x "$ATOM_PATH" ] || ATOM_PATH='/tmp/atom-build/Atom/atom'
|
||||
|
||||
if [ $EXPECT_OUTPUT ]; then
|
||||
"$ATOM_PATH" --executed-from="$(pwd)" --pid=$$ "$@"
|
||||
exit $?
|
||||
else
|
||||
(
|
||||
nohup "$ATOM_PATH" --executed-from="$(pwd)" --pid=$$ "$@" > /tmp/atom-nohup.out 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
cat /tmp/atom-nohup.out
|
||||
exit $?
|
||||
fi
|
||||
) &
|
||||
fi
|
||||
fi
|
||||
|
||||
# Exits this process when Atom is used as $EDITOR
|
||||
on_die() {
|
||||
exit 0
|
||||
}
|
||||
trap 'on_die' SIGQUIT SIGTERM
|
||||
|
||||
# If the wait flag is set, don't exit this process until Atom tells it to.
|
||||
if [ $WAIT ]; then
|
||||
while true; do
|
||||
sleep 1
|
||||
|
||||
+21
-14
@@ -11,10 +11,6 @@ _ = require 'underscore-plus'
|
||||
|
||||
packageJson = require '../package.json'
|
||||
|
||||
# OAuth token for atom-bot
|
||||
# TODO Remove once all repositories are public
|
||||
process.env.ATOM_ACCESS_TOKEN ?= '362295be4c5258d3f7b967bbabae662a455ca2a7'
|
||||
|
||||
# Shim harmony collections in case grunt was invoked without harmony
|
||||
# collections enabled
|
||||
_.extend(global, require('harmony-collections')) unless global.WeakMap?
|
||||
@@ -43,27 +39,35 @@ module.exports = (grunt) ->
|
||||
if process.platform is 'win32'
|
||||
appName = 'Atom'
|
||||
tmpDir = os.tmpdir()
|
||||
installRoot = process.env.ProgramFiles
|
||||
buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'atom-build')
|
||||
symbolsDir = path.join(buildDir, 'Atom.breakpad.syms')
|
||||
shellAppDir = path.join(buildDir, appName)
|
||||
contentsDir = shellAppDir
|
||||
appDir = path.join(shellAppDir, 'resources', 'app')
|
||||
atomShellDownloadDir = path.join(os.tmpdir(), 'atom-cached-atom-shells')
|
||||
else
|
||||
installDir = path.join(process.env.ProgramFiles, appName)
|
||||
else if process.platform is 'darwin'
|
||||
appName = 'Atom.app'
|
||||
tmpDir = '/tmp'
|
||||
installRoot = '/Applications'
|
||||
buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'atom-build')
|
||||
symbolsDir = path.join(buildDir, 'Atom.breakpad.syms')
|
||||
shellAppDir = path.join(buildDir, appName)
|
||||
contentsDir = path.join(shellAppDir, 'Contents')
|
||||
appDir = path.join(contentsDir, 'Resources', 'app')
|
||||
atomShellDownloadDir = '/tmp/atom-cached-atom-shells'
|
||||
|
||||
installDir = path.join(installRoot, appName)
|
||||
installDir = path.join('/Applications', appName)
|
||||
else
|
||||
appName = 'Atom'
|
||||
tmpDir = '/tmp'
|
||||
buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'atom-build')
|
||||
symbolsDir = path.join(buildDir, 'Atom.breakpad.syms')
|
||||
shellAppDir = path.join(buildDir, appName)
|
||||
contentsDir = shellAppDir
|
||||
appDir = path.join(shellAppDir, 'resources', 'app')
|
||||
atomShellDownloadDir = '/tmp/atom-cached-atom-shells'
|
||||
installDir = process.env.INSTALL_PREFIX ? '/usr/local'
|
||||
|
||||
coffeeConfig =
|
||||
options:
|
||||
sourceMap: true
|
||||
glob_to_multiple:
|
||||
expand: true
|
||||
src: [
|
||||
@@ -127,7 +131,7 @@ module.exports = (grunt) ->
|
||||
grunt.initConfig
|
||||
pkg: grunt.file.readJSON('package.json')
|
||||
|
||||
atom: {appDir, appName, buildDir, contentsDir, installDir, shellAppDir}
|
||||
atom: {appDir, appName, symbolsDir, buildDir, contentsDir, installDir, shellAppDir}
|
||||
|
||||
coffee: coffeeConfig
|
||||
|
||||
@@ -227,6 +231,9 @@ module.exports = (grunt) ->
|
||||
grunt.registerTask('compile', ['coffee', 'prebuild-less', 'cson', 'peg'])
|
||||
grunt.registerTask('lint', ['coffeelint', 'csslint', 'lesslint'])
|
||||
grunt.registerTask('test', ['shell:kill-atom', 'run-specs'])
|
||||
grunt.registerTask('ci', ['output-disk-space', 'download-atom-shell', 'build', 'set-version', 'lint', 'test', 'codesign', 'publish-build'])
|
||||
grunt.registerTask('ci', ['output-disk-space', 'download-atom-shell', 'build', 'dump-symbols', 'set-version', 'check-licenses', 'lint', 'test', 'codesign', 'publish-build'])
|
||||
grunt.registerTask('docs', ['markdown:guides', 'build-docs'])
|
||||
grunt.registerTask('default', ['download-atom-shell', 'build', 'set-version', 'install'])
|
||||
|
||||
defaultTasks = ['download-atom-shell', 'build', 'set-version']
|
||||
defaultTasks.push 'install' unless process.platform is 'linux'
|
||||
grunt.registerTask('default', defaultTasks)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "~0.2.9",
|
||||
"biscotto": "0.6.0",
|
||||
"biscotto": ">=2.1.1 <3.0",
|
||||
"formidable": "~1.0.14",
|
||||
"fs-plus": "2.x",
|
||||
"github-releases": "~0.2.0",
|
||||
@@ -17,20 +17,25 @@
|
||||
"grunt-contrib-csslint": "~0.1.2",
|
||||
"grunt-contrib-coffee": "~0.9.0",
|
||||
"grunt-contrib-less": "~0.8.0",
|
||||
"grunt-cson": "0.6.0",
|
||||
"grunt-download-atom-shell": "git+https://atom-bot:362295be4c5258d3f7b967bbabae662a455ca2a7@github.com/atom/grunt-download-atom-shell#v0.6.0",
|
||||
"grunt-cson": "0.8.0",
|
||||
"grunt-download-atom-shell": "~0.7.0",
|
||||
"grunt-lesslint": "0.13.0",
|
||||
"grunt-markdown": "~0.4.0",
|
||||
"grunt-peg": "~1.1.0",
|
||||
"grunt-shell": "~0.3.1",
|
||||
"harmony-collections": "~0.3.8",
|
||||
"json-front-matter": "~0.1.3",
|
||||
"legal-eagle": "~0.4.0",
|
||||
"minidump": "0.5.x",
|
||||
"read-package-json": "1.1.8",
|
||||
"normalize-package-data": "0.2.12",
|
||||
"rcedit": "~0.1.2",
|
||||
"request": "~2.27.0",
|
||||
"rimraf": "~2.2.2",
|
||||
"runas": "0.5.x",
|
||||
"underscore-plus": "1.x",
|
||||
"unzip": "~0.1.9",
|
||||
"vm-compatibility-layer": "~0.1.0"
|
||||
"vm-compatibility-layer": "~0.1.0",
|
||||
"npm": "~1.4.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ module.exports = (grunt) ->
|
||||
|
||||
if process.platform is 'darwin'
|
||||
cp 'atom-shell/Atom.app', shellAppDir
|
||||
else if process.platform is 'win32'
|
||||
else
|
||||
cp 'atom-shell', shellAppDir
|
||||
|
||||
mkdir appDir
|
||||
@@ -47,7 +47,9 @@ module.exports = (grunt) ->
|
||||
path.join('less', 'test')
|
||||
path.join('bootstrap', 'docs')
|
||||
path.join('bootstrap', 'examples')
|
||||
path.join('spellchecker', 'vendor')
|
||||
path.join('pegjs', 'examples')
|
||||
# Add .* to avoid matching hunspell_dictionaries.
|
||||
path.join('spellchecker', 'vendor', 'hunspell', '.*')
|
||||
path.join('xmldom', 'test')
|
||||
path.join('jasmine-reporters', 'ext')
|
||||
path.join('build', 'Release', 'obj.target')
|
||||
@@ -56,6 +58,9 @@ module.exports = (grunt) ->
|
||||
path.join('resources', 'mac')
|
||||
path.join('resources', 'win')
|
||||
]
|
||||
# Hunspell dictionaries are only not needed on OS X.
|
||||
if process.platform is 'darwin'
|
||||
ignoredPaths.push path.join('spellchecker', 'vendor', 'hunspell_dictionaries')
|
||||
ignoredPaths = ignoredPaths.map (ignoredPath) -> "(#{ignoredPath})"
|
||||
nodeModulesFilter = new RegExp(ignoredPaths.join('|'))
|
||||
packageFilter = new RegExp("(#{ignoredPaths.join('|')})|(.+\\.(cson|coffee)$)")
|
||||
@@ -74,7 +79,7 @@ module.exports = (grunt) ->
|
||||
unless /.+\.plist/.test(sourcePath)
|
||||
grunt.file.copy(sourcePath, path.resolve(appDir, '..', subDirectory, filename))
|
||||
|
||||
dependencies = ['compile']
|
||||
dependencies = ['compile', "generate-license:save"]
|
||||
dependencies.push('copy-info-plist') if process.platform is 'darwin'
|
||||
dependencies.push('set-exe-icon') if process.platform is 'win32'
|
||||
grunt.task.run(dependencies...)
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
module.exports = (grunt) ->
|
||||
grunt.registerTask 'check-licenses', 'Report the licenses of all dependencies', ->
|
||||
legalEagle = require 'legal-eagle'
|
||||
{size, keys} = require 'underscore-plus'
|
||||
done = @async()
|
||||
|
||||
options =
|
||||
path: process.cwd()
|
||||
omitPermissive: true
|
||||
overrides: require './license-overrides'
|
||||
|
||||
legalEagle options, (err, summary) ->
|
||||
if err?
|
||||
console.error(err)
|
||||
exit 1
|
||||
|
||||
for key of summary
|
||||
delete summary[key] if key.match /^atom@/
|
||||
|
||||
if size(summary)
|
||||
console.error "Found dependencies without permissive licenses:"
|
||||
for name in keys(summary).sort()
|
||||
console.error "#{name}"
|
||||
console.error " License: #{summary[name].license}"
|
||||
console.error " Repository: #{summary[name].repository}"
|
||||
process.exit 1
|
||||
done()
|
||||
@@ -25,6 +25,7 @@ module.exports = (grunt) ->
|
||||
'--title', 'Atom API Documentation'
|
||||
'-o', 'docs/output/api'
|
||||
'-r', 'docs/README.md'
|
||||
'--stability', '1'
|
||||
'src/'
|
||||
includePaths...
|
||||
]
|
||||
@@ -142,6 +143,8 @@ downloadFileFromRepo = ({repo, file}, callback) ->
|
||||
|
||||
downloadIncludes = (callback) ->
|
||||
includes = [
|
||||
{repo: 'atom-keymap', file: 'src/keymap.coffee'}
|
||||
{repo: 'atom-keymap', file: 'src/key-binding.coffee'}
|
||||
{repo: 'first-mate', file: 'src/grammar.coffee'}
|
||||
{repo: 'first-mate', file: 'src/grammar-registry.coffee'}
|
||||
{repo: 'node-pathwatcher', file: 'src/directory.coffee'}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
async = require 'async'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
minidump = require 'minidump'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{mkdir, rm} = require('./task-helpers')(grunt)
|
||||
|
||||
dumpSymbolTo = (binaryPath, targetDirectory, callback) ->
|
||||
minidump.dumpSymbol binaryPath, (error, content) ->
|
||||
return callback(error) if error?
|
||||
|
||||
moduleLine = /MODULE [^ ]+ [^ ]+ ([0-9A-F]+) (.*)\n/.exec(content)
|
||||
if moduleLine.length isnt 3
|
||||
return callback("Invalid output when dumping symbol for #{binaryPath}")
|
||||
|
||||
filename = moduleLine[2]
|
||||
targetPathDirname = path.join(targetDirectory, filename, moduleLine[1])
|
||||
mkdir targetPathDirname
|
||||
|
||||
targetPath = path.join(targetPathDirname, "#{filename}.sym")
|
||||
fs.writeFile(targetPath, content, callback)
|
||||
|
||||
grunt.registerTask 'dump-symbols', 'Dump symbols for native modules', ->
|
||||
done = @async()
|
||||
|
||||
symbolsDir = grunt.config.get('atom.symbolsDir')
|
||||
rm symbolsDir
|
||||
mkdir symbolsDir
|
||||
|
||||
tasks = []
|
||||
onFile = (binaryPath) ->
|
||||
if /.*\.node$/.test(binaryPath)
|
||||
tasks.push(dumpSymbolTo.bind(this, binaryPath, symbolsDir))
|
||||
onDirectory = ->
|
||||
true
|
||||
fs.traverseTreeSync 'node_modules', onFile, onDirectory
|
||||
|
||||
async.parallel tasks, done
|
||||
@@ -0,0 +1,46 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
|
||||
module.exports = (grunt) ->
|
||||
grunt.registerTask 'generate-license', 'Generate the license, including the licenses of all dependencies', (mode) ->
|
||||
legalEagle = require 'legal-eagle'
|
||||
done = @async()
|
||||
|
||||
options =
|
||||
path: process.cwd()
|
||||
overrides: require './license-overrides'
|
||||
|
||||
legalEagle options, (err, dependencyLicenses) ->
|
||||
if err?
|
||||
console.error(err)
|
||||
exit 1
|
||||
|
||||
licenseText = getLicenseText(dependencyLicenses)
|
||||
if mode is 'save'
|
||||
targetPath = path.join(grunt.config.get('atom.appDir'), 'LICENSE.md')
|
||||
fs.writeFileSync(targetPath, licenseText)
|
||||
else
|
||||
console.log licenseText
|
||||
done()
|
||||
|
||||
getLicenseText = (dependencyLicenses) ->
|
||||
{keys} = require 'underscore-plus'
|
||||
text = """
|
||||
#{fs.readFileSync('LICENSE.md', 'utf8')}
|
||||
|
||||
This application bundles the following third-party packages in accordance
|
||||
with the following licenses:\n\n
|
||||
"""
|
||||
names = keys(dependencyLicenses).sort()
|
||||
for name in names
|
||||
{license, source, sourceText} = dependencyLicenses[name]
|
||||
|
||||
text += "-------------------------------------------------------------------------\n\n"
|
||||
text += "Package: #{name}\n"
|
||||
text += "License: #{license}\n"
|
||||
text += "License Source: #{source}\n" if source?
|
||||
if sourceText?
|
||||
text += "Source Text:\n\n"
|
||||
text += sourceText
|
||||
text += '\n'
|
||||
text
|
||||
@@ -1,4 +1,6 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
fs = require 'fs-plus'
|
||||
runas = null
|
||||
|
||||
module.exports = (grunt) ->
|
||||
@@ -15,7 +17,23 @@ module.exports = (grunt) ->
|
||||
|
||||
createShortcut = path.resolve 'script', 'create-shortcut.cmd'
|
||||
runas('cmd', ['/c', createShortcut, path.join(installDir, 'atom.exe'), 'Atom'])
|
||||
else
|
||||
else if process.platform is 'darwin'
|
||||
rm installDir
|
||||
mkdir path.dirname(installDir)
|
||||
cp shellAppDir, installDir
|
||||
else
|
||||
binDir = path.join(installDir, 'bin')
|
||||
shareDir = path.join(installDir, 'share', 'atom')
|
||||
|
||||
mkdir binDir
|
||||
cp 'atom.sh', path.join(binDir, 'atom')
|
||||
rm shareDir
|
||||
mkdir path.dirname(shareDir)
|
||||
cp shellAppDir, shareDir
|
||||
|
||||
# Create relative symbol link for apm.
|
||||
process.chdir(binDir)
|
||||
rm('apm')
|
||||
fs.symlinkSync(path.join('..', 'share', 'atom', 'resources', 'app', 'apm', 'node_modules', '.bin', 'apm'), 'apm')
|
||||
|
||||
fs.chmodSync(path.join(shareDir, 'atom'), "755")
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
module.exports =
|
||||
'aws-sign@0.3.0':
|
||||
repository: 'https://github.com/mikeal/aws-sign'
|
||||
license: 'MIT'
|
||||
source: 'index.js'
|
||||
sourceText: """
|
||||
/*!
|
||||
* knox - auth
|
||||
* Copyright(c) 2010 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
<content omitted>
|
||||
"""
|
||||
'bufferjs@2.0.0':
|
||||
repository: 'https://github.com/coolaj86/node-bufferjs'
|
||||
license: 'MIT'
|
||||
source: 'LICENSE.MIT'
|
||||
sourceText: """
|
||||
Copyright (c) 2010 AJ ONeal (and Contributors)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
"""
|
||||
'buffers@0.1.1':
|
||||
repository: "http://github.com/substack/node-buffers"
|
||||
license: 'MIT'
|
||||
source: 'README.markdown'
|
||||
sourceText: """
|
||||
<content omitted>
|
||||
license
|
||||
=======
|
||||
|
||||
MIT/X11
|
||||
"""
|
||||
'specificity@0.1.3':
|
||||
repository: 'https://github.com/keeganstreet/specificity'
|
||||
license: 'MIT'
|
||||
source: 'package.json in repository'
|
||||
|
||||
'promzard@0.2.0':
|
||||
license: 'ISC'
|
||||
source: 'LICENSE in the repository'
|
||||
sourceText: """
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
"""
|
||||
@@ -0,0 +1,31 @@
|
||||
fs = require 'fs'
|
||||
path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
fillTemplate = (filePath, data) ->
|
||||
template = _.template(String(fs.readFileSync(filePath + '.in')))
|
||||
filled = template(data)
|
||||
fs.writeFileSync(filePath, filled)
|
||||
|
||||
module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
grunt.registerTask 'mkdeb', 'Create debian package', ->
|
||||
done = @async()
|
||||
|
||||
{name, version, description} = grunt.file.readJSON('package.json')
|
||||
section = 'devel'
|
||||
arch = 'amd64'
|
||||
maintainer = 'GitHub <atom@github.com>'
|
||||
data = {name, version, description, section, arch, maintainer}
|
||||
|
||||
control = path.join('resources', 'linux', 'debian', 'control')
|
||||
fillTemplate(control, data)
|
||||
desktop = path.join('resources', 'linux', 'Atom.desktop')
|
||||
fillTemplate(desktop, data)
|
||||
icon = path.join('resources', 'atom.png')
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
|
||||
cmd = path.join('script', 'mkdeb')
|
||||
args = [version, control, desktop, icon, buildDir]
|
||||
spawn({cmd, args}, done)
|
||||
@@ -2,14 +2,17 @@ child_process = require 'child_process'
|
||||
path = require 'path'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
async = require 'async'
|
||||
fs = require 'fs-plus'
|
||||
GitHub = require 'github-releases'
|
||||
request = require 'request'
|
||||
|
||||
grunt = null
|
||||
maxReleases = 10
|
||||
assetName = 'atom-mac.zip'
|
||||
assetPath = "/tmp/atom-build/#{assetName}"
|
||||
assets = [
|
||||
{assetName: 'atom-mac.zip', sourceName: 'Atom.app'}
|
||||
{assetName: 'atom-mac-symbols.zip', sourceName: 'Atom.breakpad.syms'}
|
||||
]
|
||||
commitSha = process.env.JANKY_SHA1
|
||||
token = process.env.ATOM_ACCESS_TOKEN
|
||||
defaultHeaders =
|
||||
@@ -18,58 +21,41 @@ defaultHeaders =
|
||||
|
||||
module.exports = (gruntObject) ->
|
||||
grunt = gruntObject
|
||||
|
||||
grunt.registerTask 'publish-build', 'Publish the built app', ->
|
||||
return unless process.platform is 'darwin'
|
||||
return if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH isnt 'master'
|
||||
|
||||
done = @async()
|
||||
buildDir = grunt.config.get('atom.buildDir')
|
||||
|
||||
createBuildRelease (error, release) ->
|
||||
zipApps buildDir, assets, (error) ->
|
||||
return done(error) if error?
|
||||
zipApp (error) ->
|
||||
getAtomDraftRelease (error, release) ->
|
||||
return done(error) if error?
|
||||
uploadAsset release, (error) ->
|
||||
assetNames = (asset.assetName for asset in assets)
|
||||
deleteExistingAssets release, assetNames, (error) ->
|
||||
return done(error) if error?
|
||||
publishRelease release, (error) ->
|
||||
return done(error) if error?
|
||||
getAtomDraftRelease (error, release) ->
|
||||
return done(error) if error?
|
||||
deleteExistingAsset release, (error) ->
|
||||
return done(error) if error?
|
||||
uploadAsset(release, done)
|
||||
uploadAssets(release, buildDir, assets, done)
|
||||
|
||||
logError = (message, error, details) ->
|
||||
grunt.log.error(message)
|
||||
grunt.log.error(error.message ? error) if error?
|
||||
grunt.log.error(details) if details
|
||||
|
||||
zipApp = (callback) ->
|
||||
fs.removeSync(assetPath)
|
||||
zipApps = (buildDir, assets, callback) ->
|
||||
zip = (directory, sourceName, assetName, callback) ->
|
||||
options = {cwd: directory, maxBuffer: Infinity}
|
||||
child_process.exec "zip -r --symlinks #{assetName} #{sourceName}", options, (error, stdout, stderr) ->
|
||||
if error?
|
||||
logError("Zipping #{sourceName} failed", error, stderr)
|
||||
callback(error)
|
||||
|
||||
options = {cwd: path.dirname(assetPath), maxBuffer: Infinity}
|
||||
child_process.exec "zip -r --symlinks #{assetName} Atom.app", options, (error, stdout, stderr) ->
|
||||
if error?
|
||||
logError('Zipping Atom.app failed', error, stderr)
|
||||
callback(error)
|
||||
|
||||
getRelease = (callback) ->
|
||||
options =
|
||||
uri: 'https://api.github.com/repos/atom/atom-master-builds/releases'
|
||||
method: 'GET'
|
||||
headers: defaultHeaders
|
||||
json: true
|
||||
request options, (error, response, releases=[]) ->
|
||||
if error? or response.statusCode isnt 200
|
||||
logError('Fetching releases failed', error, releases)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
if releases.length > maxReleases
|
||||
deleteRelease(release) for release in releases[maxReleases..]
|
||||
|
||||
for release in releases when release.name is commitSha
|
||||
callback(null, release)
|
||||
return
|
||||
callback()
|
||||
tasks = []
|
||||
for {assetName, sourceName} in assets
|
||||
fs.removeSync(path.join(buildDir, assetName))
|
||||
tasks.push(zip.bind(this, buildDir, sourceName, assetName))
|
||||
async.parallel(tasks, callback)
|
||||
|
||||
getAtomDraftRelease = (callback) ->
|
||||
atomRepo = new GitHub({repo: 'atom/atom', token})
|
||||
@@ -93,10 +79,12 @@ deleteRelease = (release) ->
|
||||
if error? or response.statusCode isnt 204
|
||||
logError('Deleting release failed', error, body)
|
||||
|
||||
deleteExistingAsset = (release, callback) ->
|
||||
for asset in release.assets when asset.name is assetName
|
||||
deleteExistingAssets = (release, assetNames, callback) ->
|
||||
[callback, assetNames] = [assetNames, callback] if not callback?
|
||||
|
||||
deleteAsset = (url, callback) ->
|
||||
options =
|
||||
uri: asset.url
|
||||
uri: url
|
||||
method: 'DELETE'
|
||||
headers: defaultHeaders
|
||||
request options, (error, response, body='') ->
|
||||
@@ -106,67 +94,32 @@ deleteExistingAsset = (release, callback) ->
|
||||
else
|
||||
callback()
|
||||
|
||||
return
|
||||
|
||||
callback()
|
||||
|
||||
createBuildRelease = (callback) ->
|
||||
getRelease (error, release) ->
|
||||
if error?
|
||||
callback(error)
|
||||
return
|
||||
|
||||
if release?
|
||||
deleteExistingAsset release, (error) ->
|
||||
callback(error, release)
|
||||
return
|
||||
tasks = []
|
||||
for asset in release.assets when not assetNames? or asset.name in assetNames
|
||||
tasks.push(deleteAsset.bind(this, asset.url))
|
||||
async.parallel(tasks, callback)
|
||||
|
||||
uploadAssets = (release, buildDir, assets, callback) ->
|
||||
upload = (release, assetName, assetPath, callback) ->
|
||||
options =
|
||||
uri: 'https://api.github.com/repos/atom/atom-master-builds/releases'
|
||||
uri: release.upload_url.replace(/\{.*$/, "?name=#{assetName}")
|
||||
method: 'POST'
|
||||
headers: defaultHeaders
|
||||
json:
|
||||
tag_name: "v#{commitSha}"
|
||||
target_commitish: 'master'
|
||||
name: commitSha
|
||||
body: "Build of [atom@#{commitSha.substring(0, 7)}](https://github.com/atom/atom/commits/#{commitSha})"
|
||||
draft: true
|
||||
prerelease: true
|
||||
request options, (error, response, release={}) ->
|
||||
if error? or response.statusCode isnt 201
|
||||
logError('Creating release failed', error, release)
|
||||
headers: _.extend({
|
||||
'Content-Type': 'application/zip'
|
||||
'Content-Length': fs.getSizeSync(assetPath)
|
||||
}, defaultHeaders)
|
||||
|
||||
assetRequest = request options, (error, response, body='') ->
|
||||
if error? or response.statusCode >= 400
|
||||
logError("Upload release asset #{assetName} failed", error, body)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
callback(null, release)
|
||||
|
||||
uploadAsset = (release, callback) ->
|
||||
options =
|
||||
uri: release.upload_url.replace(/\{.*$/, "?name=#{assetName}")
|
||||
method: 'POST'
|
||||
headers: _.extend({
|
||||
'Content-Type': 'application/zip'
|
||||
'Content-Length': fs.getSizeSync(assetPath)
|
||||
}, defaultHeaders)
|
||||
fs.createReadStream(assetPath).pipe(assetRequest)
|
||||
|
||||
assetRequest = request options, (error, response, body='') ->
|
||||
if error? or response.statusCode >= 400
|
||||
logError('Upload release asset failed', error, body)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
callback(null, release)
|
||||
|
||||
fs.createReadStream(assetPath).pipe(assetRequest)
|
||||
|
||||
publishRelease = (release, callback) ->
|
||||
options =
|
||||
uri: release.url
|
||||
method: 'POST'
|
||||
headers: defaultHeaders
|
||||
json:
|
||||
draft: false
|
||||
request options, (error, response, body={}) ->
|
||||
if error? or response.statusCode isnt 200
|
||||
logError('Creating release failed', error, body)
|
||||
callback(error ? new Error(response.statusCode))
|
||||
else
|
||||
callback()
|
||||
tasks = []
|
||||
for {assetName, sourceName} in assets
|
||||
assetPath = path.join(buildDir, assetName)
|
||||
tasks.push(upload.bind(this, release, assetName, assetPath))
|
||||
async.parallel(tasks, callback)
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
async = require 'async'
|
||||
request = require 'request'
|
||||
|
||||
# Configure and publish all packages in package.json to atom.io
|
||||
#
|
||||
# This task should be run whenever you want to be sure that atom.io contains
|
||||
# all the packages and versions referenced in Atom's package.json file.
|
||||
module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
baseUrl = "https://www.atom.io/api/packages"
|
||||
|
||||
packageExists = (packageName, token, callback) ->
|
||||
requestSettings =
|
||||
url: "#{baseUrl}/#{packageName}"
|
||||
json: true
|
||||
headers:
|
||||
authorization: token
|
||||
request.get requestSettings, (error, response, body={}) ->
|
||||
if error?
|
||||
callback(error)
|
||||
else
|
||||
callback(null, response.statusCode is 404)
|
||||
|
||||
createPackage = (packageName, token, callback) ->
|
||||
requestSettings =
|
||||
url: baseUrl
|
||||
json: true
|
||||
headers:
|
||||
authorization: token
|
||||
method: 'POST'
|
||||
body:
|
||||
repository: "atom/#{packageName}"
|
||||
|
||||
request.get requestSettings, (error, response, body={}) ->
|
||||
if error?
|
||||
callback(error)
|
||||
else if response.statusCode isnt 201
|
||||
message = body.message ? body.error ? body
|
||||
callback(new Error("Creating package failed: #{message}"))
|
||||
else
|
||||
callback()
|
||||
|
||||
createPackageVersion = (packageName, tag, token, callback) ->
|
||||
requestSettings =
|
||||
url: "#{baseUrl}/#{packageName}/versions"
|
||||
json: true
|
||||
method: 'POST'
|
||||
headers:
|
||||
authorization: token
|
||||
body:
|
||||
tag: tag
|
||||
|
||||
request.get requestSettings, (error, response, body={}) ->
|
||||
if error?
|
||||
callback(error)
|
||||
else if response.statusCode isnt 201
|
||||
message = body.message ? body.error ? body
|
||||
if message is 'Version exists'
|
||||
callback()
|
||||
else
|
||||
callback(new Error("Creating new version failed: #{message}"))
|
||||
else
|
||||
callback()
|
||||
|
||||
getToken = (callback) ->
|
||||
if token = process.env.ATOM_ACCESS_TOKEN
|
||||
callback(null, token)
|
||||
else
|
||||
spawn {cmd: 'security', args: ['-q', 'find-generic-password', '-ws', 'GitHub API Token']}, (error, result, code) ->
|
||||
token = result.toString() unless error?
|
||||
callback(error, token)
|
||||
|
||||
grunt.registerTask 'publish-packages', 'Publish all bundled packages', ->
|
||||
done = @async()
|
||||
|
||||
getToken (error, token) ->
|
||||
unless token
|
||||
grunt.log.error('Token not found in keychain or ATOM_ACCESS_TOKEN environment variable')
|
||||
done(false)
|
||||
|
||||
{packageDependencies} = grunt.file.readJSON('package.json') ? {}
|
||||
tasks = []
|
||||
for name, version of packageDependencies
|
||||
do (name, version) ->
|
||||
tasks.push (callback) ->
|
||||
grunt.verbose.writeln("Publishing #{name}@#{version}")
|
||||
tag = "v#{version}"
|
||||
packageExists name, token, (error, exists) ->
|
||||
if error?
|
||||
callback(error)
|
||||
return
|
||||
|
||||
if exists
|
||||
createPackage name, token, (error) ->
|
||||
if error?
|
||||
callback(error)
|
||||
else
|
||||
createPackageVersion(name, tag, token, callback)
|
||||
else
|
||||
createPackageVersion(name, tag, token, callback)
|
||||
|
||||
async.waterfall tasks, (error) ->
|
||||
if error?
|
||||
grunt.log.error(error.message)
|
||||
done(false)
|
||||
else
|
||||
done()
|
||||
@@ -5,14 +5,18 @@ module.exports = (grunt) ->
|
||||
{spawn} = require('./task-helpers')(grunt)
|
||||
|
||||
getVersion = (callback) ->
|
||||
if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH is 'master'
|
||||
{version} = require(path.join(grunt.config.get('atom.appDir'), 'package.json'))
|
||||
onBuildMachine = process.env.JANKY_SHA1 and process.env.JANKY_BRANCH is 'master'
|
||||
inRepository = fs.existsSync(path.resolve(__dirname, '..', '..', '.git'))
|
||||
{version} = require(path.join(grunt.config.get('atom.appDir'), 'package.json'))
|
||||
if onBuildMachine or not inRepository
|
||||
callback(null, version)
|
||||
else
|
||||
cmd = 'git'
|
||||
args = ['rev-parse', '--short', 'HEAD']
|
||||
spawn {cmd, args}, (error, {stdout}={}, code) ->
|
||||
callback(error, stdout?.trim?())
|
||||
commitHash = stdout?.trim?()
|
||||
combinedVersion = "#{version}-#{commitHash}"
|
||||
callback(error, combinedVersion)
|
||||
|
||||
grunt.registerTask 'set-version', 'Set the version in the plist and package.json', ->
|
||||
done = @async()
|
||||
@@ -48,3 +52,5 @@ module.exports = (grunt) ->
|
||||
|
||||
rcedit = require('rcedit')
|
||||
rcedit(shellExePath, {'version-string': strings}, done)
|
||||
else
|
||||
done()
|
||||
|
||||
+4
-2
@@ -1,6 +1,6 @@
|
||||
# Welcome to the Atom API Documentation
|
||||
|
||||

|
||||

|
||||
|
||||
## FAQ
|
||||
|
||||
@@ -31,6 +31,7 @@ The classes available from `require 'atom'` are:
|
||||
* [SelectListView][SelectListView]
|
||||
* [View][View]
|
||||
* [WorkspaceView][WorkspaceView]
|
||||
* [Workspace][Workspace]
|
||||
|
||||
### How do I create a package?
|
||||
|
||||
@@ -54,5 +55,6 @@ Atom ships with node 0.11.10 and the comprehensive node API docs are available
|
||||
[SelectListView]: ../classes/SelectListView.html
|
||||
[View]: ../classes/View.html
|
||||
[WorkspaceView]: ../classes/WorkspaceView.html
|
||||
[creating-a-package]: https://www.atom.io/docs/latest/creating-a-package
|
||||
[Workspace]: ../classes/Workspace.html
|
||||
[creating-a-package]: https://atom.io/docs/latest/creating-a-package
|
||||
[node-docs]: http://nodejs.org/docs/v0.11.10/api
|
||||
|
||||
@@ -83,6 +83,22 @@ file. For now, we've opted to handle cases where selector ordering is critical
|
||||
by breaking the keymap into two separate files, such as `snippets-1.cson` and
|
||||
`snippets-2.cson`.
|
||||
|
||||
## Removing Bindings
|
||||
|
||||
When the keymap system encounters a binding with the `unset!` directive as its
|
||||
command, it will treat the current element as if it had no key bindings matching
|
||||
the current keystroke sequence and continue searching from its parent. If you
|
||||
want to remove a binding from a keymap you don't control, such as keymaps in
|
||||
Atom core or in packages, use the `unset!` directive.
|
||||
|
||||
## Forcing Chromium's Native Keystroke Handling
|
||||
|
||||
If you want to force the native browser behavior for a given keystroke, use the
|
||||
`native!` directive as the command of a binding. This can be useful to enable
|
||||
the correct behavior in native input elements, for example. If you apply the
|
||||
`.native-key-bindings` class to an element, all the keystrokes typically handled
|
||||
by the browser will be assigned the `native!` directive.
|
||||
|
||||
## Overloading Key Bindings
|
||||
|
||||
Occasionally, it makes sense to layer multiple actions on top of the same key
|
||||
|
||||
@@ -19,7 +19,7 @@ module.exports =
|
||||
activate: (state) ->
|
||||
@myObject =
|
||||
if state
|
||||
deserialize(state)
|
||||
atom.deserializers.deserialize(state)
|
||||
else
|
||||
new MyObject("Hello")
|
||||
|
||||
@@ -31,7 +31,8 @@ module.exports =
|
||||
|
||||
```coffee-script
|
||||
class MyObject
|
||||
registerDeserializer(this)
|
||||
atom.deserializers.add(this)
|
||||
|
||||
@deserialize: ({data}) -> new MyObject(data)
|
||||
constructor: (@data) ->
|
||||
serialize: -> { deserializer: 'MyObject', data: @data }
|
||||
@@ -50,8 +51,8 @@ class-level method on the same class that implements `serialize`. This method's
|
||||
job is to convert a state object returned from a previous call `serialize` back
|
||||
into a genuine object.
|
||||
|
||||
#### registerDeserializer(klass)
|
||||
You need to call the global `registerDeserializer` method with your class in
|
||||
#### atom.deserializers.add(klass)
|
||||
You need to call the `atom.deserializers.add` method with your class in
|
||||
order to make it available to the deserialization system. Now you can call the
|
||||
global `deserialize` method with state returned from `serialize`, and your
|
||||
class's `deserialize` method will be selected automatically.
|
||||
@@ -60,14 +61,15 @@ class's `deserialize` method will be selected automatically.
|
||||
|
||||
```coffee-script
|
||||
class MyObject
|
||||
atom.deserializers.add(this)
|
||||
|
||||
@version: 2
|
||||
@deserialize: (state) -> ...
|
||||
serialize: -> { version: MyObject.version, ... }
|
||||
serialize: -> { version: @constructor.version, ... }
|
||||
```
|
||||
|
||||
Your serializable class can optionally have a class-level `@version` property
|
||||
and include a `version` key in its serialized state. When deserializing, Atom
|
||||
will only attempt to call deserialize if the two versions match, and otherwise
|
||||
return undefined. We plan on implementing a migration system in the future, but
|
||||
this at least protects you from improperly deserializing old state. If you find
|
||||
yourself in dire need of the migration system, let us know.
|
||||
this at least protects you from improperly deserializing old state.
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# FreeBSD
|
||||
|
||||
FreeBSD -RELEASE 64-bit is the recommended platform.
|
||||
|
||||
## Requirements
|
||||
|
||||
* FreeBSD
|
||||
* `pkg install node`
|
||||
* `pkg install npm`
|
||||
* `pkg install libgnome-keyring`
|
||||
* `npm config set python /usr/local/bin/python2 -g` to ensure that gyp uses Python 2
|
||||
|
||||
## Instructions
|
||||
|
||||
```sh
|
||||
git clone https://github.com/atom/atom
|
||||
cd atom
|
||||
script/build # Creates application at /tmp/atom-build/Atom
|
||||
sudo script/grunt install # Installs command to /usr/local/bin/atom
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
@@ -0,0 +1,23 @@
|
||||
# Linux
|
||||
|
||||
Ubuntu LTS 12.04 64-bit is the recommended platform.
|
||||
|
||||
## Requirements
|
||||
|
||||
* OS with 64-bit architecture
|
||||
* [node.js](http://nodejs.org/download/) v0.10.x
|
||||
* [npm](http://www.npmjs.org/) v1.4.x
|
||||
* libgnome-keyring-dev `sudo apt-get install libgnome-keyring-dev` (refer to your distribution's manual on how to install packages if you are not on Debian or Ubuntu-based systems)
|
||||
* `npm config set python /usr/bin/python2 -g` to ensure that gyp uses Python 2
|
||||
|
||||
## Instructions
|
||||
|
||||
```sh
|
||||
git clone https://github.com/atom/atom
|
||||
cd atom
|
||||
script/build # Creates application at /tmp/atom-build/Atom
|
||||
sudo script/grunt install # Installs command to /usr/local/bin/atom
|
||||
script/grunt mkdeb # Generates a .deb package at /tmp/atom-build
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
@@ -0,0 +1,17 @@
|
||||
# OS X
|
||||
|
||||
## Requirements
|
||||
|
||||
* OS X 10.8 or later
|
||||
* [node.js](http://nodejs.org/download/) v0.10.x
|
||||
* Command Line Tools for [Xcode](https://developer.apple.com/xcode/downloads/) (run `xcode-select --install` to install)
|
||||
|
||||
## Instructions
|
||||
|
||||
```sh
|
||||
git clone https://github.com/atom/atom
|
||||
cd atom
|
||||
script/build # Creates application at /Applications/Atom.app
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
@@ -0,0 +1,42 @@
|
||||
# Windows
|
||||
|
||||
## Requirements
|
||||
|
||||
* Windows 7 or later
|
||||
* [Visual C++ 2010 SP1 Express](http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs#DownloadFamilies_4)
|
||||
* [node.js - 32bit](http://nodejs.org/download/) v0.10.x
|
||||
* [Python 2.7.x](http://www.python.org/download/)
|
||||
* [GitHub for Windows](http://windows.github.com/)
|
||||
to your PATH
|
||||
* Open the Windows GitHub shell (NOT the Standard PowerShell, the shortcut labeled 'Git Shell' - make sure you have logged in at least once to the GitHub for Windows GUI App)
|
||||
* `$env:Path = $env:Path + ";C:\path\to\atom\repo\node_modules"`
|
||||
|
||||
## Instructions
|
||||
|
||||
```bat
|
||||
cd C:\Users\<user>\github
|
||||
git clone https://github.com/atom/atom/
|
||||
cd atom
|
||||
script\build
|
||||
```
|
||||
|
||||
## Why do I have to use GitHub for Windows? Can't I just use my existing Git?
|
||||
|
||||
You totally can! GitHub for Windows's Git Shell just takes less work to set up. You need to have Posix tools in your `%PATH%` (i.e. `grep`, `sed`, et al.), which isn't the default configuration when you install Git. To fix this, you probably need to fiddle with your system PATH.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Some of the most common errors include:
|
||||
|
||||
gyp WARN install got an error, rolling back install
|
||||
and
|
||||
|
||||
>> The system cannot find the path specified.
|
||||
|
||||
These two error messages can usually be ignored. The solution to these errors is to re-run `script\build`, possibly several times.
|
||||
|
||||
If your Visual Studio is in a non-standard location, and you get the error `You must have Visual Studio 2010 or 2012 installed`, you need to modify `apm\node_modules\atom-package-manager\lib\config.js` around line 90 and replace the variable with your Visual Studio directory plus Common7/IDE.
|
||||
|
||||
Example:
|
||||
|
||||
vs2010Path = "H:/VS2010/Common7/IDE"
|
||||
@@ -1,34 +0,0 @@
|
||||
## 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 OS X 10.8 or later
|
||||
* Install the latest node 0.10.x release (32bit preferable)
|
||||
* Clone [atom][atom-git] to `~/github/atom`
|
||||
* Run `~/github/atom/script/build`
|
||||
|
||||
## 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]: https://github.com/atom/atom/releases/latest
|
||||
[win-node]: http://nodejs.org/download/
|
||||
[win-python]: http://www.python.org/download/
|
||||
[win-github]: http://windows.github.com/
|
||||
[win-vs2010]: http://www.microsoft.com/visualstudio/eng/products/visual-studio-2010-express
|
||||
[atom-git]: https://github.com/atom/atom/
|
||||
@@ -116,8 +116,10 @@ dependencies up to date by running `apm update` after pulling upstream changes.
|
||||
|
||||
* Use [TomDoc](http://tomdoc.org).
|
||||
* Use [Markdown](https://daringfireball.net/projects/markdown).
|
||||
* Reference classes with the custom `{ClassName}` notation.
|
||||
* Reference methods with the custom `{ClassName.methodName}` notation.
|
||||
* Reference methods and classes in markdown with the custom `{}` notation:
|
||||
* Reference classes with `{ClassName}`
|
||||
* Reference instance methods with `{ClassName::methodName}`
|
||||
* Reference class methods with `{ClassName.methodName}`
|
||||
|
||||
### Example
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ apm help init
|
||||
|
||||
You should see a message print out with details about the `apm init` command.
|
||||
|
||||
If you do not, launch Atom and run the _Atom > Install Shell Commmands_ menu
|
||||
If you do not, launch Atom and run the _Atom > Install Shell Commands_ menu
|
||||
to install the `apm` and `atom` commands.
|
||||
|
||||
### Convert the Package
|
||||
@@ -40,7 +40,7 @@ the editor to see it in action!
|
||||
|
||||
### Further Reading
|
||||
|
||||
* Check out [Publishing a Package](publish-a-package.html) for more information
|
||||
* Check out [Publishing a Package](publishing-a-package.html) for more information
|
||||
on publishing the package you just created to [atom.io][atomio].
|
||||
|
||||
[atomio]: https://atom.io
|
||||
@@ -49,4 +49,4 @@ the editor to see it in action!
|
||||
[plist]: http://en.wikipedia.org/wiki/Property_list
|
||||
[R]: http://en.wikipedia.org/wiki/R_(programming_language)
|
||||
[TextMate]: http://macromates.com
|
||||
[TextMateOrg]: https://github.com/textmate/r.tmbundle
|
||||
[TextMateOrg]: https://github.com/textmate
|
||||
|
||||
@@ -25,7 +25,7 @@ apm help init
|
||||
|
||||
You should see a message print out with details about the `apm init` command.
|
||||
|
||||
If you do not, launch Atom and run the _Atom > Install Shell Commmands_ menu
|
||||
If you do not, launch Atom and run the _Atom > Install Shell Commands_ menu
|
||||
to install the `apm` and `atom` commands.
|
||||
|
||||
You can now run `apm help init` to see all the options for initializing new
|
||||
@@ -57,7 +57,7 @@ __Syntax Theme__ dropdown menu to enable your new theme.
|
||||
|
||||
### Further Reading
|
||||
|
||||
* Check out [Publishing a Package](publish-a-package.html) for more information
|
||||
* Check out [Publishing a Package](publishing-a-package.html) for more information
|
||||
on publishing the theme you just created to [atom.io][atomio].
|
||||
|
||||
[atomio]: https://atom.io
|
||||
|
||||
@@ -24,6 +24,9 @@ Not every package will have (or need) all of these directories.
|
||||
|
||||
We have [a tutorial on creating your first package][first-package].
|
||||
|
||||
There are also guides for converting [TextMate bundles][convert-bundle] and
|
||||
[TextMate themes][convert-theme] so they work in Atom.
|
||||
|
||||
## package.json
|
||||
|
||||
Similar to [npm packages][npm], Atom packages contain a _package.json_ file
|
||||
@@ -127,7 +130,7 @@ 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-shift-P`) and search for _styleguide_,
|
||||
or just type `cmd-ctrl-G`.
|
||||
or just type `cmd-ctrl-shift-G`.
|
||||
|
||||
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
|
||||
@@ -322,27 +325,20 @@ Your package **should** have tests, and if they're placed in the _spec_
|
||||
directory, they can be run by Atom.
|
||||
|
||||
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**
|
||||
|
||||
* jasmine
|
||||
* jasmine-focused
|
||||
* `spec/fixtures` and global.project
|
||||
* setTimeout
|
||||
* whatever else is different in spec-helper
|
||||
available there is also available to your package.
|
||||
|
||||
## Running Tests
|
||||
|
||||
TODO: Probably use the menu option now.
|
||||
Once you've got your test suite written, you can run it by pressing
|
||||
`cmd-alt-ctrl-p` or via the _Developer > Run Package Specs_ menu.
|
||||
|
||||
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
|
||||
code depending on whether tests passed or failed.
|
||||
You can also use the `apm test` command to run them from the command line. It
|
||||
prints the test output and results to the console and returns the proper status
|
||||
code depending on whether the tests passed or failed.
|
||||
|
||||
## Publishing
|
||||
|
||||
Atom bundles a command line utility called [apm] which can be used to publish
|
||||
Atom bundles a command line utility called apm which can be used to publish
|
||||
Atom packages to the public registry.
|
||||
|
||||
Once your package is written and ready for distribution you can run the
|
||||
@@ -360,25 +356,11 @@ registry.
|
||||
Run `apm help publish` to see all the available options and `apm help` to see
|
||||
all the other available commands.
|
||||
|
||||
## Included Libraries
|
||||
|
||||
FIXME: Describe `require 'atom'
|
||||
|
||||
In addition to core node.js modules, all packages can `require` the following
|
||||
popular libraries into their packages:
|
||||
|
||||
* [SpacePen] (as `require 'space-pen'`)
|
||||
* [jQuery] (as `require 'jquery'`)
|
||||
* [Underscore] (as `require 'underscore'`)
|
||||
|
||||
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]: advanced/keymaps.md
|
||||
@@ -388,8 +370,10 @@ Additional libraries can be found by browsing Atom's *node_modules* folder.
|
||||
[path]: http://nodejs.org/docs/latest/api/path.html
|
||||
[jquery]: http://jquery.com/
|
||||
[underscore]: http://underscorejs.org/
|
||||
[jasmine]: https://github.com/pivotal/jasmine
|
||||
[jasmine]: http://jasmine.github.io
|
||||
[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
|
||||
[convert-bundle]: converting-a-text-mate-bundle.html
|
||||
[convert-theme]: converting-a-text-mate-theme.html
|
||||
|
||||
+65
-55
@@ -1,124 +1,134 @@
|
||||
# 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
|
||||
with a few handy extensions.
|
||||
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 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.
|
||||
Atom supports two types of themes: _UI_ and _syntax_. UI themes style
|
||||
elements such as the tree view, the tabs, drop-down lists, and the status bar.
|
||||
Syntax themes style the code inside the editor.
|
||||
|
||||
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.
|
||||
Themes can be installed and changed from the settings view which you can open
|
||||
by selecting the _Atom > Preferences..._ menu and navigating to the _Themes_
|
||||
section on the left hand side.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Themes are pretty straight forward but it's still helpful to be familiar with
|
||||
Themes are pretty straightforward 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
|
||||
variables. If you aren't familiar with its syntax take a few minutes
|
||||
variables. If you aren't familiar with its syntax, take a few minutes
|
||||
to [familiarize yourself][less-tutorial].
|
||||
* 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.
|
||||
|
||||
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.
|
||||
is used to help distribute your theme to Atom users.
|
||||
* Your theme's _package.json_ must contain a `"theme"` key with a value
|
||||
of `"ui"` or `"syntax"` for Atom to recognize and load it as a theme.
|
||||
* You can find existing themes to install or fork on [atom.io](atomio).
|
||||
|
||||
## Creating a Syntax Theme
|
||||
|
||||
Let's create your first theme.
|
||||
|
||||
To get started, hit `cmd-shift-P`, and start typing "Generate Theme" to generate
|
||||
a package. Select "Generate Theme," and you'll be asked for a theme name. Let's
|
||||
call ours _motif_.
|
||||
To get started, hit `cmd-shift-P`, and start typing "Generate Syntax Theme" to
|
||||
generate a new theme package. Select "Generate Syntax Theme," and you'll be
|
||||
asked for the path where your theme will be created. Let's call ours
|
||||
_motif-syntax_. __Tip:__ syntax themes should end with _-syntax_.
|
||||
|
||||
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."
|
||||
Atom will pop open a new window, showing the _motif-syntax_ theme, with a
|
||||
default set of folders and files created for us. If you open the settings view
|
||||
(`cmd-,`) and navigate to the _Themes_ section on the left, you'll see the
|
||||
_Motif_ theme listed in the _Syntax Theme_ drop-down. Select it from the menu to
|
||||
activate it, now when you open an editor you should see that your new
|
||||
_motif-syntax_ theme in action.
|
||||
|
||||
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.
|
||||
Then open _stylesheets/base.less_ and modify the various selectors that have
|
||||
been already been defined. These selectors style different parts of code in the
|
||||
editor such as comments, strings and the line numbers in the gutter.
|
||||
|
||||
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!
|
||||
Reload Atom by pressing `cmd-alt-ctrl-l` to see the changes you made reflected
|
||||
in your Atom window. Pretty neat!
|
||||
|
||||
__Tip:__ You can avoid reloading to see changes you make by opening an atom
|
||||
window in dev mode. To open a Dev Mode Atom window run `atom --dev .` in the
|
||||
terminal, use `cmd-shift-o` or use the _View > Developer > Open in Dev Mode_
|
||||
menu. When you edit your theme, changes will instantly be reflected!
|
||||
|
||||
## Creating an Interface Theme
|
||||
|
||||
Interface themes **must** provide a `ui-variables.less` file which contains all
|
||||
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]
|
||||
1. [atom-light-ui]
|
||||
1. Open a terminal in the forked theme's directory
|
||||
1. Open your new theme in a Dev Mode Atom window (run `atom -d .` in the terminal or use the __View > Developer > Open in Dev Mode__ menu)
|
||||
1. Change the name of the theme in the theme's `package.json` file
|
||||
1. Run `apm link` to tell Atom about your new theme
|
||||
1. Reload Atom (`cmd-r`)
|
||||
1. Enable the theme via the themes panel in settings
|
||||
1. Make changes! Since you opened the theme in a Dev Mode window, changes will
|
||||
1. Fork one of the following repositories:
|
||||
* [atom-dark-ui]
|
||||
* [atom-light-ui]
|
||||
2. Clone the forked repository to the local filesystem
|
||||
3. Open a terminal in the forked theme's directory
|
||||
4. Open your new theme in a Dev Mode Atom window run `atom --dev .` in the
|
||||
terminal or use the _View > Developer > Open in Dev Mode_ menu
|
||||
5. Change the name of the theme in the theme's `package.json` file
|
||||
6. Name your theme end with a `-ui`. i.e. `super-white-ui`
|
||||
7. Run `apm link` to symlink your repository to `~/.atom/packages`
|
||||
8. Reload Atom using `cmd-alt-ctrl-L`
|
||||
9. Enable the theme via _UI Theme_ drop-down in the _Themes_ section of the
|
||||
settings view
|
||||
10. Make changes! Since you opened the theme in a Dev Mode window, changes will
|
||||
be instantly reflected in the editor without having to reload.
|
||||
|
||||
## Development workflow
|
||||
|
||||
There are a few of tools to help make theme development faster.
|
||||
There are a few of tools to help make theme development faster and easier.
|
||||
|
||||
### Live Reload
|
||||
|
||||
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.
|
||||
Reloading by hitting `cmd-alt-ctrl-L` after you make changes to your theme is
|
||||
less than ideal. Atom supports [live updating][livereload] of styles on Dev Mode
|
||||
Atom windows.
|
||||
|
||||
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!
|
||||
__View > Developer > Open in Dev Mode__ menu or by hitting the `cmd-shift-o`
|
||||
shortcut
|
||||
2. Make a change to your theme file and save it. Your change should be
|
||||
immediately applied!
|
||||
|
||||
If you'd like to reload all the styles at any time, you can use the shortcut
|
||||
`cmd-ctrl-R`.
|
||||
`cmd-ctrl-shift-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-alt-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.
|
||||
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
|
||||
changes affect all the components in the system. The [styleguide] is a page with
|
||||
every component Atom supports rendered.
|
||||
changes affect all the components in the system. The [styleguide] is a page that
|
||||
renders every component Atom supports.
|
||||
|
||||
To open the styleguide, open the command palette (`cmd-shift-P`) and search for
|
||||
_styleguide_, or use the shortcut `cmd-ctrl-shift-g`.
|
||||
|
||||
![styleguide-img]
|
||||
|
||||
[atomio]: http://atom.io/packages
|
||||
[less]: http://lesscss.org/
|
||||
[git]: http://git-scm.com/
|
||||
[atom]: https://atom.io/
|
||||
|
||||
+78
-38
@@ -1,34 +1,57 @@
|
||||
# Customizing Atom
|
||||
|
||||
To change a setting, configure a theme, or install a package just open the
|
||||
Settings pane in the current window by pressing `cmd+,`.
|
||||
Settings view in the current window by pressing `cmd-,`.
|
||||
|
||||
## Changing The Theme
|
||||
|
||||
Because Atom themes are based on CSS, it's possible (and encouraged) to have
|
||||
multiple themes active at the same time. Atom comes with both light and dark
|
||||
interface themes as well as several syntax themes (you can also [create your
|
||||
own][create-theme]).
|
||||
Atom comes with both light and dark UI themes as well as several syntax themes.
|
||||
You are also encouraged to [create or fork][create-theme] your own theme.
|
||||
|
||||
To change the active themes just open the Settings pane (`cmd-,`) and select the
|
||||
`Themes` tab. You can install non-bundled themes by going to the `Available
|
||||
Themes` section on the `Packages` tab within the Settings panel.
|
||||
To change the active theme just open the Settings view (`cmd-,`) and select the
|
||||
`Themes` section from the left hand side. You will see a drop-down menu to
|
||||
change the active _Syntax_ and _UI_ themes.
|
||||
|
||||
You can also install more themes from here by browsing the featured themes or
|
||||
searching for a specific theme.
|
||||
|
||||
## Installing Packages
|
||||
|
||||
You can install non-bundled packages by going to the `Available Packages`
|
||||
section on the `Packages` tab within the Settings panel (`cmd-,`).
|
||||
You can install non-bundled packages by going to the `Packages` section on left
|
||||
hand side of the Settings view (`cmd-,`). You will see several featured packages
|
||||
and you can also search for packages from here. The packages listed here have
|
||||
been published to [atom.io](http://atom.io/packages) which is the official
|
||||
registry for Atom packages.
|
||||
|
||||
You can also install packages from the command line using the
|
||||
[apm](https://github.com/atom/apm) command:
|
||||
You can also install packages from the command line using `apm`.
|
||||
|
||||
`apm install <package_name>` to install the latest version.
|
||||
Check that you have `apm` installed by running the following command in your
|
||||
terminal:
|
||||
|
||||
`apm install <package_name>@<package_version>` to install a specific version.
|
||||
```sh
|
||||
apm help install
|
||||
```
|
||||
|
||||
You should see a message print out with details about the `apm install` command.
|
||||
|
||||
If you do not, launch Atom and run the _Atom > Install Shell Commands_ menu
|
||||
to install the `apm` and `atom` commands.
|
||||
|
||||
You can also install packages by using the `apm install` command:
|
||||
|
||||
* `apm install <package_name>` to install the latest version.
|
||||
|
||||
* `apm install <package_name>@<package_version>` to install a specific version.
|
||||
|
||||
For example `apm install emmet@0.1.5` installs the `0.1.5` release of the
|
||||
[Emmet](https://github.com/atom/emmet) package into `~/.atom/packages`.
|
||||
|
||||
You can also use `apm` to find new packages to install:
|
||||
|
||||
* `apm search coffee` to search for CoffeeScript packages.
|
||||
|
||||
* `apm view emmet` to see more information about a specific package.
|
||||
|
||||
## Customizing Key Bindings
|
||||
|
||||
Atom keymaps work similarly to stylesheets. Just as stylesheets use selectors
|
||||
@@ -36,13 +59,12 @@ to apply styles to elements, Atom keymaps use selectors to associate keystrokes
|
||||
with events in specific contexts. Here's a small example, excerpted from Atom's
|
||||
built-in keymaps:
|
||||
|
||||
```coffee-script
|
||||
```coffee
|
||||
'.editor':
|
||||
'enter': 'editor:newline'
|
||||
|
||||
'body':
|
||||
'ctrl-P': 'core:move-up'
|
||||
'ctrl-p': 'core:move-down'
|
||||
'.mini.editor input':
|
||||
'enter': 'core:confirm'
|
||||
```
|
||||
|
||||
This keymap defines the meaning of `enter` in two different contexts. In a
|
||||
@@ -55,6 +77,8 @@ 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 can open this file in an editor from the _Atom > Open Your Keymap_ menu.
|
||||
|
||||
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.
|
||||
@@ -62,18 +86,20 @@ currently in use.
|
||||
## Advanced Configuration
|
||||
|
||||
Atom loads configuration settings from the `config.cson` file in your _~/.atom_
|
||||
directory, which contains CoffeeScript-style JSON:
|
||||
directory, which contains [CoffeeScript-style JSON][CSON] (CSON):
|
||||
|
||||
```coffeescript
|
||||
core:
|
||||
excludeVcsIgnoredPaths: true
|
||||
editor:
|
||||
fontSize: 18
|
||||
```coffee
|
||||
'core':
|
||||
'excludeVcsIgnoredPaths': true
|
||||
'editor':
|
||||
'fontSize': 18
|
||||
```
|
||||
|
||||
The configuration itself is grouped by the package name or one of the two core
|
||||
namespaces: `core` and `editor`.
|
||||
|
||||
You can open this file in an editor from the _Atom > Open Your Config_ menu.
|
||||
|
||||
### Configuration Key Reference
|
||||
|
||||
- `core`
|
||||
@@ -84,7 +110,6 @@ namespaces: `core` and `editor`.
|
||||
- `themes`: An array of theme names to load, in cascading order
|
||||
- `editor`
|
||||
- `autoIndent`: Enable/disable basic auto-indent (defaults to `true`)
|
||||
- `autoIndentOnPaste`: Enable/disable auto-indented pasted text (defaults to `false`)
|
||||
- `nonWordCharacters`: A string of non-word characters to define word boundaries
|
||||
- `fontSize`: The editor font size
|
||||
- `fontFamily`: The editor font family
|
||||
@@ -108,38 +133,53 @@ namespaces: `core` and `editor`.
|
||||
- `removeTrailingWhitespace`: Enable/disable striping of whitespace at the end of lines (defaults to `true`)
|
||||
- `wrap-guide`
|
||||
- `columns`: Array of hashes with a `pattern` and `column` key to match the
|
||||
the path of the current editor to a column position.
|
||||
the path of the current editor to a column position.
|
||||
|
||||
### Quick Personal Hacks
|
||||
|
||||
### init.coffee
|
||||
|
||||
When Atom finishes loading, it will evaluate _init.coffee_ in your _~/.atom_
|
||||
directory, giving you a chance to run arbitrary personal CoffeeScript code to
|
||||
directory, giving you a chance to run arbitrary personal [CoffeeScript][] code to
|
||||
make customizations. You have full access to Atom's API from code in this file.
|
||||
If customizations become extensive, consider [creating a
|
||||
package][create-a-package].
|
||||
If customizations become extensive, consider [creating a package][creating-a-package].
|
||||
|
||||
You can open this file in an editor from the _Atom > Open Your Init Script_
|
||||
menu.
|
||||
|
||||
For example, if you have the Audio Beep configuration setting enabled, you
|
||||
could add the following code to your _~/.atom/init.coffee_ file to have Atom
|
||||
greet you with an audio beep every time it loads:
|
||||
|
||||
```coffee
|
||||
atom.beep()
|
||||
```
|
||||
|
||||
This file can also be named _init.js_ and contain JavaScript code.
|
||||
|
||||
### styles.css
|
||||
### styles.less
|
||||
|
||||
If you want to apply quick-and-dirty personal styling changes without creating
|
||||
an entire theme that you intend to distribute, you can add styles to
|
||||
_styles.css_ in your _~/.atom_ directory.
|
||||
an entire theme that you intend to publish, you can add styles to the
|
||||
_styles.less_ file in your _~/.atom_ directory.
|
||||
|
||||
For example, to change the color of the highlighted line number for the line
|
||||
that contains the cursor, you could add the following style to _styles.css_:
|
||||
You can open this file in an editor from the _Atom > Open Your Stylesheet_ menu.
|
||||
|
||||
```css
|
||||
For example, to change the color of the cursor, you could add the following
|
||||
rule to your _~/.atom/styles.less_ file:
|
||||
|
||||
```less
|
||||
.editor .cursor {
|
||||
border-color: pink;
|
||||
}
|
||||
```
|
||||
|
||||
You can also name the file _styles.less_ if you want to style Atom using
|
||||
[LESS][LESS].
|
||||
Unfamiliar with LESS? Read more about it [here][LESS].
|
||||
|
||||
[create-a-package]: creating-packages.md
|
||||
This file can also be named _styles.css_ and contain CSS.
|
||||
|
||||
[creating-a-package]: creating-a-package.md
|
||||
[create-theme]: creating-a-theme.md
|
||||
[LESS]: http://www.lesscss.org
|
||||
[CSON]: https://github.com/atom/season
|
||||
[CoffeeScript]: http://coffeescript.org/
|
||||
|
||||
+21
-16
@@ -22,7 +22,7 @@ bindings][key-bindings] section.
|
||||
Atom windows are scoped to a single directory on disk. If you launch Atom from
|
||||
the command line via the `atom` command and don't specify a path, Atom opens a
|
||||
window for the current working directory. The current window's directory will be
|
||||
visible as the root of the tree view at the left, and also serve as the context
|
||||
visible as the root of the tree view on the left, and also serve as the context
|
||||
for all file-related operations.
|
||||
|
||||
#### Finding Files
|
||||
@@ -30,7 +30,8 @@ for all file-related operations.
|
||||
The fastest way to find a file is to use the fuzzy finder. Press `cmd-t` and
|
||||
begin typing the name of the file you're looking for. If you are looking for a
|
||||
file that is already open press `cmd-b` to bring up a searchable list of open
|
||||
files.
|
||||
files. If you are using Git you can use `cmd-shift-b` to search the list of
|
||||
files modified and untracked in your project's repository.
|
||||
|
||||
You can also use the tree view to navigate to a file. To open and focus the
|
||||
the tree view, press `ctrl-0`. The tree view can be toggled open and closed with
|
||||
@@ -38,11 +39,10 @@ the tree view, press `ctrl-0`. The tree view can be toggled open and closed with
|
||||
|
||||
#### Adding, Moving, Deleting Files
|
||||
|
||||
Currently, all file modification is performed via the tree view. Add, move, or
|
||||
delete a file by right-clicking in the tree view and selecting the desired
|
||||
operation from the context menu. You can also perform these operations from the
|
||||
keyboard by selecting a file or directory and using `a` to add, `m` to move, and
|
||||
`delete` to delete.
|
||||
You can add, move, and delete files and folders by right-clicking them in the
|
||||
tree view and selecting the desired operation from the context menu. You can
|
||||
also perform these operations from the keyboard by selecting a file or folder
|
||||
and using `a` to add, `m` to move, and `delete` to delete.
|
||||
|
||||
### Searching
|
||||
|
||||
@@ -58,17 +58,23 @@ list of all symbols in the current file, which you can fuzzy filter similarly to
|
||||
`cmd-t`.
|
||||
|
||||
To search for symbols across your project, use `cmd-shift-r`. First you'll need
|
||||
to make sure you have ctags installed and a tags file generated for your
|
||||
project. Also, if you're editing CoffeeScript, it's a good idea to update your
|
||||
`~/.ctags` file to understand the language. Here is [a good example][ctags].
|
||||
to make sure you have `tags` (or `TAGS`) file generated for your project.
|
||||
This can be done by installing [ctags](http://ctags.sourceforge.net/) and
|
||||
running a command such as `ctags -R src/` from the command line in your
|
||||
project's root directory. Using [Homebrew](http://brew.sh/)? Just run
|
||||
`brew install ctags`.
|
||||
|
||||
You can customize how tags are generated by creating your own `.ctags` file
|
||||
in your home directory (`~/.ctags`). Here is [a good example][ctags] to start
|
||||
from.
|
||||
|
||||
### Split Panes
|
||||
|
||||
You can split any editor pane horizontally or vertically by using `cmd-k right`
|
||||
or `cmd-k down`. Once you have a split pane, you can move focus between them
|
||||
with `cmd-k cmd-right` or `cmd-k cmd-down`. To close a pane, close all its
|
||||
editors with `meta-w`, then press `meta-w` one more time to close the pane. You
|
||||
can configure panes to auto-close with empty in the preferences.
|
||||
editors with `cmd-w`, then press `cmd-w` one more time to close the pane. You
|
||||
can configure panes to auto-close when empty in the Settings view.
|
||||
|
||||
### Folding
|
||||
|
||||
@@ -89,9 +95,8 @@ in preferences.
|
||||
|
||||
## Configuration
|
||||
|
||||
Press `cmd-,` to display the preferences pane. This serves as the primary
|
||||
interface for adjusting config settings, installing packages and changing
|
||||
themes.
|
||||
Press `cmd-,` to open the Settings view. This is the place to change settings,
|
||||
install packages, and change the theme.
|
||||
|
||||
For more advanced configuration see the [customization guide][customization].
|
||||
|
||||
@@ -101,4 +106,4 @@ For more advanced configuration see the [customization guide][customization].
|
||||
[customization]: customizing-atom.md
|
||||
[key-bindings]: customizing-atom.md#customizing-key-bindings
|
||||
[command palette]: https://f.cloud.github.com/assets/1424/1091618/ee7c3554-166a-11e3-9955-aaa61bb5509c.png
|
||||
[ctags]: https://github.com/kevinsawicki/dotfiles/blob/master/.ctags
|
||||
[ctags]: https://github.com/atom/symbols-view/blob/master/lib/.ctags
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
* [Customizing Atom](customizing-atom.md)
|
||||
* [Creating a Package](creating-a-package.md)
|
||||
* [Creating a Theme](creating-a-theme.md)
|
||||
* [Publishing a Package](publishing-a-package.md)
|
||||
* [Converting a TextMate Bundle](converting-a-text-mate-bundle.md)
|
||||
* [Converting a TextMate Theme](converting-a-text-mate-theme.md)
|
||||
* [Contributing](contributing.md)
|
||||
|
||||
### Advanced Topics
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Publishing a package allows other people to install it and use it in Atom. It
|
||||
is a great way to share what you've made and get feedback and contributions from
|
||||
others.
|
||||
|
||||
This guide assumes your package's name is `my-package` and but you should pick a
|
||||
This guide assumes your package's name is `my-package` but you should pick a
|
||||
better name.
|
||||
|
||||
### Install apm
|
||||
@@ -24,7 +24,7 @@ apm help publish
|
||||
|
||||
You should see a message print out with details about the `apm publish` command.
|
||||
|
||||
If you do not, launch Atom and run the _Atom > Install Shell Commmands_ menu
|
||||
If you do not, launch Atom and run the _Atom > Install Shell Commands_ menu
|
||||
to install the `apm` and `atom` commands.
|
||||
|
||||
### Prepare Your Package
|
||||
@@ -42,7 +42,7 @@ If not, there are a few things you should check before publishing:
|
||||
* Your package is in a Git repository that has been pushed to
|
||||
[GitHub][github]. Follow [this guide][repo-guide] if your package isn't
|
||||
already on GitHub.
|
||||
|
||||
|
||||
### Publish Your Package
|
||||
|
||||
Before you publish a package it is a good idea to check ahead of time if
|
||||
@@ -59,7 +59,7 @@ Now let's review what the `apm publish` command does:
|
||||
3. Creates a new [Git tag][git-tag] for the version being published.
|
||||
4. Pushes the tag and current branch up to GitHub.
|
||||
5. Updates atom.io with the new version being published.
|
||||
|
||||
|
||||
Now run the following commands to publish your package:
|
||||
|
||||
```sh
|
||||
@@ -80,7 +80,7 @@ digit of the version before publishing so the published version will be `0.1.0`
|
||||
and the Git tag created will be `v0.1.0`.
|
||||
|
||||
In the future you can run `apm publish major` to publish the `1.0.0` version but
|
||||
since this was the first version being published it is a good idead to start
|
||||
since this was the first version being published it is a good idea to start
|
||||
with a minor release.
|
||||
|
||||
### Further Reading
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
# Setting up Travis CI
|
||||
|
||||
Packages under the [atom org][atom-org] should use [Travis CI][travis-ci] for
|
||||
builds.
|
||||
|
||||
Currently we have a [Travis Pro][travis-pro] account since the repositories
|
||||
are private. This process will be simpler and have fewer steps once the
|
||||
package repos are made public.
|
||||
|
||||
Each package builds using the [build-package][build-package] script, any
|
||||
changes to the build should be made in that script and will affect all
|
||||
package builds immediately.
|
||||
|
||||
Each package builds against the latest successful build of atom@master. The
|
||||
master builds are stored in the [atom-master-builds][atom-master-builds] repo
|
||||
as releases named by the SHA-1 built.
|
||||
|
||||
## Configuring a package
|
||||
|
||||
* Run `cd ~/github/my-package` to navigate to the package repo locally
|
||||
* Run `apm test` to verify that the package currently builds via [apm][apm]
|
||||
* Add the package repo to the [Travis CI team][travis-ci-team]
|
||||
* Run `gem install travis` to install the [travis gem][travis-gem]
|
||||
* Run `travis login --pro` and log in using the [atom-build][atom-build] user
|
||||
and the password from the *Shared-Developers* folder in LastPass
|
||||
* Run `apm ci` to add a `.travis.yml` file to the repo and to configure Travis
|
||||
* Log into [Travis][travis-ci] as the `atom-build` user and you should now see
|
||||
the package listed and building
|
||||
|
||||
|
||||
[apm]: https://github.com/atom/apm
|
||||
[build-package]: https://github.com/atom/apm/blob/master/script/build-package
|
||||
[atom-build]: https://github.com/atom-build
|
||||
[atom-master-builds]: https://github.com/atom/atom-master-builds/releases
|
||||
[atom-org]: https://github.com/atom
|
||||
[travis-ci]: https://magnum.travis-ci.com
|
||||
[travis-ci-team]: https://github.com/organizations/atom/teams/596636
|
||||
[travis-gem]: https://rubygems.org/gems/travis
|
||||
[travis-pro]: http://about.travis-ci.org/docs/user/travis-pro
|
||||
@@ -20,7 +20,8 @@ The final package can be viewed at
|
||||
To begin, press `cmd-shift-P` to bring up the [Command
|
||||
Palette](https://github.com/atom/command-palette). Type "generate package" and
|
||||
select the "Package Generator: Generate Package" command. Now we need to name
|
||||
the package. Let's call it _ascii-art_.
|
||||
the package. Try to avoid naming your package with the *atom-* prefix, for
|
||||
example we are going to call this package _ascii-art_.
|
||||
|
||||
Atom will open a new window with the contents of our new _ascii-art_ package
|
||||
displayed in the Tree View. Because this window is opened **after** the package
|
||||
@@ -151,5 +152,5 @@ ASCII art professional!
|
||||
* [Creating a package guide](creating-a-package.html) for more information
|
||||
on the mechanics of packages
|
||||
|
||||
* [Publishing a package guide](publish-a-package.html) for more information
|
||||
* [Publishing a package guide](publishing-a-package.html) for more information
|
||||
on publishing your package to [atom.io](https://atom.io)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
storage
|
||||
compile-cache
|
||||
dev
|
||||
.npm
|
||||
.node-gyp
|
||||
@@ -12,7 +12,7 @@
|
||||
# '.editor':
|
||||
# 'enter': 'editor:newline'
|
||||
#
|
||||
# 'body':
|
||||
# '.workspace':
|
||||
# 'ctrl-P': 'core:move-up'
|
||||
# 'ctrl-p': 'core:move-down'
|
||||
#
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
*
|
||||
* This stylesheet is loaded when Atom starts up and is reloaded automatically
|
||||
* when it is changed.
|
||||
*
|
||||
* If you are unfamiliar with LESS, you can read more about it here:
|
||||
* http://www.lesscss.org
|
||||
*/
|
||||
|
||||
.tree-view {
|
||||
@@ -1,10 +1,8 @@
|
||||
{Point, Range} = require 'text-buffer'
|
||||
|
||||
module.exports =
|
||||
_: require 'underscore-plus'
|
||||
BufferedNodeProcess: require '../src/buffered-node-process'
|
||||
BufferedProcess: require '../src/buffered-process'
|
||||
fs: require 'fs-plus'
|
||||
Git: require '../src/git'
|
||||
Point: Point
|
||||
Range: Range
|
||||
@@ -23,3 +21,4 @@ unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
|
||||
module.exports.Task = require '../src/task'
|
||||
module.exports.View = View
|
||||
module.exports.WorkspaceView = require '../src/workspace-view'
|
||||
module.exports.Workspace = require '../src/workspace'
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
'.editor':
|
||||
# Platform Bindings
|
||||
'alt-left': 'editor:move-to-beginning-of-word'
|
||||
'alt-right': 'editor:move-to-end-of-word'
|
||||
'alt-shift-left': 'editor:select-to-beginning-of-word'
|
||||
'alt-shift-right': 'editor:select-to-end-of-word'
|
||||
'home': 'editor:move-to-first-character-of-line'
|
||||
'end': 'editor:move-to-end-of-screen-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'
|
||||
@@ -21,8 +14,6 @@
|
||||
'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.panel-left, .tool-panel.panel-right':
|
||||
'escape': 'tool-panel:unfocus'
|
||||
|
||||
+14
-2
@@ -65,6 +65,8 @@
|
||||
'cmd-}': 'pane:show-next-item'
|
||||
'cmd-alt-left': 'pane:show-previous-item'
|
||||
'cmd-alt-right': 'pane:show-next-item'
|
||||
'ctrl-tab': 'pane:show-next-item'
|
||||
'ctrl-shift-tab': 'pane:show-previous-item'
|
||||
'cmd-=': 'window:increase-font-size'
|
||||
'cmd-+': 'window:increase-font-size'
|
||||
'cmd--': 'window:decrease-font-size'
|
||||
@@ -94,8 +96,15 @@
|
||||
'cmd-9': 'pane:show-item-9'
|
||||
|
||||
'.editor':
|
||||
# Platform Bindings
|
||||
'alt-left': 'editor:move-to-beginning-of-word'
|
||||
'alt-right': 'editor:move-to-end-of-word'
|
||||
'alt-shift-left': 'editor:select-to-beginning-of-word'
|
||||
'alt-shift-right': 'editor:select-to-end-of-word'
|
||||
|
||||
# Apple Specific
|
||||
'cmd-backspace': 'editor:backspace-to-beginning-of-line'
|
||||
'cmd-shift-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'
|
||||
@@ -118,6 +127,7 @@
|
||||
'cmd-k cmd-u': 'editor:upper-case'
|
||||
'cmd-k cmd-l': 'editor:lower-case'
|
||||
'cmd-l': 'editor:select-line'
|
||||
'ctrl-t': 'editor:transpose'
|
||||
|
||||
'.workspace .editor:not(.mini)':
|
||||
# Atom specific
|
||||
@@ -133,9 +143,11 @@
|
||||
'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-j': 'editor:join-lines'
|
||||
'cmd-D': 'editor:duplicate-lines'
|
||||
'cmd-L': 'editor:split-selections-into-lines'
|
||||
'ctrl-shift-up': 'editor:add-selection-above'
|
||||
'ctrl-shift-down': 'editor:add-selection-below'
|
||||
|
||||
'cmd-alt-[': 'editor:fold-current-row'
|
||||
'cmd-alt-]': 'editor:unfold-current-row'
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
'body':
|
||||
# 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'
|
||||
'ctrl-shift-o': 'application:open-dev'
|
||||
'F11': 'window:toggle-full-screen'
|
||||
|
||||
# Sublime Parity
|
||||
'ctrl-N': 'application:new-window'
|
||||
'ctrl-W': 'window:close'
|
||||
'ctrl-o': 'application:open-file'
|
||||
'ctrl-q': 'application:quit'
|
||||
'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-_': 'window:decrease-font-size'
|
||||
'ctrl-0': 'window:reset-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-p': 'window:focus-previous-pane'
|
||||
'ctrl-k ctrl-n': 'window:focus-next-pane'
|
||||
'ctrl-k ctrl-up': 'window:focus-pane-above'
|
||||
'ctrl-k ctrl-down': 'window:focus-pane-below'
|
||||
'ctrl-k ctrl-left': 'window:focus-pane-on-left'
|
||||
'ctrl-k ctrl-right': 'window:focus-pane-on-right'
|
||||
|
||||
'.workspace .editor':
|
||||
# Platform Bindings
|
||||
'ctrl-left': 'editor:move-to-beginning-of-word'
|
||||
'ctrl-right': 'editor:move-to-end-of-word'
|
||||
'ctrl-shift-left': 'editor:select-to-beginning-of-word'
|
||||
'ctrl-shift-right': 'editor:select-to-end-of-word'
|
||||
'ctrl-backspace': 'editor:backspace-to-beginning-of-word'
|
||||
'ctrl-delete': 'editor:delete-to-end-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'
|
||||
|
||||
'.workspace .editor:not(.mini)':
|
||||
# Atom specific
|
||||
'alt-ctrl-z': 'editor:checkout-head-revision'
|
||||
'ctrl-<': 'editor:scroll-to-cursor'
|
||||
'alt-ctrl-f': 'editor:fold-selection'
|
||||
|
||||
# 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-lines'
|
||||
'ctrl-D': 'editor:duplicate-lines'
|
||||
'alt-shift-up': 'editor:add-selection-above'
|
||||
'alt-shift-down': 'editor:add-selection-below'
|
||||
|
||||
'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
|
||||
'body .native-key-bindings':
|
||||
'ctrl-z': 'native!'
|
||||
'ctrl-Z': 'native!'
|
||||
'ctrl-x': 'native!'
|
||||
'ctrl-c': 'native!'
|
||||
'ctrl-v': 'native!'
|
||||
+10
-4
@@ -14,7 +14,7 @@
|
||||
# Sublime Parity
|
||||
'ctrl-N': 'application:new-window'
|
||||
'ctrl-W': 'window:close'
|
||||
'ctrl-o': 'application:open'
|
||||
'ctrl-o': 'application:open-file'
|
||||
'ctrl-T': 'pane:reopen-closed-item'
|
||||
'ctrl-n': 'application:new-file'
|
||||
'ctrl-s': 'core:save'
|
||||
@@ -33,6 +33,7 @@
|
||||
'pageup': 'core:page-up'
|
||||
'pagedown': 'core:page-down'
|
||||
'backspace': 'core:backspace'
|
||||
'shift-backspace': 'core:backspace'
|
||||
'ctrl-tab': 'pane:show-next-item'
|
||||
'ctrl-shift-tab': 'pane:show-previous-item'
|
||||
'ctrl-shift-up': 'core:move-up'
|
||||
@@ -57,7 +58,12 @@
|
||||
'ctrl-k ctrl-right': 'window:focus-pane-on-right'
|
||||
|
||||
'.workspace .editor':
|
||||
# Windows specific
|
||||
# Platform Bindings
|
||||
'ctrl-left': 'editor:move-to-beginning-of-word'
|
||||
'ctrl-right': 'editor:move-to-end-of-word'
|
||||
'ctrl-shift-left': 'editor:select-to-beginning-of-word'
|
||||
'ctrl-shift-right': 'editor:select-to-end-of-word'
|
||||
'ctrl-backspace': 'editor:backspace-to-beginning-of-word'
|
||||
'ctrl-delete': 'editor:backspace-to-beginning-of-word'
|
||||
|
||||
# Sublime Parity
|
||||
@@ -80,8 +86,8 @@
|
||||
'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-j': 'editor:join-lines'
|
||||
'ctrl-D': 'editor:duplicate-lines'
|
||||
|
||||
'ctrl-alt-[': 'editor:fold-current-row'
|
||||
'ctrl-alt-]': 'editor:unfold-current-row'
|
||||
|
||||
+27
-7
@@ -3,9 +3,11 @@
|
||||
label: 'Atom'
|
||||
submenu: [
|
||||
{ label: 'About Atom', command: 'application:about' }
|
||||
{ label: "VERSION", enabled: false }
|
||||
{ label: "Restart and Install Update", command: 'application:install-update', visible: false}
|
||||
{ label: "Check for Update", command: 'application:check-for-update', visible: false}
|
||||
{ label: 'View License', command: 'application:open-license' }
|
||||
{ label: 'VERSION', enabled: false }
|
||||
{ label: 'Restart and Install Update', command: 'application:install-update', visible: false}
|
||||
{ label: 'Check for Update', command: 'application:check-for-update', visible: false}
|
||||
{ label: 'Downloading Update', enabled: false, visible: false}
|
||||
{ type: 'separator' }
|
||||
{ label: 'Preferences...', command: 'application:show-settings' }
|
||||
{ label: 'Open Your Config', command: 'application:open-your-config' }
|
||||
@@ -36,7 +38,7 @@
|
||||
{ label: 'Save All', command: 'window:save-all' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Close Buffer', command: 'core:close' }
|
||||
{ label: 'Close All Buffers', command: 'pane:close' }
|
||||
{ label: 'Close Pane', command: 'pane:close' }
|
||||
{ label: 'Close Window', command: 'window:close' }
|
||||
]
|
||||
}
|
||||
@@ -63,9 +65,9 @@
|
||||
{ type: 'separator' }
|
||||
{ label: 'Move Line Up', command: 'editor:move-line-up' }
|
||||
{ label: 'Move Line Down', command: 'editor:move-line-down' }
|
||||
{ label: 'Duplicate Line', command: 'editor:duplicate-line' }
|
||||
{ label: 'Duplicate Lines', command: 'editor:duplicate-lines' }
|
||||
{ label: 'Delete Line', command: 'editor:delete-line' }
|
||||
{ label: 'Join Lines', command: 'editor:join-line' }
|
||||
{ label: 'Join Lines', command: 'editor:join-lines' }
|
||||
]
|
||||
}
|
||||
{
|
||||
@@ -132,6 +134,25 @@
|
||||
submenu: [
|
||||
{ label: 'Reload', command: 'window:reload' }
|
||||
{ label: 'Toggle Full Screen', command: 'window:toggle-full-screen' }
|
||||
{
|
||||
label: 'Panes'
|
||||
submenu: [
|
||||
{ label: 'Split Up', command: 'pane:split-up' }
|
||||
{ label: 'Split Down', command: 'pane:split-down' }
|
||||
{ label: 'Split Left', command: 'pane:split-left' }
|
||||
{ label: 'Split Right', command: 'pane:split-right' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Focus Next Pane', command: 'window:focus-next-pane' }
|
||||
{ label: 'Focus Previous Pane', command: 'window:focus-previous-pane' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Focus Pane Above', command: 'window:focus-pane-above' }
|
||||
{ label: 'Focus Pane Below', command: 'window:focus-pane-below' }
|
||||
{ label: 'Focus Pane On Left', command: 'window:focus-pane-on-left' }
|
||||
{ label: 'Focus Pane On Right', command: 'window:focus-pane-on-right' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Close Pane', command: 'pane:close' }
|
||||
]
|
||||
}
|
||||
{
|
||||
label: 'Developer'
|
||||
submenu: [
|
||||
@@ -165,7 +186,6 @@
|
||||
label: 'Help'
|
||||
submenu: [
|
||||
{ label: 'Documentation', command: 'application:open-documentation' }
|
||||
{ label: 'Report an Issue', command: 'application:report-issue' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
'menu': [
|
||||
{
|
||||
label: '&File'
|
||||
submenu: [
|
||||
{ label: 'New &Window', command: 'application:new-window' }
|
||||
{ label: '&New File', command: 'application:new-file' }
|
||||
{ label: '&Open File...', command: 'application:open-file' }
|
||||
{ label: 'Open Folder...', command: 'application:open-folder' }
|
||||
{ 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: 'Quit', 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 Lines', command: 'editor:duplicate-lines' }
|
||||
{ label: 'D&elete Line', command: 'editor:delete-line' }
|
||||
{ label: '&Join Lines', command: 'editor:join-lines' }
|
||||
]
|
||||
}
|
||||
{
|
||||
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' }
|
||||
{ label: 'S&plit into Lines', command: 'editor:split-selections-into-lines'}
|
||||
{ type: 'separator' }
|
||||
{ label: 'Select to &Top', command: 'core:select-to-top' }
|
||||
{ label: 'Select to Botto&m', command: 'core:select-to-bottom' }
|
||||
{ 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: '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: 'View &License', command: 'application:open-license' }
|
||||
{ label: "VERSION", enabled: false }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Documentation', command: 'application:open-documentation' }
|
||||
{ type: 'separator' }
|
||||
]
|
||||
}
|
||||
]
|
||||
+24
-3
@@ -4,7 +4,8 @@
|
||||
submenu: [
|
||||
{ label: 'New &Window', command: 'application:new-window' }
|
||||
{ label: '&New File', command: 'application:new-file' }
|
||||
{ label: '&Open...', command: 'application:open' }
|
||||
{ label: '&Open File...', command: 'application:open-file' }
|
||||
{ label: 'Open Folder...', command: 'application:open-folder' }
|
||||
{ label: 'Reopen Last &Item', command: 'pane:reopen-closed-item' }
|
||||
{ type: 'separator' }
|
||||
{ label: '&Preferences...', command: 'application:show-settings' }
|
||||
@@ -43,9 +44,9 @@
|
||||
{ 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: 'Du&plicate Lines', command: 'editor:duplicate-lines' }
|
||||
{ label: 'D&elete Line', command: 'editor:delete-line' }
|
||||
{ label: '&Join Lines', command: 'editor:join-line' }
|
||||
{ label: '&Join Lines', command: 'editor:join-lines' }
|
||||
]
|
||||
}
|
||||
{
|
||||
@@ -87,6 +88,25 @@
|
||||
submenu: [
|
||||
{ label: '&Reload', command: 'window:reload' }
|
||||
{ label: 'Toggle &Full Screen', command: 'window:toggle-full-screen' }
|
||||
{
|
||||
label: 'Panes'
|
||||
submenu: [
|
||||
{ label: 'Split Up', command: 'pane:split-up' }
|
||||
{ label: 'Split Down', command: 'pane:split-down' }
|
||||
{ label: 'Split Left', command: 'pane:split-left' }
|
||||
{ label: 'Split Right', command: 'pane:split-right' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Focus Next Pane', command: 'window:focus-next-pane' }
|
||||
{ label: 'Focus Previous Pane', command: 'window:focus-previous-pane' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Focus Pane Above', command: 'window:focus-pane-above' }
|
||||
{ label: 'Focus Pane Below', command: 'window:focus-pane-below' }
|
||||
{ label: 'Focus Pane On Left', command: 'window:focus-pane-on-left' }
|
||||
{ label: 'Focus Pane On Right', command: 'window:focus-pane-on-right' }
|
||||
{ type: 'separator' }
|
||||
{ label: 'Close pane', command: 'pane:close' }
|
||||
]
|
||||
}
|
||||
{
|
||||
label: 'Developer'
|
||||
submenu: [
|
||||
@@ -145,6 +165,7 @@
|
||||
label: '&Help'
|
||||
submenu: [
|
||||
{ label: '&About Atom...', command: 'application:about' }
|
||||
{ label: 'View &License', command: 'application:open-license' }
|
||||
{ label: "VERSION", enabled: false }
|
||||
{ label: "Install &update", command: 'application:install-update', visible: false }
|
||||
{ type: 'separator' }
|
||||
|
||||
+106
-106
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"name": "atom",
|
||||
"productName": "Atom",
|
||||
"version": "0.51.0",
|
||||
"version": "0.95.0",
|
||||
"description": "A hackable text editor for the 21st Century.",
|
||||
"main": "./src/browser/main.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -12,130 +13,129 @@
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
"type": "Apache",
|
||||
"type": "MIT",
|
||||
"url": "http://github.com/atom/atom/raw/master/LICENSE.md"
|
||||
}
|
||||
],
|
||||
"atomShellVersion": "0.9.2",
|
||||
"atomShellVersion": "0.12.4",
|
||||
"dependencies": {
|
||||
"async": "0.2.6",
|
||||
"bootstrap": "git://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
|
||||
"clear-cut": "0.2.0",
|
||||
"atom-keymap": "^0.19.0",
|
||||
"bootstrap": "git+https://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372",
|
||||
"clear-cut": "0.4.0",
|
||||
"coffee-script": "1.7.0",
|
||||
"coffeestack": "0.7.0",
|
||||
"delegato": "1.x",
|
||||
"emissary": "1.x",
|
||||
"first-mate": ">=1.1.5 <2.0",
|
||||
"fs-plus": "2.x",
|
||||
"delegato": "^1",
|
||||
"emissary": "^1.2.1",
|
||||
"first-mate": "^1.5.3",
|
||||
"fs-plus": "^2.2.2",
|
||||
"fstream": "0.1.24",
|
||||
"fuzzaldrin": "1.x",
|
||||
"git-utils": "1.x",
|
||||
"fuzzaldrin": "^1.1",
|
||||
"git-utils": "^1.3",
|
||||
"grim": "0.10.0",
|
||||
"guid": "0.0.10",
|
||||
"jasmine-tagged": "1.x",
|
||||
"mkdirp": "0.3.5",
|
||||
"keytar": "0.15.1",
|
||||
"jasmine-tagged": "^1.1.1",
|
||||
"less-cache": "0.12.0",
|
||||
"mixto": "1.x",
|
||||
"mixto": "^1",
|
||||
"mkdirp": "0.3.5",
|
||||
"nslog": "0.5.0",
|
||||
"oniguruma": "1.x",
|
||||
"oniguruma": "^1.0.6",
|
||||
"optimist": "0.4.0",
|
||||
"pathwatcher": "0.16.0",
|
||||
"pegjs": "0.8.0",
|
||||
"property-accessors": "1.x",
|
||||
"q": "1.0.x",
|
||||
"pathwatcher": "^1.3.1",
|
||||
"property-accessors": "^1",
|
||||
"q": "^1.0.1",
|
||||
"random-words": "0.0.1",
|
||||
"runas": "0.5.x",
|
||||
"scandal": "0.14.0",
|
||||
"season": "1.x",
|
||||
"react": "^0.10.0",
|
||||
"reactionary": "^0.9.0",
|
||||
"runas": "^0.5",
|
||||
"scandal": "0.15.2",
|
||||
"scoped-property-store": "^0.9.0",
|
||||
"scrollbar-style": "^0.4.0",
|
||||
"season": "^1.0.2",
|
||||
"semver": "1.1.4",
|
||||
"serializable": "1.x",
|
||||
"serializable": "^1",
|
||||
"space-pen": "3.1.1",
|
||||
"temp": "0.5.0",
|
||||
"text-buffer": ">=1.1.2 <2.0",
|
||||
"theorist": "1.x",
|
||||
"underscore-plus": "1.x",
|
||||
"text-buffer": "^2.2.2",
|
||||
"theorist": "^1",
|
||||
"underscore-plus": "^1.2.1",
|
||||
"vm-compatibility-layer": "0.1.0"
|
||||
},
|
||||
"packageDependencies": {
|
||||
"atom-dark-syntax": "0.13.0",
|
||||
"atom-dark-ui": "0.22.0",
|
||||
"atom-light-syntax": "0.13.0",
|
||||
"atom-light-ui": "0.21.0",
|
||||
"base16-tomorrow-dark-theme": "0.11.0",
|
||||
"solarized-dark-syntax": "0.9.0",
|
||||
"solarized-light-syntax": "0.5.0",
|
||||
"archive-view": "0.23.0",
|
||||
"autocomplete": "0.23.0",
|
||||
"autoflow": "0.14.0",
|
||||
"autosave": "0.11.0",
|
||||
"background-tips": "0.7.0",
|
||||
"bookmarks": "0.20.0",
|
||||
"bracket-matcher": "0.20.0",
|
||||
"command-palette": "0.17.0",
|
||||
"dev-live-reload": "0.26.0",
|
||||
"exception-reporting": "0.13.0",
|
||||
"feedback": "0.24.0",
|
||||
"find-and-replace": "0.83.0",
|
||||
"fuzzy-finder": "0.35.0",
|
||||
"gists": "0.17.0",
|
||||
"git-diff": "0.24.0",
|
||||
"github-sign-in": "0.19.0",
|
||||
"go-to-line": "0.16.0",
|
||||
"grammar-selector": "0.20.0",
|
||||
"image-view": "0.24.0",
|
||||
"keybinding-resolver": "0.10.0",
|
||||
"link": "0.17.0",
|
||||
"markdown-preview": "0.32.0",
|
||||
"metrics": "0.26.0",
|
||||
"open-on-github": "0.20.0",
|
||||
"package-generator": "0.26.0",
|
||||
"release-notes": "0.20.0",
|
||||
"settings-view": "0.76.0",
|
||||
"snippets": "0.30.0",
|
||||
"spell-check": "0.25.0",
|
||||
"status-bar": "0.32.0",
|
||||
"styleguide": "0.24.0",
|
||||
"symbols-view": "0.36.0",
|
||||
"tabs": "0.19.0",
|
||||
"timecop": "0.13.0",
|
||||
"tree-view": "0.69.0",
|
||||
"update-package-dependencies": "0.3.0",
|
||||
"welcome": "0.4.0",
|
||||
"whitespace": "0.14.0",
|
||||
"wrap-guide": "0.14.0",
|
||||
"language-c": "0.8.0",
|
||||
"language-clojure": "0.2.0",
|
||||
"language-coffee-script": "0.9.0",
|
||||
"language-css": "0.7.0",
|
||||
"language-gfm": "0.17.0",
|
||||
"language-git": "0.6.0",
|
||||
"language-go": "0.4.0",
|
||||
"language-html": "0.5.0",
|
||||
"language-hyperlink": "0.5.0",
|
||||
"language-java": "0.5.0",
|
||||
"language-javascript": "0.8.0",
|
||||
"language-json": "0.5.0",
|
||||
"language-less": "0.4.0",
|
||||
"language-make": "0.4.0",
|
||||
"language-mustache": "0.3.0",
|
||||
"language-objective-c": "0.5.0",
|
||||
"language-pegjs": "0.3.0",
|
||||
"language-perl": "0.5.0",
|
||||
"language-php": "0.6.0",
|
||||
"language-property-list": "0.5.0",
|
||||
"language-puppet": "0.5.0",
|
||||
"language-python": "0.5.0",
|
||||
"language-ruby": "0.11.0",
|
||||
"language-ruby-on-rails": "0.6.0",
|
||||
"language-sass": "0.6.0",
|
||||
"language-shellscript": "0.5.0",
|
||||
"language-source": "0.5.0",
|
||||
"language-sql": "0.5.0",
|
||||
"language-text": "0.4.0",
|
||||
"language-todo": "0.4.0",
|
||||
"language-toml": "0.9.0",
|
||||
"language-xml": "0.5.0",
|
||||
"language-yaml": "0.4.0"
|
||||
"atom-dark-syntax": "0.15.0",
|
||||
"atom-dark-ui": "0.26.0",
|
||||
"atom-light-syntax": "0.17.0",
|
||||
"atom-light-ui": "0.24.0",
|
||||
"base16-tomorrow-dark-theme": "0.15.0",
|
||||
"solarized-dark-syntax": "0.14.0",
|
||||
"solarized-light-syntax": "0.7.0",
|
||||
"archive-view": "0.30.0",
|
||||
"autocomplete": "0.27.0",
|
||||
"autoflow": "0.16.0",
|
||||
"autosave": "0.13.0",
|
||||
"background-tips": "0.13.0",
|
||||
"bookmarks": "0.22.0",
|
||||
"bracket-matcher": "0.33.0",
|
||||
"command-palette": "0.21.0",
|
||||
"deprecation-cop": "0.5.0",
|
||||
"dev-live-reload": "0.30.0",
|
||||
"exception-reporting": "0.17.0",
|
||||
"feedback": "0.33.0",
|
||||
"find-and-replace": "0.101.0",
|
||||
"fuzzy-finder": "0.50.0",
|
||||
"git-diff": "0.28.0",
|
||||
"go-to-line": "0.19.0",
|
||||
"grammar-selector": "0.26.0",
|
||||
"image-view": "0.33.0",
|
||||
"keybinding-resolver": "0.17.0",
|
||||
"link": "0.22.0",
|
||||
"markdown-preview": "0.71.0",
|
||||
"metrics": "0.32.0",
|
||||
"open-on-github": "0.28.0",
|
||||
"package-generator": "0.30.0",
|
||||
"release-notes": "0.29.0",
|
||||
"settings-view": "0.114.0",
|
||||
"snippets": "0.43.0",
|
||||
"spell-check": "0.35.0",
|
||||
"status-bar": "0.40.0",
|
||||
"styleguide": "0.29.0",
|
||||
"symbols-view": "0.51.0",
|
||||
"tabs": "0.39.0",
|
||||
"timecop": "0.18.0",
|
||||
"tree-view": "0.92.0",
|
||||
"update-package-dependencies": "0.6.0",
|
||||
"welcome": "0.13.0",
|
||||
"whitespace": "0.22.0",
|
||||
"wrap-guide": "0.18.0",
|
||||
"language-c": "0.15.0",
|
||||
"language-coffee-script": "0.22.0",
|
||||
"language-css": "0.16.0",
|
||||
"language-gfm": "0.35.0",
|
||||
"language-git": "0.9.0",
|
||||
"language-go": "0.11.0",
|
||||
"language-html": "0.21.0",
|
||||
"language-hyperlink": "0.9.0",
|
||||
"language-java": "0.10.0",
|
||||
"language-javascript": "0.25.0",
|
||||
"language-json": "0.8.0",
|
||||
"language-less": "0.9.0",
|
||||
"language-make": "0.10.0",
|
||||
"language-objective-c": "0.11.0",
|
||||
"language-perl": "0.8.0",
|
||||
"language-php": "0.14.0",
|
||||
"language-property-list": "0.7.0",
|
||||
"language-python": "0.15.0",
|
||||
"language-ruby": "0.23.0",
|
||||
"language-ruby-on-rails": "0.13.0",
|
||||
"language-sass": "0.10.0",
|
||||
"language-shellscript": "0.8.0",
|
||||
"language-source": "0.7.0",
|
||||
"language-sql": "0.8.0",
|
||||
"language-text": "0.6.0",
|
||||
"language-todo": "0.10.0",
|
||||
"language-toml": "0.12.0",
|
||||
"language-xml": "0.12.0",
|
||||
"language-yaml": "0.6.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
Arquivo binário não exibido.
|
Antes Largura: | Altura: | Tamanho: 48 KiB Depois Largura: | Altura: | Tamanho: 284 KiB |
@@ -0,0 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Name=Atom
|
||||
Comment=<%= description %>
|
||||
Exec=/usr/share/atom/atom %U
|
||||
Icon=atom
|
||||
Type=Application
|
||||
StartupNotify=true
|
||||
Categories=GNOME;GTK;Utility;TextEditor;
|
||||
@@ -0,0 +1,8 @@
|
||||
Package: <%= name %>
|
||||
Version: <%= version %>
|
||||
Section: <%= section %>
|
||||
Priority: optional
|
||||
Architecture: <%= arch %>
|
||||
Installed-Size: `du -ks usr|cut -f 1`
|
||||
Maintainer: <%= maintainer %>
|
||||
Description: <%= description %>
|
||||
+270
-90
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
Arquivo binário não exibido.
Arquivo binário não exibido.
+19
-17
@@ -1,13 +1,15 @@
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
#!/usr/bin/env node
|
||||
|
||||
var nodeMinorVersion = process.versions.node.split('.')[1]
|
||||
if (nodeMinorVersion !== '10') {
|
||||
console.warn("You must run script/bootstrap and script/build with node v0.10.x");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var safeExec = require('./utils/child-process-wrapper.js').safeExec;
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
// OAuth token for atom-bot
|
||||
// TODO Remove once all repositories are public
|
||||
if (!process.env.ATOM_ACCESS_TOKEN)
|
||||
process.env.ATOM_ACCESS_TOKEN = '362295be4c5258d3f7b967bbabae662a455ca2a7';
|
||||
|
||||
// Executes an array of commands one by one.
|
||||
function executeCommands(commands, done, index) {
|
||||
index = (index == undefined ? 0 : index);
|
||||
@@ -23,28 +25,28 @@ function executeCommands(commands, done, index) {
|
||||
done(null);
|
||||
}
|
||||
|
||||
var apmVendorPath = path.resolve(__dirname, '..', 'vendor', 'apm');
|
||||
var apmInstallPath = path.resolve(__dirname, '..', 'apm');
|
||||
if (!fs.existsSync(apmInstallPath))
|
||||
fs.mkdirSync(apmInstallPath);
|
||||
if (!fs.existsSync(path.join(apmInstallPath, 'node_modules')))
|
||||
fs.mkdirSync(path.join(apmInstallPath, 'node_modules'));
|
||||
|
||||
var apmPath = path.resolve(__dirname, '..', 'apm', 'node_modules', 'atom-package-manager', 'bin', 'apm')
|
||||
var apmFlags = process.env.JANKY_SHA1 || process.argv.indexOf('--no-color') !== -1 ? '--no-color' : '';
|
||||
|
||||
var npmPath = path.resolve(__dirname, '..', 'build', 'node_modules', '.bin', 'npm');
|
||||
var npmFlags = ' --userconfig=' + path.resolve('.npmrc') + ' ';
|
||||
|
||||
var packagesToDedupe = ['fs-plus', 'humanize-plus', 'oniguruma', 'roaster', 'season'];
|
||||
var echoNewLine = process.platform == 'win32' ? 'echo.' : 'echo';
|
||||
|
||||
var commands = [
|
||||
'git submodule --quiet sync',
|
||||
'git submodule --quiet update --recursive --init',
|
||||
{command: 'npm install --quiet', options: {cwd: path.resolve(__dirname, '..', 'build'), ignoreStdout: true}},
|
||||
{command: 'npm install --quiet', options: {cwd: apmVendorPath, ignoreStdout: true}},
|
||||
{command: 'npm install --quiet ' + apmVendorPath, options: {cwd: apmInstallPath, ignoreStdout: true}},
|
||||
{command: 'npm install --quiet ' + apmVendorPath, options: {ignoreStdout: true}},
|
||||
{command: 'node ../../apm/node_modules/atom-package-manager/bin/apm rebuild', options: {cwd: path.resolve('node_modules', 'atom-package-manager'), ignoreStdout: true}},
|
||||
{command: 'npm' + npmFlags + 'install --quiet', options: {cwd: path.resolve(__dirname, '..', 'build'), ignoreStdout: true}},
|
||||
{command: npmPath + npmFlags + 'install --quiet', options: {cwd: apmInstallPath, ignoreStdout: true}},
|
||||
echoNewLine,
|
||||
'node apm/node_modules/atom-package-manager/bin/apm clean ' + apmFlags,
|
||||
'node apm/node_modules/atom-package-manager/bin/apm install --quiet ' + apmFlags,
|
||||
'node apm/node_modules/atom-package-manager/bin/apm dedupe --quiet ' + apmFlags + ' ' + packagesToDedupe.join(' '),
|
||||
apmPath + ' clean ' + apmFlags,
|
||||
apmPath + ' install --quiet ' + apmFlags,
|
||||
apmPath + ' dedupe --quiet ' + apmFlags + ' ' + packagesToDedupe.join(' '),
|
||||
];
|
||||
|
||||
process.chdir(path.dirname(__dirname));
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
#!/usr/bin/env node
|
||||
var cp = require('./utils/child-process-wrapper.js');
|
||||
var path = require('path');
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
#!/usr/bin/env node
|
||||
var cp = require('./utils/child-process-wrapper.js');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
#!/usr/bin/env node
|
||||
var cp = require('./utils/child-process-wrapper.js');
|
||||
var path = require('path');
|
||||
var os = require('os');
|
||||
@@ -18,13 +18,13 @@ var commands = [
|
||||
[__dirname, '..', 'node_modules'],
|
||||
[__dirname, '..', 'build', 'node_modules'],
|
||||
[__dirname, '..', 'apm', 'node_modules'],
|
||||
[__dirname, '..', 'vendor', 'apm', 'node_modules'],
|
||||
[__dirname, '..', 'atom-shell'],
|
||||
[home, '.atom', '.node-gyp'],
|
||||
[home, '.atom', 'storage'],
|
||||
[home, '.atom', '.npm'],
|
||||
[home, '.atom', 'compile-cache'],
|
||||
[tmpdir, 'atom-build'],
|
||||
[tmpdir, 'atom-cached-atom-shells'],
|
||||
[tmpdir, 'atom-compile-cache'],
|
||||
];
|
||||
var run = function() {
|
||||
var next = commands.shift();
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
#!/usr/bin/env node
|
||||
var cp = require('./utils/child-process-wrapper.js');
|
||||
var path = require('path');
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
@IF EXIST "%~dp0\node.exe" (
|
||||
"%~dp0\node.exe" "%~dp0\grunt" %*
|
||||
) ELSE (
|
||||
node "%~dp0\grunt" %*
|
||||
)
|
||||
Arquivo executável
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
# mkdeb version control-file-path deb-file-path
|
||||
|
||||
SCRIPT=`readlink -f "$0"`
|
||||
ROOT=`readlink -f $(dirname $SCRIPT)/..`
|
||||
cd $ROOT
|
||||
|
||||
VERSION="$1"
|
||||
CONTROL_FILE="$2"
|
||||
DESKTOP_FILE="$3"
|
||||
ICON_FILE="$4"
|
||||
DEB_PATH="$5"
|
||||
|
||||
TARGET_ROOT="`mktemp -d`"
|
||||
TARGET="$TARGET_ROOT/atom-$VERSION-amd64"
|
||||
|
||||
mkdir -p "$TARGET/usr"
|
||||
env INSTALL_PREFIX="$TARGET/usr" script/grunt install
|
||||
|
||||
mkdir -p "$TARGET/DEBIAN"
|
||||
mv "$CONTROL_FILE" "$TARGET/DEBIAN/control"
|
||||
|
||||
mkdir -p "$TARGET/usr/share/applications"
|
||||
mv "$DESKTOP_FILE" "$TARGET/usr/share/applications"
|
||||
|
||||
mkdir -p "$TARGET/usr/share/pixmaps"
|
||||
cp "$ICON_FILE" "$TARGET/usr/share/pixmaps"
|
||||
|
||||
dpkg-deb -b "$TARGET"
|
||||
mv "$TARGET_ROOT/atom-$VERSION-amd64.deb" "$DEB_PATH"
|
||||
rm -rf $TARGET_ROOT
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env node --harmony_collections
|
||||
#!/usr/bin/env node
|
||||
var safeExec = require('./utils/child-process-wrapper.js').safeExec;
|
||||
var path = require('path');
|
||||
|
||||
|
||||
+38
-25
@@ -1,4 +1,4 @@
|
||||
{$, $$, fs, WorkspaceView} = require 'atom'
|
||||
{$, $$, WorkspaceView} = require 'atom'
|
||||
Exec = require('child_process').exec
|
||||
path = require 'path'
|
||||
Package = require '../src/package'
|
||||
@@ -113,18 +113,31 @@ describe "the `atom` global", ->
|
||||
promise
|
||||
|
||||
it "triggers the activation event on all handlers registered during activation", ->
|
||||
atom.workspaceView.openSync()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
eventHandler = jasmine.createSpy("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
|
||||
editorView.trigger 'activation-event'
|
||||
expect(mainModule.activationEventCallCount).toBe 2
|
||||
expect(eventHandler.callCount).toBe 2
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open()
|
||||
|
||||
runs ->
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
eventHandler = jasmine.createSpy("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
|
||||
editorView.trigger 'activation-event'
|
||||
expect(mainModule.activationEventCallCount).toBe 2
|
||||
expect(eventHandler.callCount).toBe 2
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
|
||||
it "activates the package immediately when the events are empty", ->
|
||||
mainModule = require './fixtures/packages/package-with-empty-activation-events/index'
|
||||
spyOn(mainModule, 'activate').andCallThrough()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('package-with-empty-activation-events')
|
||||
|
||||
runs ->
|
||||
expect(mainModule.activate.callCount).toBe 1
|
||||
|
||||
describe "when the package has no main module", ->
|
||||
it "does not throw an exception", ->
|
||||
@@ -160,28 +173,28 @@ describe "the `atom` global", ->
|
||||
element2 = $$ -> @div class: 'test-2'
|
||||
element3 = $$ -> @div class: 'test-3'
|
||||
|
||||
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
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element2[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element3[0])).toHaveLength 0
|
||||
|
||||
atom.packages.activatePackage("package-with-keymaps")
|
||||
|
||||
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
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])[0].command).toBe "test-1"
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element2[0])[0].command).toBe "test-2"
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element3[0])).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(atom.keymap.keyBindingsForKeystrokeMatchingElement('ctrl-z', element1)).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])).toHaveLength 0
|
||||
|
||||
atom.packages.activatePackage("package-with-keymaps-manifest")
|
||||
|
||||
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
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:element1[0])[0].command).toBe 'keymap-1'
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-n', target:element1[0])[0].command).toBe 'keymap-2'
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-y', target:element3[0])).toHaveLength 0
|
||||
|
||||
describe "menu loading", ->
|
||||
beforeEach ->
|
||||
@@ -367,8 +380,8 @@ describe "the `atom` global", ->
|
||||
|
||||
runs ->
|
||||
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
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:$$ -> @div class: 'test-1'[0])).toHaveLength 0
|
||||
expect(atom.keymaps.findKeyBindings(keystrokes:'ctrl-z', target:$$ -> @div class: 'test-2'[0])).toHaveLength 0
|
||||
|
||||
it "removes the package's stylesheets", ->
|
||||
waitsForPromise ->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{fs} = require 'atom'
|
||||
path = require 'path'
|
||||
fs = require 'fs-plus'
|
||||
temp = require 'temp'
|
||||
installer = require '../src/command-installer'
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{fs} = require 'atom'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
CSON = require 'season'
|
||||
fs = require 'fs-plus'
|
||||
|
||||
describe "Config", ->
|
||||
dotAtomPath = path.join(temp.dir, 'dot-atom-dir')
|
||||
@@ -36,15 +36,53 @@ describe "Config", ->
|
||||
|
||||
describe "when the value equals the default value", ->
|
||||
it "does not store the value", ->
|
||||
atom.config.setDefaults("foo", same: 1, changes: 1)
|
||||
atom.config.setDefaults "foo",
|
||||
same: 1
|
||||
changes: 1
|
||||
sameArray: [1, 2, 3]
|
||||
sameObject: {a: 1, b: 2}
|
||||
null: null
|
||||
undefined: undefined
|
||||
expect(atom.config.settings.foo).toBeUndefined()
|
||||
|
||||
atom.config.set('foo.same', 1)
|
||||
atom.config.set('foo.changes', 2)
|
||||
atom.config.set('foo.sameArray', [1, 2, 3])
|
||||
atom.config.set('foo.null', undefined)
|
||||
atom.config.set('foo.undefined', null)
|
||||
atom.config.set('foo.sameObject', {b: 2, a: 1})
|
||||
expect(atom.config.settings.foo).toEqual {changes: 2}
|
||||
|
||||
atom.config.set('foo.changes', 1)
|
||||
expect(atom.config.settings.foo).toEqual {}
|
||||
|
||||
describe ".getDefault(keyPath)", ->
|
||||
it "returns a clone of the default value", ->
|
||||
atom.config.setDefaults("foo", same: 1, changes: 1)
|
||||
expect(atom.config.getDefault('foo.same')).toBe 1
|
||||
expect(atom.config.getDefault('foo.changes')).toBe 1
|
||||
|
||||
atom.config.set('foo.same', 2)
|
||||
atom.config.set('foo.changes', 3)
|
||||
expect(atom.config.getDefault('foo.same')).toBe 1
|
||||
expect(atom.config.getDefault('foo.changes')).toBe 1
|
||||
|
||||
initialDefaultValue = [1, 2, 3]
|
||||
atom.config.setDefaults("foo", bar: initialDefaultValue)
|
||||
expect(atom.config.getDefault('foo.bar')).toEqual initialDefaultValue
|
||||
expect(atom.config.getDefault('foo.bar')).not.toBe initialDefaultValue
|
||||
|
||||
describe ".isDefault(keyPath)", ->
|
||||
it "returns true when the value of the key path is its default value", ->
|
||||
atom.config.setDefaults("foo", same: 1, changes: 1)
|
||||
expect(atom.config.isDefault('foo.same')).toBe true
|
||||
expect(atom.config.isDefault('foo.changes')).toBe true
|
||||
|
||||
atom.config.set('foo.same', 2)
|
||||
atom.config.set('foo.changes', 3)
|
||||
expect(atom.config.isDefault('foo.same')).toBe false
|
||||
expect(atom.config.isDefault('foo.changes')).toBe false
|
||||
|
||||
describe ".toggle(keyPath)", ->
|
||||
it "negates the boolean value of the current key path value", ->
|
||||
atom.config.set('foo.a', 1)
|
||||
@@ -170,6 +208,13 @@ describe "Config", ->
|
||||
expect(atom.config.get("foo.quux.x")).toBe 0
|
||||
expect(atom.config.get("foo.quux.y")).toBe 1
|
||||
|
||||
it "emits an updated event", ->
|
||||
updatedCallback = jasmine.createSpy('updated')
|
||||
atom.config.observe('foo.bar.baz.a', callNow: false, updatedCallback)
|
||||
expect(updatedCallback.callCount).toBe 0
|
||||
atom.config.setDefaults("foo.bar.baz", a: 2)
|
||||
expect(updatedCallback.callCount).toBe 1
|
||||
|
||||
describe ".observe(keyPath)", ->
|
||||
[observeHandler, observeSubscription] = []
|
||||
|
||||
@@ -217,7 +262,7 @@ describe "Config", ->
|
||||
expect(fs.existsSync(atom.config.configDirPath)).toBeFalsy()
|
||||
|
||||
afterEach ->
|
||||
fs.removeSync(dotAtomPath) if fs.existsSync(dotAtomPath)
|
||||
fs.removeSync(dotAtomPath)
|
||||
|
||||
describe "when the configDirPath doesn't exist", ->
|
||||
it "copies the contents of dot-atom to ~/.atom", ->
|
||||
@@ -234,7 +279,7 @@ describe "Config", ->
|
||||
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'snippets.cson'))).toBeTruthy()
|
||||
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'config.cson'))).toBeTruthy()
|
||||
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'init.coffee'))).toBeTruthy()
|
||||
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'styles.css'))).toBeTruthy()
|
||||
expect(fs.isFileSync(path.join(atom.config.configDirPath, 'styles.less'))).toBeTruthy()
|
||||
|
||||
describe ".loadUserConfig()", ->
|
||||
beforeEach ->
|
||||
@@ -243,7 +288,7 @@ describe "Config", ->
|
||||
expect(fs.existsSync(atom.config.configDirPath)).toBeFalsy()
|
||||
|
||||
afterEach ->
|
||||
fs.removeSync(dotAtomPath) if fs.existsSync(dotAtomPath)
|
||||
fs.removeSync(dotAtomPath)
|
||||
|
||||
describe "when the config file contains valid cson", ->
|
||||
beforeEach ->
|
||||
@@ -286,7 +331,7 @@ describe "Config", ->
|
||||
|
||||
afterEach ->
|
||||
atom.config.unobserveUserConfig()
|
||||
fs.removeSync(dotAtomPath) if fs.existsSync(dotAtomPath)
|
||||
fs.removeSync(dotAtomPath)
|
||||
|
||||
describe "when the config file changes to contain valid cson", ->
|
||||
it "updates the config data", ->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
DisplayBuffer = require '../src/display-buffer'
|
||||
{_} = require 'atom'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
describe "DisplayBuffer", ->
|
||||
[displayBuffer, buffer, changeHandler, tabLength] = []
|
||||
@@ -18,10 +18,10 @@ describe "DisplayBuffer", ->
|
||||
displayBuffer.destroy()
|
||||
buffer.release()
|
||||
|
||||
describe ".copy()", ->
|
||||
describe "::copy()", ->
|
||||
it "creates a new DisplayBuffer with the same initial state", ->
|
||||
marker1 = displayBuffer.markBufferRange([[1, 2], [3, 4]], id: 1)
|
||||
marker2 = displayBuffer.markBufferRange([[2, 3], [4, 5]], isReversed: true, id: 2)
|
||||
marker2 = displayBuffer.markBufferRange([[2, 3], [4, 5]], reversed: true, id: 2)
|
||||
marker3 = displayBuffer.markBufferPosition([5, 6], id: 3)
|
||||
displayBuffer.createFold(3, 5)
|
||||
|
||||
@@ -37,7 +37,7 @@ describe "DisplayBuffer", ->
|
||||
expect(displayBuffer2.isFoldedAtBufferRow(3)).toBeTruthy()
|
||||
|
||||
# can diverge from origin
|
||||
displayBuffer2.destroyFoldsContainingBufferRow(3)
|
||||
displayBuffer2.unfoldBufferRow(3)
|
||||
expect(displayBuffer2.isFoldedAtBufferRow(3)).not.toBe displayBuffer.isFoldedAtBufferRow(3)
|
||||
|
||||
describe "when the buffer changes", ->
|
||||
@@ -82,7 +82,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe "when there is no whitespace before the boundary", ->
|
||||
it "wraps the line exactly at the boundary since there's no more graceful place to wrap it", ->
|
||||
buffer.change([[0, 0], [1, 0]], 'abcdefghijklmnopqrstuvwxyz\n')
|
||||
buffer.setTextInRange([[0, 0], [1, 0]], 'abcdefghijklmnopqrstuvwxyz\n')
|
||||
displayBuffer.setEditorWidthInChars(10)
|
||||
expect(displayBuffer.lineForRow(0).text).toBe 'abcdefghij'
|
||||
expect(displayBuffer.lineForRow(1).text).toBe 'klmnopqrst'
|
||||
@@ -142,7 +142,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe "when buffer lines are removed", ->
|
||||
it "removes lines and emits a change event", ->
|
||||
buffer.change([[3, 21], [7, 5]], ';')
|
||||
buffer.setTextInRange([[3, 21], [7, 5]], ';')
|
||||
expect(displayBuffer.lineForRow(3).text).toBe ' var pivot = items;'
|
||||
expect(displayBuffer.lineForRow(4).text).toBe ' return '
|
||||
expect(displayBuffer.lineForRow(5).text).toBe 'sort(left).concat(pivot).concat(sort(right));'
|
||||
@@ -200,6 +200,12 @@ describe "DisplayBuffer", ->
|
||||
expect(tokensText displayBuffer.lineForRow(12).tokens).toBe 'sort(left).concat(pivot).concat(sort(rig'
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 0, end: 15, screenDelta: 3, bufferDelta: 0)
|
||||
|
||||
it "only allows positive widths to be assigned", ->
|
||||
displayBuffer.setEditorWidthInChars(0)
|
||||
expect(displayBuffer.editorWidthInChars).not.toBe 0
|
||||
displayBuffer.setEditorWidthInChars(-1)
|
||||
expect(displayBuffer.editorWidthInChars).not.toBe -1
|
||||
|
||||
describe "primitive folding", ->
|
||||
beforeEach ->
|
||||
displayBuffer.destroy()
|
||||
@@ -324,7 +330,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe "when the old range surrounds a fold", ->
|
||||
beforeEach ->
|
||||
buffer.change([[1, 0], [5, 1]], 'party!')
|
||||
buffer.setTextInRange([[1, 0], [5, 1]], 'party!')
|
||||
|
||||
it "removes the fold and replaces the selection with the new text", ->
|
||||
expect(displayBuffer.lineForRow(0).text).toBe "0"
|
||||
@@ -346,7 +352,7 @@ describe "DisplayBuffer", ->
|
||||
displayBuffer.createFold(2, 9)
|
||||
changeHandler.reset()
|
||||
|
||||
buffer.change([[1, 0], [10, 0]], 'goodbye')
|
||||
buffer.setTextInRange([[1, 0], [10, 0]], 'goodbye')
|
||||
|
||||
expect(displayBuffer.lineForRow(0).text).toBe "0"
|
||||
expect(displayBuffer.lineForRow(1).text).toBe "goodbye10"
|
||||
@@ -365,7 +371,7 @@ describe "DisplayBuffer", ->
|
||||
describe "when the old range precedes lines with a fold", ->
|
||||
describe "when the new range precedes lines with a fold", ->
|
||||
it "updates the buffer and re-positions subsequent folds", ->
|
||||
buffer.change([[0, 0], [1, 1]], 'abc')
|
||||
buffer.setTextInRange([[0, 0], [1, 1]], 'abc')
|
||||
|
||||
expect(displayBuffer.lineForRow(0).text).toBe "abc"
|
||||
expect(displayBuffer.lineForRow(1).fold).toBe fold1
|
||||
@@ -388,7 +394,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe "when the old range straddles the beginning of a fold", ->
|
||||
it "destroys the fold", ->
|
||||
buffer.change([[1, 1], [3, 0]], "a\nb\nc\nd\n")
|
||||
buffer.setTextInRange([[1, 1], [3, 0]], "a\nb\nc\nd\n")
|
||||
expect(displayBuffer.lineForRow(1).text).toBe '1a'
|
||||
expect(displayBuffer.lineForRow(2).text).toBe 'b'
|
||||
expect(displayBuffer.lineForRow(2).fold).toBeUndefined()
|
||||
@@ -396,7 +402,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe "when the old range follows a fold", ->
|
||||
it "re-positions the screen ranges for the change event based on the preceding fold", ->
|
||||
buffer.change([[10, 0], [11, 0]], 'abc')
|
||||
buffer.setTextInRange([[10, 0], [11, 0]], 'abc')
|
||||
|
||||
expect(displayBuffer.lineForRow(1).text).toBe "1"
|
||||
expect(displayBuffer.lineForRow(2).fold).toBe fold1
|
||||
@@ -424,7 +430,7 @@ describe "DisplayBuffer", ->
|
||||
|
||||
describe "when the end of the new range exceeds the end of the fold", ->
|
||||
it "expands the fold to contain all the inserted lines", ->
|
||||
buffer.change([[3, 0], [4, 0]], 'a\nb\nc\nd\n')
|
||||
buffer.setTextInRange([[3, 0], [4, 0]], 'a\nb\nc\nd\n')
|
||||
expect(fold1.getStartRow()).toBe 2
|
||||
expect(fold1.getEndRow()).toBe 7
|
||||
|
||||
@@ -441,7 +447,7 @@ describe "DisplayBuffer", ->
|
||||
describe "when the end of the new range precedes the end of the fold", ->
|
||||
it "destroys the fold", ->
|
||||
fold2.destroy()
|
||||
buffer.change([[3, 0], [6, 0]], 'a\n')
|
||||
buffer.setTextInRange([[3, 0], [6, 0]], 'a\n')
|
||||
expect(displayBuffer.lineForRow(2).text).toBe '2'
|
||||
expect(displayBuffer.lineForRow(2).fold).toBeUndefined()
|
||||
expect(displayBuffer.lineForRow(3).text).toBe 'a'
|
||||
@@ -460,6 +466,12 @@ describe "DisplayBuffer", ->
|
||||
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 3, end: 3, screenDelta: 1, bufferDelta: 1)
|
||||
|
||||
describe "when the change starts at the beginning of a fold but does not extend to the end (regression)", ->
|
||||
it "preserves a proper mapping between buffer and screen coordinates", ->
|
||||
expect(displayBuffer.screenPositionForBufferPosition([8, 0])).toEqual [4, 0]
|
||||
buffer.setTextInRange([[2, 0], [3, 0]], "\n")
|
||||
expect(displayBuffer.screenPositionForBufferPosition([8, 0])).toEqual [4, 0]
|
||||
|
||||
describe "position translation", ->
|
||||
it "translates positions to account for folded lines and characters and the placeholder", ->
|
||||
fold = displayBuffer.createFold(4, 7)
|
||||
@@ -495,7 +507,7 @@ describe "DisplayBuffer", ->
|
||||
expect(displayBuffer.bufferPositionForScreenPosition([5, 0])).toEqual [5, 0]
|
||||
expect(displayBuffer.bufferPositionForScreenPosition([9, 2])).toEqual [9, 2]
|
||||
|
||||
describe ".destroyFoldsContainingBufferRow(row)", ->
|
||||
describe ".unfoldBufferRow(row)", ->
|
||||
it "destroys all folds containing the given row", ->
|
||||
displayBuffer.createFold(2, 4)
|
||||
displayBuffer.createFold(2, 6)
|
||||
@@ -506,14 +518,25 @@ describe "DisplayBuffer", ->
|
||||
expect(displayBuffer.lineForRow(1).text).toBe '1'
|
||||
expect(displayBuffer.lineForRow(2).text).toBe '10'
|
||||
|
||||
displayBuffer.destroyFoldsContainingBufferRow(2)
|
||||
displayBuffer.unfoldBufferRow(2)
|
||||
expect(displayBuffer.lineForRow(1).text).toBe '1'
|
||||
expect(displayBuffer.lineForRow(2).text).toBe '2'
|
||||
expect(displayBuffer.lineForRow(7).fold).toBeDefined()
|
||||
expect(displayBuffer.lineForRow(8).text).toMatch /^9-+/
|
||||
expect(displayBuffer.lineForRow(10).fold).toBeDefined()
|
||||
|
||||
describe ".clipScreenPosition(screenPosition, wrapBeyondNewlines: false, wrapAtSoftNewlines: false, skipAtomicTokens: false)", ->
|
||||
describe ".outermostFoldsInBufferRowRange(startRow, endRow)", ->
|
||||
it "returns the outermost folds entirely contained in the given row range, exclusive of end row", ->
|
||||
fold1 = displayBuffer.createFold(4, 7)
|
||||
fold2 = displayBuffer.createFold(5, 6)
|
||||
fold3 = displayBuffer.createFold(11, 15)
|
||||
fold4 = displayBuffer.createFold(12, 13)
|
||||
fold5 = displayBuffer.createFold(16, 17)
|
||||
|
||||
expect(displayBuffer.outermostFoldsInBufferRowRange(3, 18)).toEqual [fold1, fold3, fold5]
|
||||
expect(displayBuffer.outermostFoldsInBufferRowRange(5, 16)).toEqual [fold3]
|
||||
|
||||
describe "::clipScreenPosition(screenPosition, wrapBeyondNewlines: false, wrapAtSoftNewlines: false, skipAtomicTokens: false)", ->
|
||||
beforeEach ->
|
||||
displayBuffer.setSoftWrap(true)
|
||||
displayBuffer.setEditorWidthInChars(50)
|
||||
@@ -575,7 +598,7 @@ describe "DisplayBuffer", ->
|
||||
expect(displayBuffer.clipScreenPosition([0, 1], skipAtomicTokens: true)).toEqual [0, tabLength]
|
||||
expect(displayBuffer.clipScreenPosition([0, tabLength], skipAtomicTokens: true)).toEqual [0, tabLength]
|
||||
|
||||
describe ".screenPositionForBufferPosition(bufferPosition, options)", ->
|
||||
describe "::screenPositionForBufferPosition(bufferPosition, options)", ->
|
||||
it "clips the specified buffer position", ->
|
||||
expect(displayBuffer.screenPositionForBufferPosition([0, 2])).toEqual [0, 2]
|
||||
expect(displayBuffer.screenPositionForBufferPosition([0, 100000])).toEqual [0, 29]
|
||||
@@ -588,13 +611,20 @@ describe "DisplayBuffer", ->
|
||||
expect(displayBuffer.screenPositionForBufferPosition([0, 1])).toEqual [0, 2]
|
||||
expect(displayBuffer.bufferPositionForScreenPosition([0, 2])).toEqual [0, 1]
|
||||
|
||||
describe ".getMaxLineLength()", ->
|
||||
it "correctly translates positions on soft wrapped lines containing tabs", ->
|
||||
buffer.setText('\t\taa bb cc dd ee ff gg')
|
||||
displayBuffer.setSoftWrap(true)
|
||||
displayBuffer.setEditorWidthInChars(10)
|
||||
expect(displayBuffer.screenPositionForBufferPosition([0, 10], wrapAtSoftNewlines: true)).toEqual [1, 0]
|
||||
expect(displayBuffer.bufferPositionForScreenPosition([1, 0])).toEqual [0, 10]
|
||||
|
||||
describe "::getMaxLineLength()", ->
|
||||
it "returns the length of the longest screen line", ->
|
||||
expect(displayBuffer.getMaxLineLength()).toBe 65
|
||||
buffer.delete([[6, 0], [6, 65]])
|
||||
expect(displayBuffer.getMaxLineLength()).toBe 62
|
||||
|
||||
describe ".destroy()", ->
|
||||
describe "::destroy()", ->
|
||||
it "unsubscribes all display buffer markers from their underlying buffer marker (regression)", ->
|
||||
marker = displayBuffer.markBufferPosition([12, 2])
|
||||
displayBuffer.destroy()
|
||||
@@ -684,7 +714,7 @@ describe "DisplayBuffer", ->
|
||||
}
|
||||
markerChangedHandler.reset()
|
||||
|
||||
displayBuffer.destroyFoldsContainingBufferRow(4)
|
||||
displayBuffer.unfoldBufferRow(4)
|
||||
expect(markerChangedHandler).toHaveBeenCalled()
|
||||
expect(markerChangedHandler.argsForCall[0][0]).toEqual {
|
||||
oldHeadScreenPosition: [8, 23]
|
||||
@@ -747,17 +777,17 @@ describe "DisplayBuffer", ->
|
||||
isValid: true
|
||||
}
|
||||
|
||||
xit "triggers the 'changed' event whenever the marker is invalidated or revalidated", ->
|
||||
it "triggers the 'changed' event whenever the marker is invalidated or revalidated", ->
|
||||
buffer.deleteRow(8)
|
||||
expect(markerChangedHandler).toHaveBeenCalled()
|
||||
expect(markerChangedHandler.argsForCall[0][0]).toEqual {
|
||||
oldHeadScreenPosition: [5, 10]
|
||||
oldHeadBufferPosition: [8, 10]
|
||||
newHeadScreenPosition: [5, 10]
|
||||
newHeadScreenPosition: [5, 0]
|
||||
newHeadBufferPosition: [8, 0]
|
||||
oldTailScreenPosition: [5, 4]
|
||||
oldTailBufferPosition: [8, 4]
|
||||
newTailScreenPosition: [5, 4]
|
||||
newTailScreenPosition: [5, 0]
|
||||
newTailBufferPosition: [8, 0]
|
||||
textChanged: true
|
||||
isValid: false
|
||||
@@ -768,12 +798,12 @@ describe "DisplayBuffer", ->
|
||||
|
||||
expect(markerChangedHandler).toHaveBeenCalled()
|
||||
expect(markerChangedHandler.argsForCall[0][0]).toEqual {
|
||||
oldHeadScreenPosition: [5, 10]
|
||||
oldHeadBufferPosition: [8, 10]
|
||||
oldHeadScreenPosition: [5, 0]
|
||||
oldHeadBufferPosition: [8, 0]
|
||||
newHeadScreenPosition: [5, 10]
|
||||
newHeadBufferPosition: [8, 10]
|
||||
oldTailScreenPosition: [5, 4]
|
||||
oldTailBufferPosition: [8, 4]
|
||||
oldTailScreenPosition: [5, 0]
|
||||
oldTailBufferPosition: [8, 0]
|
||||
newTailScreenPosition: [5, 4]
|
||||
newTailBufferPosition: [8, 4]
|
||||
textChanged: true
|
||||
@@ -784,7 +814,7 @@ describe "DisplayBuffer", ->
|
||||
displayBuffer.createFold(10, 11)
|
||||
expect(markerChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
xit "updates markers before emitting buffer change events, but does not notify their observers until the change event", ->
|
||||
it "updates markers before emitting buffer change events, but does not notify their observers until the change event", ->
|
||||
marker2 = displayBuffer.markBufferRange([[8, 1], [8, 1]])
|
||||
marker2.on 'changed', marker2ChangedHandler = jasmine.createSpy("marker2ChangedHandler")
|
||||
displayBuffer.on 'changed', changeHandler = jasmine.createSpy("changeHandler").andCallFake -> onDisplayBufferChange()
|
||||
@@ -801,7 +831,7 @@ describe "DisplayBuffer", ->
|
||||
expect(marker.getTailScreenPosition()).toEqual [5, 7]
|
||||
expect(marker2.isValid()).toBeFalsy()
|
||||
|
||||
buffer.change([[8, 0], [8, 2]], ".....")
|
||||
buffer.setTextInRange([[8, 0], [8, 2]], ".....")
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
expect(markerChangedHandler).toHaveBeenCalled()
|
||||
expect(marker2ChangedHandler).toHaveBeenCalled()
|
||||
@@ -867,12 +897,12 @@ describe "DisplayBuffer", ->
|
||||
expect(marker.getHeadScreenPosition()).toEqual [8, 10]
|
||||
expect(marker.getTailScreenPosition()).toEqual [8, 4]
|
||||
|
||||
displayBuffer.destroyFoldsContainingBufferRow(4)
|
||||
displayBuffer.unfoldBufferRow(4)
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
expect(markerChangedHandler).toHaveBeenCalled()
|
||||
|
||||
describe ".findMarkers(attributes)", ->
|
||||
describe "::findMarkers(attributes)", ->
|
||||
it "allows the startBufferRow and endBufferRow to be specified", ->
|
||||
marker1 = displayBuffer.markBufferRange([[0, 0], [3, 0]], class: 'a')
|
||||
marker2 = displayBuffer.markBufferRange([[0, 0], [5, 0]], class: 'a')
|
||||
@@ -902,7 +932,7 @@ describe "DisplayBuffer", ->
|
||||
buffer.getMarker(marker2.id).destroy()
|
||||
expect(destroyedHandler).toHaveBeenCalled()
|
||||
|
||||
describe "DisplayBufferMarker.copy(attributes)", ->
|
||||
describe "DisplayBufferMarker::copy(attributes)", ->
|
||||
it "creates a copy of the marker with the given attributes merged in", ->
|
||||
initialMarkerCount = displayBuffer.getMarkerCount()
|
||||
marker1 = displayBuffer.markScreenRange([[5, 4], [5, 10]], a: 1, b: 2)
|
||||
@@ -913,3 +943,71 @@ describe "DisplayBuffer", ->
|
||||
expect(displayBuffer.getMarkerCount()).toBe initialMarkerCount + 2
|
||||
expect(marker1.getAttributes()).toEqual a: 1, b: 2
|
||||
expect(marker2.getAttributes()).toEqual a: 1, b: 3
|
||||
|
||||
describe "DisplayBufferMarker::getPixelRange()", ->
|
||||
it "returns the start and end positions of the marker based on the line height and character widths assigned to the DisplayBuffer", ->
|
||||
marker = displayBuffer.markScreenRange([[5, 10], [6, 4]])
|
||||
|
||||
displayBuffer.setLineHeight(20)
|
||||
displayBuffer.setDefaultCharWidth(10)
|
||||
displayBuffer.setScopedCharWidths(["source.js", "keyword.control.js"], r: 11, e: 11, t: 11, u: 11, n: 11)
|
||||
|
||||
{start, end} = marker.getPixelRange()
|
||||
expect(start.top).toBe 5 * 20
|
||||
expect(start.left).toBe (4 * 10) + (6 * 11)
|
||||
|
||||
describe "::setScrollTop", ->
|
||||
beforeEach ->
|
||||
displayBuffer.manageScrollPosition = true
|
||||
displayBuffer.setLineHeight(10)
|
||||
|
||||
it "disallows negative values", ->
|
||||
displayBuffer.setHeight(displayBuffer.getScrollHeight() + 100)
|
||||
expect(displayBuffer.setScrollTop(-10)).toBe 0
|
||||
expect(displayBuffer.getScrollTop()).toBe 0
|
||||
|
||||
it "disallows values that would make ::getScrollBottom() exceed ::getScrollHeight()", ->
|
||||
displayBuffer.setHeight(50)
|
||||
maxScrollTop = displayBuffer.getScrollHeight() - displayBuffer.getHeight()
|
||||
|
||||
expect(displayBuffer.setScrollTop(maxScrollTop)).toBe maxScrollTop
|
||||
expect(displayBuffer.getScrollTop()).toBe maxScrollTop
|
||||
|
||||
expect(displayBuffer.setScrollTop(maxScrollTop + 50)).toBe maxScrollTop
|
||||
expect(displayBuffer.getScrollTop()).toBe maxScrollTop
|
||||
|
||||
describe "::setScrollLeft", ->
|
||||
beforeEach ->
|
||||
displayBuffer.manageScrollPosition = true
|
||||
displayBuffer.setLineHeight(10)
|
||||
displayBuffer.setDefaultCharWidth(10)
|
||||
|
||||
it "disallows negative values", ->
|
||||
displayBuffer.setWidth(displayBuffer.getScrollWidth() + 100)
|
||||
expect(displayBuffer.setScrollLeft(-10)).toBe 0
|
||||
expect(displayBuffer.getScrollLeft()).toBe 0
|
||||
|
||||
it "disallows values that would make ::getScrollRight() exceed ::getScrollWidth()", ->
|
||||
displayBuffer.setWidth(50)
|
||||
maxScrollLeft = displayBuffer.getScrollWidth() - displayBuffer.getWidth()
|
||||
|
||||
expect(displayBuffer.setScrollLeft(maxScrollLeft)).toBe maxScrollLeft
|
||||
expect(displayBuffer.getScrollLeft()).toBe maxScrollLeft
|
||||
|
||||
expect(displayBuffer.setScrollLeft(maxScrollLeft + 50)).toBe maxScrollLeft
|
||||
expect(displayBuffer.getScrollLeft()).toBe maxScrollLeft
|
||||
|
||||
describe "::scrollToScreenPosition(position)", ->
|
||||
it "sets the scroll top and scroll left so the given screen position is in view", ->
|
||||
displayBuffer.manageScrollPosition = true
|
||||
displayBuffer.setLineHeight(10)
|
||||
displayBuffer.setDefaultCharWidth(10)
|
||||
displayBuffer.setHorizontalScrollbarHeight(0)
|
||||
|
||||
displayBuffer.setHeight(50)
|
||||
displayBuffer.setWidth(50)
|
||||
maxScrollTop = displayBuffer.getScrollHeight() - displayBuffer.getHeight()
|
||||
|
||||
displayBuffer.scrollToScreenPosition([8, 20])
|
||||
expect(displayBuffer.getScrollBottom()).toBe (9 + displayBuffer.getVerticalScrollMargin()) * 10
|
||||
expect(displayBuffer.getScrollRight()).toBe (20 + displayBuffer.getHorizontalScrollMargin()) * 10
|
||||
|
||||
@@ -0,0 +1,707 @@
|
||||
{extend, flatten, toArray, last} = require 'underscore-plus'
|
||||
ReactEditorView = require '../src/react-editor-view'
|
||||
nbsp = String.fromCharCode(160)
|
||||
|
||||
describe "EditorComponent", ->
|
||||
[contentNode, editor, wrapperView, component, node, verticalScrollbarNode, horizontalScrollbarNode] = []
|
||||
[lineHeightInPixels, charWidth, delayAnimationFrames, nextAnimationFrame] = []
|
||||
|
||||
beforeEach ->
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
runs ->
|
||||
spyOn(window, "setInterval").andCallFake window.fakeSetInterval
|
||||
spyOn(window, "clearInterval").andCallFake window.fakeClearInterval
|
||||
|
||||
delayAnimationFrames = false
|
||||
nextAnimationFrame = null
|
||||
spyOn(window, 'requestAnimationFrame').andCallFake (fn) ->
|
||||
if delayAnimationFrames
|
||||
nextAnimationFrame = fn
|
||||
else
|
||||
fn()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample.js').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
contentNode = document.querySelector('#jasmine-content')
|
||||
contentNode.style.width = '1000px'
|
||||
|
||||
wrapperView = new ReactEditorView(editor)
|
||||
wrapperView.attachToDom()
|
||||
{component} = wrapperView
|
||||
component.setLineHeight(1.3)
|
||||
component.setFontSize(20)
|
||||
|
||||
lineHeightInPixels = editor.getLineHeight()
|
||||
charWidth = editor.getDefaultCharWidth()
|
||||
node = component.getDOMNode()
|
||||
verticalScrollbarNode = node.querySelector('.vertical-scrollbar')
|
||||
horizontalScrollbarNode = node.querySelector('.horizontal-scrollbar')
|
||||
|
||||
afterEach ->
|
||||
contentNode.style.width = ''
|
||||
|
||||
describe "line rendering", ->
|
||||
it "renders only the currently-visible lines", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
lines = node.querySelectorAll('.line')
|
||||
expect(lines.length).toBe 6
|
||||
expect(lines[0].textContent).toBe editor.lineForScreenRow(0).text
|
||||
expect(lines[5].textContent).toBe editor.lineForScreenRow(5).text
|
||||
|
||||
verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels
|
||||
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
|
||||
expect(node.querySelector('.scroll-view-content').style['-webkit-transform']).toBe "translate3d(0px, #{-2.5 * lineHeightInPixels}px, 0)"
|
||||
|
||||
lineNodes = node.querySelectorAll('.line')
|
||||
expect(lineNodes.length).toBe 6
|
||||
expect(lineNodes[0].offsetTop).toBe 2 * lineHeightInPixels
|
||||
expect(lineNodes[0].textContent).toBe editor.lineForScreenRow(2).text
|
||||
expect(lineNodes[5].textContent).toBe editor.lineForScreenRow(7).text
|
||||
|
||||
it "updates absolute positions of subsequent lines when lines are inserted or removed", ->
|
||||
editor.getBuffer().deleteRows(0, 1)
|
||||
lineNodes = node.querySelectorAll('.line')
|
||||
expect(lineNodes[0].offsetTop).toBe 0
|
||||
expect(lineNodes[1].offsetTop).toBe 1 * lineHeightInPixels
|
||||
expect(lineNodes[2].offsetTop).toBe 2 * lineHeightInPixels
|
||||
|
||||
editor.getBuffer().insert([0, 0], '\n\n')
|
||||
lineNodes = node.querySelectorAll('.line')
|
||||
expect(lineNodes[0].offsetTop).toBe 0
|
||||
expect(lineNodes[1].offsetTop).toBe 1 * lineHeightInPixels
|
||||
expect(lineNodes[2].offsetTop).toBe 2 * lineHeightInPixels
|
||||
expect(lineNodes[3].offsetTop).toBe 3 * lineHeightInPixels
|
||||
expect(lineNodes[4].offsetTop).toBe 4 * lineHeightInPixels
|
||||
|
||||
describe "when indent guides are enabled", ->
|
||||
beforeEach ->
|
||||
component.setShowIndentGuide(true)
|
||||
|
||||
it "adds an 'indent-guide' class to spans comprising the leading whitespace", ->
|
||||
lines = node.querySelectorAll('.line')
|
||||
line1LeafNodes = getLeafNodes(lines[1])
|
||||
expect(line1LeafNodes[0].textContent).toBe ' '
|
||||
expect(line1LeafNodes[0].classList.contains('indent-guide')).toBe true
|
||||
expect(line1LeafNodes[1].classList.contains('indent-guide')).toBe false
|
||||
|
||||
line2LeafNodes = getLeafNodes(lines[2])
|
||||
expect(line2LeafNodes[0].textContent).toBe ' '
|
||||
expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe true
|
||||
expect(line2LeafNodes[1].textContent).toBe ' '
|
||||
expect(line2LeafNodes[1].classList.contains('indent-guide')).toBe true
|
||||
expect(line2LeafNodes[2].classList.contains('indent-guide')).toBe false
|
||||
|
||||
it "renders leading whitespace spans with the 'indent-guide' class for empty lines", ->
|
||||
editor.getBuffer().insert([1, Infinity], '\n')
|
||||
|
||||
lines = node.querySelectorAll('.line')
|
||||
line2LeafNodes = getLeafNodes(lines[2])
|
||||
|
||||
expect(line2LeafNodes.length).toBe 3
|
||||
expect(line2LeafNodes[0].textContent).toBe ' '
|
||||
expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe true
|
||||
expect(line2LeafNodes[1].textContent).toBe ' '
|
||||
expect(line2LeafNodes[1].classList.contains('indent-guide')).toBe true
|
||||
expect(line2LeafNodes[2].textContent).toBe ' '
|
||||
expect(line2LeafNodes[2].classList.contains('indent-guide')).toBe true
|
||||
|
||||
it "renders indent guides correctly on lines containing only whitespace", ->
|
||||
editor.getBuffer().insert([1, Infinity], '\n ')
|
||||
lines = node.querySelectorAll('.line')
|
||||
line2LeafNodes = getLeafNodes(lines[2])
|
||||
expect(line2LeafNodes.length).toBe 3
|
||||
expect(line2LeafNodes[0].textContent).toBe ' '
|
||||
expect(line2LeafNodes[0].classList.contains('indent-guide')).toBe true
|
||||
expect(line2LeafNodes[1].textContent).toBe ' '
|
||||
expect(line2LeafNodes[1].classList.contains('indent-guide')).toBe true
|
||||
expect(line2LeafNodes[2].textContent).toBe ' '
|
||||
expect(line2LeafNodes[2].classList.contains('indent-guide')).toBe true
|
||||
|
||||
it "does not render indent guides in trailing whitespace for lines containing non whitespace characters", ->
|
||||
editor.getBuffer().setText (" hi ")
|
||||
lines = node.querySelectorAll('.line')
|
||||
line0LeafNodes = getLeafNodes(lines[0])
|
||||
expect(line0LeafNodes[0].textContent).toBe ' '
|
||||
expect(line0LeafNodes[0].classList.contains('indent-guide')).toBe true
|
||||
expect(line0LeafNodes[1].textContent).toBe ' '
|
||||
expect(line0LeafNodes[1].classList.contains('indent-guide')).toBe false
|
||||
|
||||
getLeafNodes = (node) ->
|
||||
if node.children.length > 0
|
||||
flatten(toArray(node.children).map(getLeafNodes))
|
||||
else
|
||||
[node]
|
||||
|
||||
describe "gutter rendering", ->
|
||||
it "renders the currently-visible line numbers", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
lines = node.querySelectorAll('.line-number')
|
||||
expect(lines.length).toBe 6
|
||||
expect(lines[0].textContent).toBe "#{nbsp}1"
|
||||
expect(lines[5].textContent).toBe "#{nbsp}6"
|
||||
|
||||
verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels
|
||||
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
|
||||
expect(node.querySelector('.line-numbers').style['-webkit-transform']).toBe "translate3d(0, #{-2.5 * lineHeightInPixels}px, 0)"
|
||||
|
||||
lineNumberNodes = node.querySelectorAll('.line-number')
|
||||
expect(lineNumberNodes.length).toBe 6
|
||||
expect(lineNumberNodes[0].offsetTop).toBe 2 * lineHeightInPixels
|
||||
expect(lineNumberNodes[5].offsetTop).toBe 7 * lineHeightInPixels
|
||||
expect(lineNumberNodes[0].textContent).toBe "#{nbsp}3"
|
||||
expect(lineNumberNodes[5].textContent).toBe "#{nbsp}8"
|
||||
|
||||
it "updates absolute positions of subsequent line numbers when lines are inserted or removed", ->
|
||||
editor.getBuffer().insert([0, 0], '\n\n')
|
||||
|
||||
lineNumberNodes = node.querySelectorAll('.line-number')
|
||||
expect(lineNumberNodes[0].offsetTop).toBe 0
|
||||
expect(lineNumberNodes[1].offsetTop).toBe 1 * lineHeightInPixels
|
||||
expect(lineNumberNodes[2].offsetTop).toBe 2 * lineHeightInPixels
|
||||
expect(lineNumberNodes[3].offsetTop).toBe 3 * lineHeightInPixels
|
||||
expect(lineNumberNodes[4].offsetTop).toBe 4 * lineHeightInPixels
|
||||
|
||||
editor.getBuffer().insert([0, 0], '\n\n')
|
||||
lineNumberNodes = node.querySelectorAll('.line-number')
|
||||
expect(lineNumberNodes[0].offsetTop).toBe 0
|
||||
expect(lineNumberNodes[1].offsetTop).toBe 1 * lineHeightInPixels
|
||||
expect(lineNumberNodes[2].offsetTop).toBe 2 * lineHeightInPixels
|
||||
expect(lineNumberNodes[3].offsetTop).toBe 3 * lineHeightInPixels
|
||||
expect(lineNumberNodes[4].offsetTop).toBe 4 * lineHeightInPixels
|
||||
|
||||
it "renders • characters for soft-wrapped lines", ->
|
||||
editor.setSoftWrap(true)
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
node.style.width = 30 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
lines = node.querySelectorAll('.line-number')
|
||||
expect(lines.length).toBe 6
|
||||
expect(lines[0].textContent).toBe "#{nbsp}1"
|
||||
expect(lines[1].textContent).toBe "#{nbsp}•"
|
||||
expect(lines[2].textContent).toBe "#{nbsp}2"
|
||||
expect(lines[3].textContent).toBe "#{nbsp}•"
|
||||
expect(lines[4].textContent).toBe "#{nbsp}3"
|
||||
expect(lines[5].textContent).toBe "#{nbsp}•"
|
||||
|
||||
it "pads line numbers to be right justified based on the maximum number of line number digits", ->
|
||||
editor.getBuffer().setText([1..10].join('\n'))
|
||||
lineNumberNodes = toArray(node.querySelectorAll('.line-number'))
|
||||
|
||||
for node, i in lineNumberNodes[0..8]
|
||||
expect(node.textContent).toBe "#{nbsp}#{i + 1}"
|
||||
expect(lineNumberNodes[9].textContent).toBe '10'
|
||||
|
||||
# Removes padding when the max number of digits goes down
|
||||
editor.getBuffer().delete([[1, 0], [2, 0]])
|
||||
lineNumberNodes = toArray(node.querySelectorAll('.line-number'))
|
||||
for node, i in lineNumberNodes
|
||||
expect(node.textContent).toBe "#{i + 1}"
|
||||
|
||||
describe "cursor rendering", ->
|
||||
it "renders the currently visible cursors", ->
|
||||
cursor1 = editor.getCursor()
|
||||
cursor1.setScreenPosition([0, 5])
|
||||
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
cursorNodes = node.querySelectorAll('.cursor')
|
||||
expect(cursorNodes.length).toBe 1
|
||||
expect(cursorNodes[0].offsetHeight).toBe lineHeightInPixels
|
||||
expect(cursorNodes[0].offsetWidth).toBe charWidth
|
||||
expect(cursorNodes[0].offsetTop).toBe 0
|
||||
expect(cursorNodes[0].offsetLeft).toBe 5 * charWidth
|
||||
|
||||
cursor2 = editor.addCursorAtScreenPosition([6, 11])
|
||||
cursor3 = editor.addCursorAtScreenPosition([4, 10])
|
||||
|
||||
cursorNodes = node.querySelectorAll('.cursor')
|
||||
expect(cursorNodes.length).toBe 2
|
||||
expect(cursorNodes[0].offsetTop).toBe 0
|
||||
expect(cursorNodes[0].offsetLeft).toBe 5 * charWidth
|
||||
expect(cursorNodes[1].offsetTop).toBe 4 * lineHeightInPixels
|
||||
expect(cursorNodes[1].offsetLeft).toBe 10 * charWidth
|
||||
|
||||
verticalScrollbarNode.scrollTop = 2.5 * lineHeightInPixels
|
||||
verticalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
|
||||
cursorNodes = node.querySelectorAll('.cursor')
|
||||
expect(cursorNodes.length).toBe 2
|
||||
expect(cursorNodes[0].offsetTop).toBe 6 * lineHeightInPixels
|
||||
expect(cursorNodes[0].offsetLeft).toBe 11 * charWidth
|
||||
expect(cursorNodes[1].offsetTop).toBe 4 * lineHeightInPixels
|
||||
expect(cursorNodes[1].offsetLeft).toBe 10 * charWidth
|
||||
|
||||
cursor3.destroy()
|
||||
cursorNodes = node.querySelectorAll('.cursor')
|
||||
expect(cursorNodes.length).toBe 1
|
||||
expect(cursorNodes[0].offsetTop).toBe 6 * lineHeightInPixels
|
||||
expect(cursorNodes[0].offsetLeft).toBe 11 * charWidth
|
||||
|
||||
it "accounts for character widths when positioning cursors", ->
|
||||
atom.config.set('editor.fontFamily', 'sans-serif')
|
||||
editor.setCursorScreenPosition([0, 16])
|
||||
|
||||
cursor = node.querySelector('.cursor')
|
||||
cursorRect = cursor.getBoundingClientRect()
|
||||
|
||||
cursorLocationTextNode = node.querySelector('.storage.type.function.js').firstChild.firstChild
|
||||
range = document.createRange()
|
||||
range.setStart(cursorLocationTextNode, 0)
|
||||
range.setEnd(cursorLocationTextNode, 1)
|
||||
rangeRect = range.getBoundingClientRect()
|
||||
|
||||
expect(cursorRect.left).toBe rangeRect.left
|
||||
expect(cursorRect.width).toBe rangeRect.width
|
||||
|
||||
it "blinks cursors when they aren't moving", ->
|
||||
editor.addCursorAtScreenPosition([1, 0])
|
||||
[cursorNode1, cursorNode2] = node.querySelectorAll('.cursor')
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe false
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe false
|
||||
|
||||
advanceClock(component.props.cursorBlinkPeriod / 2)
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe true
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe true
|
||||
|
||||
advanceClock(component.props.cursorBlinkPeriod / 2)
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe false
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe false
|
||||
|
||||
advanceClock(component.props.cursorBlinkPeriod / 2)
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe true
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe true
|
||||
|
||||
# Stop blinking immediately when cursors move
|
||||
advanceClock(component.props.cursorBlinkPeriod / 4)
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe true
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe true
|
||||
|
||||
# Stop blinking for one full period after moving the cursor
|
||||
editor.moveCursorRight()
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe false
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe false
|
||||
|
||||
advanceClock(component.props.cursorBlinkResumeDelay / 2)
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe false
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe false
|
||||
|
||||
advanceClock(component.props.cursorBlinkResumeDelay / 2)
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe true
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe true
|
||||
|
||||
advanceClock(component.props.cursorBlinkPeriod / 2)
|
||||
expect(cursorNode1.classList.contains('blink-off')).toBe false
|
||||
expect(cursorNode2.classList.contains('blink-off')).toBe false
|
||||
|
||||
it "renders the hidden input field at the position of the last cursor if it is on screen", ->
|
||||
inputNode = node.querySelector('.hidden-input')
|
||||
node.style.height = 5 * lineHeightInPixels + 'px'
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, 0]
|
||||
editor.setScrollTop(3 * lineHeightInPixels)
|
||||
editor.setScrollLeft(3 * charWidth)
|
||||
expect(inputNode.offsetTop).toBe 0
|
||||
expect(inputNode.offsetLeft).toBe 0
|
||||
|
||||
editor.setCursorBufferPosition([5, 5])
|
||||
cursorRect = editor.getCursor().getPixelRect()
|
||||
cursorTop = cursorRect.top
|
||||
cursorLeft = cursorRect.left
|
||||
expect(inputNode.offsetTop).toBe cursorTop - editor.getScrollTop()
|
||||
expect(inputNode.offsetLeft).toBe cursorLeft - editor.getScrollLeft()
|
||||
|
||||
it "does not render cursors that are associated with non-empty selections", ->
|
||||
editor.setSelectedScreenRange([[0, 4], [4, 6]])
|
||||
editor.addCursorAtScreenPosition([6, 8])
|
||||
|
||||
cursorNodes = node.querySelectorAll('.cursor')
|
||||
expect(cursorNodes.length).toBe 1
|
||||
expect(cursorNodes[0].offsetTop).toBe 6 * lineHeightInPixels
|
||||
expect(cursorNodes[0].offsetLeft).toBe 8 * charWidth
|
||||
|
||||
describe "selection rendering", ->
|
||||
scrollViewClientLeft = null
|
||||
|
||||
beforeEach ->
|
||||
scrollViewClientLeft = node.querySelector('.scroll-view').getBoundingClientRect().left
|
||||
|
||||
it "renders 1 region for 1-line selections", ->
|
||||
# 1-line selection
|
||||
editor.setSelectedScreenRange([[1, 6], [1, 10]])
|
||||
regions = node.querySelectorAll('.selection .region')
|
||||
|
||||
expect(regions.length).toBe 1
|
||||
regionRect = regions[0].getBoundingClientRect()
|
||||
expect(regionRect.top).toBe 1 * lineHeightInPixels
|
||||
expect(regionRect.height).toBe 1 * lineHeightInPixels
|
||||
expect(regionRect.left).toBe scrollViewClientLeft + 6 * charWidth
|
||||
expect(regionRect.width).toBe 4 * charWidth
|
||||
|
||||
it "renders 2 regions for 2-line selections", ->
|
||||
editor.setSelectedScreenRange([[1, 6], [2, 10]])
|
||||
regions = node.querySelectorAll('.selection .region')
|
||||
expect(regions.length).toBe 2
|
||||
|
||||
region1Rect = regions[0].getBoundingClientRect()
|
||||
expect(region1Rect.top).toBe 1 * lineHeightInPixels
|
||||
expect(region1Rect.height).toBe 1 * lineHeightInPixels
|
||||
expect(region1Rect.left).toBe scrollViewClientLeft + 6 * charWidth
|
||||
expect(Math.ceil(region1Rect.right)).toBe node.clientWidth # TODO: Remove ceiling when react-wrapper is removed
|
||||
|
||||
region2Rect = regions[1].getBoundingClientRect()
|
||||
expect(region2Rect.top).toBe 2 * lineHeightInPixels
|
||||
expect(region2Rect.height).toBe 1 * lineHeightInPixels
|
||||
expect(region2Rect.left).toBe scrollViewClientLeft + 0
|
||||
expect(region2Rect.width).toBe 10 * charWidth
|
||||
|
||||
it "renders 3 regions for selections with more than 2 lines", ->
|
||||
editor.setSelectedScreenRange([[1, 6], [5, 10]])
|
||||
regions = node.querySelectorAll('.selection .region')
|
||||
expect(regions.length).toBe 3
|
||||
|
||||
region1Rect = regions[0].getBoundingClientRect()
|
||||
expect(region1Rect.top).toBe 1 * lineHeightInPixels
|
||||
expect(region1Rect.height).toBe 1 * lineHeightInPixels
|
||||
expect(region1Rect.left).toBe scrollViewClientLeft + 6 * charWidth
|
||||
expect(Math.ceil(region1Rect.right)).toBe node.clientWidth # TODO: Remove ceiling when react-wrapper is removed
|
||||
|
||||
region2Rect = regions[1].getBoundingClientRect()
|
||||
expect(region2Rect.top).toBe 2 * lineHeightInPixels
|
||||
expect(region2Rect.height).toBe 3 * lineHeightInPixels
|
||||
expect(region2Rect.left).toBe scrollViewClientLeft + 0
|
||||
expect(Math.ceil(region2Rect.right)).toBe node.clientWidth # TODO: Remove ceiling when react-wrapper is removed
|
||||
|
||||
region3Rect = regions[2].getBoundingClientRect()
|
||||
expect(region3Rect.top).toBe 5 * lineHeightInPixels
|
||||
expect(region3Rect.height).toBe 1 * lineHeightInPixels
|
||||
expect(region3Rect.left).toBe scrollViewClientLeft + 0
|
||||
expect(region3Rect.width).toBe 10 * charWidth
|
||||
|
||||
it "does not render empty selections", ->
|
||||
expect(editor.getSelection().isEmpty()).toBe true
|
||||
expect(node.querySelectorAll('.selection').length).toBe 0
|
||||
|
||||
describe "mouse interactions", ->
|
||||
linesNode = null
|
||||
|
||||
beforeEach ->
|
||||
delayAnimationFrames = true
|
||||
linesNode = node.querySelector('.lines')
|
||||
|
||||
describe "when a non-folded line is single-clicked", ->
|
||||
describe "when no modifier keys are held down", ->
|
||||
it "moves the cursor to the nearest screen position", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
editor.setScrollTop(3.5 * lineHeightInPixels)
|
||||
editor.setScrollLeft(2 * charWidth)
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([4, 8])))
|
||||
expect(editor.getCursorScreenPosition()).toEqual [4, 8]
|
||||
|
||||
describe "when the shift key is held down", ->
|
||||
it "selects to the nearest screen position", ->
|
||||
editor.setCursorScreenPosition([3, 4])
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 6]), shiftKey: true))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[3, 4], [5, 6]]
|
||||
|
||||
describe "when the command key is held down", ->
|
||||
it "adds a cursor at the nearest screen position", ->
|
||||
editor.setCursorScreenPosition([3, 4])
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 6]), metaKey: true))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual [[[3, 4], [3, 4]], [[5, 6], [5, 6]]]
|
||||
|
||||
describe "when a non-folded line is double-clicked", ->
|
||||
it "selects the word containing the nearest screen position", ->
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 10]), detail: 2))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[5, 6], [5, 13]]
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([6, 6]), detail: 1))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[6, 6], [6, 6]]
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([8, 8]), detail: 1, shiftKey: true))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[6, 6], [8, 8]]
|
||||
|
||||
describe "when a non-folded line is triple-clicked", ->
|
||||
it "selects the line containing the nearest screen position", ->
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([5, 10]), detail: 3))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[5, 0], [6, 0]]
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([6, 6]), detail: 1, shiftKey: true))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[5, 0], [7, 0]]
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([7, 5]), detail: 1))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([8, 8]), detail: 1, shiftKey: true))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[7, 5], [8, 8]]
|
||||
|
||||
describe "when the mouse is clicked and dragged", ->
|
||||
it "selects to the nearest screen position until the mouse button is released", ->
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([2, 4]), which: 1))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenPosition([6, 8]), which: 1))
|
||||
nextAnimationFrame()
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[2, 4], [6, 8]]
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenPosition([10, 0]), which: 1))
|
||||
nextAnimationFrame()
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[2, 4], [10, 0]]
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mouseup'))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenPosition([12, 0]), which: 1))
|
||||
nextAnimationFrame()
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[2, 4], [10, 0]]
|
||||
|
||||
it "stops selecting if the mouse is dragged into the dev tools", ->
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([2, 4]), which: 1))
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenPosition([6, 8]), which: 1))
|
||||
nextAnimationFrame()
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[2, 4], [6, 8]]
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenPosition([10, 0]), which: 0))
|
||||
nextAnimationFrame()
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[2, 4], [6, 8]]
|
||||
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenPosition([8, 0]), which: 1))
|
||||
nextAnimationFrame()
|
||||
expect(editor.getSelectedScreenRange()).toEqual [[2, 4], [6, 8]]
|
||||
|
||||
clientCoordinatesForScreenPosition = (screenPosition) ->
|
||||
positionOffset = editor.pixelPositionForScreenPosition(screenPosition)
|
||||
scrollViewClientRect = node.querySelector('.scroll-view').getBoundingClientRect()
|
||||
clientX = scrollViewClientRect.left + positionOffset.left - editor.getScrollLeft()
|
||||
clientY = scrollViewClientRect.top + positionOffset.top - editor.getScrollTop()
|
||||
{clientX, clientY}
|
||||
|
||||
buildMouseEvent = (type, properties...) ->
|
||||
properties = extend({bubbles: true, cancelable: true}, properties...)
|
||||
event = new MouseEvent(type, properties)
|
||||
Object.defineProperty(event, 'which', get: -> properties.which) if properties.which?
|
||||
event
|
||||
|
||||
describe "focus handling", ->
|
||||
inputNode = null
|
||||
|
||||
beforeEach ->
|
||||
inputNode = node.querySelector('.hidden-input')
|
||||
|
||||
it "transfers focus to the hidden input", ->
|
||||
expect(document.activeElement).toBe document.body
|
||||
node.focus()
|
||||
expect(document.activeElement).toBe inputNode
|
||||
|
||||
it "adds the 'is-focused' class to the editor when the hidden input is focused", ->
|
||||
expect(document.activeElement).toBe document.body
|
||||
inputNode.focus()
|
||||
expect(node.classList.contains('is-focused')).toBe true
|
||||
inputNode.blur()
|
||||
expect(node.classList.contains('is-focused')).toBe false
|
||||
|
||||
describe "scrolling", ->
|
||||
it "updates the vertical scrollbar when the scrollTop is changed in the model", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
expect(verticalScrollbarNode.scrollTop).toBe 0
|
||||
|
||||
editor.setScrollTop(10)
|
||||
expect(verticalScrollbarNode.scrollTop).toBe 10
|
||||
|
||||
it "updates the horizontal scrollbar and scroll view content x transform based on the scrollLeft of the model", ->
|
||||
node.style.width = 30 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
scrollViewContentNode = node.querySelector('.scroll-view-content')
|
||||
expect(scrollViewContentNode.style['-webkit-transform']).toBe "translate3d(0px, 0px, 0)"
|
||||
expect(horizontalScrollbarNode.scrollLeft).toBe 0
|
||||
|
||||
editor.setScrollLeft(100)
|
||||
expect(scrollViewContentNode.style['-webkit-transform']).toBe "translate3d(-100px, 0px, 0)"
|
||||
expect(horizontalScrollbarNode.scrollLeft).toBe 100
|
||||
|
||||
it "updates the scrollLeft of the model when the scrollLeft of the horizontal scrollbar changes", ->
|
||||
node.style.width = 30 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
expect(editor.getScrollLeft()).toBe 0
|
||||
horizontalScrollbarNode.scrollLeft = 100
|
||||
horizontalScrollbarNode.dispatchEvent(new UIEvent('scroll'))
|
||||
|
||||
expect(editor.getScrollLeft()).toBe 100
|
||||
|
||||
it "does not obscure the last line with the horizontal scrollbar", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
editor.setScrollBottom(editor.getScrollHeight())
|
||||
lastLineNode = last(node.querySelectorAll('.line'))
|
||||
bottomOfLastLine = lastLineNode.getBoundingClientRect().bottom
|
||||
topOfHorizontalScrollbar = horizontalScrollbarNode.getBoundingClientRect().top
|
||||
expect(bottomOfLastLine).toBe topOfHorizontalScrollbar
|
||||
|
||||
# Scroll so there's no space below the last line when the horizontal scrollbar disappears
|
||||
node.style.width = 100 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
lastLineNode = last(node.querySelectorAll('.line'))
|
||||
bottomOfLastLine = lastLineNode.getBoundingClientRect().bottom
|
||||
bottomOfEditor = node.getBoundingClientRect().bottom
|
||||
expect(bottomOfLastLine).toBe bottomOfEditor
|
||||
|
||||
it "does not obscure the last character of the longest line with the vertical scrollbar", ->
|
||||
node.style.height = 7 * lineHeightInPixels + 'px'
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
editor.setScrollLeft(Infinity)
|
||||
|
||||
lineNodes = node.querySelectorAll('.line')
|
||||
rightOfLongestLine = lineNodes[6].getBoundingClientRect().right
|
||||
leftOfVerticalScrollbar = verticalScrollbarNode.getBoundingClientRect().left
|
||||
|
||||
expect(rightOfLongestLine).toBe leftOfVerticalScrollbar - 1 # Leave 1 px so the cursor is visible on the end of the line
|
||||
|
||||
it "only displays dummy scrollbars when scrollable in that direction", ->
|
||||
expect(verticalScrollbarNode.style.display).toBe 'none'
|
||||
expect(horizontalScrollbarNode.style.display).toBe 'none'
|
||||
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
node.style.width = '1000px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
expect(verticalScrollbarNode.style.display).toBe ''
|
||||
expect(horizontalScrollbarNode.style.display).toBe 'none'
|
||||
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
expect(verticalScrollbarNode.style.display).toBe ''
|
||||
expect(horizontalScrollbarNode.style.display).toBe ''
|
||||
|
||||
node.style.height = 20 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
expect(verticalScrollbarNode.style.display).toBe 'none'
|
||||
expect(horizontalScrollbarNode.style.display).toBe ''
|
||||
|
||||
it "makes the dummy scrollbar divs only as tall/wide as the actual scrollbars", ->
|
||||
node.style.height = 4 * lineHeightInPixels + 'px'
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
atom.themes.applyStylesheet "test", """
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
"""
|
||||
|
||||
scrollbarCornerNode = node.querySelector('.scrollbar-corner')
|
||||
expect(verticalScrollbarNode.offsetWidth).toBe 8
|
||||
expect(horizontalScrollbarNode.offsetHeight).toBe 8
|
||||
expect(scrollbarCornerNode.offsetWidth).toBe 8
|
||||
expect(scrollbarCornerNode.offsetHeight).toBe 8
|
||||
|
||||
it "assigns the bottom/right of the scrollbars to the width of the opposite scrollbar if it is visible", ->
|
||||
scrollbarCornerNode = node.querySelector('.scrollbar-corner')
|
||||
|
||||
expect(verticalScrollbarNode.style.bottom).toBe ''
|
||||
expect(horizontalScrollbarNode.style.right).toBe ''
|
||||
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
node.style.width = '1000px'
|
||||
component.measureHeightAndWidth()
|
||||
expect(verticalScrollbarNode.style.bottom).toBe ''
|
||||
expect(horizontalScrollbarNode.style.right).toBe verticalScrollbarNode.offsetWidth + 'px'
|
||||
expect(scrollbarCornerNode.style.display).toBe 'none'
|
||||
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
expect(verticalScrollbarNode.style.bottom).toBe horizontalScrollbarNode.offsetHeight + 'px'
|
||||
expect(horizontalScrollbarNode.style.right).toBe verticalScrollbarNode.offsetWidth + 'px'
|
||||
expect(scrollbarCornerNode.style.display).toBe ''
|
||||
|
||||
node.style.height = 20 * lineHeightInPixels + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
expect(verticalScrollbarNode.style.bottom).toBe horizontalScrollbarNode.offsetHeight + 'px'
|
||||
expect(horizontalScrollbarNode.style.right).toBe ''
|
||||
expect(scrollbarCornerNode.style.display).toBe 'none'
|
||||
|
||||
it "accounts for the width of the gutter in the scrollWidth of the horizontal scrollbar", ->
|
||||
gutterNode = node.querySelector('.gutter')
|
||||
node.style.width = 10 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
expect(horizontalScrollbarNode.scrollWidth).toBe gutterNode.offsetWidth + editor.getScrollWidth()
|
||||
|
||||
describe "when a mousewheel event occurs on the editor", ->
|
||||
it "updates the horizontal or vertical scrollbar depending on which delta is greater (x or y)", ->
|
||||
node.style.height = 4.5 * lineHeightInPixels + 'px'
|
||||
node.style.width = 20 * charWidth + 'px'
|
||||
component.measureHeightAndWidth()
|
||||
|
||||
expect(verticalScrollbarNode.scrollTop).toBe 0
|
||||
expect(horizontalScrollbarNode.scrollLeft).toBe 0
|
||||
|
||||
node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -5, wheelDeltaY: -10))
|
||||
expect(verticalScrollbarNode.scrollTop).toBe 10
|
||||
expect(horizontalScrollbarNode.scrollLeft).toBe 0
|
||||
|
||||
node.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -15, wheelDeltaY: -5))
|
||||
expect(verticalScrollbarNode.scrollTop).toBe 10
|
||||
expect(horizontalScrollbarNode.scrollLeft).toBe 15
|
||||
|
||||
describe "input events", ->
|
||||
inputNode = null
|
||||
|
||||
beforeEach ->
|
||||
inputNode = node.querySelector('.hidden-input')
|
||||
|
||||
it "inserts the newest character in the input's value into the buffer", ->
|
||||
inputNode.value = 'x'
|
||||
inputNode.dispatchEvent(new Event('input'))
|
||||
expect(editor.lineForBufferRow(0)).toBe 'xvar quicksort = function () {'
|
||||
|
||||
inputNode.value = 'xy'
|
||||
inputNode.dispatchEvent(new Event('input'))
|
||||
expect(editor.lineForBufferRow(0)).toBe 'xyvar quicksort = function () {'
|
||||
|
||||
it "replaces the last character if the length of the input's value doesn't increase, as occurs with the accented character menu", ->
|
||||
inputNode.value = 'u'
|
||||
inputNode.dispatchEvent(new Event('input'))
|
||||
expect(editor.lineForBufferRow(0)).toBe 'uvar quicksort = function () {'
|
||||
|
||||
inputNode.value = 'ü'
|
||||
inputNode.dispatchEvent(new Event('input'))
|
||||
expect(editor.lineForBufferRow(0)).toBe 'üvar quicksort = function () {'
|
||||
|
||||
describe "commands", ->
|
||||
describe "editor:consolidate-selections", ->
|
||||
it "consolidates selections on the editor model, aborting the key binding if there is only one selection", ->
|
||||
spyOn(editor, 'consolidateSelections').andCallThrough()
|
||||
|
||||
event = new CustomEvent('editor:consolidate-selections', bubbles: true, cancelable: true)
|
||||
event.abortKeyBinding = jasmine.createSpy("event.abortKeyBinding")
|
||||
node.dispatchEvent(event)
|
||||
|
||||
expect(editor.consolidateSelections).toHaveBeenCalled()
|
||||
expect(event.abortKeyBinding).toHaveBeenCalled()
|
||||
+2757
-2400
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
+288
-221
@@ -7,22 +7,30 @@ path = require 'path'
|
||||
temp = require 'temp'
|
||||
|
||||
describe "EditorView", ->
|
||||
[buffer, editorView, editor, cachedLineHeight, cachedCharWidth] = []
|
||||
[buffer, editorView, editor, cachedEditor, cachedLineHeight, cachedCharWidth, fart] = []
|
||||
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
buffer = editor.buffer
|
||||
editorView = new EditorView(editor)
|
||||
editorView.lineOverdraw = 2
|
||||
editorView.isFocused = true
|
||||
editorView.enableKeymap()
|
||||
editorView.calculateHeightInLines = ->
|
||||
Math.ceil(@height() / @lineHeight)
|
||||
editorView.attachToDom = ({ heightInLines, widthInChars } = {}) ->
|
||||
heightInLines ?= @getEditor().getBuffer().getLineCount()
|
||||
@height(getLineHeight() * heightInLines)
|
||||
@width(getCharWidth() * widthInChars) if widthInChars
|
||||
$('#jasmine-content').append(this)
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js').then (o) -> editor = o
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.less').then (o) -> cachedEditor = o
|
||||
|
||||
runs ->
|
||||
buffer = editor.buffer
|
||||
editorView = new EditorView(editor)
|
||||
editorView.lineOverdraw = 2
|
||||
editorView.isFocused = true
|
||||
editorView.enableKeymap()
|
||||
|
||||
editorView.calculateHeightInLines = ->
|
||||
Math.ceil(@height() / @lineHeight)
|
||||
|
||||
editorView.attachToDom = ({ heightInLines, widthInChars } = {}) ->
|
||||
heightInLines ?= @getEditor().getBuffer().getLineCount()
|
||||
@height(getLineHeight() * heightInLines)
|
||||
@width(getCharWidth() * widthInChars) if widthInChars
|
||||
$('#jasmine-content').append(this)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-text', sync: true)
|
||||
@@ -41,7 +49,7 @@ describe "EditorView", ->
|
||||
cachedCharWidth
|
||||
|
||||
calcDimensions = ->
|
||||
editorForMeasurement = new EditorView(editor: atom.project.openSync('sample.js'))
|
||||
editorForMeasurement = new EditorView({editor: cachedEditor})
|
||||
editorForMeasurement.attachToDom()
|
||||
cachedLineHeight = editorForMeasurement.lineHeight
|
||||
cachedCharWidth = editorForMeasurement.charWidth
|
||||
@@ -105,18 +113,23 @@ describe "EditorView", ->
|
||||
|
||||
describe "when the editor's file is modified on disk", ->
|
||||
it "triggers an alert", ->
|
||||
fileChangeHandler = null
|
||||
filePath = path.join(temp.dir, 'atom-changed-file.txt')
|
||||
fs.writeFileSync(filePath, "")
|
||||
editor = atom.project.openSync(filePath)
|
||||
editorView.edit(editor)
|
||||
editor.insertText("now the buffer is modified")
|
||||
|
||||
fileChangeHandler = jasmine.createSpy('fileChange')
|
||||
editor.buffer.file.on 'contents-changed', fileChangeHandler
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath).then (o) -> editor = o
|
||||
|
||||
spyOn(atom, "confirm")
|
||||
runs ->
|
||||
editorView.edit(editor)
|
||||
editor.insertText("now the buffer is modified")
|
||||
|
||||
fs.writeFileSync(filePath, "a file change")
|
||||
fileChangeHandler = jasmine.createSpy('fileChange')
|
||||
editor.buffer.file.on 'contents-changed', fileChangeHandler
|
||||
|
||||
spyOn(atom, "confirm")
|
||||
|
||||
fs.writeFileSync(filePath, "a file change")
|
||||
|
||||
waitsFor "file to trigger contents-changed event", ->
|
||||
fileChangeHandler.callCount > 0
|
||||
@@ -133,8 +146,11 @@ describe "EditorView", ->
|
||||
[newEditor, newBuffer] = []
|
||||
|
||||
beforeEach ->
|
||||
newEditor = atom.project.openSync('two-hundred.txt')
|
||||
newBuffer = newEditor.buffer
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('two-hundred.txt').then (o) -> newEditor = o
|
||||
|
||||
runs ->
|
||||
newBuffer = newEditor.buffer
|
||||
|
||||
it "updates the rendered lines, cursors, selections, scroll position, and event subscriptions to match the given edit session", ->
|
||||
editorView.attachToDom(heightInLines: 5, widthInChars: 30)
|
||||
@@ -170,17 +186,24 @@ describe "EditorView", ->
|
||||
expect(editorView.lineElementForScreenRow(6).text()).toMatch /^ currentgoodbye/
|
||||
|
||||
it "triggers alert if edit session's buffer goes into conflict with changes on disk", ->
|
||||
contentsConflictedHandler = null
|
||||
filePath = path.join(temp.dir, 'atom-changed-file.txt')
|
||||
fs.writeFileSync(filePath, "")
|
||||
tempEditor = atom.project.openSync(filePath)
|
||||
editorView.edit(tempEditor)
|
||||
tempEditor.insertText("a buffer change")
|
||||
tempEditor = null
|
||||
|
||||
spyOn(atom, "confirm")
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath).then (o) -> tempEditor = o
|
||||
|
||||
runs ->
|
||||
editorView.edit(tempEditor)
|
||||
tempEditor.insertText("a buffer change")
|
||||
|
||||
spyOn(atom, "confirm")
|
||||
|
||||
contentsConflictedHandler = jasmine.createSpy("contentsConflictedHandler")
|
||||
tempEditor.on 'contents-conflicted', contentsConflictedHandler
|
||||
fs.writeFileSync(filePath, "a file change")
|
||||
|
||||
contentsConflictedHandler = jasmine.createSpy("contentsConflictedHandler")
|
||||
tempEditor.on 'contents-conflicted', contentsConflictedHandler
|
||||
fs.writeFileSync(filePath, "a file change")
|
||||
waitsFor ->
|
||||
contentsConflictedHandler.callCount > 0
|
||||
|
||||
@@ -281,26 +304,34 @@ describe "EditorView", ->
|
||||
it "emits event when editor view view receives a new buffer", ->
|
||||
eventHandler = jasmine.createSpy('eventHandler')
|
||||
editorView.on 'editor:path-changed', eventHandler
|
||||
editorView.edit(atom.project.openSync(filePath))
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath).then (editor) ->
|
||||
editorView.edit(editor)
|
||||
|
||||
runs ->
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
|
||||
it "stops listening to events on previously set buffers", ->
|
||||
eventHandler = jasmine.createSpy('eventHandler')
|
||||
oldBuffer = editor.getBuffer()
|
||||
newEditor = atom.project.openSync(filePath)
|
||||
editorView.on 'editor:path-changed', eventHandler
|
||||
newEditor = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath).then (o) -> newEditor = o
|
||||
|
||||
editorView.edit(newEditor)
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
runs ->
|
||||
editorView.on 'editor:path-changed', eventHandler
|
||||
|
||||
eventHandler.reset()
|
||||
oldBuffer.saveAs(path.join(temp.dir, 'atom-bad.txt'))
|
||||
expect(eventHandler).not.toHaveBeenCalled()
|
||||
editorView.edit(newEditor)
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
|
||||
eventHandler.reset()
|
||||
newEditor.getBuffer().saveAs(path.join(temp.dir, 'atom-new.txt'))
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
eventHandler.reset()
|
||||
oldBuffer.saveAs(path.join(temp.dir, 'atom-bad.txt'))
|
||||
expect(eventHandler).not.toHaveBeenCalled()
|
||||
|
||||
eventHandler.reset()
|
||||
newEditor.getBuffer().saveAs(path.join(temp.dir, 'atom-new.txt'))
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
|
||||
it "loads the grammar for the new path", ->
|
||||
expect(editor.getGrammar().name).toBe 'JavaScript'
|
||||
@@ -432,9 +463,9 @@ describe "EditorView", ->
|
||||
editorView.setFontFamily('sans-serif')
|
||||
|
||||
it "positions the cursor to the clicked row and column", ->
|
||||
{top, left} = editorView.pixelOffsetForScreenPosition([3, 30])
|
||||
editorView.renderedLines.trigger mousedownEvent(pageX: left, pageY: top)
|
||||
expect(editor.getCursorScreenPosition()).toEqual [3, 30]
|
||||
{top, left} = editorView.pixelOffsetForScreenPosition([3, 30])
|
||||
editorView.renderedLines.trigger mousedownEvent(pageX: left, pageY: top)
|
||||
expect(editor.getCursorScreenPosition()).toEqual [3, 30]
|
||||
|
||||
describe "double-click", ->
|
||||
it "selects the word under the cursor, and expands the selection wordwise in either direction on a subsequent shift-click", ->
|
||||
@@ -464,7 +495,21 @@ describe "EditorView", ->
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [3, 12], originalEvent: {detail: 1}, shiftKey: true)
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[3, 10], [3, 12]]
|
||||
|
||||
describe "when clicking between a word and a non-word", ->
|
||||
it "stops selecting by word when another selection is made", ->
|
||||
expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0)
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 8], originalEvent: {detail: 1})
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 8], originalEvent: {detail: 2})
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
expect(editor.getSelectedText()).toBe "quicksort"
|
||||
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [3, 10])
|
||||
editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [3, 12], which: 1)
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[3, 10], [3, 12]]
|
||||
|
||||
describe "when double-clicking between a word and a non-word", ->
|
||||
it "selects the word", ->
|
||||
expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0)
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [1, 21], originalEvent: {detail: 1})
|
||||
@@ -487,6 +532,30 @@ describe "EditorView", ->
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
expect(editor.getSelectedText()).toBe "{"
|
||||
|
||||
describe "when double-clicking on whitespace", ->
|
||||
it "selects all adjacent whitespace", ->
|
||||
editor.setText(" some text ")
|
||||
editor.setCursorBufferPosition([0, 2])
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 2], originalEvent: {detail: 1})
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 2], originalEvent: {detail: 2})
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 3]]
|
||||
|
||||
editor.setCursorBufferPosition([0, 8])
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 8], originalEvent: {detail: 1})
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 8], originalEvent: {detail: 2})
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[0, 7], [0, 9]]
|
||||
|
||||
editor.setCursorBufferPosition([0, 14])
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 14], originalEvent: {detail: 1})
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 14], originalEvent: {detail: 2})
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[0, 13], [0, 17]]
|
||||
|
||||
describe "triple/quardruple/etc-click", ->
|
||||
it "selects the line under the cursor", ->
|
||||
expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0)
|
||||
@@ -587,7 +656,7 @@ describe "EditorView", ->
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [4, 10])
|
||||
|
||||
# moving changes selection
|
||||
$(document).trigger mousemoveEvent(editorView: editorView, point: [5, 27])
|
||||
$(document).trigger mousemoveEvent(editorView: editorView, point: [5, 27], which: 1)
|
||||
|
||||
range = editor.getSelection().getScreenRange()
|
||||
expect(range.start).toEqual({row: 4, column: 10})
|
||||
@@ -617,12 +686,12 @@ describe "EditorView", ->
|
||||
originalScrollTop = editorView.scrollTop()
|
||||
|
||||
# moving changes selection
|
||||
$(document).trigger mousemoveEvent(editorView: editorView, pageX: 0, pageY: -1)
|
||||
$(document).trigger mousemoveEvent(editorView: editorView, pageX: 0, pageY: -1, which: 1)
|
||||
expect(editorView.scrollTop()).toBe originalScrollTop - editorView.lineHeight
|
||||
|
||||
# every mouse move selects more text
|
||||
for x in [0..10]
|
||||
$(document).trigger mousemoveEvent(editorView: editorView, pageX: 0, pageY: -1)
|
||||
$(document).trigger mousemoveEvent(editorView: editorView, pageX: 0, pageY: -1, which: 1)
|
||||
|
||||
expect(editorView.scrollTop()).toBe 0
|
||||
|
||||
@@ -633,7 +702,7 @@ describe "EditorView", ->
|
||||
event = mousedownEvent(editorView: editorView, point: [4, 10])
|
||||
event.originalEvent.which = 2
|
||||
editorView.renderedLines.trigger(event)
|
||||
$(document).trigger mousemoveEvent(editorView: editorView, point: [5, 27])
|
||||
$(document).trigger mousemoveEvent(editorView: editorView, point: [5, 27], which: 1)
|
||||
$(document).trigger 'mouseup'
|
||||
|
||||
range = editor.getSelection().getScreenRange()
|
||||
@@ -654,6 +723,25 @@ describe "EditorView", ->
|
||||
expect(range.start).toEqual({row: 4, column: 10})
|
||||
expect(range.end).toEqual({row: 4, column: 10})
|
||||
|
||||
describe "when the editor is hidden", ->
|
||||
it "stops scrolling the editor", ->
|
||||
editorView.vScrollMargin = 0
|
||||
editorView.attachToDom(heightInLines: 5)
|
||||
editorView.scrollToBottom()
|
||||
|
||||
spyOn(window, 'setInterval').andCallFake ->
|
||||
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [12, 0])
|
||||
originalScrollTop = editorView.scrollTop()
|
||||
|
||||
$(document).trigger mousemoveEvent(editorView: editorView, pageX: 0, pageY: -1, which: 1)
|
||||
expect(editorView.scrollTop()).toBe originalScrollTop - editorView.lineHeight
|
||||
|
||||
editorView.hide()
|
||||
|
||||
$(document).trigger mousemoveEvent(editorView: editorView, pageX: 100000, pageY: -1, which: 1)
|
||||
expect(editorView.scrollTop()).toBe originalScrollTop - editorView.lineHeight
|
||||
|
||||
describe "double-click and drag", ->
|
||||
it "selects the word under the cursor, then continues to select by word in either direction as the mouse is dragged", ->
|
||||
expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0)
|
||||
@@ -662,11 +750,11 @@ describe "EditorView", ->
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 8], originalEvent: {detail: 2})
|
||||
expect(editor.getSelectedText()).toBe "quicksort"
|
||||
|
||||
editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [1, 8])
|
||||
editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [1, 8], which: 1)
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[0, 4], [1, 10]]
|
||||
expect(editor.getCursorBufferPosition()).toEqual [1, 10]
|
||||
|
||||
editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [0, 1])
|
||||
editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [0, 1], which: 1)
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 13]]
|
||||
expect(editor.getCursorBufferPosition()).toEqual [0, 0]
|
||||
|
||||
@@ -691,12 +779,12 @@ describe "EditorView", ->
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[4, 0], [5, 0]]
|
||||
|
||||
# moving changes selection linewise
|
||||
editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [5, 27])
|
||||
editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [5, 27], which: 1)
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[4, 0], [6, 0]]
|
||||
expect(editor.getCursorBufferPosition()).toEqual [6, 0]
|
||||
|
||||
# moving changes selection linewise
|
||||
editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [2, 27])
|
||||
editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [2, 27], which: 1)
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[2, 0], [5, 0]]
|
||||
expect(editor.getCursorBufferPosition()).toEqual [2, 0]
|
||||
|
||||
@@ -706,11 +794,11 @@ describe "EditorView", ->
|
||||
describe "meta-click and drag", ->
|
||||
it "adds an additional selection", ->
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [4, 10])
|
||||
editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [5, 27])
|
||||
editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [5, 27], which: 1)
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
|
||||
editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [6, 10], metaKey: true)
|
||||
editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [8, 27], metaKey: true)
|
||||
editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [8, 27], metaKey: true, which: 1)
|
||||
editorView.renderedLines.trigger 'mouseup'
|
||||
|
||||
selections = editor.getSelections()
|
||||
@@ -1352,7 +1440,7 @@ describe "EditorView", ->
|
||||
expect(editorView.renderedLines.find(".line:first").text()).toBe buffer.lineForRow(6)
|
||||
expect(editorView.renderedLines.find(".line:last").text()).toBe buffer.lineForRow(12)
|
||||
|
||||
buffer.change([[1,0], [3,0]], "1\n2\n3\n")
|
||||
buffer.setTextInRange([[1,0], [3,0]], "1\n2\n3\n")
|
||||
expect(editorView.renderedLines.find(".line").length).toBe 7
|
||||
expect(editorView.renderedLines.find(".line:first").text()).toBe buffer.lineForRow(6)
|
||||
expect(editorView.renderedLines.find(".line:last").text()).toBe buffer.lineForRow(12)
|
||||
@@ -1365,21 +1453,21 @@ describe "EditorView", ->
|
||||
expect(editorView.renderedLines.find(".line:first").text()).toBe buffer.lineForRow(6)
|
||||
expect(editorView.renderedLines.find(".line:last").text()).toBe buffer.lineForRow(12)
|
||||
|
||||
buffer.change([[2,0], [7,0]], "2\n3\n4\n5\n6\n7\n8\n9\n")
|
||||
buffer.setTextInRange([[2,0], [7,0]], "2\n3\n4\n5\n6\n7\n8\n9\n")
|
||||
expect(editorView.renderedLines.find(".line").length).toBe 7
|
||||
expect(editorView.renderedLines.find(".line:first").text()).toBe buffer.lineForRow(6)
|
||||
expect(editorView.renderedLines.find(".line:last").text()).toBe buffer.lineForRow(12)
|
||||
|
||||
describe "when the change straddles the last rendered row", ->
|
||||
it "doesn't render rows that were not previously rendered", ->
|
||||
buffer.change([[2,0], [7,0]], "2\n3\n4\n5\n6\n7\n8\n")
|
||||
buffer.setTextInRange([[2,0], [7,0]], "2\n3\n4\n5\n6\n7\n8\n")
|
||||
expect(editorView.renderedLines.find(".line").length).toBe 7
|
||||
expect(editorView.renderedLines.find(".line:first").text()).toBe buffer.lineForRow(0)
|
||||
expect(editorView.renderedLines.find(".line:last").text()).toBe buffer.lineForRow(6)
|
||||
|
||||
describe "when the change the follows the last rendered row", ->
|
||||
it "does not change the rendered lines", ->
|
||||
buffer.change([[12,0], [12,0]], "12\n13\n14\n")
|
||||
buffer.setTextInRange([[12,0], [12,0]], "12\n13\n14\n")
|
||||
expect(editorView.renderedLines.find(".line").length).toBe 7
|
||||
expect(editorView.renderedLines.find(".line:first").text()).toBe buffer.lineForRow(0)
|
||||
expect(editorView.renderedLines.find(".line:last").text()).toBe buffer.lineForRow(6)
|
||||
@@ -1389,7 +1477,7 @@ describe "EditorView", ->
|
||||
setEditorWidthInChars(editorView, maxLineLength)
|
||||
widthBefore = editorView.renderedLines.width()
|
||||
expect(widthBefore).toBe editorView.scrollView.width() + 20
|
||||
buffer.change([[12,0], [12,0]], [1..maxLineLength*2].join(''))
|
||||
buffer.setTextInRange([[12,0], [12,0]], [1..maxLineLength*2].join(''))
|
||||
expect(editorView.renderedLines.width()).toBeGreaterThan widthBefore
|
||||
|
||||
describe "when lines are removed", ->
|
||||
@@ -1399,7 +1487,7 @@ describe "EditorView", ->
|
||||
it "sets the rendered screen line's width to either the max line length or the scollView's width (whichever is greater)", ->
|
||||
maxLineLength = editor.getMaxScreenLineLength()
|
||||
setEditorWidthInChars(editorView, maxLineLength)
|
||||
buffer.change([[12,0], [12,0]], [1..maxLineLength*2].join(''))
|
||||
buffer.setTextInRange([[12,0], [12,0]], [1..maxLineLength*2].join(''))
|
||||
expect(editorView.renderedLines.width()).toBeGreaterThan editorView.scrollView.width()
|
||||
widthBefore = editorView.renderedLines.width()
|
||||
buffer.delete([[12, 0], [12, Infinity]])
|
||||
@@ -1412,7 +1500,7 @@ describe "EditorView", ->
|
||||
expect(editorView.renderedLines.find(".line:first").text()).toBe buffer.lineForRow(6)
|
||||
expect(editorView.renderedLines.find(".line:last").text()).toBe buffer.lineForRow(12)
|
||||
|
||||
buffer.change([[1,0], [2,0]], "")
|
||||
buffer.setTextInRange([[1,0], [2,0]], "")
|
||||
expect(editorView.renderedLines.find(".line").length).toBe 6
|
||||
expect(editorView.renderedLines.find(".line:first").text()).toBe buffer.lineForRow(6)
|
||||
expect(editorView.renderedLines.find(".line:last").text()).toBe buffer.lineForRow(11)
|
||||
@@ -1424,21 +1512,21 @@ describe "EditorView", ->
|
||||
expect(editorView.renderedLines.find(".line:first").text()).toBe buffer.lineForRow(6)
|
||||
expect(editorView.renderedLines.find(".line:last").text()).toBe buffer.lineForRow(12)
|
||||
|
||||
buffer.change([[7,0], [11,0]], "1\n2\n")
|
||||
buffer.setTextInRange([[7,0], [11,0]], "1\n2\n")
|
||||
expect(editorView.renderedLines.find(".line").length).toBe 5
|
||||
expect(editorView.renderedLines.find(".line:first").text()).toBe buffer.lineForRow(6)
|
||||
expect(editorView.renderedLines.find(".line:last").text()).toBe buffer.lineForRow(10)
|
||||
|
||||
describe "when the change straddles the last rendered row", ->
|
||||
it "renders the correct rows", ->
|
||||
buffer.change([[2,0], [7,0]], "")
|
||||
buffer.setTextInRange([[2,0], [7,0]], "")
|
||||
expect(editorView.renderedLines.find(".line").length).toBe 7
|
||||
expect(editorView.renderedLines.find(".line:first").text()).toBe buffer.lineForRow(0)
|
||||
expect(editorView.renderedLines.find(".line:last").text()).toBe buffer.lineForRow(6)
|
||||
|
||||
describe "when the change the follows the last rendered row", ->
|
||||
it "does not change the rendered lines", ->
|
||||
buffer.change([[10,0], [12,0]], "")
|
||||
buffer.setTextInRange([[10,0], [12,0]], "")
|
||||
expect(editorView.renderedLines.find(".line").length).toBe 7
|
||||
expect(editorView.renderedLines.find(".line:first").text()).toBe buffer.lineForRow(0)
|
||||
expect(editorView.renderedLines.find(".line:last").text()).toBe buffer.lineForRow(6)
|
||||
@@ -1496,14 +1584,18 @@ describe "EditorView", ->
|
||||
|
||||
describe "when autoscrolling at the end of the document", ->
|
||||
it "renders lines properly", ->
|
||||
editorView.edit(atom.project.openSync('two-hundred.txt'))
|
||||
editorView.attachToDom(heightInLines: 5.5)
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('two-hundred.txt').then (editor) ->
|
||||
editorView.edit(editor)
|
||||
|
||||
expect(editorView.renderedLines.find('.line').length).toBe 8
|
||||
runs ->
|
||||
editorView.attachToDom(heightInLines: 5.5)
|
||||
|
||||
editor.moveCursorToBottom()
|
||||
expect(editorView.renderedLines.find('.line').length).toBe 8
|
||||
|
||||
expect(editorView.renderedLines.find('.line').length).toBe 8
|
||||
editor.moveCursorToBottom()
|
||||
|
||||
expect(editorView.renderedLines.find('.line').length).toBe 8
|
||||
|
||||
describe "when line has a character that could push it to be too tall (regression)", ->
|
||||
it "does renders the line at a consistent height", ->
|
||||
@@ -1563,7 +1655,7 @@ describe "EditorView", ->
|
||||
editor.setSoftWrap(true)
|
||||
|
||||
it "doesn't show the end of line invisible at the end of lines broken due to wrapping", ->
|
||||
editor.setText "a line that wraps"
|
||||
editor.setText "a line that wraps "
|
||||
editorView.attachToDom()
|
||||
editorView.setWidthInChars(6)
|
||||
atom.config.set "editor.showInvisibles", true
|
||||
@@ -1571,11 +1663,11 @@ describe "EditorView", ->
|
||||
expect(space).toBeTruthy()
|
||||
eol = editorView.invisibles?.eol
|
||||
expect(eol).toBeTruthy()
|
||||
expect(editorView.renderedLines.find('.line:first').text()).toBe "a line#{space}"
|
||||
expect(editorView.renderedLines.find('.line:last').text()).toBe "wraps#{eol}"
|
||||
expect(editorView.renderedLines.find('.line:first').text()).toBe "a line "
|
||||
expect(editorView.renderedLines.find('.line:last').text()).toBe "wraps#{space}#{eol}"
|
||||
|
||||
it "displays trailing carriage return using a visible non-empty value", ->
|
||||
editor.setText "a line that\r\n"
|
||||
editor.setText "a line that \r\n"
|
||||
editorView.attachToDom()
|
||||
editorView.setWidthInChars(6)
|
||||
atom.config.set "editor.showInvisibles", true
|
||||
@@ -1585,8 +1677,8 @@ describe "EditorView", ->
|
||||
expect(cr).toBeTruthy()
|
||||
eol = editorView.invisibles?.eol
|
||||
expect(eol).toBeTruthy()
|
||||
expect(editorView.renderedLines.find('.line:first').text()).toBe "a line#{space}"
|
||||
expect(editorView.renderedLines.find('.line:eq(1)').text()).toBe "that#{cr}#{eol}"
|
||||
expect(editorView.renderedLines.find('.line:first').text()).toBe "a line "
|
||||
expect(editorView.renderedLines.find('.line:eq(1)').text()).toBe "that#{space}#{cr}#{eol}"
|
||||
expect(editorView.renderedLines.find('.line:last').text()).toBe "#{eol}"
|
||||
|
||||
describe "when editor.showIndentGuide is set to true", ->
|
||||
@@ -1751,10 +1843,14 @@ describe "EditorView", ->
|
||||
expect(editor.bufferPositionForScreenPosition(editor.getCursorScreenPosition())).toEqual [3, 60]
|
||||
|
||||
it "does not wrap the lines of any newly assigned buffers", ->
|
||||
otherEditor = atom.project.openSync()
|
||||
otherEditor.buffer.setText([1..100].join(''))
|
||||
editorView.edit(otherEditor)
|
||||
expect(editorView.renderedLines.find('.line').length).toBe(1)
|
||||
otherEditor = null
|
||||
waitsForPromise ->
|
||||
atom.workspace.open().then (o) -> otherEditor = o
|
||||
|
||||
runs ->
|
||||
otherEditor.buffer.setText([1..100].join(''))
|
||||
editorView.edit(otherEditor)
|
||||
expect(editorView.renderedLines.find('.line').length).toBe(1)
|
||||
|
||||
it "unwraps lines when softwrap is disabled", ->
|
||||
editorView.toggleSoftWrap()
|
||||
@@ -1783,15 +1879,15 @@ describe "EditorView", ->
|
||||
expect(editor.getCursorScreenPosition()).toEqual [11, 0]
|
||||
|
||||
it "calls .setWidthInChars() when the editor view is attached because now its dimensions are available to calculate it", ->
|
||||
otherEditor = new EditorView(editor: atom.project.openSync('sample.js'))
|
||||
spyOn(otherEditor, 'setWidthInChars')
|
||||
otherEditorView = new EditorView(editor)
|
||||
spyOn(otherEditorView, 'setWidthInChars')
|
||||
|
||||
otherEditor.editor.setSoftWrap(true)
|
||||
expect(otherEditor.setWidthInChars).not.toHaveBeenCalled()
|
||||
otherEditorView.editor.setSoftWrap(true)
|
||||
expect(otherEditorView.setWidthInChars).not.toHaveBeenCalled()
|
||||
|
||||
otherEditor.simulateDomAttachment()
|
||||
expect(otherEditor.setWidthInChars).toHaveBeenCalled()
|
||||
otherEditor.remove()
|
||||
otherEditorView.simulateDomAttachment()
|
||||
expect(otherEditorView.setWidthInChars).toHaveBeenCalled()
|
||||
otherEditorView.remove()
|
||||
|
||||
describe "when the editor view's width changes", ->
|
||||
it "updates the width in characters on the edit session", ->
|
||||
@@ -1806,6 +1902,17 @@ describe "EditorView", ->
|
||||
runs ->
|
||||
expect(editor.getSoftWrapColumn()).toBeLessThan previousSoftWrapColumn
|
||||
|
||||
it "accounts for the width of the scrollbar if there is one", ->
|
||||
# force the scrollbar to always be visible, regardless of OS visibility setting
|
||||
$('#jasmine-content').prepend """
|
||||
<style>
|
||||
::-webkit-scrollbar { width: 15px; }
|
||||
</style>
|
||||
"""
|
||||
setEditorHeightInLines(editorView, 5)
|
||||
setEditorWidthInChars(editorView, 40)
|
||||
expect(editor.lineForScreenRow(2).text.length).toBe 34
|
||||
|
||||
describe "gutter rendering", ->
|
||||
beforeEach ->
|
||||
editorView.attachToDom(heightInLines: 5.5)
|
||||
@@ -1931,15 +2038,19 @@ describe "EditorView", ->
|
||||
|
||||
describe "when the switching from an edit session for a long buffer to an edit session for a short buffer", ->
|
||||
it "updates the line numbers to reflect the shorter buffer", ->
|
||||
emptyEditor = atom.project.openSync(null)
|
||||
editorView.edit(emptyEditor)
|
||||
expect(editorView.gutter.lineNumbers.find('.line-number').length).toBe 1
|
||||
emptyEditor = null
|
||||
waitsForPromise ->
|
||||
atom.workspace.open().then (o) -> emptyEditor = o
|
||||
|
||||
editorView.edit(editor)
|
||||
expect(editorView.gutter.lineNumbers.find('.line-number').length).toBeGreaterThan 1
|
||||
runs ->
|
||||
editorView.edit(emptyEditor)
|
||||
expect(editorView.gutter.lineNumbers.find('.line-number').length).toBe 1
|
||||
|
||||
editorView.edit(emptyEditor)
|
||||
expect(editorView.gutter.lineNumbers.find('.line-number').length).toBe 1
|
||||
editorView.edit(editor)
|
||||
expect(editorView.gutter.lineNumbers.find('.line-number').length).toBeGreaterThan 1
|
||||
|
||||
editorView.edit(emptyEditor)
|
||||
expect(editorView.gutter.lineNumbers.find('.line-number').length).toBe 1
|
||||
|
||||
describe "when the editor view is mini", ->
|
||||
it "hides the gutter", ->
|
||||
@@ -2159,10 +2270,13 @@ describe "EditorView", ->
|
||||
|
||||
describe "folding", ->
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('two-hundred.txt')
|
||||
buffer = editor.buffer
|
||||
editorView.edit(editor)
|
||||
editorView.attachToDom()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('two-hundred.txt').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
buffer = editor.buffer
|
||||
editorView.edit(editor)
|
||||
editorView.attachToDom()
|
||||
|
||||
describe "when a fold-selection event is triggered", ->
|
||||
it "folds the lines covered by the selection into a single line with a fold class and marker", ->
|
||||
@@ -2216,7 +2330,7 @@ describe "EditorView", ->
|
||||
it "adds/removes the 'fold-selected' class to the fold's line element and hides the cursor if it is on the fold line", ->
|
||||
editor.createFold(2, 4)
|
||||
|
||||
editor.setSelectedBufferRange([[1, 0], [2, 0]], preserveFolds: true, isReversed: true)
|
||||
editor.setSelectedBufferRange([[1, 0], [2, 0]], preserveFolds: true, reversed: true)
|
||||
expect(editorView.lineElementForScreenRow(2)).toMatchSelector('.fold.fold-selected')
|
||||
|
||||
editor.setSelectedBufferRange([[1, 0], [1, 1]], preserveFolds: true)
|
||||
@@ -2297,8 +2411,11 @@ describe "EditorView", ->
|
||||
beforeEach ->
|
||||
filePath = atom.project.resolve('git/working-dir/file.txt')
|
||||
originalPathText = fs.readFileSync(filePath, 'utf8')
|
||||
editor = atom.project.openSync(filePath)
|
||||
editorView.edit(editor)
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
editorView.edit(editor)
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(filePath, originalPathText)
|
||||
@@ -2419,51 +2536,11 @@ describe "EditorView", ->
|
||||
editorView.underlayer.trigger event
|
||||
expect(editor.getSelection().getScreenRange()).toEqual [[0,0], [12,2]]
|
||||
|
||||
# TODO: Move to editor-spec
|
||||
describe ".reloadGrammar()", ->
|
||||
[filePath] = []
|
||||
|
||||
beforeEach ->
|
||||
tmpdir = fs.absolute(temp.dir)
|
||||
filePath = path.join(tmpdir, "grammar-change.txt")
|
||||
fs.writeFileSync(filePath, "var i;")
|
||||
|
||||
afterEach ->
|
||||
fs.removeSync(filePath) if fs.existsSync(filePath)
|
||||
|
||||
it "updates all the rendered lines when the grammar changes", ->
|
||||
editor = atom.project.openSync(filePath)
|
||||
editorView.edit(editor)
|
||||
expect(editor.getGrammar().name).toBe 'Plain Text'
|
||||
atom.syntax.setGrammarOverrideForPath(filePath, 'source.js')
|
||||
editor.reloadGrammar()
|
||||
expect(editor.getGrammar().name).toBe 'JavaScript'
|
||||
|
||||
tokenizedBuffer = editorView.editor.displayBuffer.tokenizedBuffer
|
||||
line0 = tokenizedBuffer.lineForScreenRow(0)
|
||||
expect(line0.tokens.length).toBe 3
|
||||
expect(line0.tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.modifier.js'])
|
||||
|
||||
it "doesn't update the rendered lines when the grammar doesn't change", ->
|
||||
expect(editor.getGrammar().name).toBe 'JavaScript'
|
||||
spyOn(editorView, 'updateDisplay').andCallThrough()
|
||||
editor.reloadGrammar()
|
||||
expect(editor.reloadGrammar()).toBeFalsy()
|
||||
expect(editorView.updateDisplay).not.toHaveBeenCalled()
|
||||
expect(editor.getGrammar().name).toBe 'JavaScript'
|
||||
|
||||
it "emits an editor:grammar-changed event when updated", ->
|
||||
editor = atom.project.openSync(filePath)
|
||||
editorView.edit(editor)
|
||||
|
||||
describe "when the editor's grammar is changed", ->
|
||||
it "emits an editor:grammar-changed event", ->
|
||||
eventHandler = jasmine.createSpy('eventHandler')
|
||||
editorView.on('editor:grammar-changed', eventHandler)
|
||||
editor.reloadGrammar()
|
||||
|
||||
expect(eventHandler).not.toHaveBeenCalled()
|
||||
|
||||
atom.syntax.setGrammarOverrideForPath(filePath, 'source.js')
|
||||
editor.reloadGrammar()
|
||||
editor.setGrammar(atom.syntax.selectGrammar('.coffee'))
|
||||
expect(eventHandler).toHaveBeenCalled()
|
||||
|
||||
describe ".replaceSelectedText()", ->
|
||||
@@ -2810,64 +2887,9 @@ describe "EditorView", ->
|
||||
expect(buffer.lineForRow(1)).toBe ' if (items.length <= 1) return items;'
|
||||
expect(buffer.lineForRow(2)).toBe ' var sort = function(items) {'
|
||||
|
||||
describe "when editor:duplicate-line is triggered", ->
|
||||
describe "where there is no selection", ->
|
||||
describe "when the cursor isn't on a folded line", ->
|
||||
it "duplicates the current line below and moves the cursor down one row", ->
|
||||
editor.setCursorBufferPosition([0, 5])
|
||||
editorView.trigger 'editor:duplicate-line'
|
||||
expect(buffer.lineForRow(0)).toBe 'var quicksort = function () {'
|
||||
expect(buffer.lineForRow(1)).toBe 'var quicksort = function () {'
|
||||
expect(editor.getCursorBufferPosition()).toEqual [1, 5]
|
||||
|
||||
describe "when the cursor is on a folded line", ->
|
||||
it "duplicates the entire fold before and moves the cursor to the new fold", ->
|
||||
editor.setCursorBufferPosition([4])
|
||||
editor.foldCurrentRow()
|
||||
editorView.trigger 'editor:duplicate-line'
|
||||
expect(editor.getCursorScreenPosition()).toEqual [5]
|
||||
expect(editor.isFoldedAtScreenRow(4)).toBeTruthy()
|
||||
expect(editor.isFoldedAtScreenRow(5)).toBeTruthy()
|
||||
expect(buffer.lineForRow(8)).toBe ' while(items.length > 0) {'
|
||||
expect(buffer.lineForRow(9)).toBe ' current = items.shift();'
|
||||
expect(buffer.lineForRow(10)).toBe ' current < pivot ? left.push(current) : right.push(current);'
|
||||
expect(buffer.lineForRow(11)).toBe ' }'
|
||||
|
||||
describe "when the cursor is on the last line and it doesn't have a trailing newline", ->
|
||||
it "inserts a newline and the duplicated line", ->
|
||||
editor.moveCursorToBottom()
|
||||
editorView.trigger 'editor:duplicate-line'
|
||||
expect(buffer.lineForRow(12)).toBe '};'
|
||||
expect(buffer.lineForRow(13)).toBe '};'
|
||||
expect(buffer.lineForRow(14)).toBeUndefined()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [13, 2]
|
||||
|
||||
describe "when the cursor in on the last line and it is only a newline", ->
|
||||
it "duplicates the current line below and moves the cursor down one row", ->
|
||||
editor.moveCursorToBottom()
|
||||
editor.insertNewline()
|
||||
editor.moveCursorToBottom()
|
||||
editorView.trigger 'editor:duplicate-line'
|
||||
expect(buffer.lineForRow(13)).toBe ''
|
||||
expect(buffer.lineForRow(14)).toBe ''
|
||||
expect(buffer.lineForRow(15)).toBeUndefined()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [14, 0]
|
||||
|
||||
describe "when the cursor is on the second to last line and the last line only a newline", ->
|
||||
it "duplicates the current line below and moves the cursor down one row", ->
|
||||
editor.moveCursorToBottom()
|
||||
editor.insertNewline()
|
||||
editor.setCursorBufferPosition([12])
|
||||
editorView.trigger 'editor:duplicate-line'
|
||||
expect(buffer.lineForRow(12)).toBe '};'
|
||||
expect(buffer.lineForRow(13)).toBe '};'
|
||||
expect(buffer.lineForRow(14)).toBe ''
|
||||
expect(buffer.lineForRow(15)).toBeUndefined()
|
||||
expect(editor.getCursorBufferPosition()).toEqual [13, 0]
|
||||
|
||||
describe "when the escape key is pressed on the editor view", ->
|
||||
it "clears multiple selections if there are any, and otherwise allows other bindings to be handled", ->
|
||||
atom.keymap.bindKeys 'name', '.editor', {'escape': 'test-event'}
|
||||
atom.keymaps.add 'name', '.editor': {'escape': 'test-event'}
|
||||
testEventHandler = jasmine.createSpy("testEventHandler")
|
||||
|
||||
editorView.on 'test-event', testEventHandler
|
||||
@@ -2884,22 +2906,26 @@ describe "EditorView", ->
|
||||
describe "when the editor view is attached but invisible", ->
|
||||
describe "when the editor view's text is changed", ->
|
||||
it "redraws the editor view when it is next shown", ->
|
||||
displayUpdatedHandler = null
|
||||
atom.workspaceView = new WorkspaceView
|
||||
atom.workspaceView.openSync('sample.js')
|
||||
atom.workspaceView.attachToDom()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
waitsForPromise ->
|
||||
atom.workspaceView.open('sample.txt').then (o) -> editor = o
|
||||
|
||||
view = $$ -> @div id: 'view', tabindex: -1, 'View'
|
||||
editorView.getPane().activateItem(view)
|
||||
expect(editorView.isVisible()).toBeFalsy()
|
||||
runs ->
|
||||
atom.workspaceView.attachToDom()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
|
||||
editor.setText('hidden changes')
|
||||
editor.setCursorBufferPosition([0,4])
|
||||
view = $$ -> @div id: 'view', tabindex: -1, 'View'
|
||||
editorView.getPane().activateItem(view)
|
||||
expect(editorView.isVisible()).toBeFalsy()
|
||||
|
||||
displayUpdatedHandler = jasmine.createSpy("displayUpdatedHandler")
|
||||
editorView.on 'editor:display-updated', displayUpdatedHandler
|
||||
editorView.getPane().activateItem(editorView.getModel())
|
||||
expect(editorView.isVisible()).toBeTruthy()
|
||||
editor.setText('hidden changes')
|
||||
editor.setCursorBufferPosition([0,4])
|
||||
|
||||
displayUpdatedHandler = jasmine.createSpy("displayUpdatedHandler")
|
||||
editorView.on 'editor:display-updated', displayUpdatedHandler
|
||||
editorView.getPane().activateItem(editorView.getModel())
|
||||
expect(editorView.isVisible()).toBeTruthy()
|
||||
|
||||
waitsFor ->
|
||||
displayUpdatedHandler.callCount is 1
|
||||
@@ -2939,14 +2965,17 @@ describe "EditorView", ->
|
||||
describe "when the editor view is removed", ->
|
||||
it "fires a editor:will-be-removed event", ->
|
||||
atom.workspaceView = new WorkspaceView
|
||||
atom.workspaceView.openSync('sample.js')
|
||||
atom.workspaceView.attachToDom()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js')
|
||||
|
||||
willBeRemovedHandler = jasmine.createSpy('willBeRemovedHandler')
|
||||
editorView.on 'editor:will-be-removed', willBeRemovedHandler
|
||||
editorView.getPane().destroyActiveItem()
|
||||
expect(willBeRemovedHandler).toHaveBeenCalled()
|
||||
runs ->
|
||||
atom.workspaceView.attachToDom()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
|
||||
willBeRemovedHandler = jasmine.createSpy('willBeRemovedHandler')
|
||||
editorView.on 'editor:will-be-removed', willBeRemovedHandler
|
||||
editorView.getPane().destroyActiveItem()
|
||||
expect(willBeRemovedHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when setInvisibles is toggled (regression)", ->
|
||||
it "renders inserted newlines properly", ->
|
||||
@@ -2959,6 +2988,20 @@ describe "EditorView", ->
|
||||
for rowNumber in [1..5]
|
||||
expect(editorView.lineElementForScreenRow(rowNumber).text()).toBe buffer.lineForRow(rowNumber)
|
||||
|
||||
it "correctly calculates the position left for non-monospaced invisibles", ->
|
||||
editorView.setShowInvisibles(true)
|
||||
editorView.setInvisibles tab: '♘'
|
||||
editor.setText('\tx')
|
||||
|
||||
editorView.setFontFamily('serif')
|
||||
editorView.setFontSize(10)
|
||||
editorView.attachToDom()
|
||||
editorView.setWidthInChars(5)
|
||||
|
||||
expect(editorView.pixelPositionForScreenPosition([0, 0]).left).toEqual 0
|
||||
expect(editorView.pixelPositionForScreenPosition([0, 1]).left).toEqual 10
|
||||
expect(editorView.pixelPositionForScreenPosition([0, 2]).left).toEqual 13
|
||||
|
||||
describe "when the window is resized", ->
|
||||
it "updates the active edit session with the current soft wrap column", ->
|
||||
editorView.attachToDom()
|
||||
@@ -2987,6 +3030,30 @@ describe "EditorView", ->
|
||||
editorView.pixelPositionForScreenPosition([1, 5])
|
||||
expect(editorView.measureToColumn.callCount).toBe 0
|
||||
|
||||
describe "when stylesheets are changed", ->
|
||||
afterEach ->
|
||||
atom.themes.removeStylesheet 'line-height'
|
||||
atom.themes.removeStylesheet 'char-width'
|
||||
|
||||
it "updates the editor if the line height or character width changes due to a stylesheet change", ->
|
||||
editorView.attachToDom()
|
||||
editor.setCursorScreenPosition([1, 3])
|
||||
expect(editorView.pixelPositionForScreenPosition([1, 3])).toEqual {top: 20, left: 30}
|
||||
expect(editorView.getCursorView().position()).toEqual {top: 20, left: 30}
|
||||
|
||||
atom.themes.applyStylesheet 'line-height', """
|
||||
.editor { line-height: 2; }
|
||||
"""
|
||||
|
||||
expect(editorView.pixelPositionForScreenPosition([1, 3])).toEqual {top: 20, left: 30}
|
||||
expect(editorView.getCursorView().position()).toEqual {top: 20, left: 30}
|
||||
|
||||
atom.themes.applyStylesheet 'char-width', """
|
||||
.editor { letter-spacing: 2px; }
|
||||
"""
|
||||
expect(editorView.pixelPositionForScreenPosition([1, 3])).toEqual {top: 20, left: 36}
|
||||
expect(editorView.getCursorView().position()).toEqual {top: 20, left: 36}
|
||||
|
||||
describe "when the editor contains hard tabs", ->
|
||||
it "correctly calculates the the position left for a column", ->
|
||||
editor.setText('\ttest')
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = activate: ->
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "no events",
|
||||
"version": "0.1.0",
|
||||
"activationEvents": []
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Look, this is a comment. Don't go making assumtions that I want soft tabs
|
||||
* because this block comment has leading spaces, Geez.
|
||||
*/
|
||||
|
||||
if (beNice) {
|
||||
console.log('Thank you for being nice.');
|
||||
}
|
||||
+33
-10
@@ -1,6 +1,6 @@
|
||||
temp = require 'temp'
|
||||
Git = require '../src/git'
|
||||
{fs} = require 'atom'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
Task = require '../src/task'
|
||||
|
||||
@@ -177,6 +177,24 @@ describe "Git", ->
|
||||
status = repo.getPathStatus(filePath)
|
||||
expect(statusHandler.callCount).toBe 1
|
||||
|
||||
describe ".getDirectoryStatus(path)", ->
|
||||
[directoryPath, filePath, originalPathText] = []
|
||||
|
||||
beforeEach ->
|
||||
repo = new Git(path.join(__dirname, 'fixtures', 'git', 'working-dir'))
|
||||
directoryPath = path.join(__dirname, 'fixtures', 'git', 'working-dir', 'dir')
|
||||
filePath = require.resolve('./fixtures/git/working-dir/dir/b.txt')
|
||||
originalPathText = fs.readFileSync(filePath, 'utf8')
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(filePath, originalPathText)
|
||||
|
||||
it "gets the status based on the files inside the directory", ->
|
||||
expect(repo.isStatusModified(repo.getDirectoryStatus(directoryPath))).toBe false
|
||||
fs.writeFileSync(filePath, 'abc')
|
||||
repo.getPathStatus(filePath)
|
||||
expect(repo.isStatusModified(repo.getDirectoryStatus(directoryPath))).toBe true
|
||||
|
||||
describe ".refreshStatus()", ->
|
||||
[newPath, modifiedPath, cleanPath, originalModifiedPathText] = []
|
||||
|
||||
@@ -203,17 +221,19 @@ describe "Git", ->
|
||||
statusHandler.callCount > 0
|
||||
|
||||
runs ->
|
||||
statuses = repo.statuses
|
||||
expect(statuses[cleanPath]).toBeUndefined()
|
||||
expect(repo.isStatusNew(statuses[newPath])).toBeTruthy()
|
||||
expect(repo.isStatusModified(statuses[modifiedPath])).toBeTruthy()
|
||||
expect(repo.getCachedPathStatus(cleanPath)).toBeUndefined()
|
||||
expect(repo.isStatusNew(repo.getCachedPathStatus(newPath))).toBeTruthy()
|
||||
expect(repo.isStatusModified(repo.getCachedPathStatus(modifiedPath))).toBeTruthy()
|
||||
|
||||
describe "buffer events", ->
|
||||
[originalContent, editor] = []
|
||||
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
originalContent = editor.getText()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
originalContent = editor.getText()
|
||||
|
||||
afterEach ->
|
||||
fs.writeFileSync(editor.getPath(), originalContent)
|
||||
@@ -257,9 +277,12 @@ describe "Git", ->
|
||||
project2?.destroy()
|
||||
|
||||
it "subscribes to all the serialized buffers in the project", ->
|
||||
atom.project.openSync('sample.js')
|
||||
project2 = atom.project.testSerialization()
|
||||
buffer = project2.getBuffers()[0]
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js')
|
||||
|
||||
runs ->
|
||||
project2 = atom.project.testSerialization()
|
||||
buffer = project2.getBuffers()[0]
|
||||
|
||||
waitsFor ->
|
||||
buffer.loaded
|
||||
|
||||
@@ -1,414 +0,0 @@
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
Keymap = require '../src/keymap'
|
||||
{$, $$, WorkspaceView} = require 'atom'
|
||||
|
||||
describe "Keymap", ->
|
||||
fragment = null
|
||||
keymap = null
|
||||
resourcePath = atom.getLoadSettings().resourcePath
|
||||
configDirPath = null
|
||||
|
||||
beforeEach ->
|
||||
configDirPath = temp.mkdirSync('atom')
|
||||
keymap = new Keymap({configDirPath, resourcePath})
|
||||
fragment = $ """
|
||||
<div class="command-mode">
|
||||
<div class="child-node">
|
||||
<div class="grandchild-node"/>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
afterEach ->
|
||||
keymap.destroy()
|
||||
|
||||
describe ".handleKeyEvent(event)", ->
|
||||
deleteCharHandler = null
|
||||
insertCharHandler = null
|
||||
commandZHandler = null
|
||||
|
||||
beforeEach ->
|
||||
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
|
||||
fragment.on 'commandZPressed', commandZHandler
|
||||
|
||||
describe "when no binding matches the event's keystroke", ->
|
||||
it "does not return false so the event continues to propagate", ->
|
||||
expect(keymap.handleKeyEvent(keydownEvent('0', target: fragment[0]))).not.toBe false
|
||||
|
||||
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, 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", ->
|
||||
it "triggers the command event associated with that binding on the target node and returns false", ->
|
||||
result = keymap.handleKeyEvent(keydownEvent('x', target: fragment[0]))
|
||||
expect(result).toBe(false)
|
||||
expect(deleteCharHandler).toHaveBeenCalled()
|
||||
expect(insertCharHandler).not.toHaveBeenCalled()
|
||||
|
||||
deleteCharHandler.reset()
|
||||
fragment.removeClass('command-mode').addClass('insert-mode')
|
||||
|
||||
event = keydownEvent('x', target: fragment[0])
|
||||
keymap.handleKeyEvent(event)
|
||||
expect(deleteCharHandler).not.toHaveBeenCalled()
|
||||
expect(insertCharHandler).toHaveBeenCalled()
|
||||
|
||||
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", ->
|
||||
target = fragment.find('.child-node')[0]
|
||||
result = keymap.handleKeyEvent(keydownEvent('x', target: target))
|
||||
expect(result).toBe(false)
|
||||
expect(deleteCharHandler).toHaveBeenCalled()
|
||||
expect(insertCharHandler).not.toHaveBeenCalled()
|
||||
|
||||
deleteCharHandler.reset()
|
||||
fragment.removeClass('command-mode').addClass('insert-mode')
|
||||
|
||||
keymap.handleKeyEvent(keydownEvent('x', target: target))
|
||||
expect(deleteCharHandler).not.toHaveBeenCalled()
|
||||
expect(insertCharHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when the event's target node descends from multiple nodes that match selectors with a binding", ->
|
||||
beforeEach ->
|
||||
keymap.bindKeys 'name', '.child-node', 'x': 'foo'
|
||||
|
||||
it "only triggers bindings on selectors associated with the closest ancestor node", ->
|
||||
fooHandler = jasmine.createSpy 'fooHandler'
|
||||
fragment.on 'foo', fooHandler
|
||||
|
||||
target = fragment.find('.grandchild-node')[0]
|
||||
keymap.handleKeyEvent(keydownEvent('x', target: target))
|
||||
expect(fooHandler).toHaveBeenCalled()
|
||||
expect(deleteCharHandler).not.toHaveBeenCalled()
|
||||
expect(insertCharHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe "when 'abortKeyBinding' is called on the triggered event", ->
|
||||
[fooHandler1, fooHandler2] = []
|
||||
|
||||
beforeEach ->
|
||||
fooHandler1 = jasmine.createSpy('fooHandler1').andCallFake (e) ->
|
||||
expect(deleteCharHandler).not.toHaveBeenCalled()
|
||||
e.abortKeyBinding()
|
||||
fooHandler2 = jasmine.createSpy('fooHandler2')
|
||||
|
||||
fragment.find('.child-node').on 'foo', fooHandler1
|
||||
fragment.on 'foo', fooHandler2
|
||||
|
||||
it "aborts the current event and tries again with the next-most-specific key binding", ->
|
||||
target = fragment.find('.grandchild-node')[0]
|
||||
keymap.handleKeyEvent(keydownEvent('x', target: target))
|
||||
expect(fooHandler1).toHaveBeenCalled()
|
||||
expect(fooHandler2).not.toHaveBeenCalled()
|
||||
expect(deleteCharHandler).toHaveBeenCalled()
|
||||
|
||||
it "does not throw an exception if the event was not triggered by the keymap", ->
|
||||
fragment.find('.grandchild-node').trigger 'foo'
|
||||
|
||||
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 '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'
|
||||
bazHandler = jasmine.createSpy 'bazHandler'
|
||||
fragment.on 'foo', fooHandler
|
||||
fragment.on 'bar', barHandler
|
||||
fragment.on 'baz', bazHandler
|
||||
|
||||
target = fragment.find('.grandchild-node')[0]
|
||||
keymap.handleKeyEvent(keydownEvent('x', target: target))
|
||||
|
||||
expect(fooHandler).not.toHaveBeenCalled()
|
||||
expect(barHandler).not.toHaveBeenCalled()
|
||||
expect(bazHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when the matching selectors have the same specificity", ->
|
||||
it "triggers the bindings for the most recently declared selector", ->
|
||||
keymap.bindKeys 'name', '.child-node', 'x': 'foo', 'y': 'baz'
|
||||
keymap.bindKeys 'name', '.child-node', 'x': 'bar'
|
||||
|
||||
fooHandler = jasmine.createSpy 'fooHandler'
|
||||
barHandler = jasmine.createSpy 'barHandler'
|
||||
bazHandler = jasmine.createSpy 'bazHandler'
|
||||
fragment.on 'foo', fooHandler
|
||||
fragment.on 'bar', barHandler
|
||||
fragment.on 'baz', bazHandler
|
||||
|
||||
target = fragment.find('.grandchild-node')[0]
|
||||
keymap.handleKeyEvent(keydownEvent('x', target: target))
|
||||
|
||||
expect(barHandler).toHaveBeenCalled()
|
||||
expect(fooHandler).not.toHaveBeenCalled()
|
||||
|
||||
keymap.handleKeyEvent(keydownEvent('y', target: target))
|
||||
expect(bazHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when the event's target is the document body", ->
|
||||
it "triggers the mapped event on the workspaceView", ->
|
||||
atom.workspaceView = new WorkspaceView
|
||||
atom.workspaceView.attachToDom()
|
||||
keymap.bindKeys 'name', 'body', 'x': 'foo'
|
||||
fooHandler = jasmine.createSpy("fooHandler")
|
||||
atom.workspaceView.on 'foo', fooHandler
|
||||
|
||||
result = keymap.handleKeyEvent(keydownEvent('x', target: document.body))
|
||||
expect(result).toBe(false)
|
||||
expect(fooHandler).toHaveBeenCalled()
|
||||
expect(deleteCharHandler).not.toHaveBeenCalled()
|
||||
expect(insertCharHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe "when the event matches a 'native!' binding", ->
|
||||
it "returns true, allowing the browser's native key handling to process the event", ->
|
||||
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
|
||||
expect(nativeHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe "when at least one binding partially matches the event's keystroke", ->
|
||||
[quitHandler, closeOtherWindowsHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
keymap.bindKeys 'name', "*",
|
||||
'ctrl-x ctrl-c': 'quit'
|
||||
'ctrl-x 1': 'close-other-windows'
|
||||
|
||||
quitHandler = jasmine.createSpy('quitHandler')
|
||||
closeOtherWindowsHandler = jasmine.createSpy('closeOtherWindowsHandler')
|
||||
fragment.on 'quit', quitHandler
|
||||
fragment.on 'close-other-windows', closeOtherWindowsHandler
|
||||
|
||||
it "only matches entire keystroke patterns", ->
|
||||
expect(keymap.handleKeyEvent(keydownEvent('c', target: fragment[0]))).not.toBe false
|
||||
|
||||
describe "when the event's target node matches a selector with a partially matching multi-stroke binding", ->
|
||||
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()
|
||||
expect(closeOtherWindowsHandler).not.toHaveBeenCalled()
|
||||
quitHandler.reset()
|
||||
|
||||
expect(keymap.handleKeyEvent(keydownEvent('x', target: fragment[0], ctrlKey: true))).toBeFalsy()
|
||||
expect(keymap.handleKeyEvent(keydownEvent('1', target: fragment[0]))).toBeFalsy()
|
||||
|
||||
expect(quitHandler).not.toHaveBeenCalled()
|
||||
expect(closeOtherWindowsHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when a second keystroke added to the first doesn't match any bindings", ->
|
||||
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()
|
||||
expect(closeOtherWindowsHandler).not.toHaveBeenCalled()
|
||||
|
||||
expect(keymap.handleKeyEvent(keydownEvent('c', target: fragment[0]))).not.toBe false
|
||||
|
||||
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 '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
|
||||
|
||||
expect(keymap.handleKeyEvent(keydownEvent('x', target: grandchildNode, ctrlKey: true))).toBeFalsy()
|
||||
expect(keymap.handleKeyEvent(keydownEvent('1', target: grandchildNode))).toBeFalsy()
|
||||
expect(quitHandler).not.toHaveBeenCalled()
|
||||
expect(moreSpecificQuitHandler).not.toHaveBeenCalled()
|
||||
expect(closeOtherWindowsHandler).toHaveBeenCalled()
|
||||
closeOtherWindowsHandler.reset()
|
||||
|
||||
expect(keymap.handleKeyEvent(keydownEvent('x', target: grandchildNode, ctrlKey: true))).toBeFalsy()
|
||||
expect(keymap.handleKeyEvent(keydownEvent('c', target: grandchildNode, ctrlKey: true))).toBeFalsy()
|
||||
expect(quitHandler).not.toHaveBeenCalled()
|
||||
expect(closeOtherWindowsHandler).not.toHaveBeenCalled()
|
||||
expect(moreSpecificQuitHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when there is a complete binding with a less specific selector", ->
|
||||
it "favors the more specific partial match", ->
|
||||
|
||||
describe "when there is a complete binding with a more specific selector", ->
|
||||
it "favors the more specific complete match", ->
|
||||
|
||||
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 '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 'name', '*', 'ctrl-alt--': 'foo'
|
||||
result = keymap.handleKeyEvent(keydownEvent('-', ctrlKey: true, altKey: true, target: fragment[0]))
|
||||
expect(result).toBe(false)
|
||||
expect(fooHandler).toHaveBeenCalled()
|
||||
|
||||
describe ".remove(name)", ->
|
||||
it "removes the binding set with the given selector and bindings", ->
|
||||
keymap.add 'nature',
|
||||
'.green':
|
||||
'ctrl-c': 'cultivate'
|
||||
'.brown':
|
||||
'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.keyBindingsMatchingElement($$ -> @div class: 'green')).toHaveLength 1
|
||||
expect(keymap.keyBindingsMatchingElement($$ -> @div class: 'brown')).toEqual []
|
||||
|
||||
describe ".keystrokeStringForEvent(event)", ->
|
||||
describe "when no modifiers are pressed", ->
|
||||
it "returns a string that identifies the key pressed", ->
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('a'))).toBe 'a'
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('['))).toBe '['
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('*'))).toBe '*'
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('left'))).toBe 'left'
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('\b'))).toBe 'backspace'
|
||||
|
||||
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 'cmd-['
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('*', ctrlKey: true))).toBe 'ctrl-*'
|
||||
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", ->
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('A', shiftKey: true))).toBe 'A'
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('{', shiftKey: true))).toBe '{'
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('left', shiftKey: true))).toBe 'shift-left'
|
||||
expect(keymap.keystrokeStringForEvent(keydownEvent('Left', shiftKey: true))).toBe 'shift-left'
|
||||
|
||||
describe ".keyBindingsMatchingElement(element)", ->
|
||||
it "returns the matching bindings for the element", ->
|
||||
keymap.bindKeys 'name', '.command-mode', 'c': 'c'
|
||||
keymap.bindKeys 'name', '.grandchild-node', 'g': '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 '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.keyBindingsMatchingElement(fragment.find('.grandchild-node'))
|
||||
expect(bindings).toHaveLength 3
|
||||
expect(bindings[0].command).toEqual "cmd-and-grandchild-node"
|
||||
|
||||
describe ".keyBindingsForCommandMatchingElement(element)", ->
|
||||
beforeEach ->
|
||||
keymap.add 'nature',
|
||||
'.green':
|
||||
'ctrl-c': 'cultivate'
|
||||
'.green-2':
|
||||
'ctrl-o': 'cultivate'
|
||||
'.brown':
|
||||
'ctrl-h': 'harvest'
|
||||
'.blue':
|
||||
'ctrl-c': 'fly'
|
||||
|
||||
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"
|
||||
|
||||
it "no keymap an element without that map", ->
|
||||
el = $$ -> @div class: 'brown'
|
||||
bindings = keymap.keyBindingsForCommandMatchingElement('cultivate', el)
|
||||
expect(bindings).toHaveLength 0
|
||||
|
||||
describe "loading platform specific keybindings", ->
|
||||
customKeymap = null
|
||||
|
||||
beforeEach ->
|
||||
resourcePath = temp.mkdirSync('atom')
|
||||
customKeymap = new Keymap({configDirPath, resourcePath})
|
||||
|
||||
afterEach ->
|
||||
customKeymap.destroy()
|
||||
|
||||
it "doesn't load keybindings from other platforms", ->
|
||||
win32FilePath = path.join(resourcePath, "keymaps", "win32.cson")
|
||||
darwinFilePath = path.join(resourcePath, "keymaps", "darwin.cson")
|
||||
fs.writeFileSync(win32FilePath, '"body": "ctrl-l": "core:win32-move-left"')
|
||||
fs.writeFileSync(darwinFilePath, '"body": "ctrl-l": "core:darwin-move-left"')
|
||||
|
||||
customKeymap.loadBundledKeymaps()
|
||||
keyBindings = customKeymap.keyBindingsForKeystroke('ctrl-l')
|
||||
expect(keyBindings).toHaveLength 1
|
||||
expect(keyBindings[0].command).toBe "core:#{process.platform}-move-left"
|
||||
|
||||
describe "when the user keymap file is changed", ->
|
||||
it "is reloaded", ->
|
||||
keymapFilePath = path.join(configDirPath, "keymap.cson")
|
||||
fs.writeFileSync(keymapFilePath, '"body": "ctrl-l": "core:move-left"')
|
||||
keymap.loadUserKeymap()
|
||||
|
||||
spyOn(keymap, 'loadUserKeymap').andCallThrough()
|
||||
fs.writeFileSync(keymapFilePath, "'body': 'ctrl-l': 'core:move-right'")
|
||||
|
||||
waitsFor ->
|
||||
keymap.loadUserKeymap.callCount > 0
|
||||
|
||||
runs ->
|
||||
keyBinding = keymap.keyBindingsForKeystroke('ctrl-l')[0]
|
||||
expect(keyBinding.command).toBe 'core:move-right'
|
||||
keymap.loadUserKeymap.reset()
|
||||
fs.removeSync(keymapFilePath)
|
||||
|
||||
waitsFor ->
|
||||
keymap.loadUserKeymap.callCount > 0
|
||||
|
||||
runs ->
|
||||
keyBinding = keymap.keyBindingsForKeystroke('ctrl-l')[0]
|
||||
expect(keyBinding).toBeUndefined()
|
||||
|
||||
describe "when adding a binding with an invalid selector", ->
|
||||
it "logs a warning and does not add it", ->
|
||||
spyOn(console, 'warn')
|
||||
keybinding =
|
||||
'##selector':
|
||||
'cmd-a': 'invalid-command'
|
||||
keymap.add('test', keybinding)
|
||||
|
||||
expect(console.warn.callCount).toBe 1
|
||||
expect(console.warn.argsForCall[0][0].length).toBeGreaterThan 0
|
||||
expect(-> keymap.keyBindingsMatchingElement(document.body)).not.toThrow()
|
||||
expect(keymap.keyBindingsForCommand('invalid:command')).toEqual []
|
||||
@@ -6,8 +6,10 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "javascript", ->
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js', autoIndent: false).then (o) ->
|
||||
editor = o
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
@@ -46,6 +48,16 @@ describe "LanguageMode", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 0)
|
||||
expect(buffer.lineForRow(0)).toBe " // var i;"
|
||||
|
||||
buffer.setText(' ')
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 0)
|
||||
expect(buffer.lineForRow(0)).toBe " // "
|
||||
|
||||
buffer.setText (' a\n \n b')
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 2)
|
||||
expect(buffer.lineForRow(0)).toBe " // a"
|
||||
expect(buffer.lineForRow(1)).toBe " // "
|
||||
expect(buffer.lineForRow(2)).toBe " // b"
|
||||
|
||||
describe ".rowRangeForCodeFoldAtBufferRow(bufferRow)", ->
|
||||
it "returns the start/end rows of the foldable region starting at the given row", ->
|
||||
expect(languageMode.rowRangeForCodeFoldAtBufferRow(0)).toEqual [0, 12]
|
||||
@@ -102,8 +114,10 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "coffeescript", ->
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('coffee.coffee', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('coffee.coffee', autoIndent: false).then (o) ->
|
||||
editor = o
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-coffee-script')
|
||||
@@ -151,8 +165,10 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "css", ->
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('css.css', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('css.css', autoIndent: false).then (o) ->
|
||||
editor = o
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-css')
|
||||
@@ -168,34 +184,36 @@ describe "LanguageMode", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(2, 2)
|
||||
expect(buffer.lineForRow(0)).toBe "/*body {"
|
||||
expect(buffer.lineForRow(1)).toBe " font-size: 1234px;*/"
|
||||
expect(buffer.lineForRow(2)).toBe "/* width: 110%;*/"
|
||||
expect(buffer.lineForRow(2)).toBe " /*width: 110%;*/"
|
||||
expect(buffer.lineForRow(3)).toBe " font-weight: bold !important;"
|
||||
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 1)
|
||||
expect(buffer.lineForRow(0)).toBe "body {"
|
||||
expect(buffer.lineForRow(1)).toBe " font-size: 1234px;"
|
||||
expect(buffer.lineForRow(2)).toBe "/* width: 110%;*/"
|
||||
expect(buffer.lineForRow(2)).toBe " /*width: 110%;*/"
|
||||
expect(buffer.lineForRow(3)).toBe " font-weight: bold !important;"
|
||||
|
||||
it "uncomments lines with leading whitespace", ->
|
||||
buffer.change([[2, 0], [2, Infinity]], " /*width: 110%;*/")
|
||||
buffer.setTextInRange([[2, 0], [2, Infinity]], " /*width: 110%;*/")
|
||||
languageMode.toggleLineCommentsForBufferRows(2, 2)
|
||||
expect(buffer.lineForRow(2)).toBe " width: 110%;"
|
||||
|
||||
it "uncomments lines with trailing whitespace", ->
|
||||
buffer.change([[2, 0], [2, Infinity]], "/*width: 110%;*/ ")
|
||||
buffer.setTextInRange([[2, 0], [2, Infinity]], "/*width: 110%;*/ ")
|
||||
languageMode.toggleLineCommentsForBufferRows(2, 2)
|
||||
expect(buffer.lineForRow(2)).toBe "width: 110%; "
|
||||
|
||||
it "uncomments lines with leading and trailing whitespace", ->
|
||||
buffer.change([[2, 0], [2, Infinity]], " /*width: 110%;*/ ")
|
||||
buffer.setTextInRange([[2, 0], [2, Infinity]], " /*width: 110%;*/ ")
|
||||
languageMode.toggleLineCommentsForBufferRows(2, 2)
|
||||
expect(buffer.lineForRow(2)).toBe " width: 110%; "
|
||||
|
||||
describe "less", ->
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('sample.less', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.less', autoIndent: false).then (o) ->
|
||||
editor = o
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-less')
|
||||
@@ -208,10 +226,28 @@ describe "LanguageMode", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 0)
|
||||
expect(buffer.lineForRow(0)).toBe "// @color: #4D926F;"
|
||||
|
||||
describe "xml", ->
|
||||
beforeEach ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.xml', autoIndent: false).then (o) ->
|
||||
editor = o
|
||||
editor.setText("<!-- test -->")
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-xml')
|
||||
|
||||
describe "when uncommenting lines", ->
|
||||
it "removes the leading whitespace from the comment end pattern match", ->
|
||||
languageMode.toggleLineCommentsForBufferRows(0, 0)
|
||||
expect(buffer.lineForRow(0)).toBe "test"
|
||||
|
||||
describe "folding", ->
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('sample.js', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js', autoIndent: false).then (o) ->
|
||||
editor = o
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
@@ -285,21 +321,6 @@ describe "LanguageMode", ->
|
||||
expect(fold.getStartRow()).toBe 0
|
||||
expect(fold.getEndRow()).toBe 13
|
||||
|
||||
describe ".unfoldBufferRow(bufferRow)", ->
|
||||
describe "when bufferRow can be unfolded", ->
|
||||
it "destroys a fold based on the syntactic region starting at the given row", ->
|
||||
languageMode.foldBufferRow(1)
|
||||
expect(editor.lineForScreenRow(1).fold).toBeDefined()
|
||||
|
||||
languageMode.unfoldBufferRow(1)
|
||||
expect(editor.lineForScreenRow(1).fold).toBeUndefined()
|
||||
|
||||
describe "when bufferRow can't be unfolded", ->
|
||||
it "does not throw an error", ->
|
||||
expect(editor.lineForScreenRow(1).fold).toBeUndefined()
|
||||
languageMode.unfoldBufferRow(1)
|
||||
expect(editor.lineForScreenRow(1).fold).toBeUndefined()
|
||||
|
||||
describe ".isFoldableAtBufferRow(bufferRow)", ->
|
||||
it "returns true if the line starts a foldable row range", ->
|
||||
expect(languageMode.isFoldableAtBufferRow(0)).toBe true
|
||||
@@ -310,8 +331,10 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "folding with comments", ->
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('sample-with-comments.js', autoIndent: false)
|
||||
{buffer, languageMode} = editor
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample-with-comments.js', autoIndent: false).then (o) ->
|
||||
editor = o
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
@@ -376,7 +399,10 @@ describe "LanguageMode", ->
|
||||
|
||||
describe "css", ->
|
||||
beforeEach ->
|
||||
editor = atom.project.openSync('css.css', autoIndent: true)
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('css.css', autoIndent: true).then (o) ->
|
||||
editor = o
|
||||
{buffer, languageMode} = editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-source')
|
||||
|
||||
@@ -2,7 +2,7 @@ path = require 'path'
|
||||
temp = require 'temp'
|
||||
PaneContainerView = require '../src/pane-container-view'
|
||||
PaneView = require '../src/pane-view'
|
||||
{_, $, View, $$} = require 'atom'
|
||||
{$, View, $$} = require 'atom'
|
||||
|
||||
describe "PaneContainerView", ->
|
||||
[TestView, container, pane1, pane2, pane3] = []
|
||||
@@ -27,7 +27,7 @@ describe "PaneContainerView", ->
|
||||
afterEach ->
|
||||
atom.deserializers.remove(TestView)
|
||||
|
||||
describe ".getActivePane()", ->
|
||||
describe ".getActivePaneView()", ->
|
||||
it "returns the most-recently focused pane", ->
|
||||
focusStealer = $$ -> @div tabindex: -1, "focus stealer"
|
||||
focusStealer.attachToDom()
|
||||
@@ -35,20 +35,20 @@ describe "PaneContainerView", ->
|
||||
|
||||
pane2.focus()
|
||||
expect(container.getFocusedPane()).toBe pane2
|
||||
expect(container.getActivePane()).toBe pane2
|
||||
expect(container.getActivePaneView()).toBe pane2
|
||||
|
||||
focusStealer.focus()
|
||||
expect(container.getFocusedPane()).toBeUndefined()
|
||||
expect(container.getActivePane()).toBe pane2
|
||||
expect(container.getActivePaneView()).toBe pane2
|
||||
|
||||
pane3.focus()
|
||||
expect(container.getFocusedPane()).toBe pane3
|
||||
expect(container.getActivePane()).toBe pane3
|
||||
expect(container.getActivePaneView()).toBe pane3
|
||||
|
||||
describe ".eachPane(callback)", ->
|
||||
describe ".eachPaneView(callback)", ->
|
||||
it "runs the callback with all current and future panes until the subscription is cancelled", ->
|
||||
panes = []
|
||||
subscription = container.eachPane (pane) -> panes.push(pane)
|
||||
subscription = container.eachPaneView (pane) -> panes.push(pane)
|
||||
expect(panes).toEqual [pane1, pane2, pane3]
|
||||
|
||||
panes = []
|
||||
@@ -66,7 +66,7 @@ describe "PaneContainerView", ->
|
||||
|
||||
container.saveAll()
|
||||
|
||||
for pane in container.getPanes()
|
||||
for pane in container.getPaneViews()
|
||||
for item in pane.getItems()
|
||||
expect(item.saved).toBeTruthy()
|
||||
|
||||
@@ -240,30 +240,30 @@ describe "PaneContainerView", ->
|
||||
pane2.remove()
|
||||
expect(activeItemChangedHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe ".focusNextPane()", ->
|
||||
describe ".focusNextPaneView()", ->
|
||||
it "focuses the pane following the focused pane or the first pane if no pane has focus", ->
|
||||
container.attachToDom()
|
||||
container.focusNextPane()
|
||||
container.focusNextPaneView()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPane()
|
||||
container.focusNextPaneView()
|
||||
expect(pane2.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPane()
|
||||
container.focusNextPaneView()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
container.focusNextPane()
|
||||
container.focusNextPaneView()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".focusPreviousPane()", ->
|
||||
describe ".focusPreviousPaneView()", ->
|
||||
it "focuses the pane preceding the focused pane or the last pane if no pane has focus", ->
|
||||
container.attachToDom()
|
||||
container.getPanes()[0].focus() # activate first pane
|
||||
container.getPaneViews()[0].focus() # activate first pane
|
||||
|
||||
container.focusPreviousPane()
|
||||
container.focusPreviousPaneView()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPane()
|
||||
container.focusPreviousPaneView()
|
||||
expect(pane2.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPane()
|
||||
container.focusPreviousPaneView()
|
||||
expect(pane1.activeItem).toMatchSelector ':focus'
|
||||
container.focusPreviousPane()
|
||||
container.focusPreviousPaneView()
|
||||
expect(pane3.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe "changing focus directionally between panes", ->
|
||||
@@ -300,54 +300,54 @@ describe "PaneContainerView", ->
|
||||
container.width(400)
|
||||
container.attachToDom()
|
||||
|
||||
describe ".focusPaneAbove()", ->
|
||||
describe ".focusPaneViewAbove()", ->
|
||||
describe "when there are multiple rows above the focused pane", ->
|
||||
it "focuses up to the adjacent row", ->
|
||||
pane8.focus()
|
||||
container.focusPaneAbove()
|
||||
container.focusPaneViewAbove()
|
||||
expect(pane5.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe "when there are no rows above the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane2.focus()
|
||||
container.focusPaneAbove()
|
||||
container.focusPaneViewAbove()
|
||||
expect(pane2.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".focusPaneBelow()", ->
|
||||
describe ".focusPaneViewBelow()", ->
|
||||
describe "when there are multiple rows below the focused pane", ->
|
||||
it "focuses down to the adjacent row", ->
|
||||
pane2.focus()
|
||||
container.focusPaneBelow()
|
||||
container.focusPaneViewBelow()
|
||||
expect(pane5.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe "when there are no rows below the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane8.focus()
|
||||
container.focusPaneBelow()
|
||||
container.focusPaneViewBelow()
|
||||
expect(pane8.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".focusPaneOnLeft()", ->
|
||||
describe ".focusPaneViewOnLeft()", ->
|
||||
describe "when there are multiple columns to the left of the focused pane", ->
|
||||
it "focuses left to the adjacent column", ->
|
||||
pane6.focus()
|
||||
container.focusPaneOnLeft()
|
||||
container.focusPaneViewOnLeft()
|
||||
expect(pane5.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe "when there are no columns to the left of the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane4.focus()
|
||||
container.focusPaneOnLeft()
|
||||
container.focusPaneViewOnLeft()
|
||||
expect(pane4.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe ".focusPaneOnRight()", ->
|
||||
describe ".focusPaneViewOnRight()", ->
|
||||
describe "when there are multiple columns to the right of the focused pane", ->
|
||||
it "focuses right to the adjacent column", ->
|
||||
pane4.focus()
|
||||
container.focusPaneOnRight()
|
||||
container.focusPaneViewOnRight()
|
||||
expect(pane5.activeItem).toMatchSelector ':focus'
|
||||
|
||||
describe "when there are no columns to the right of the focused pane", ->
|
||||
it "keeps the current pane focused", ->
|
||||
pane6.focus()
|
||||
container.focusPaneOnRight()
|
||||
container.focusPaneViewOnRight()
|
||||
expect(pane6.activeItem).toMatchSelector ':focus'
|
||||
|
||||
@@ -292,7 +292,7 @@ describe "Pane", ->
|
||||
pane.activeItem.path = __filename
|
||||
pane.activeItem.saveAs = jasmine.createSpy("saveAs")
|
||||
pane.saveActiveItemAs()
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalledWith(__dirname)
|
||||
expect(atom.showSaveDialogSync).toHaveBeenCalledWith(__filename)
|
||||
expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path')
|
||||
|
||||
describe "when the current item does not have a saveAs method", ->
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
PaneContainerView = require '../src/pane-container-view'
|
||||
PaneView = require '../src/pane-view'
|
||||
{fs, $, View} = require 'atom'
|
||||
fs = require 'fs-plus'
|
||||
{$, View} = require 'atom'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
|
||||
@@ -20,11 +21,16 @@ describe "PaneView", ->
|
||||
container = new PaneContainerView
|
||||
view1 = new TestView(id: 'view-1', text: 'View 1')
|
||||
view2 = new TestView(id: 'view-2', text: 'View 2')
|
||||
editor1 = atom.project.openSync('sample.js')
|
||||
editor2 = atom.project.openSync('sample.txt')
|
||||
pane = container.getRoot()
|
||||
paneModel = pane.model
|
||||
paneModel.addItems([view1, editor1, view2, editor2])
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.js').then (o) -> editor1 = o
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('sample.txt').then (o) -> editor2 = o
|
||||
|
||||
runs ->
|
||||
pane = container.getRoot()
|
||||
paneModel = pane.model
|
||||
paneModel.addItems([view1, editor1, view2, editor2])
|
||||
|
||||
afterEach ->
|
||||
atom.deserializers.remove(TestView)
|
||||
@@ -158,13 +164,19 @@ describe "PaneView", ->
|
||||
|
||||
describe "when an unmodifed buffer's path is deleted", ->
|
||||
it "removes the pane item", ->
|
||||
editor = null
|
||||
jasmine.unspy(window, 'setTimeout')
|
||||
filePath = temp.openSync('atom').path
|
||||
editor = atom.project.openSync(filePath)
|
||||
pane.activateItem(editor)
|
||||
expect(pane.items).toHaveLength(5)
|
||||
|
||||
fs.removeSync(filePath)
|
||||
waitsFor 30000, ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(filePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
pane.activateItem(editor)
|
||||
expect(pane.items).toHaveLength(5)
|
||||
fs.removeSync(filePath)
|
||||
|
||||
waitsFor ->
|
||||
pane.items.length == 4
|
||||
|
||||
describe "when a pane is destroyed", ->
|
||||
|
||||
+82
-121
@@ -1,7 +1,8 @@
|
||||
temp = require 'temp'
|
||||
fstream = require 'fstream'
|
||||
Project = require '../src/project'
|
||||
{_, fs} = require 'atom'
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
path = require 'path'
|
||||
platform = require './spec-helper-platform'
|
||||
BufferedProcess = require '../src/buffered-process'
|
||||
@@ -17,111 +18,50 @@ describe "Project", ->
|
||||
deserializedProject?.destroy()
|
||||
|
||||
it "does not include unretained buffers in the serialized state", ->
|
||||
atom.project.bufferForPathSync('a')
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
waitsForPromise ->
|
||||
atom.project.bufferForPath('a')
|
||||
|
||||
deserializedProject = atom.project.testSerialization()
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
runs ->
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
deserializedProject = atom.project.testSerialization()
|
||||
expect(deserializedProject.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
|
||||
deserializedProject = atom.project.testSerialization()
|
||||
waitsForPromise ->
|
||||
atom.project.open('a')
|
||||
|
||||
expect(deserializedProject.getBuffers().length).toBe 1
|
||||
deserializedProject.getBuffers()[0].destroy()
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
runs ->
|
||||
expect(atom.project.getBuffers().length).toBe 1
|
||||
deserializedProject = atom.project.testSerialization()
|
||||
|
||||
describe "when an edit session is destroyed", ->
|
||||
it "removes edit session and calls destroy on buffer (if buffer is not referenced by other edit sessions)", ->
|
||||
editor = atom.project.openSync("a")
|
||||
anotherEditor = atom.project.openSync("a")
|
||||
expect(deserializedProject.getBuffers().length).toBe 1
|
||||
deserializedProject.getBuffers()[0].destroy()
|
||||
expect(deserializedProject.getBuffers().length).toBe 0
|
||||
|
||||
expect(atom.project.editors.length).toBe 2
|
||||
expect(editor.buffer).toBe anotherEditor.buffer
|
||||
|
||||
editor.destroy()
|
||||
expect(atom.project.editors.length).toBe 1
|
||||
|
||||
anotherEditor.destroy()
|
||||
expect(atom.project.editors.length).toBe 0
|
||||
|
||||
describe "when an edit session is saved and the project has no path", ->
|
||||
describe "when an editor 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
|
||||
atom.project.setPath(undefined)
|
||||
expect(atom.project.getPath()).toBeUndefined()
|
||||
editor = atom.project.openSync()
|
||||
editor.saveAs(tempFile)
|
||||
expect(atom.project.getPath()).toBe path.dirname(tempFile)
|
||||
editor = null
|
||||
|
||||
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
|
||||
waitsForPromise ->
|
||||
atom.project.open().then (o) -> editor = o
|
||||
|
||||
editor1 = atom.project.openSync("a")
|
||||
expect(handler.callCount).toBe 1
|
||||
expect(atom.project.getEditors().length).toBe 1
|
||||
expect(atom.project.getEditors()[0]).toBe editor1
|
||||
|
||||
editor2 = editor1.copy()
|
||||
expect(handler.callCount).toBe 2
|
||||
expect(atom.project.getEditors().length).toBe 2
|
||||
expect(atom.project.getEditors()[0]).toBe editor1
|
||||
expect(atom.project.getEditors()[1]).toBe editor2
|
||||
|
||||
describe ".openSync(path)", ->
|
||||
[absolutePath, newBufferHandler, newEditorHandler] = []
|
||||
beforeEach ->
|
||||
absolutePath = require.resolve('./fixtures/dir/a')
|
||||
newBufferHandler = jasmine.createSpy('newBufferHandler')
|
||||
atom.project.on 'buffer-created', newBufferHandler
|
||||
newEditorHandler = jasmine.createSpy('newEditorHandler')
|
||||
atom.project.on 'editor-created', newEditorHandler
|
||||
|
||||
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 'editor-created' events", ->
|
||||
editor = atom.project.openSync(absolutePath)
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when given a relative path that hasn't been opened previously", ->
|
||||
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = atom.project.openSync('a')
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed the path to a buffer that has already been opened", ->
|
||||
it "returns a new edit session containing previously opened buffer and emits a 'editor-created' event", ->
|
||||
editor = atom.project.openSync(absolutePath)
|
||||
newBufferHandler.reset()
|
||||
expect(atom.project.openSync(absolutePath).buffer).toBe editor.buffer
|
||||
expect(atom.project.openSync('a').buffer).toBe editor.buffer
|
||||
expect(newBufferHandler).not.toHaveBeenCalled()
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when not passed a path", ->
|
||||
it "returns a new edit session and emits 'buffer-created' and 'editor-created' events", ->
|
||||
editor = atom.project.openSync()
|
||||
expect(editor.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer)
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
runs ->
|
||||
editor.saveAs(tempFile)
|
||||
expect(atom.project.getPath()).toBe path.dirname(tempFile)
|
||||
|
||||
describe ".open(path)", ->
|
||||
[absolutePath, newBufferHandler, newEditorHandler] = []
|
||||
[absolutePath, newBufferHandler] = []
|
||||
|
||||
beforeEach ->
|
||||
absolutePath = require.resolve('./fixtures/dir/a')
|
||||
newBufferHandler = jasmine.createSpy('newBufferHandler')
|
||||
atom.project.on 'buffer-created', newBufferHandler
|
||||
newEditorHandler = jasmine.createSpy('newEditorHandler')
|
||||
atom.project.on 'editor-created', newEditorHandler
|
||||
|
||||
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 'editor-created' events", ->
|
||||
it "returns a new edit session for the given path and emits 'buffer-created'", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
atom.project.open(absolutePath).then (o) -> editor = o
|
||||
@@ -129,10 +69,9 @@ describe "Project", ->
|
||||
runs ->
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when given a relative path that isn't currently opened", ->
|
||||
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created' and 'editor-created' events", ->
|
||||
it "returns a new edit session for the given path (relative to the project root) and emits 'buffer-created'", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
atom.project.open(absolutePath).then (o) -> editor = o
|
||||
@@ -140,23 +79,28 @@ describe "Project", ->
|
||||
runs ->
|
||||
expect(editor.buffer.getPath()).toBe absolutePath
|
||||
expect(newBufferHandler).toHaveBeenCalledWith editor.buffer
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when passed the path to a buffer that is currently opened", ->
|
||||
it "returns a new edit session containing currently opened buffer and emits a 'editor-created' event", ->
|
||||
it "returns a new edit session containing currently opened buffer", ->
|
||||
editor = null
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open(absolutePath).then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
newBufferHandler.reset()
|
||||
expect(atom.project.openSync(absolutePath).buffer).toBe editor.buffer
|
||||
expect(atom.project.openSync('a').buffer).toBe editor.buffer
|
||||
expect(newBufferHandler).not.toHaveBeenCalled()
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open(absolutePath).then ({buffer}) ->
|
||||
expect(buffer).toBe editor.buffer
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('a').then ({buffer}) ->
|
||||
expect(buffer).toBe editor.buffer
|
||||
expect(newBufferHandler).not.toHaveBeenCalled()
|
||||
|
||||
describe "when not passed a path", ->
|
||||
it "returns a new edit session and emits 'buffer-created' and 'editor-created' events", ->
|
||||
it "returns a new edit session and emits 'buffer-created'", ->
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
atom.project.open().then (o) -> editor = o
|
||||
@@ -164,7 +108,6 @@ describe "Project", ->
|
||||
runs ->
|
||||
expect(editor.buffer.getPath()).toBeUndefined()
|
||||
expect(newBufferHandler).toHaveBeenCalledWith(editor.buffer)
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
it "returns number of read bytes as progress indicator", ->
|
||||
filePath = atom.project.resolve 'a'
|
||||
@@ -178,20 +121,6 @@ describe "Project", ->
|
||||
runs ->
|
||||
expect(totalBytes).toBe fs.statSync(filePath).size
|
||||
|
||||
describe ".bufferForPathSync(path)", ->
|
||||
describe "when opening a previously opened path", ->
|
||||
it "does not create a new buffer", ->
|
||||
buffer = atom.project.bufferForPathSync("a").retain()
|
||||
expect(atom.project.bufferForPathSync("a")).toBe buffer
|
||||
|
||||
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 = atom.project.bufferForPathSync("a").retain().release()
|
||||
expect(atom.project.bufferForPathSync("a").retain().release()).not.toBe buffer
|
||||
|
||||
describe ".bufferForPath(path)", ->
|
||||
[buffer] = []
|
||||
beforeEach ->
|
||||
@@ -281,10 +210,15 @@ describe "Project", ->
|
||||
|
||||
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()
|
||||
|
||||
editor = null
|
||||
results = []
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample.js').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor.isModified()).toBeFalsy()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.replace /items/gi, 'items', [filePath], (result) ->
|
||||
results.push(result)
|
||||
@@ -297,10 +231,12 @@ describe "Project", ->
|
||||
expect(editor.isModified()).toBeFalsy()
|
||||
|
||||
it "does not replace when the path is not specified", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor = atom.project.openSync('sample-with-comments.js')
|
||||
|
||||
editor = null
|
||||
results = []
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample-with-comments.js').then (o) -> editor = o
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.replace /items/gi, 'items', [commentFilePath], (result) ->
|
||||
results.push(result)
|
||||
@@ -310,11 +246,16 @@ describe "Project", ->
|
||||
expect(results[0].filePath).toBe commentFilePath
|
||||
|
||||
it "does NOT save when modified", ->
|
||||
editor = atom.project.openSync('sample.js')
|
||||
editor.buffer.change([[0,0],[0,0]], 'omg')
|
||||
expect(editor.isModified()).toBeTruthy()
|
||||
|
||||
editor = null
|
||||
results = []
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('sample.js').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
editor.buffer.setTextInRange([[0,0],[0,0]], 'omg')
|
||||
expect(editor.isModified()).toBeTruthy()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.replace /items/gi, 'okthen', [filePath], (result) ->
|
||||
results.push(result)
|
||||
@@ -478,9 +419,14 @@ describe "Project", ->
|
||||
expect(resultHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "scans buffer contents if the buffer is modified", ->
|
||||
editor = atom.project.openSync("a")
|
||||
editor.setText("Elephant")
|
||||
editor = null
|
||||
results = []
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('a').then (o) ->
|
||||
editor = o
|
||||
editor.setText("Elephant")
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.scan /a|Elephant/, (result) -> results.push result
|
||||
|
||||
@@ -490,6 +436,21 @@ describe "Project", ->
|
||||
expect(resultForA.matches).toHaveLength 1
|
||||
expect(resultForA.matches[0].matchText).toBe 'Elephant'
|
||||
|
||||
it "ignores buffers outside the project", ->
|
||||
editor = null
|
||||
results = []
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open(temp.openSync().path).then (o) ->
|
||||
editor = o
|
||||
editor.setText("Elephant")
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.scan /Elephant/, (result) -> results.push result
|
||||
|
||||
runs ->
|
||||
expect(results).toHaveLength 0
|
||||
|
||||
describe ".eachBuffer(callback)", ->
|
||||
beforeEach ->
|
||||
atom.project.bufferForPathSync('a')
|
||||
|
||||
@@ -43,6 +43,15 @@ describe "SelectListView", ->
|
||||
expect(list.find('li:eq(0)')).toHaveClass 'A'
|
||||
expect(selectList.getSelectedItem()).toBe items[0]
|
||||
|
||||
it "allows raw HTML to be returned", ->
|
||||
selectList.viewForItem = (item) ->
|
||||
"<li>#{item}</li>"
|
||||
|
||||
selectList.setItems(['Bermuda', 'Bahama'])
|
||||
|
||||
expect(list.find('li:eq(0)')).toHaveText 'Bermuda'
|
||||
expect(selectList.getSelectedItem()).toBe 'Bermuda'
|
||||
|
||||
describe "when the text of the mini editor changes", ->
|
||||
beforeEach ->
|
||||
selectList.attachToDom()
|
||||
|
||||
@@ -58,7 +58,7 @@ describe "Selection", ->
|
||||
|
||||
describe "when only the selection's tail is moved (regression)", ->
|
||||
it "emits the 'screen-range-changed' event", ->
|
||||
selection.setBufferRange([[2, 0], [2, 10]], isReversed: true)
|
||||
selection.setBufferRange([[2, 0], [2, 10]], reversed: true)
|
||||
changeScreenRangeHandler = jasmine.createSpy('changeScreenRangeHandler')
|
||||
selection.on 'screen-range-changed', changeScreenRangeHandler
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# Start the crash reporter before anything else.
|
||||
require('crash-reporter').start(productName: 'Atom', companyName: 'GitHub')
|
||||
|
||||
path = require 'path'
|
||||
|
||||
try
|
||||
require '../src/window'
|
||||
Atom = require '../src/atom'
|
||||
@@ -12,6 +14,12 @@ try
|
||||
|
||||
{runSpecSuite} = require './jasmine-helper'
|
||||
|
||||
# Add 'src/exports' to module search path.
|
||||
exportsPath = path.resolve(atom.getLoadSettings().resourcePath, 'exports')
|
||||
require('module').globalPaths.push(exportsPath)
|
||||
# Still set NODE_PATH since tasks may need it.
|
||||
process.env.NODE_PATH = exportsPath
|
||||
|
||||
document.title = "Spec Suite"
|
||||
runSpecSuite './spec-suite', atom.getLoadSettings().logFile
|
||||
catch error
|
||||
|
||||
@@ -4,8 +4,11 @@ atom.restoreWindowDimensions()
|
||||
|
||||
require '../vendor/jasmine-jquery'
|
||||
path = require 'path'
|
||||
{_, $, WorkspaceView, fs} = require 'atom'
|
||||
Keymap = require '../src/keymap'
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
Grim = require 'grim'
|
||||
KeymapManager = require '../src/keymap-extensions'
|
||||
{$, WorkspaceView, Workspace} = require 'atom'
|
||||
Config = require '../src/config'
|
||||
{Point} = require 'text-buffer'
|
||||
Project = require '../src/project'
|
||||
@@ -20,8 +23,8 @@ atom.themes.requireStylesheet '../static/jasmine'
|
||||
|
||||
fixturePackagesPath = path.resolve(__dirname, './fixtures/packages')
|
||||
atom.packages.packageDirPaths.unshift(fixturePackagesPath)
|
||||
atom.keymap.loadBundledKeymaps()
|
||||
keyBindingsToRestore = atom.keymap.getKeyBindings()
|
||||
atom.keymaps.loadBundledKeymaps()
|
||||
keyBindingsToRestore = atom.keymaps.getKeyBindings()
|
||||
|
||||
$(window).on 'core:close', -> window.close()
|
||||
$(window).on 'unload', ->
|
||||
@@ -35,6 +38,7 @@ jasmine.getEnv().defaultTimeoutInterval = 5000
|
||||
specPackageName = null
|
||||
specPackagePath = null
|
||||
specProjectPath = null
|
||||
isCoreSpec = false
|
||||
|
||||
{specDirectory, resourcePath} = atom.getLoadSettings()
|
||||
|
||||
@@ -44,11 +48,15 @@ if specDirectory
|
||||
specPackageName = JSON.parse(fs.readFileSync(path.join(specPackagePath, 'package.json')))?.name
|
||||
specProjectPath = path.join(specDirectory, 'fixtures')
|
||||
|
||||
isCoreSpec = specDirectory == fs.realpathSync(__dirname)
|
||||
|
||||
beforeEach ->
|
||||
Grim.clearDeprecations() if isCoreSpec
|
||||
$.fx.off = true
|
||||
projectPath = specProjectPath ? path.join(@specDirectory, 'fixtures')
|
||||
atom.project = new Project(path: projectPath)
|
||||
atom.keymap.keyBindings = _.clone(keyBindingsToRestore)
|
||||
atom.workspace = new Workspace()
|
||||
atom.keymaps.keyBindings = _.clone(keyBindingsToRestore)
|
||||
|
||||
window.resetTimeouts()
|
||||
atom.packages.packageStates = {}
|
||||
@@ -75,11 +83,13 @@ beforeEach ->
|
||||
spyOn(config, 'save')
|
||||
config.setDefaults('core', WorkspaceView.configDefaults)
|
||||
config.setDefaults('editor', EditorView.configDefaults)
|
||||
config.set "core.destroyEmptyPanes", false
|
||||
config.set "editor.fontFamily", "Courier"
|
||||
config.set "editor.fontSize", 16
|
||||
config.set "editor.autoIndent", false
|
||||
config.set "core.disabledPackages", ["package-that-throws-an-exception",
|
||||
"package-with-broken-package-json", "package-with-broken-keymap"]
|
||||
config.set "core.useReactEditor", false
|
||||
config.save.reset()
|
||||
atom.config = config
|
||||
|
||||
@@ -109,7 +119,7 @@ afterEach ->
|
||||
atom.workspaceView = null
|
||||
delete atom.state.workspace
|
||||
|
||||
atom.project?.destroy?()
|
||||
atom.project?.destroy()
|
||||
atom.project = null
|
||||
|
||||
delete atom.state.packageStates
|
||||
@@ -119,6 +129,7 @@ afterEach ->
|
||||
jasmine.unspy(atom, 'saveSync')
|
||||
ensureNoPathSubscriptions()
|
||||
atom.syntax.off()
|
||||
ensureNoDeprecatedFunctionsCalled() if isCoreSpec
|
||||
waits(0) # yield to ui thread to make screen update more frequently
|
||||
|
||||
ensureNoPathSubscriptions = ->
|
||||
@@ -127,6 +138,27 @@ ensureNoPathSubscriptions = ->
|
||||
if watchedPaths.length > 0
|
||||
throw new Error("Leaking subscriptions for paths: " + watchedPaths.join(", "))
|
||||
|
||||
ensureNoDeprecatedFunctionsCalled = ->
|
||||
deprecations = Grim.getDeprecations()
|
||||
if deprecations.length > 0
|
||||
originalPrepareStackTrace = Error.prepareStackTrace
|
||||
Error.prepareStackTrace = (error, stack) ->
|
||||
output = []
|
||||
for deprecation in deprecations
|
||||
output.push "#{deprecation.originName} is deprecated. #{deprecation.message}"
|
||||
output.push _.multiplyString("-", output[output.length - 1].length)
|
||||
for stack in deprecation.getStacks()
|
||||
for {functionName, location} in stack
|
||||
output.push "#{functionName} -- #{location}"
|
||||
output.push ""
|
||||
output.join("\n")
|
||||
|
||||
error = new Error("Deprecated function(s) #{deprecations.map(({originName}) -> originName).join ', '}) were called.")
|
||||
error.stack
|
||||
Error.prepareStackTrace = originalPrepareStackTrace
|
||||
|
||||
throw error
|
||||
|
||||
emitObject = jasmine.StringPrettyPrinter.prototype.emitObject
|
||||
jasmine.StringPrettyPrinter.prototype.emitObject = (obj) ->
|
||||
if obj.inspect
|
||||
@@ -177,7 +209,15 @@ window.keyIdentifierForKey = (key) ->
|
||||
"U+00" + charCode.toString(16)
|
||||
|
||||
window.keydownEvent = (key, properties={}) ->
|
||||
properties = $.extend({originalEvent: { keyIdentifier: keyIdentifierForKey(key) }}, properties)
|
||||
originalEventProperties = {}
|
||||
originalEventProperties.ctrl = properties.ctrlKey
|
||||
originalEventProperties.alt = properties.altKey
|
||||
originalEventProperties.shift = properties.shiftKey
|
||||
originalEventProperties.cmd = properties.metaKey
|
||||
originalEventProperties.target = properties.target?[0] ? properties.target
|
||||
originalEventProperties.which = properties.which
|
||||
originalEvent = KeymapManager.keydownEvent(key, originalEventProperties)
|
||||
properties = $.extend({originalEvent}, properties)
|
||||
$.Event("keydown", properties)
|
||||
|
||||
window.mouseEvent = (type, properties) ->
|
||||
@@ -231,6 +271,15 @@ window.fakeSetTimeout = (callback, ms) ->
|
||||
window.fakeClearTimeout = (idToClear) ->
|
||||
window.timeouts = window.timeouts.filter ([id]) -> id != idToClear
|
||||
|
||||
window.fakeSetInterval = (callback, ms) ->
|
||||
action = ->
|
||||
callback()
|
||||
window.fakeSetTimeout(action, ms)
|
||||
window.fakeSetTimeout(action, ms)
|
||||
|
||||
window.fakeClearInterval = (idToClear) ->
|
||||
window.fakeClearTimeout(idToClear)
|
||||
|
||||
window.advanceClock = (delta=1) ->
|
||||
window.now += delta
|
||||
callbacks = []
|
||||
@@ -267,7 +316,11 @@ $.fn.resultOfTrigger = (type) ->
|
||||
event.result
|
||||
|
||||
$.fn.enableKeymap = ->
|
||||
@on 'keydown', (e) => atom.keymap.handleKeyEvent(e)
|
||||
@on 'keydown', (e) ->
|
||||
originalEvent = e.originalEvent ? e
|
||||
Object.defineProperty(originalEvent, 'target', get: -> e.target) unless originalEvent.target?
|
||||
atom.keymaps.handleKeyboardEvent(originalEvent)
|
||||
not e.originalEvent.defaultPrevented
|
||||
|
||||
$.fn.attachToDom = ->
|
||||
@appendTo($('#jasmine-content'))
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
{_, fs, Git} = require 'atom'
|
||||
_ = require 'underscore-plus'
|
||||
fs = require 'fs-plus'
|
||||
{Git} = require 'atom'
|
||||
path = require 'path'
|
||||
require './spec-helper'
|
||||
|
||||
requireSpecs = (specDirectory, specType) ->
|
||||
for specFilePath in fs.listTreeSync(specDirectory) when /-spec\.coffee$/.test specFilePath
|
||||
for specFilePath in fs.listTreeSync(specDirectory) when /-spec\.(coffee|js)$/.test specFilePath
|
||||
require specFilePath
|
||||
|
||||
# Set spec directory on spec for setting up the project in spec-helper
|
||||
@@ -24,6 +26,7 @@ setSpecDirectory = (specDirectory) ->
|
||||
|
||||
runAllSpecs = ->
|
||||
{resourcePath} = atom.getLoadSettings()
|
||||
|
||||
# Only run core specs when resource path is the Atom repository
|
||||
if Git.exists(resourcePath)
|
||||
requireSpecs(path.join(resourcePath, 'spec'))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{fs} = require 'atom'
|
||||
path = require 'path'
|
||||
fs = require 'fs-plus'
|
||||
temp = require 'temp'
|
||||
|
||||
describe "the `syntax` global", ->
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
path = require 'path'
|
||||
|
||||
{$, $$, fs, WorkspaceView} = require 'atom'
|
||||
{$, $$, WorkspaceView} = require 'atom'
|
||||
fs = require 'fs-plus'
|
||||
temp = require 'temp'
|
||||
|
||||
ThemeManager = require '../src/theme-manager'
|
||||
@@ -35,6 +36,22 @@ describe "ThemeManager", ->
|
||||
themes = themeManager.getActiveThemes()
|
||||
expect(themes).toHaveLength(names.length)
|
||||
|
||||
describe "when the core.themes config value contains invalid entry", ->
|
||||
it "ignores theme", ->
|
||||
atom.config.set 'core.themes', [
|
||||
'atom-light-ui'
|
||||
null
|
||||
undefined
|
||||
''
|
||||
false
|
||||
4
|
||||
{}
|
||||
[]
|
||||
'atom-dark-ui'
|
||||
]
|
||||
|
||||
expect(themeManager.getEnabledThemeNames()).toEqual ['atom-dark-ui', 'atom-light-ui']
|
||||
|
||||
describe "getImportPaths()", ->
|
||||
it "returns the theme directories before the themes are loaded", ->
|
||||
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui', 'atom-light-ui'])
|
||||
@@ -114,15 +131,21 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "requireStylesheet(path)", ->
|
||||
it "synchronously loads css at the given path and installs a style tag for it in the head", ->
|
||||
themeManager.on 'stylesheets-changed', stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
themeManager.on 'stylesheet-added', stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
|
||||
cssPath = atom.project.resolve('css.css')
|
||||
lengthBefore = $('head style').length
|
||||
|
||||
themeManager.requireStylesheet(cssPath)
|
||||
expect($('head style').length).toBe lengthBefore + 1
|
||||
|
||||
expect(stylesheetAddedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetsChangedHandler).toHaveBeenCalled()
|
||||
|
||||
element = $('head style[id*="css.css"]')
|
||||
expect(element.attr('id')).toBe themeManager.stringToId(cssPath)
|
||||
expect(element.text()).toBe fs.readFileSync(cssPath, 'utf8')
|
||||
expect(element[0].sheet).toBe stylesheetAddedHandler.argsForCall[0][0]
|
||||
|
||||
# doesn't append twice
|
||||
themeManager.requireStylesheet(cssPath)
|
||||
@@ -169,9 +192,21 @@ describe "ThemeManager", ->
|
||||
expect($(document.body).css('font-weight')).not.toBe("bold")
|
||||
themeManager.requireStylesheet(cssPath)
|
||||
expect($(document.body).css('font-weight')).toBe("bold")
|
||||
|
||||
themeManager.on 'stylesheet-removed', stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
|
||||
themeManager.on 'stylesheets-changed', stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
|
||||
themeManager.removeStylesheet(cssPath)
|
||||
|
||||
expect($(document.body).css('font-weight')).not.toBe("bold")
|
||||
|
||||
expect(stylesheetRemovedHandler).toHaveBeenCalled()
|
||||
stylesheet = stylesheetRemovedHandler.argsForCall[0][0]
|
||||
expect(stylesheet instanceof CSSStyleSheet).toBe true
|
||||
expect(stylesheet.cssRules[0].selectorText).toBe 'body'
|
||||
|
||||
expect(stylesheetsChangedHandler).toHaveBeenCalled()
|
||||
|
||||
describe "base stylesheet loading", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView = new WorkspaceView
|
||||
@@ -199,17 +234,21 @@ describe "ThemeManager", ->
|
||||
|
||||
describe "when the user stylesheet changes", ->
|
||||
it "reloads it", ->
|
||||
userStylesheetPath = path.join(temp.mkdirSync("atom"), 'styles.css')
|
||||
[stylesheetRemovedHandler, stylesheetAddedHandler, stylesheetsChangedHandler] = []
|
||||
userStylesheetPath = path.join(temp.mkdirSync("atom"), 'styles.less')
|
||||
fs.writeFileSync(userStylesheetPath, 'body {border-style: dotted !important;}')
|
||||
|
||||
spyOn(themeManager, 'getUserStylesheetPath').andReturn userStylesheetPath
|
||||
|
||||
waitsForPromise ->
|
||||
themeManager.activateThemes()
|
||||
|
||||
runs ->
|
||||
expect($(document.body).css('border-style')).toBe 'dotted'
|
||||
themeManager.on 'stylesheets-changed', stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler")
|
||||
themeManager.on 'stylesheet-removed', stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler")
|
||||
themeManager.on 'stylesheet-added', stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler")
|
||||
spyOn(themeManager, 'loadUserStylesheet').andCallThrough()
|
||||
|
||||
expect($(document.body).css('border-style')).toBe 'dotted'
|
||||
fs.writeFileSync(userStylesheetPath, 'body {border-style: dashed}')
|
||||
|
||||
waitsFor ->
|
||||
@@ -217,13 +256,27 @@ describe "ThemeManager", ->
|
||||
|
||||
runs ->
|
||||
expect($(document.body).css('border-style')).toBe 'dashed'
|
||||
|
||||
expect(stylesheetRemovedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dotted'
|
||||
|
||||
expect(stylesheetAddedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetAddedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed'
|
||||
|
||||
expect(stylesheetsChangedHandler).toHaveBeenCalled()
|
||||
|
||||
stylesheetRemovedHandler.reset()
|
||||
stylesheetsChangedHandler.reset()
|
||||
fs.removeSync(userStylesheetPath)
|
||||
|
||||
waitsFor ->
|
||||
themeManager.loadUserStylesheet.callCount is 2
|
||||
|
||||
runs ->
|
||||
expect(stylesheetRemovedHandler).toHaveBeenCalled()
|
||||
expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed'
|
||||
expect($(document.body).css('border-style')).toBe 'none'
|
||||
expect(stylesheetsChangedHandler).toHaveBeenCalled()
|
||||
|
||||
describe "when a non-existent theme is present in the config", ->
|
||||
it "logs a warning but does not throw an exception (regression)", ->
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{_} = require 'atom'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
module.exports =
|
||||
class TimeReporter extends jasmine.Reporter
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
{_} = require 'atom'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
describe "TokenizedBuffer", ->
|
||||
[tokenizedBuffer, buffer, changeHandler] = []
|
||||
@@ -119,7 +119,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "when there is a buffer change surrounding an invalid row", ->
|
||||
it "pushes the invalid row to the end of the change", ->
|
||||
buffer.change([[4, 0], [6, 0]], "\n\n\n")
|
||||
buffer.setTextInRange([[4, 0], [6, 0]], "\n\n\n")
|
||||
changeHandler.reset()
|
||||
|
||||
expect(tokenizedBuffer.firstInvalidRow()).toBe 8
|
||||
@@ -128,7 +128,7 @@ describe "TokenizedBuffer", ->
|
||||
describe "when there is a buffer change inside an invalid region", ->
|
||||
it "does not attempt to tokenize the lines in the change, and preserves the existing invalid row", ->
|
||||
expect(tokenizedBuffer.firstInvalidRow()).toBe 5
|
||||
buffer.change([[6, 0], [7, 0]], "\n\n\n")
|
||||
buffer.setTextInRange([[6, 0], [7, 0]], "\n\n\n")
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(6).ruleStack?).toBeFalsy()
|
||||
expect(tokenizedBuffer.lineForScreenRow(7).ruleStack?).toBeFalsy()
|
||||
@@ -143,7 +143,7 @@ describe "TokenizedBuffer", ->
|
||||
describe "when there is a buffer change that is smaller than the chunk size", ->
|
||||
describe "when lines are updated, but none are added or removed", ->
|
||||
it "updates tokens to reflect the change", ->
|
||||
buffer.change([[0, 0], [2, 0]], "foo()\n7\n")
|
||||
buffer.setTextInRange([[0, 0], [2, 0]], "foo()\n7\n")
|
||||
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[1]).toEqual(value: '(', scopes: ['source.js', 'meta.brace.round.js'])
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[0]).toEqual(value: '7', scopes: ['source.js', 'constant.numeric.js'])
|
||||
@@ -185,7 +185,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "when lines are both updated and removed", ->
|
||||
it "updates tokens to reflect the change", ->
|
||||
buffer.change([[1, 0], [3, 0]], "foo()")
|
||||
buffer.setTextInRange([[1, 0], [3, 0]], "foo()")
|
||||
|
||||
# previous line 0 remains
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.modifier.js'])
|
||||
@@ -209,7 +209,7 @@ describe "TokenizedBuffer", ->
|
||||
buffer.insert([5, 30], '/* */')
|
||||
changeHandler.reset()
|
||||
|
||||
buffer.change([[2, 0], [3, 0]], '/*')
|
||||
buffer.setTextInRange([[2, 0], [3, 0]], '/*')
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js']
|
||||
expect(tokenizedBuffer.lineForScreenRow(3).tokens[0].scopes).toEqual ['source.js']
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
@@ -228,7 +228,7 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "when lines are both updated and inserted", ->
|
||||
it "updates tokens to reflect the change", ->
|
||||
buffer.change([[1, 0], [2, 0]], "foo()\nbar()\nbaz()\nquux()")
|
||||
buffer.setTextInRange([[1, 0], [2, 0]], "foo()\nbar()\nbaz()\nquux()")
|
||||
|
||||
# previous line 0 remains
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[0]).toEqual( value: 'var', scopes: ['source.js', 'storage.modifier.js'])
|
||||
@@ -452,3 +452,88 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokenizedBuffer.tokenForPosition([0,0]).value).toBe ' '
|
||||
atom.config.set('editor.tabLength', 6)
|
||||
expect(tokenizedBuffer.tokenForPosition([0,0]).value).toBe ' '
|
||||
|
||||
it "does not allow the tab length to be less than 1", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
buffer.setText('\ttest')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenForPosition([0,0]).value).toBe ' '
|
||||
atom.config.set('editor.tabLength', 1)
|
||||
expect(tokenizedBuffer.tokenForPosition([0,0]).value).toBe ' '
|
||||
atom.config.set('editor.tabLength', 0)
|
||||
expect(tokenizedBuffer.tokenForPosition([0,0]).value).toBe ' '
|
||||
|
||||
describe "leading and trailing whitespace", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
it "sets ::hasLeadingWhitespace to true on tokens that have leading whitespace", ->
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[0].hasLeadingWhitespace).toBe false
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[0].hasLeadingWhitespace).toBe true
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].hasLeadingWhitespace).toBe false
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[0].hasLeadingWhitespace).toBe true
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].hasLeadingWhitespace).toBe true
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[2].hasLeadingWhitespace).toBe false
|
||||
|
||||
# The 4th token *has* leading whitespace, but isn't entirely whitespace
|
||||
buffer.insert([5, 0], ' ')
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[3].hasLeadingWhitespace).toBe true
|
||||
expect(tokenizedBuffer.lineForScreenRow(5).tokens[4].hasLeadingWhitespace).toBe false
|
||||
|
||||
# Lines that are *only* whitespace are not considered to have leading whitespace
|
||||
buffer.insert([10, 0], ' ')
|
||||
expect(tokenizedBuffer.lineForScreenRow(10).tokens[0].hasLeadingWhitespace).toBe false
|
||||
|
||||
it "sets ::hasTrailingWhitespace to true on tokens that have trailing whitespace", ->
|
||||
buffer.insert([0, Infinity], ' ')
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[11].hasTrailingWhitespace).toBe false
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).tokens[12].hasTrailingWhitespace).toBe true
|
||||
|
||||
# The last token *has* trailing whitespace, but isn't entirely whitespace
|
||||
buffer.setTextInRange([[2, 39], [2, 40]], ' ')
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[14].hasTrailingWhitespace).toBe false
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).tokens[15].hasTrailingWhitespace).toBe true
|
||||
|
||||
# Lines that are *only* whitespace are considered to have trailing whitespace
|
||||
buffer.insert([10, 0], ' ')
|
||||
expect(tokenizedBuffer.lineForScreenRow(10).tokens[0].hasTrailingWhitespace).toBe true
|
||||
|
||||
it "only marks trailing whitespace on the last segment of a soft-wrapped line", ->
|
||||
buffer.insert([0, Infinity], ' ')
|
||||
tokenizedLine = tokenizedBuffer.lineForScreenRow(0)
|
||||
[segment1, segment2] = tokenizedLine.softWrapAt(16)
|
||||
expect(segment1.tokens[5].value).toBe ' '
|
||||
expect(segment1.tokens[5].hasTrailingWhitespace).toBe false
|
||||
expect(segment2.tokens[6].value).toBe ' '
|
||||
expect(segment2.tokens[6].hasTrailingWhitespace).toBe true
|
||||
|
||||
describe "indent level", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({buffer})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
describe "when the line is non-empty", ->
|
||||
it "has an indent level based on the leading whitespace on the line", ->
|
||||
expect(tokenizedBuffer.lineForScreenRow(0).indentLevel).toBe 0
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).indentLevel).toBe 1
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).indentLevel).toBe 2
|
||||
buffer.insert([2, 0], ' ')
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).indentLevel).toBe 2.5
|
||||
|
||||
describe "when the line is empty", ->
|
||||
it "assumes the indentation level of the first non-empty line below or above if one exists", ->
|
||||
buffer.insert([12, 0], ' ')
|
||||
buffer.insert([12, Infinity], '\n\n')
|
||||
expect(tokenizedBuffer.lineForScreenRow(13).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(14).indentLevel).toBe 2
|
||||
|
||||
buffer.insert([1, Infinity], '\n\n')
|
||||
expect(tokenizedBuffer.lineForScreenRow(2).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.lineForScreenRow(3).indentLevel).toBe 2
|
||||
|
||||
buffer.setText('\n\n\n')
|
||||
expect(tokenizedBuffer.lineForScreenRow(1).indentLevel).toBe 0
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
describe "TokenizedLine", ->
|
||||
editor = null
|
||||
|
||||
beforeEach ->
|
||||
waitsForPromise -> atom.packages.activatePackage('language-coffee-script')
|
||||
|
||||
describe "::getScopeTree()", ->
|
||||
it "returns a tree whose inner nodes are scopes and whose leaf nodes are tokens in those scopes", ->
|
||||
[tokens, tokenIndex] = []
|
||||
|
||||
ensureValidScopeTree = (scopeTree, scopes=[]) ->
|
||||
if scopeTree.children?
|
||||
for child in scopeTree.children
|
||||
ensureValidScopeTree(child, scopes.concat([scopeTree.scope]))
|
||||
else
|
||||
expect(scopeTree).toBe tokens[tokenIndex++]
|
||||
expect(scopes).toEqual scopeTree.scopes
|
||||
|
||||
waitsForPromise ->
|
||||
atom.project.open('coffee.coffee').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
tokenIndex = 0
|
||||
tokens = editor.lineForScreenRow(1).tokens
|
||||
scopeTree = editor.lineForScreenRow(1).getScopeTree()
|
||||
ensureValidScopeTree(scopeTree)
|
||||
+40
-21
@@ -1,4 +1,4 @@
|
||||
{$, $$, fs} = require 'atom'
|
||||
{$, $$} = require 'atom'
|
||||
path = require 'path'
|
||||
Editor = require '../src/editor'
|
||||
WindowEventHandler = require '../src/window-event-handler'
|
||||
@@ -64,49 +64,68 @@ describe "Window", ->
|
||||
|
||||
describe "when pane items are are modified", ->
|
||||
it "prompts user to save and and calls workspaceView.confirmClose", ->
|
||||
editor = null
|
||||
spyOn(atom.workspaceView, 'confirmClose').andCallThrough()
|
||||
spyOn(atom, "confirm").andReturn(2)
|
||||
editor = atom.workspaceView.openSync("sample.js")
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.workspaceView.confirmClose).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open("sample.js").then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.workspaceView.confirmClose).toHaveBeenCalled()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "prompts user to save and handler returns true if don't save", ->
|
||||
editor = null
|
||||
spyOn(atom, "confirm").andReturn(2)
|
||||
editor = atom.workspaceView.openSync("sample.js")
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open("sample.js").then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
it "prompts user to save and handler returns false if dialog is canceled", ->
|
||||
editor = null
|
||||
spyOn(atom, "confirm").andReturn(1)
|
||||
editor = atom.workspaceView.openSync("sample.js")
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open("sample.js").then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
editor.insertText("I look different, I feel different.")
|
||||
$(window).trigger(beforeUnloadEvent)
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
|
||||
describe ".unloadEditorWindow()", ->
|
||||
it "saves the serialized state of the window so it can be deserialized after reload", ->
|
||||
workspaceState = atom.workspace.serialize()
|
||||
syntaxState = atom.syntax.serialize()
|
||||
projectState = atom.project.serialize()
|
||||
|
||||
atom.unloadEditorWindow()
|
||||
|
||||
expect(atom.state.workspace).toEqual workspaceState
|
||||
expect(atom.state.syntax).toEqual syntaxState
|
||||
expect(atom.state.project).toEqual projectState
|
||||
expect(atom.saveSync).toHaveBeenCalled()
|
||||
|
||||
it "unsubscribes from all buffers", ->
|
||||
atom.workspaceView.openSync('sample.js')
|
||||
buffer = atom.workspaceView.getActivePaneItem().buffer
|
||||
pane = atom.workspaceView.getActivePane()
|
||||
pane.splitRight(pane.copyActiveItem())
|
||||
expect(atom.workspaceView.find('.editor').length).toBe 2
|
||||
waitsForPromise ->
|
||||
atom.workspace.open("sample.js")
|
||||
|
||||
atom.unloadEditorWindow()
|
||||
runs ->
|
||||
buffer = atom.workspace.getActivePaneItem().buffer
|
||||
pane = atom.workspaceView.getActivePaneView()
|
||||
pane.splitRight(pane.copyActiveItem())
|
||||
expect(atom.workspaceView.find('.editor').length).toBe 2
|
||||
|
||||
expect(buffer.getSubscriptionCount()).toBe 0
|
||||
atom.unloadEditorWindow()
|
||||
|
||||
expect(buffer.getSubscriptionCount()).toBe 0
|
||||
|
||||
describe "drag and drop", ->
|
||||
buildDragEvent = (type, files) ->
|
||||
|
||||
+158
-79
@@ -5,7 +5,7 @@ describe "Workspace", ->
|
||||
|
||||
beforeEach ->
|
||||
atom.project.setPath(atom.project.resolve('dir'))
|
||||
workspace = new Workspace
|
||||
atom.workspace = workspace = new Workspace
|
||||
|
||||
describe "::open(uri, options)", ->
|
||||
beforeEach ->
|
||||
@@ -14,25 +14,40 @@ describe "Workspace", ->
|
||||
describe "when the 'searchAllPanes' option is false (default)", ->
|
||||
describe "when called without a uri", ->
|
||||
it "adds and activates an empty editor on the active pane", ->
|
||||
editor = null
|
||||
[editor1, editor2] = []
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open().then (o) -> editor = o
|
||||
workspace.open().then (editor) -> editor1 = editor
|
||||
|
||||
runs ->
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(workspace.activePane.items).toEqual [editor]
|
||||
expect(workspace.activePaneItem).toBe editor
|
||||
expect(editor1.getPath()).toBeUndefined()
|
||||
expect(workspace.activePane.items).toEqual [editor1]
|
||||
expect(workspace.activePaneItem).toBe editor1
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open().then (editor) -> editor2 = editor
|
||||
|
||||
runs ->
|
||||
expect(editor2.getPath()).toBeUndefined()
|
||||
expect(workspace.activePane.items).toEqual [editor1, editor2]
|
||||
expect(workspace.activePaneItem).toBe editor2
|
||||
expect(workspace.activePane.activate).toHaveBeenCalled()
|
||||
|
||||
describe "when called with a uri", ->
|
||||
describe "when the active pane already has an editor for the given uri", ->
|
||||
it "activates the existing editor on the active pane", ->
|
||||
editor1 = workspace.openSync('a')
|
||||
editor2 = workspace.openSync('b')
|
||||
|
||||
editor = null
|
||||
editor1 = null
|
||||
editor2 = null
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open('a').then (o) -> editor = o
|
||||
workspace.open('a').then (o) ->
|
||||
editor1 = o
|
||||
workspace.open('b').then (o) ->
|
||||
editor2 = o
|
||||
workspace.open('a').then (o) ->
|
||||
editor = o
|
||||
|
||||
runs ->
|
||||
expect(editor).toBe editor1
|
||||
@@ -54,11 +69,21 @@ describe "Workspace", ->
|
||||
describe "when the 'searchAllPanes' option is true", ->
|
||||
describe "when an editor for the given uri is already open on an inactive pane", ->
|
||||
it "activates the existing editor on the inactive pane, then activates that pane", ->
|
||||
editor1 = workspace.openSync('a')
|
||||
pane1 = workspace.activePane
|
||||
pane2 = workspace.activePane.splitRight()
|
||||
editor2 = workspace.openSync('b')
|
||||
expect(workspace.activePaneItem).toBe editor2
|
||||
editor1 = null
|
||||
editor2 = null
|
||||
pane1 = workspace.getActivePane()
|
||||
pane2 = workspace.getActivePane().splitRight()
|
||||
|
||||
waitsForPromise ->
|
||||
pane1.activate()
|
||||
workspace.open('a').then (o) -> editor1 = o
|
||||
|
||||
waitsForPromise ->
|
||||
pane2.activate()
|
||||
workspace.open('b').then (o) -> editor2 = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePaneItem).toBe editor2
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open('a', searchAllPanes: true)
|
||||
@@ -102,6 +127,22 @@ describe "Workspace", ->
|
||||
expect(pane1.items).toEqual [editor]
|
||||
expect(pane2.items).toEqual []
|
||||
|
||||
describe "when a pane axis is the leftmost sibling of the current pane", ->
|
||||
it "opens the new item in the current pane", ->
|
||||
editor = null
|
||||
pane1 = workspace.activePane
|
||||
pane2 = pane1.splitLeft()
|
||||
pane3 = pane2.splitDown()
|
||||
pane1.activate()
|
||||
expect(workspace.activePane).toBe pane1
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open('a', split: 'left').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePane).toBe pane1
|
||||
expect(pane1.items).toEqual [editor]
|
||||
|
||||
describe "when the 'split' option is 'right'", ->
|
||||
it "opens the editor in the rightmost pane of the current pane axis", ->
|
||||
editor = null
|
||||
@@ -126,6 +167,26 @@ describe "Workspace", ->
|
||||
expect(pane1.items).toEqual []
|
||||
expect(pane2.items).toEqual [editor]
|
||||
|
||||
describe "when a pane axis is the rightmost sibling of the current pane", ->
|
||||
it "opens the new item in a new pane split to the right of the current pane", ->
|
||||
editor = null
|
||||
pane1 = workspace.activePane
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitDown()
|
||||
pane1.activate()
|
||||
expect(workspace.activePane).toBe pane1
|
||||
pane4 = null
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open('a', split: 'right').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
pane4 = workspace.getPanes().filter((p) -> p != pane1)[0]
|
||||
expect(workspace.activePane).toBe pane4
|
||||
expect(pane4.items).toEqual [editor]
|
||||
expect(workspace.paneContainer.root.children[0]).toBe pane1
|
||||
expect(workspace.paneContainer.root.children[1]).toBe pane4
|
||||
|
||||
describe "when passed a path that matches a custom opener", ->
|
||||
it "returns the resource returned by the custom opener", ->
|
||||
fooOpener = (pathToOpen, options) -> { foo: pathToOpen, options } if pathToOpen?.match(/\.foo/)
|
||||
@@ -142,80 +203,67 @@ describe "Workspace", ->
|
||||
workspace.open("bar://baz").then (item) ->
|
||||
expect(item).toEqual { bar: "bar://baz" }
|
||||
|
||||
describe "::openSync(uri, options)", ->
|
||||
[activePane, initialItemCount] = []
|
||||
it "emits an 'editor-created' event", ->
|
||||
absolutePath = require.resolve('./fixtures/dir/a')
|
||||
newEditorHandler = jasmine.createSpy('newEditorHandler')
|
||||
workspace.on 'editor-created', newEditorHandler
|
||||
|
||||
beforeEach ->
|
||||
activePane = workspace.activePane
|
||||
spyOn(activePane, 'activate')
|
||||
initialItemCount = activePane.items.length
|
||||
editor = null
|
||||
waitsForPromise ->
|
||||
workspace.open(absolutePath).then (e) -> editor = e
|
||||
|
||||
describe "when called without a uri", ->
|
||||
it "adds and activates an empty editor on the active pane", ->
|
||||
editor = workspace.openSync()
|
||||
expect(activePane.items.length).toBe initialItemCount + 1
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(editor.getPath()).toBeUndefined()
|
||||
expect(activePane.activate).toHaveBeenCalled()
|
||||
runs ->
|
||||
expect(newEditorHandler).toHaveBeenCalledWith editor
|
||||
|
||||
describe "when called with a uri", ->
|
||||
describe "when the active pane already has an editor for the given uri", ->
|
||||
it "activates the existing editor on the active pane", ->
|
||||
editor1 = workspace.openSync('a')
|
||||
editor2 = workspace.openSync('b')
|
||||
expect(activePane.activeItem).toBe editor2
|
||||
expect(activePane.items.length).toBe 2
|
||||
|
||||
editor = workspace.openSync(editor1.getPath())
|
||||
expect(editor).toBe editor1
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(activePane.activate).toHaveBeenCalled()
|
||||
expect(activePane.items.length).toBe 2
|
||||
|
||||
describe "when the active pane does not have an editor for the given uri", ->
|
||||
it "adds and activates a new editor for the given path on the active pane", ->
|
||||
editor = workspace.openSync('a')
|
||||
expect(activePane.items.length).toBe 1
|
||||
expect(activePane.activeItem).toBe editor
|
||||
expect(activePane.activate).toHaveBeenCalled()
|
||||
|
||||
describe "when the 'activatePane' option is false", ->
|
||||
it "does not activate the active pane", ->
|
||||
workspace.openSync('b', activatePane: false)
|
||||
expect(activePane.activate).not.toHaveBeenCalled()
|
||||
|
||||
describe "::reopenItemSync()", ->
|
||||
describe "::reopenItem()", ->
|
||||
it "opens the uri associated with the last closed pane that isn't currently open", ->
|
||||
pane = workspace.activePane
|
||||
workspace.openSync('a')
|
||||
workspace.openSync('b')
|
||||
workspace.openSync('file1')
|
||||
workspace.openSync()
|
||||
waitsForPromise ->
|
||||
workspace.open('a').then ->
|
||||
workspace.open('b').then ->
|
||||
workspace.open('file1').then ->
|
||||
workspace.open()
|
||||
|
||||
# does not reopen items with no uri
|
||||
expect(workspace.activePaneItem.getUri()).toBeUndefined()
|
||||
pane.destroyActiveItem()
|
||||
workspace.reopenItemSync()
|
||||
expect(workspace.activePaneItem.getUri()).not.toBeUndefined()
|
||||
runs ->
|
||||
# does not reopen items with no uri
|
||||
expect(workspace.activePaneItem.getUri()).toBeUndefined()
|
||||
pane.destroyActiveItem()
|
||||
|
||||
# destroy all items
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')
|
||||
pane.destroyActiveItem()
|
||||
waitsForPromise ->
|
||||
workspace.reopenItem()
|
||||
|
||||
# reopens items with uris
|
||||
expect(workspace.activePaneItem).toBeUndefined()
|
||||
workspace.reopenItemSync()
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')
|
||||
runs ->
|
||||
expect(workspace.activePaneItem.getUri()).not.toBeUndefined()
|
||||
|
||||
# destroy all items
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
|
||||
pane.destroyActiveItem()
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')
|
||||
pane.destroyActiveItem()
|
||||
|
||||
# reopens items with uris
|
||||
expect(workspace.activePaneItem).toBeUndefined()
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.reopenItem()
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')
|
||||
|
||||
# does not reopen items that are already open
|
||||
workspace.openSync('b')
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
|
||||
workspace.reopenItemSync()
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')
|
||||
waitsForPromise ->
|
||||
workspace.open('b')
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.reopenItem()
|
||||
|
||||
runs ->
|
||||
expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')
|
||||
|
||||
describe "::increase/decreaseFontSize()", ->
|
||||
it "increases/decreases the font size without going below 1", ->
|
||||
@@ -230,3 +278,34 @@ describe "Workspace", ->
|
||||
expect(atom.config.get('editor.fontSize')).toBe 1
|
||||
workspace.decreaseFontSize()
|
||||
expect(atom.config.get('editor.fontSize')).toBe 1
|
||||
|
||||
describe "::openLicense()", ->
|
||||
it "opens the license as plain-text in a buffer", ->
|
||||
waitsForPromise -> workspace.openLicense()
|
||||
runs -> expect(workspace.activePaneItem.getText()).toMatch /Copyright/
|
||||
|
||||
describe "when an editor is destroyed", ->
|
||||
it "removes the editor", ->
|
||||
editor = null
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open("a").then (e) -> editor = e
|
||||
|
||||
runs ->
|
||||
expect(workspace.getEditors()).toHaveLength 1
|
||||
editor.destroy()
|
||||
expect(workspace.getEditors()).toHaveLength 0
|
||||
|
||||
describe "when an editor is copied", ->
|
||||
it "emits an 'editor-created' event", ->
|
||||
editor = null
|
||||
handler = jasmine.createSpy('editorCreatedHandler')
|
||||
workspace.on 'editor-created', handler
|
||||
|
||||
waitsForPromise ->
|
||||
workspace.open("a").then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
expect(handler.callCount).toBe 1
|
||||
editorCopy = editor.copy()
|
||||
expect(handler.callCount).toBe 2
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{$, $$, fs, WorkspaceView, View} = require 'atom'
|
||||
{$, $$, WorkspaceView, View} = require 'atom'
|
||||
Q = require 'q'
|
||||
path = require 'path'
|
||||
temp = require 'temp'
|
||||
EditorView = require '../src/editor-view'
|
||||
PaneView = require '../src/pane-view'
|
||||
Workspace = require '../src/workspace'
|
||||
|
||||
@@ -14,9 +15,12 @@ describe "WorkspaceView", ->
|
||||
atom.workspace = new Workspace
|
||||
atom.workspaceView = new WorkspaceView(atom.workspace)
|
||||
atom.workspaceView.enableKeymap()
|
||||
atom.workspaceView.openSync(pathToOpen)
|
||||
atom.workspaceView.focus()
|
||||
|
||||
waitsForPromise ->
|
||||
atom.workspace.open(pathToOpen)
|
||||
|
||||
|
||||
describe "@deserialize()", ->
|
||||
viewState = null
|
||||
|
||||
@@ -32,64 +36,82 @@ describe "WorkspaceView", ->
|
||||
describe "when the serialized WorkspaceView has an unsaved buffer", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
atom.workspaceView.attachToDom()
|
||||
atom.workspaceView.openSync()
|
||||
editorView1 = atom.workspaceView.getActiveView()
|
||||
buffer = editorView1.getEditor().getBuffer()
|
||||
editorView1.splitRight()
|
||||
expect(atom.workspaceView.getActivePane()).toBe atom.workspaceView.getPanes()[1]
|
||||
|
||||
simulateReload()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open()
|
||||
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 2
|
||||
expect(atom.workspaceView.getActivePane()).toBe atom.workspaceView.getPanes()[1]
|
||||
expect(atom.workspaceView.title).toBe "untitled - #{atom.project.getPath()}"
|
||||
runs ->
|
||||
editorView1 = atom.workspaceView.getActiveView()
|
||||
buffer = editorView1.getEditor().getBuffer()
|
||||
editorView1.splitRight()
|
||||
expect(atom.workspaceView.getActivePaneView()).toBe atom.workspaceView.getPaneViews()[1]
|
||||
|
||||
simulateReload()
|
||||
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 2
|
||||
expect(atom.workspaceView.getActivePaneView()).toBe atom.workspaceView.getPaneViews()[1]
|
||||
expect(atom.workspaceView.title).toBe "untitled - #{atom.project.getPath()}"
|
||||
|
||||
describe "when there are open editors", ->
|
||||
it "constructs the view with the same panes", ->
|
||||
atom.workspaceView.attachToDom()
|
||||
pane1 = atom.workspaceView.getActivePane()
|
||||
pane1 = atom.workspaceView.getActivePaneView()
|
||||
pane2 = pane1.splitRight()
|
||||
pane3 = pane2.splitRight()
|
||||
pane4 = pane2.splitDown()
|
||||
pane2.activateItem(atom.project.openSync('b'))
|
||||
pane3.activateItem(atom.project.openSync('../sample.js'))
|
||||
pane3.activeItem.setCursorScreenPosition([2, 4])
|
||||
pane4.activateItem(atom.project.openSync('../sample.txt'))
|
||||
pane4.activeItem.setCursorScreenPosition([0, 2])
|
||||
pane2.focus()
|
||||
pane4 = null
|
||||
|
||||
simulateReload()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('b').then (editor) ->
|
||||
pane2.activateItem(editor)
|
||||
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 4
|
||||
editorView1 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(0)').view()
|
||||
editorView3 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(1)').view()
|
||||
editorView2 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(0)').view()
|
||||
editorView4 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(1)').view()
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('../sample.js').then (editor) ->
|
||||
pane3.activateItem(editor)
|
||||
|
||||
expect(editorView1.getEditor().getPath()).toBe atom.project.resolve('a')
|
||||
expect(editorView2.getEditor().getPath()).toBe atom.project.resolve('b')
|
||||
expect(editorView3.getEditor().getPath()).toBe atom.project.resolve('../sample.js')
|
||||
expect(editorView3.getEditor().getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(editorView4.getEditor().getPath()).toBe atom.project.resolve('../sample.txt')
|
||||
expect(editorView4.getEditor().getCursorScreenPosition()).toEqual [0, 2]
|
||||
runs ->
|
||||
pane3.activeItem.setCursorScreenPosition([2, 4])
|
||||
pane4 = pane2.splitDown()
|
||||
|
||||
# ensure adjust pane dimensions is called
|
||||
expect(editorView1.width()).toBeGreaterThan 0
|
||||
expect(editorView2.width()).toBeGreaterThan 0
|
||||
expect(editorView3.width()).toBeGreaterThan 0
|
||||
expect(editorView4.width()).toBeGreaterThan 0
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('../sample.txt').then (editor) ->
|
||||
pane4.activateItem(editor)
|
||||
|
||||
# ensure correct editorView is focused again
|
||||
expect(editorView2.isFocused).toBeTruthy()
|
||||
expect(editorView1.isFocused).toBeFalsy()
|
||||
expect(editorView3.isFocused).toBeFalsy()
|
||||
expect(editorView4.isFocused).toBeFalsy()
|
||||
runs ->
|
||||
pane4.activeItem.setCursorScreenPosition([0, 2])
|
||||
pane2.focus()
|
||||
|
||||
expect(atom.workspaceView.title).toBe "#{path.basename(editorView2.getEditor().getPath())} - #{atom.project.getPath()}"
|
||||
simulateReload()
|
||||
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 4
|
||||
editorView1 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(0)').view()
|
||||
editorView3 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(1)').view()
|
||||
editorView2 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(0)').view()
|
||||
editorView4 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(1)').view()
|
||||
|
||||
expect(editorView1.getEditor().getPath()).toBe atom.project.resolve('a')
|
||||
expect(editorView2.getEditor().getPath()).toBe atom.project.resolve('b')
|
||||
expect(editorView3.getEditor().getPath()).toBe atom.project.resolve('../sample.js')
|
||||
expect(editorView3.getEditor().getCursorScreenPosition()).toEqual [2, 4]
|
||||
expect(editorView4.getEditor().getPath()).toBe atom.project.resolve('../sample.txt')
|
||||
expect(editorView4.getEditor().getCursorScreenPosition()).toEqual [0, 2]
|
||||
|
||||
# ensure adjust pane dimensions is called
|
||||
expect(editorView1.width()).toBeGreaterThan 0
|
||||
expect(editorView2.width()).toBeGreaterThan 0
|
||||
expect(editorView3.width()).toBeGreaterThan 0
|
||||
expect(editorView4.width()).toBeGreaterThan 0
|
||||
|
||||
# ensure correct editorView is focused again
|
||||
expect(editorView2).toHaveFocus()
|
||||
expect(editorView1).not.toHaveFocus()
|
||||
expect(editorView3).not.toHaveFocus()
|
||||
expect(editorView4).not.toHaveFocus()
|
||||
|
||||
expect(atom.workspaceView.title).toBe "#{path.basename(editorView2.getEditor().getPath())} - #{atom.project.getPath()}"
|
||||
|
||||
describe "where there are no open editors", ->
|
||||
it "constructs the view with no open editors", ->
|
||||
atom.workspaceView.getActivePane().remove()
|
||||
atom.workspaceView.getActivePaneView().remove()
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 0
|
||||
simulateReload()
|
||||
expect(atom.workspaceView.getEditorViews().length).toBe 0
|
||||
@@ -99,11 +121,11 @@ describe "WorkspaceView", ->
|
||||
atom.workspaceView.attachToDom()
|
||||
|
||||
it "hands off focus to the active pane", ->
|
||||
activePane = atom.workspaceView.getActivePane()
|
||||
activePane = atom.workspaceView.getActivePaneView()
|
||||
$('body').focus()
|
||||
expect(activePane.hasFocus()).toBe false
|
||||
expect(activePane).not.toHaveFocus()
|
||||
atom.workspaceView.focus()
|
||||
expect(activePane.hasFocus()).toBe true
|
||||
expect(activePane).toHaveFocus()
|
||||
|
||||
describe "keymap wiring", ->
|
||||
commandHandler = null
|
||||
@@ -111,7 +133,7 @@ describe "WorkspaceView", ->
|
||||
commandHandler = jasmine.createSpy('commandHandler')
|
||||
atom.workspaceView.on('foo-command', commandHandler)
|
||||
|
||||
atom.keymap.bindKeys('name', '*', 'x': 'foo-command')
|
||||
atom.keymaps.add('name', '*': {'x': 'foo-command'})
|
||||
|
||||
describe "when a keydown event is triggered in the WorkspaceView", ->
|
||||
it "triggers matching keybindings for that event", ->
|
||||
@@ -128,43 +150,44 @@ describe "WorkspaceView", ->
|
||||
|
||||
describe "when the project has a path", ->
|
||||
beforeEach ->
|
||||
atom.workspaceView.openSync('b')
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('b')
|
||||
|
||||
describe "when there is an active pane item", ->
|
||||
it "sets the title to the pane item's title plus the project path", ->
|
||||
item = atom.workspaceView.getActivePaneItem()
|
||||
item = atom.workspace.getActivePaneItem()
|
||||
expect(atom.workspaceView.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
|
||||
describe "when the title of the active pane item changes", ->
|
||||
it "updates the window title based on the item's new title", ->
|
||||
editor = atom.workspaceView.getActivePaneItem()
|
||||
editor = atom.workspace.getActivePaneItem()
|
||||
editor.buffer.setPath(path.join(temp.dir, 'hi'))
|
||||
expect(atom.workspaceView.title).toBe "#{editor.getTitle()} - #{atom.project.getPath()}"
|
||||
|
||||
describe "when the active pane's item changes", ->
|
||||
it "updates the title to the new item's title plus the project path", ->
|
||||
atom.workspaceView.getActivePane().showNextItem()
|
||||
item = atom.workspaceView.getActivePaneItem()
|
||||
atom.workspaceView.getActivePaneView().activateNextItem()
|
||||
item = atom.workspace.getActivePaneItem()
|
||||
expect(atom.workspaceView.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
|
||||
describe "when the last pane item is removed", ->
|
||||
it "updates the title to contain the project's path", ->
|
||||
atom.workspaceView.getActivePane().remove()
|
||||
expect(atom.workspaceView.getActivePaneItem()).toBeUndefined()
|
||||
atom.workspaceView.getActivePaneView().remove()
|
||||
expect(atom.workspace.getActivePaneItem()).toBeUndefined()
|
||||
expect(atom.workspaceView.title).toBe atom.project.getPath()
|
||||
|
||||
describe "when an inactive pane's item changes", ->
|
||||
it "does not update the title", ->
|
||||
pane = atom.workspaceView.getActivePane()
|
||||
pane = atom.workspaceView.getActivePaneView()
|
||||
pane.splitRight()
|
||||
initialTitle = atom.workspaceView.title
|
||||
pane.showNextItem()
|
||||
pane.activateNextItem()
|
||||
expect(atom.workspaceView.title).toBe initialTitle
|
||||
|
||||
describe "when the root view is deserialized", ->
|
||||
it "updates the title to contain the project's path", ->
|
||||
workspaceView2 = new WorkspaceView(atom.workspace.testSerialization())
|
||||
item = atom.workspaceView.getActivePaneItem()
|
||||
item = atom.workspace.getActivePaneItem()
|
||||
expect(workspaceView2.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}"
|
||||
workspaceView2.remove()
|
||||
|
||||
@@ -222,6 +245,14 @@ describe "WorkspaceView", ->
|
||||
expect(count).toBe 1
|
||||
expect(callbackEditor).toBe atom.workspaceView.getActiveView()
|
||||
|
||||
it "does not invoke the callback for mini editors", ->
|
||||
editorViewCreatedHandler = jasmine.createSpy('editorViewCreatedHandler')
|
||||
atom.workspaceView.eachEditorView(editorViewCreatedHandler)
|
||||
editorViewCreatedHandler.reset()
|
||||
miniEditor = new EditorView(mini: true)
|
||||
atom.workspaceView.append(miniEditor)
|
||||
expect(editorViewCreatedHandler).not.toHaveBeenCalled()
|
||||
|
||||
it "returns a subscription that can be disabled", ->
|
||||
count = 0
|
||||
callback = (editor) -> count++
|
||||
@@ -237,7 +268,27 @@ describe "WorkspaceView", ->
|
||||
describe "core:close", ->
|
||||
it "closes the active pane item until all that remains is a single empty pane", ->
|
||||
atom.config.set('core.destroyEmptyPanes', true)
|
||||
atom.project.openSync('../sample.txt')
|
||||
expect(atom.workspaceView.getActivePane().getItems()).toHaveLength 1
|
||||
|
||||
paneView1 = atom.workspaceView.getActivePaneView()
|
||||
editorView = atom.workspaceView.getActiveView()
|
||||
editorView.splitRight()
|
||||
paneView2 = atom.workspaceView.getActivePaneView()
|
||||
|
||||
expect(paneView1).not.toBe paneView2
|
||||
expect(atom.workspaceView.getPaneViews()).toHaveLength 2
|
||||
atom.workspaceView.trigger('core:close')
|
||||
expect(atom.workspaceView.getActivePane().getItems()).toHaveLength 0
|
||||
|
||||
expect(atom.workspaceView.getActivePaneView().getItems()).toHaveLength 1
|
||||
expect(atom.workspaceView.getPaneViews()).toHaveLength 1
|
||||
atom.workspaceView.trigger('core:close')
|
||||
|
||||
expect(atom.workspaceView.getActivePaneView().getItems()).toHaveLength 0
|
||||
expect(atom.workspaceView.getPaneViews()).toHaveLength 1
|
||||
|
||||
describe "the scrollbar visibility class", ->
|
||||
it "has a class based on the style of the scrollbar", ->
|
||||
scrollbarStyle = require 'scrollbar-style'
|
||||
scrollbarStyle.emitValue 'legacy'
|
||||
expect(atom.workspaceView).toHaveClass 'scrollbars-visible-always'
|
||||
scrollbarStyle.emitValue 'overlay'
|
||||
expect(atom.workspaceView).toHaveClass 'scrollbars-visible-when-scrolling'
|
||||
|
||||
+70
-43
@@ -1,6 +1,5 @@
|
||||
crypto = require 'crypto'
|
||||
ipc = require 'ipc'
|
||||
keytar = require 'keytar'
|
||||
os = require 'os'
|
||||
path = require 'path'
|
||||
remote = require 'remote'
|
||||
@@ -8,6 +7,7 @@ screen = require 'screen'
|
||||
shell = require 'shell'
|
||||
|
||||
_ = require 'underscore-plus'
|
||||
{deprecated} = require 'grim'
|
||||
{Model} = require 'theorist'
|
||||
fs = require 'fs-plus'
|
||||
|
||||
@@ -24,7 +24,7 @@ WindowEventHandler = require './window-event-handler'
|
||||
# * `atom.config` - A {Config} instance
|
||||
# * `atom.contextMenu` - A {ContextMenuManager} instance
|
||||
# * `atom.deserializers` - A {DeserializerManager} instance
|
||||
# * `atom.keymap` - A {Keymap} instance
|
||||
# * `atom.keymaps` - A {Keymap} instance
|
||||
# * `atom.menu` - A {MenuManager} instance
|
||||
# * `atom.packages` - A {PackageManager} instance
|
||||
# * `atom.project` - A {Project} instance
|
||||
@@ -34,18 +34,20 @@ WindowEventHandler = require './window-event-handler'
|
||||
# * `atom.workspaceView` - A {WorkspaceView} instance
|
||||
module.exports =
|
||||
class Atom extends Model
|
||||
@version: 1 # Increment this when the serialization format changes
|
||||
|
||||
# Public: Load or create the Atom environment in the given mode.
|
||||
#
|
||||
# - mode: Pass 'editor' or 'spec' depending on the kind of environment you
|
||||
# want to build.
|
||||
# want to build.
|
||||
#
|
||||
# Returns an Atom instance, fully initialized
|
||||
@loadOrCreate: (mode) ->
|
||||
@deserialize(@loadState(mode)) ? new this({mode, version: @getVersion()})
|
||||
@deserialize(@loadState(mode)) ? new this({mode, @version})
|
||||
|
||||
# Deserializes the Atom environment from a state object
|
||||
@deserialize: (state) ->
|
||||
new this(state) if state?.version is @getVersion()
|
||||
new this(state) if state?.version is @version
|
||||
|
||||
# Loads and returns the serialized state corresponding to this window
|
||||
# if it exists; otherwise returns undefined.
|
||||
@@ -110,7 +112,7 @@ class Atom extends Model
|
||||
|
||||
# Get the version of the Atom application.
|
||||
@getVersion: ->
|
||||
@version ?= @getLoadSettings().appVersion
|
||||
@appVersion ?= @getLoadSettings().appVersion
|
||||
|
||||
# Determine whether the current version is an official release.
|
||||
@isReleasedVersion: ->
|
||||
@@ -130,6 +132,7 @@ class Atom extends Model
|
||||
initialize: ->
|
||||
window.onerror = =>
|
||||
@openDevTools()
|
||||
@executeJavaScriptInDevTools('InspectorFrontendAPI.showConsole()')
|
||||
@emit 'uncaught-error', arguments...
|
||||
|
||||
@unsubscribe()
|
||||
@@ -138,19 +141,29 @@ class Atom extends Model
|
||||
@loadTime = null
|
||||
|
||||
Config = require './config'
|
||||
Keymap = require './keymap'
|
||||
KeymapManager = require './keymap-extensions'
|
||||
PackageManager = require './package-manager'
|
||||
Clipboard = require './clipboard'
|
||||
Syntax = require './syntax'
|
||||
ThemeManager = require './theme-manager'
|
||||
ContextMenuManager = require './context-menu-manager'
|
||||
MenuManager = require './menu-manager'
|
||||
{devMode, resourcePath} = @getLoadSettings()
|
||||
{devMode, safeMode, resourcePath} = @getLoadSettings()
|
||||
configDirPath = @getConfigDirPath()
|
||||
|
||||
# Add 'src/exports' to module search path.
|
||||
exportsPath = path.resolve(resourcePath, 'exports')
|
||||
require('module').globalPaths.push(exportsPath)
|
||||
# Still set NODE_PATH since tasks may need it.
|
||||
process.env.NODE_PATH = exportsPath
|
||||
|
||||
# Make react.js faster
|
||||
process.env.NODE_ENV ?= 'production'
|
||||
|
||||
@config = new Config({configDirPath, resourcePath})
|
||||
@keymap = new Keymap({configDirPath, resourcePath})
|
||||
@packages = new PackageManager({devMode, configDirPath, resourcePath})
|
||||
@keymaps = new KeymapManager({configDirPath, resourcePath})
|
||||
@keymap = @keymaps # Deprecated
|
||||
@packages = new PackageManager({devMode, configDirPath, resourcePath, safeMode})
|
||||
@themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath})
|
||||
@contextMenu = new ContextMenuManager(devMode)
|
||||
@menu = new MenuManager({resourcePath})
|
||||
@@ -171,9 +184,11 @@ class Atom extends Model
|
||||
|
||||
# Deprecated: Callers should be converted to use atom.deserializers
|
||||
registerRepresentationClass: ->
|
||||
deprecated("Callers should be converted to use atom.deserializers")
|
||||
|
||||
# Deprecated: Callers should be converted to use atom.deserializers
|
||||
registerRepresentationClasses: ->
|
||||
deprecated("Callers should be converted to use atom.deserializers")
|
||||
|
||||
setBodyPlatformClass: ->
|
||||
document.body.classList.add("platform-#{process.platform}")
|
||||
@@ -210,12 +225,26 @@ class Atom extends Model
|
||||
else
|
||||
@center()
|
||||
|
||||
storeDefaultWindowDimensions: ->
|
||||
dimensions = JSON.stringify(atom.getWindowDimensions())
|
||||
localStorage.setItem("defaultWindowDimensions", dimensions)
|
||||
|
||||
getDefaultWindowDimensions: ->
|
||||
{windowDimensions} = @getLoadSettings()
|
||||
return windowDimensions if windowDimensions?
|
||||
|
||||
dimensions = null
|
||||
try
|
||||
dimensions = JSON.parse(localStorage.getItem("defaultWindowDimensions"))
|
||||
catch error
|
||||
console.warn "Error parsing default window dimensions", error
|
||||
localStorage.removeItem("defaultWindowDimensions")
|
||||
|
||||
{width, height} = screen.getPrimaryDisplay().workAreaSize
|
||||
dimensions ? {x: 0, y: 0, width: Math.min(1024, width), height: height}
|
||||
|
||||
restoreWindowDimensions: ->
|
||||
workAreaSize = screen.getPrimaryDisplay().workAreaSize
|
||||
windowDimensions = @state.windowDimensions ? {}
|
||||
{initialSize} = @getLoadSettings()
|
||||
windowDimensions.height ?= initialSize?.height ? workAreaSize.height
|
||||
windowDimensions.width ?= initialSize?.width ? Math.min(workAreaSize.width, 1024)
|
||||
windowDimensions = @state.windowDimensions ? @getDefaultWindowDimensions()
|
||||
@setWindowDimensions(windowDimensions)
|
||||
|
||||
storeWindowDimensions: ->
|
||||
@@ -229,13 +258,14 @@ class Atom extends Model
|
||||
|
||||
deserializeProject: ->
|
||||
Project = require './project'
|
||||
@project ?= @deserializers.deserialize(@project) ? new Project(path: @getLoadSettings().initialPath)
|
||||
@project ?= @deserializers.deserialize(@state.project) ? new Project(path: @getLoadSettings().initialPath)
|
||||
|
||||
deserializeWorkspaceView: ->
|
||||
Workspace = require './workspace'
|
||||
WorkspaceView = require './workspace-view'
|
||||
@workspace = Workspace.deserialize(@state.workspace) ? new Workspace
|
||||
@workspaceView = new WorkspaceView(@workspace)
|
||||
@keymaps.defaultTarget = @workspaceView[0]
|
||||
$(@workspaceViewParentSelector).append(@workspaceView)
|
||||
|
||||
deserializePackageStates: ->
|
||||
@@ -260,12 +290,12 @@ class Atom extends Model
|
||||
@config.load()
|
||||
@config.setDefaults('core', require('./workspace-view').configDefaults)
|
||||
@config.setDefaults('editor', require('./editor-view').configDefaults)
|
||||
@keymap.loadBundledKeymaps()
|
||||
@keymaps.loadBundledKeymaps()
|
||||
@themes.loadBaseStylesheets()
|
||||
@packages.loadPackages()
|
||||
@deserializeEditorWindow()
|
||||
@packages.activate()
|
||||
@keymap.loadUserKeymap()
|
||||
@keymaps.loadUserKeymap()
|
||||
@requireUserInitScript()
|
||||
@menu.update()
|
||||
|
||||
@@ -280,6 +310,7 @@ class Atom extends Model
|
||||
return if not @project and not @workspaceView
|
||||
|
||||
@state.syntax = @syntax.serialize()
|
||||
@state.project = @project.serialize()
|
||||
@state.workspace = @workspace.serialize()
|
||||
@packages.deactivatePackages()
|
||||
@state.packageStates = @packages.packageStates
|
||||
@@ -288,7 +319,7 @@ class Atom extends Model
|
||||
@workspaceView = null
|
||||
@project.destroy()
|
||||
@windowEventHandler?.unsubscribe()
|
||||
@keymap.destroy()
|
||||
@keymaps.destroy()
|
||||
@windowState = null
|
||||
|
||||
loadThemes: ->
|
||||
@@ -309,7 +340,7 @@ class Atom extends Model
|
||||
# options - An {Object} with the following keys:
|
||||
# :pathsToOpen - An {Array} of {String} paths to open.
|
||||
open: (options) ->
|
||||
ipc.sendChannel('open', options)
|
||||
ipc.send('open', options)
|
||||
|
||||
# Public: Open a confirm dialog.
|
||||
#
|
||||
@@ -363,46 +394,50 @@ class Atom extends Model
|
||||
|
||||
# Public: Open the dev tools for the current window.
|
||||
openDevTools: ->
|
||||
ipc.sendChannel('call-window-method', 'openDevTools')
|
||||
ipc.send('call-window-method', 'openDevTools')
|
||||
|
||||
# Public: Toggle the visibility of the dev tools for the current window.
|
||||
toggleDevTools: ->
|
||||
ipc.sendChannel('call-window-method', 'toggleDevTools')
|
||||
ipc.send('call-window-method', 'toggleDevTools')
|
||||
|
||||
# Public: Execute code in dev tools.
|
||||
executeJavaScriptInDevTools: (code) ->
|
||||
ipc.send('call-window-method', 'executeJavaScriptInDevTools', code)
|
||||
|
||||
# Public: Reload the current window.
|
||||
reload: ->
|
||||
ipc.sendChannel('call-window-method', 'restart')
|
||||
ipc.send('call-window-method', 'restart')
|
||||
|
||||
# Public: Focus the current window.
|
||||
focus: ->
|
||||
ipc.sendChannel('call-window-method', 'focus')
|
||||
ipc.send('call-window-method', 'focus')
|
||||
$(window).focus()
|
||||
|
||||
# Public: Show the current window.
|
||||
show: ->
|
||||
ipc.sendChannel('call-window-method', 'show')
|
||||
ipc.send('call-window-method', 'show')
|
||||
|
||||
# Public: Hide the current window.
|
||||
hide: ->
|
||||
ipc.sendChannel('call-window-method', 'hide')
|
||||
ipc.send('call-window-method', 'hide')
|
||||
|
||||
# Public: Set the size of current window.
|
||||
#
|
||||
# width - The {Number} of pixels.
|
||||
# height - The {Number} of pixels.
|
||||
setSize: (width, height) ->
|
||||
ipc.sendChannel('call-window-method', 'setSize', width, height)
|
||||
ipc.send('call-window-method', 'setSize', width, height)
|
||||
|
||||
# Public: Set the position of current window.
|
||||
#
|
||||
# x - The {Number} of pixels.
|
||||
# y - The {Number} of pixels.
|
||||
setPosition: (x, y) ->
|
||||
ipc.sendChannel('call-window-method', 'setPosition', x, y)
|
||||
ipc.send('call-window-method', 'setPosition', x, y)
|
||||
|
||||
# Public: Move current window to the center of the screen.
|
||||
center: ->
|
||||
ipc.sendChannel('call-window-method', 'center')
|
||||
ipc.send('call-window-method', 'center')
|
||||
|
||||
# Schedule the window to be shown and focused on the next tick.
|
||||
#
|
||||
@@ -421,7 +456,7 @@ class Atom extends Model
|
||||
exit: (status) ->
|
||||
app = remote.require('app')
|
||||
app.emit('will-exit')
|
||||
app.exit(status)
|
||||
remote.process.exit(status)
|
||||
|
||||
# Public: Is the current window in development mode?
|
||||
inDevMode: ->
|
||||
@@ -437,13 +472,16 @@ class Atom extends Model
|
||||
|
||||
# Public: Set the full screen state of the current window.
|
||||
setFullScreen: (fullScreen=false) ->
|
||||
ipc.sendChannel('call-window-method', 'setFullScreen', fullScreen)
|
||||
ipc.send('call-window-method', 'setFullScreen', fullScreen)
|
||||
if fullScreen then document.body.classList.add("fullscreen") else document.body.classList.remove("fullscreen")
|
||||
|
||||
# Public: Is the current window in full screen mode?
|
||||
isFullScreen: ->
|
||||
@getCurrentWindow().isFullScreen()
|
||||
|
||||
# Public: Get the version of the Atom application.
|
||||
#
|
||||
# Returns the version text {String}.
|
||||
getVersion: ->
|
||||
@constructor.getVersion()
|
||||
|
||||
@@ -451,20 +489,9 @@ class Atom extends Model
|
||||
isReleasedVersion: ->
|
||||
@constructor.isReleasedVersion()
|
||||
|
||||
getGitHubAuthTokenName: ->
|
||||
'Atom GitHub API Token'
|
||||
|
||||
# Public: Set the the github token in the keychain
|
||||
setGitHubAuthToken: (token) ->
|
||||
keytar.replacePassword(@getGitHubAuthTokenName(), 'github', token)
|
||||
|
||||
# Public: Get the github token from the keychain
|
||||
getGitHubAuthToken: ->
|
||||
keytar.getPassword(@getGitHubAuthTokenName(), 'github')
|
||||
|
||||
# Public: Get the directory path to Atom's configuration area.
|
||||
#
|
||||
# Returns the absolute path to ~/.atom
|
||||
# Returns the absolute path to `~/.atom`.
|
||||
getConfigDirPath: ->
|
||||
@constructor.getConfigDirPath()
|
||||
|
||||
|
||||
@@ -9,30 +9,28 @@ _ = require 'underscore-plus'
|
||||
# and maintain the state of all menu items.
|
||||
module.exports =
|
||||
class ApplicationMenu
|
||||
version: null
|
||||
menu: null
|
||||
|
||||
constructor: (@version) ->
|
||||
@menu = Menu.buildFromTemplate @getDefaultTemplate()
|
||||
Menu.setApplicationMenu @menu
|
||||
global.atomApplication.autoUpdateManager.on 'state-changed', (state) =>
|
||||
@showUpdateMenuItem(state)
|
||||
|
||||
# Public: Updates the entire menu with the given keybindings.
|
||||
#
|
||||
# * template:
|
||||
# The Object which describes the menu to display.
|
||||
# * keystrokesByCommand:
|
||||
# An Object where the keys are commands and the values are Arrays containing
|
||||
# the keystroke.
|
||||
# template - The Object which describes the menu to display.
|
||||
# keystrokesByCommand - An Object where the keys are commands and the values
|
||||
# are Arrays containing the keystroke.
|
||||
update: (template, keystrokesByCommand) ->
|
||||
@translateTemplate(template, keystrokesByCommand)
|
||||
@substituteVersion(template)
|
||||
@menu = Menu.buildFromTemplate(template)
|
||||
Menu.setApplicationMenu(@menu)
|
||||
|
||||
@showUpdateMenuItem(global.atomApplication.autoUpdateManager.getState())
|
||||
|
||||
# Flattens the given menu and submenu items into an single Array.
|
||||
#
|
||||
# * menu:
|
||||
# A complete menu configuration object for atom-shell's menu API.
|
||||
# menu - A complete menu configuration object for atom-shell's menu API.
|
||||
#
|
||||
# Returns an Array of native menu items.
|
||||
flattenMenuItems: (menu) ->
|
||||
@@ -44,8 +42,7 @@ class ApplicationMenu
|
||||
|
||||
# Flattens the given menu template into an single Array.
|
||||
#
|
||||
# * template:
|
||||
# An object describing the menu item.
|
||||
# template - An object describing the menu item.
|
||||
#
|
||||
# Returns an Array of native menu items.
|
||||
flattenMenuTemplate: (template) ->
|
||||
@@ -57,27 +54,36 @@ class ApplicationMenu
|
||||
|
||||
# Public: Used to make all window related menu items are active.
|
||||
#
|
||||
# * enable:
|
||||
# If true enables all window specific items, if false disables all window
|
||||
# specific items.
|
||||
# enable - If true enables all window specific items, if false disables all
|
||||
# window specific items.
|
||||
enableWindowSpecificItems: (enable) ->
|
||||
for item in @flattenMenuItems(@menu)
|
||||
item.enabled = enable if item.metadata?['windowSpecific']
|
||||
|
||||
# Replaces VERSION with the current version.
|
||||
substituteVersion: (template) ->
|
||||
if (item = _.find(@flattenMenuTemplate(template), (i) -> i.label == 'VERSION'))
|
||||
if (item = _.find(@flattenMenuTemplate(template), ({label}) -> label == 'VERSION'))
|
||||
item.label = "Version #{@version}"
|
||||
|
||||
# Toggles Install Update Item
|
||||
showInstallUpdateItem: (visible=true) ->
|
||||
if (item = _.find(@flattenMenuItems(@menu), (i) -> i.label == 'Restart and Install Update'))
|
||||
item.visible = visible
|
||||
# Sets the proper visible state the update menu items
|
||||
showUpdateMenuItem: (state) ->
|
||||
checkForUpdateItem = _.find(@flattenMenuItems(@menu), ({label}) -> label == 'Check for Update')
|
||||
downloadingUpdateItem = _.find(@flattenMenuItems(@menu), ({label}) -> label == 'Downloading Update')
|
||||
installUpdateItem = _.find(@flattenMenuItems(@menu), ({label}) -> label == 'Restart and Install Update')
|
||||
|
||||
# Toggles Check For Update Item
|
||||
showCheckForUpdateItem: (visible=true) ->
|
||||
if (item = _.find(@flattenMenuItems(@menu), (i) -> i.label == 'Check for Update'))
|
||||
item.visible = visible
|
||||
return unless checkForUpdateItem? and downloadingUpdateItem? and installUpdateItem?
|
||||
|
||||
checkForUpdateItem.visible = false
|
||||
downloadingUpdateItem.visible = false
|
||||
installUpdateItem.visible = false
|
||||
|
||||
switch state
|
||||
when 'idle', 'error', 'no-update-available'
|
||||
checkForUpdateItem.visible = true
|
||||
when 'checking', 'downloading'
|
||||
downloadingUpdateItem.visible = true
|
||||
when 'update-available'
|
||||
installUpdateItem.visible = true
|
||||
|
||||
# Default list of menu items.
|
||||
#
|
||||
@@ -86,26 +92,28 @@ class ApplicationMenu
|
||||
[
|
||||
label: "Atom"
|
||||
submenu: [
|
||||
{ label: 'Reload', accelerator: 'Command+R', click: -> @focusedWindow()?.reload() }
|
||||
{ label: 'Close Window', accelerator: 'Command+Shift+W', click: -> @focusedWindow()?.close() }
|
||||
{ label: 'Toggle Dev Tools', accelerator: 'Command+Alt+I', click: -> @focusedWindow()?.toggleDevTools() }
|
||||
{ label: "Check for Update", metadata: {autoUpdate: true}}
|
||||
{ label: 'Reload', accelerator: 'Command+R', click: => @focusedWindow()?.reload() }
|
||||
{ label: 'Close Window', accelerator: 'Command+Shift+W', click: => @focusedWindow()?.close() }
|
||||
{ label: 'Toggle Dev Tools', accelerator: 'Command+Alt+I', click: => @focusedWindow()?.toggleDevTools() }
|
||||
{ label: 'Quit', accelerator: 'Command+Q', click: -> app.quit() }
|
||||
]
|
||||
]
|
||||
|
||||
focusedWindow: ->
|
||||
_.find global.atomApplication.windows, (atomWindow) -> atomWindow.isFocused()
|
||||
|
||||
# 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 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 keystroke.
|
||||
#
|
||||
# Returns a complete menu configuration object for atom-shell's menu API.
|
||||
translateTemplate: (template, keystrokesByCommand) ->
|
||||
template.forEach (item) =>
|
||||
item.metadata = {}
|
||||
item.metadata ?= {}
|
||||
if item.command
|
||||
item.accelerator = @acceleratorForCommand(item.command, keystrokesByCommand)
|
||||
item.click = => global.atomApplication.sendCommand(item.command)
|
||||
@@ -115,11 +123,9 @@ class ApplicationMenu
|
||||
|
||||
# Determine the accelerator for a given command.
|
||||
#
|
||||
# * command:
|
||||
# The name of the command.
|
||||
# * keystrokesByCommand:
|
||||
# An Object where the keys are commands and the values are Arrays containing
|
||||
# the keystroke.
|
||||
# command - The name of the command.
|
||||
# keystrokesByCommand - An Object where the keys are commands and the values
|
||||
# are Arrays containing the keystroke.
|
||||
#
|
||||
# Returns a String containing the keystroke in a format that can be interpreted
|
||||
# by atom shell to provide nice icons where available.
|
||||
@@ -130,7 +136,6 @@ class ApplicationMenu
|
||||
modifiers = firstKeystroke.split('-')
|
||||
key = modifiers.pop()
|
||||
|
||||
modifiers.push("Shift") if key != key.toLowerCase()
|
||||
modifiers = modifiers.map (modifier) ->
|
||||
modifier.replace(/shift/ig, "Shift")
|
||||
.replace(/cmd/ig, "Command")
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
AtomWindow = require './atom-window'
|
||||
ApplicationMenu = require './application-menu'
|
||||
AtomProtocolHandler = require './atom-protocol-handler'
|
||||
AutoUpdateManager = require './auto-update-manager'
|
||||
BrowserWindow = require 'browser-window'
|
||||
Menu = require 'menu'
|
||||
autoUpdater = require 'auto-updater'
|
||||
app = require 'app'
|
||||
dialog = require 'dialog'
|
||||
fs = require 'fs'
|
||||
@@ -30,7 +30,6 @@ socketPath =
|
||||
module.exports =
|
||||
class AtomApplication
|
||||
_.extend @prototype, EventEmitter.prototype
|
||||
updateVersion: null
|
||||
|
||||
# Public: The entry point into the Atom application.
|
||||
@open: (options) ->
|
||||
@@ -60,33 +59,33 @@ class AtomApplication
|
||||
exit: (status) -> app.exit(status)
|
||||
|
||||
constructor: (options) ->
|
||||
{@resourcePath, @version, @devMode} = options
|
||||
{@resourcePath, @version, @devMode, @safeMode} = options
|
||||
global.atomApplication = this
|
||||
|
||||
@pidsToOpenWindows = {}
|
||||
@pathsToOpen ?= []
|
||||
@windows = []
|
||||
|
||||
@autoUpdateManager = new AutoUpdateManager(@version)
|
||||
@applicationMenu = new ApplicationMenu(@version)
|
||||
@atomProtocolHandler = new AtomProtocolHandler(@resourcePath)
|
||||
|
||||
@listenForArgumentsFromNewProcess()
|
||||
@setupJavaScriptArguments()
|
||||
@handleEvents()
|
||||
@setupAutoUpdater()
|
||||
|
||||
@openWithOptions(options)
|
||||
|
||||
# Opens a new window based on the options provided.
|
||||
openWithOptions: ({pathsToOpen, urlsToOpen, test, pidToKillWhenClosed, devMode, newWindow, specDirectory, logFile}) ->
|
||||
openWithOptions: ({pathsToOpen, urlsToOpen, test, pidToKillWhenClosed, devMode, safeMode, newWindow, specDirectory, logFile}) ->
|
||||
if test
|
||||
@runSpecs({exitWhenDone: true, @resourcePath, specDirectory, logFile})
|
||||
else if pathsToOpen.length > 0
|
||||
@openPaths({pathsToOpen, pidToKillWhenClosed, newWindow, devMode})
|
||||
@openPaths({pathsToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode})
|
||||
else if urlsToOpen.length > 0
|
||||
@openUrl({urlToOpen, devMode}) for urlToOpen in urlsToOpen
|
||||
@openUrl({urlToOpen, devMode, safeMode}) for urlToOpen in urlsToOpen
|
||||
else
|
||||
@openPath({pidToKillWhenClosed, newWindow, devMode}) # Always open a editor window if this is the first instance of Atom.
|
||||
@openPath({pidToKillWhenClosed, newWindow, devMode, safeMode}) # Always open a editor window if this is the first instance of Atom.
|
||||
|
||||
# Public: Removes the {AtomWindow} from the global window list.
|
||||
removeWindow: (window) ->
|
||||
@@ -97,6 +96,8 @@ class AtomApplication
|
||||
addWindow: (window) ->
|
||||
@windows.push window
|
||||
@applicationMenu?.enableWindowSpecificItems(true)
|
||||
window.once 'window:loaded', =>
|
||||
@autoUpdateManager.emitUpdateAvailableEvent(window)
|
||||
|
||||
# Creates server to listen for additional atom application launches.
|
||||
#
|
||||
@@ -126,78 +127,39 @@ class AtomApplication
|
||||
|
||||
# Configures required javascript environment flags.
|
||||
setupJavaScriptArguments: ->
|
||||
app.commandLine.appendSwitch 'js-flags', '--harmony_collections --harmony-proxies'
|
||||
|
||||
# Enable updates unless running from a local build of Atom.
|
||||
setupAutoUpdater: ->
|
||||
autoUpdater.setFeedUrl "https://atom.io/api/updates?version=#{@version}"
|
||||
|
||||
autoUpdater.on 'checking-for-update', =>
|
||||
@applicationMenu.showInstallUpdateItem(false)
|
||||
@applicationMenu.showCheckForUpdateItem(false)
|
||||
|
||||
autoUpdater.on 'update-not-available', =>
|
||||
@applicationMenu.showInstallUpdateItem(false)
|
||||
@applicationMenu.showCheckForUpdateItem(true)
|
||||
|
||||
autoUpdater.on 'update-downloaded', (event, releaseNotes, releaseName, releaseDate, releaseURL) =>
|
||||
atomWindow.sendCommand('window:update-available', releaseName) for atomWindow in @windows
|
||||
@applicationMenu.showInstallUpdateItem(true)
|
||||
@applicationMenu.showCheckForUpdateItem(false)
|
||||
@updateVersion = releaseName
|
||||
|
||||
autoUpdater.on 'error', (event, message) =>
|
||||
@applicationMenu.showInstallUpdateItem(false)
|
||||
@applicationMenu.showCheckForUpdateItem(true)
|
||||
|
||||
# Check for update after Atom has fully started and the menus are created
|
||||
setTimeout((-> autoUpdater.checkForUpdates()), 5000)
|
||||
|
||||
checkForUpdate: ->
|
||||
autoUpdater.once 'update-available', ->
|
||||
dialog.showMessageBox
|
||||
type: 'info'
|
||||
buttons: ['OK']
|
||||
message: 'Update available.'
|
||||
detail: 'A new update is being downloading.'
|
||||
|
||||
autoUpdater.once 'update-not-available', =>
|
||||
dialog.showMessageBox
|
||||
type: 'info'
|
||||
buttons: ['OK']
|
||||
message: 'No update available.'
|
||||
detail: "Version #{@version} is the latest version."
|
||||
|
||||
autoUpdater.once 'error', (event, message)->
|
||||
dialog.showMessageBox
|
||||
type: 'warning'
|
||||
buttons: ['OK']
|
||||
message: 'There was an error checking for updates.'
|
||||
detail: message
|
||||
|
||||
autoUpdater.checkForUpdates()
|
||||
app.commandLine.appendSwitch 'js-flags', '--harmony'
|
||||
|
||||
# Registers basic application commands, non-idempotent.
|
||||
handleEvents: ->
|
||||
@on 'application:about', -> Menu.sendActionToFirstResponder('orderFrontStandardAboutPanel:')
|
||||
@on 'application:run-all-specs', -> @runSpecs(exitWhenDone: false, resourcePath: global.devResourcePath)
|
||||
@on 'application:run-benchmarks', -> @runBenchmarks()
|
||||
@on 'application:quit', -> app.quit()
|
||||
@on 'application:hide', -> Menu.sendActionToFirstResponder('hide:')
|
||||
@on 'application:hide-other-applications', -> Menu.sendActionToFirstResponder('hideOtherApplications:')
|
||||
@on 'application:unhide-all-applications', -> Menu.sendActionToFirstResponder('unhideAllApplications:')
|
||||
@on 'application:new-window', -> @openPath(initialSize: @getFocusedWindowSize())
|
||||
@on 'application:new-window', -> @openPath(windowDimensions: @focusedWindow()?.getDimensions())
|
||||
@on 'application:new-file', -> (@focusedWindow() ? this).openPath()
|
||||
@on 'application:open', -> @promptForPath()
|
||||
@on 'application:open', -> @promptForPath(type: 'all')
|
||||
@on 'application:open-file', -> @promptForPath(type: 'file')
|
||||
@on 'application:open-folder', -> @promptForPath(type: 'folder')
|
||||
@on 'application:open-dev', -> @promptForPath(devMode: true)
|
||||
@on 'application:minimize', -> Menu.sendActionToFirstResponder('performMiniaturize:')
|
||||
@on 'application:zoom', -> Menu.sendActionToFirstResponder('zoom:')
|
||||
@on 'application:bring-all-windows-to-front', -> Menu.sendActionToFirstResponder('arrangeInFront:')
|
||||
@on 'application:inspect', ({x,y}) -> @focusedWindow().browserWindow.inspectElement(x, y)
|
||||
@on 'application:open-documentation', -> shell.openExternal('https://www.atom.io/docs/latest/?app')
|
||||
@on 'application:report-issue', -> shell.openExternal('https://github.com/atom/atom/issues/new')
|
||||
@on 'application:install-update', -> autoUpdater.quitAndInstall()
|
||||
@on 'application:check-for-update', => @checkForUpdate()
|
||||
@on 'application:open-safe', -> @promptForPath(safeMode: true)
|
||||
@on 'application:inspect', ({x,y, atomWindow}) ->
|
||||
atomWindow ?= @focusedWindow()
|
||||
atomWindow?.browserWindow.inspectElement(x, y)
|
||||
|
||||
@on 'application:open-documentation', -> shell.openExternal('https://atom.io/docs/latest/?app')
|
||||
@on 'application:install-update', -> @autoUpdateManager.install()
|
||||
@on 'application:check-for-update', => @autoUpdateManager.check()
|
||||
|
||||
if process.platform is 'darwin'
|
||||
@on 'application:about', -> Menu.sendActionToFirstResponder('orderFrontStandardAboutPanel:')
|
||||
@on 'application:bring-all-windows-to-front', -> Menu.sendActionToFirstResponder('arrangeInFront:')
|
||||
@on 'application:hide', -> Menu.sendActionToFirstResponder('hide:')
|
||||
@on 'application:hide-other-applications', -> Menu.sendActionToFirstResponder('hideOtherApplications:')
|
||||
@on 'application:minimize', -> Menu.sendActionToFirstResponder('performMiniaturize:')
|
||||
@on 'application:unhide-all-applications', -> Menu.sendActionToFirstResponder('unhideAllApplications:')
|
||||
@on 'application:zoom', -> Menu.sendActionToFirstResponder('zoom:')
|
||||
else
|
||||
@on 'application:minimize', -> @focusedWindow()?.minimize()
|
||||
@on 'application:zoom', -> @focusedWindow()?.maximize()
|
||||
|
||||
@openPathOnEvent('application:show-settings', 'atom://config')
|
||||
@openPathOnEvent('application:open-your-config', 'atom://.atom/config')
|
||||
@@ -205,12 +167,18 @@ class AtomApplication
|
||||
@openPathOnEvent('application:open-your-keymap', 'atom://.atom/keymap')
|
||||
@openPathOnEvent('application:open-your-snippets', 'atom://.atom/snippets')
|
||||
@openPathOnEvent('application:open-your-stylesheet', 'atom://.atom/stylesheet')
|
||||
@openPathOnEvent('application:open-license', path.join(@resourcePath, 'LICENSE.md'))
|
||||
|
||||
app.on 'window-all-closed', ->
|
||||
app.quit() if process.platform is 'win32'
|
||||
app.quit() if process.platform in ['win32', 'linux']
|
||||
|
||||
app.on 'will-quit', => @deleteSocketFile()
|
||||
app.on 'will-exit', => @deleteSocketFile()
|
||||
app.on 'will-quit', =>
|
||||
@killAllProcesses()
|
||||
@deleteSocketFile()
|
||||
|
||||
app.on 'will-exit', =>
|
||||
@killAllProcesses()
|
||||
@deleteSocketFile()
|
||||
|
||||
app.on 'open-file', (event, pathToOpen) =>
|
||||
event.preventDefault()
|
||||
@@ -218,10 +186,14 @@ class AtomApplication
|
||||
|
||||
app.on 'open-url', (event, urlToOpen) =>
|
||||
event.preventDefault()
|
||||
@openUrl({urlToOpen, @devMode})
|
||||
@openUrl({urlToOpen, @devMode, @safeMode})
|
||||
|
||||
app.on 'activate-with-no-open-windows', (event) =>
|
||||
event.preventDefault()
|
||||
@emit('application:new-window')
|
||||
|
||||
# A request from the associated render process to open a new render process.
|
||||
ipc.on 'open', (processId, routingId, options) =>
|
||||
ipc.on 'open', (event, options) =>
|
||||
if options?
|
||||
if options.pathsToOpen?.length > 0
|
||||
@openPaths(options)
|
||||
@@ -230,42 +202,71 @@ class AtomApplication
|
||||
else
|
||||
@promptForPath()
|
||||
|
||||
ipc.on 'update-application-menu', (processId, routingId, template, keystrokesByCommand) =>
|
||||
ipc.on 'update-application-menu', (event, template, keystrokesByCommand) =>
|
||||
@applicationMenu.update(template, keystrokesByCommand)
|
||||
|
||||
ipc.on 'run-package-specs', (processId, routingId, specDirectory) =>
|
||||
ipc.on 'run-package-specs', (event, specDirectory) =>
|
||||
@runSpecs({resourcePath: global.devResourcePath, specDirectory: specDirectory, exitWhenDone: false})
|
||||
|
||||
ipc.on 'command', (processId, routingId, command) =>
|
||||
ipc.on 'command', (event, command) =>
|
||||
@emit(command)
|
||||
|
||||
ipc.on 'window-command', (processId, routingId, command, args...) ->
|
||||
win = BrowserWindow.fromProcessIdAndRoutingId(processId, routingId)
|
||||
ipc.on 'window-command', (event, command, args...) ->
|
||||
win = BrowserWindow.fromWebContents(event.sender)
|
||||
win.emit(command, args...)
|
||||
|
||||
ipc.on 'call-window-method', (processId, routingId, method, args...) ->
|
||||
win = BrowserWindow.fromProcessIdAndRoutingId(processId, routingId)
|
||||
ipc.on 'call-window-method', (event, method, args...) ->
|
||||
win = BrowserWindow.fromWebContents(event.sender)
|
||||
win[method](args...)
|
||||
|
||||
# Public: Executes the given command.
|
||||
#
|
||||
# If it isn't handled globally, delegate to the currently focused window.
|
||||
#
|
||||
# * command:
|
||||
# The string representing the command.
|
||||
# * args:
|
||||
# The optional arguments to pass along.
|
||||
# command - The string representing the command.
|
||||
# args - The optional arguments to pass along.
|
||||
sendCommand: (command, args...) ->
|
||||
unless @emit(command, args...)
|
||||
@focusedWindow()?.sendCommand(command, args...)
|
||||
focusedWindow = @focusedWindow()
|
||||
if focusedWindow?
|
||||
focusedWindow.sendCommand(command, args...)
|
||||
else
|
||||
@sendCommandToFirstResponder(command)
|
||||
|
||||
# Public: Executes the given command on the given window.
|
||||
#
|
||||
# command - The string representing the command.
|
||||
# atomWindow - The {AtomWindow} to send the command to.
|
||||
# args - The optional arguments to pass along.
|
||||
sendCommandToWindow: (command, atomWindow, args...) ->
|
||||
unless @emit(command, args...)
|
||||
if atomWindow?
|
||||
atomWindow.sendCommand(command, args...)
|
||||
else
|
||||
@sendCommandToFirstResponder(command)
|
||||
|
||||
# Translates the command into OS X action and sends it to application's first
|
||||
# responder.
|
||||
sendCommandToFirstResponder: (command) ->
|
||||
return false unless process.platform is 'darwin'
|
||||
|
||||
switch command
|
||||
when 'core:undo' then Menu.sendActionToFirstResponder('undo:')
|
||||
when 'core:redo' then Menu.sendActionToFirstResponder('redo:')
|
||||
when 'core:copy' then Menu.sendActionToFirstResponder('copy:')
|
||||
when 'core:cut' then Menu.sendActionToFirstResponder('cut:')
|
||||
when 'core:paste' then Menu.sendActionToFirstResponder('paste:')
|
||||
when 'core:select-all' then Menu.sendActionToFirstResponder('selectAll:')
|
||||
else return false
|
||||
true
|
||||
|
||||
# Public: Open the given path in the focused window when the event is
|
||||
# triggered.
|
||||
#
|
||||
# A new window will be created if there is no currently focused window.
|
||||
#
|
||||
# * eventName: The event to listen for.
|
||||
# * pathToOpen: The path to open when the event is triggered.
|
||||
# eventName - The event to listen for.
|
||||
# pathToOpen - The path to open when the event is triggered.
|
||||
openPathOnEvent: (eventName, pathToOpen) ->
|
||||
@on eventName, ->
|
||||
if window = @focusedWindow()
|
||||
@@ -282,84 +283,75 @@ class AtomApplication
|
||||
focusedWindow: ->
|
||||
_.find @windows, (atomWindow) -> atomWindow.isFocused()
|
||||
|
||||
# Public: Get the height and width of the focused window.
|
||||
#
|
||||
# Returns an object with height and width keys or null if there is no
|
||||
# focused window.
|
||||
getFocusedWindowSize: ->
|
||||
if focusedWindow = @focusedWindow()
|
||||
[width, height] = focusedWindow.getSize()
|
||||
{width, height}
|
||||
else
|
||||
null
|
||||
|
||||
# Public: Opens multiple paths, in existing windows if possible.
|
||||
#
|
||||
# * options
|
||||
# + pathsToOpen:
|
||||
# The array of file paths to open
|
||||
# + pidToKillWhenClosed:
|
||||
# The integer of the pid to kill
|
||||
# + newWindow:
|
||||
# Boolean of whether this should be opened in a new window.
|
||||
# + devMode:
|
||||
# Boolean to control the opened window's dev mode.
|
||||
openPaths: ({pathsToOpen, pidToKillWhenClosed, newWindow, devMode}) ->
|
||||
@openPath({pathToOpen, pidToKillWhenClosed, newWindow, devMode}) for pathToOpen in pathsToOpen ? []
|
||||
# options -
|
||||
# :pathsToOpen - The array of file paths to open
|
||||
# :pidToKillWhenClosed - The integer of the pid to kill
|
||||
# :newWindow - Boolean of whether this should be opened in a new window.
|
||||
# :devMode - Boolean to control the opened window's dev mode.
|
||||
# :safeMode - Boolean to control the opened window's safe mode.
|
||||
openPaths: ({pathsToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode}) ->
|
||||
for pathToOpen in pathsToOpen ? []
|
||||
@openPath({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode})
|
||||
|
||||
# Public: Opens a single path, in an existing window if possible.
|
||||
#
|
||||
# * options
|
||||
# + pathToOpen:
|
||||
# The file path to open
|
||||
# + pidToKillWhenClosed:
|
||||
# The integer of the pid to kill
|
||||
# + newWindow:
|
||||
# Boolean of whether this should be opened in a new window.
|
||||
# + devMode:
|
||||
# Boolean to control the opened window's dev mode.
|
||||
# + initialSize:
|
||||
# Object with height and width keys.
|
||||
openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, initialSize}={}) ->
|
||||
if devMode and not fs.existsSync(global.devResourcePath)
|
||||
dialog.showMessageBox
|
||||
type: 'warning'
|
||||
buttons: ['OK']
|
||||
message: 'Atom source directory not found.'
|
||||
detail: 'To run a window in dev mode you need to have the atom/atom repo cloned to ~/github/atom'
|
||||
return
|
||||
|
||||
# options -
|
||||
# :pathToOpen - The file path to open
|
||||
# :pidToKillWhenClosed - The integer of the pid to kill
|
||||
# :newWindow - Boolean of whether this should be opened in a new window.
|
||||
# :devMode - Boolean to control the opened window's dev mode.
|
||||
# :safeMode - Boolean to control the opened window's safe mode.
|
||||
# :windowDimensions - Object with height and width keys.
|
||||
openPath: ({pathToOpen, pidToKillWhenClosed, newWindow, devMode, safeMode, windowDimensions}={}) ->
|
||||
if pathToOpen
|
||||
[basename, initialLine] = path.basename(pathToOpen).split(':')
|
||||
if initialLine
|
||||
pathToOpen = "#{path.dirname(pathToOpen)}/#{basename}"
|
||||
initialLine -= 1 # Convert line numbers to a base of 0
|
||||
[basename, initialLine, initialColumn] = path.basename(pathToOpen).split(':')
|
||||
pathToOpen = path.join(path.dirname(pathToOpen), basename) if initialLine
|
||||
|
||||
# Convert line numbers to a base of 0
|
||||
initialLine -= 1 if initialLine
|
||||
initialColumn -= 1 if initialColumn
|
||||
|
||||
unless devMode
|
||||
existingWindow = @windowForPath(pathToOpen) unless pidToKillWhenClosed or newWindow
|
||||
if existingWindow
|
||||
openedWindow = existingWindow
|
||||
openedWindow.openPath(pathToOpen, initialLine)
|
||||
openedWindow.restore()
|
||||
else
|
||||
if devMode
|
||||
resourcePath = global.devResourcePath
|
||||
bootstrapScript = require.resolve(path.join(global.devResourcePath, 'src', 'window-bootstrap'))
|
||||
else
|
||||
resourcePath = @resourcePath
|
||||
bootstrapScript = require.resolve('../window-bootstrap')
|
||||
openedWindow = new AtomWindow({pathToOpen, initialLine, bootstrapScript, resourcePath, devMode, initialSize})
|
||||
try
|
||||
bootstrapScript = require.resolve(path.join(global.devResourcePath, 'src', 'window-bootstrap'))
|
||||
resourcePath = global.devResourcePath
|
||||
|
||||
bootstrapScript ?= require.resolve('../window-bootstrap')
|
||||
resourcePath ?= @resourcePath
|
||||
openedWindow = new AtomWindow({pathToOpen, initialLine, initialColumn, bootstrapScript, resourcePath, devMode, safeMode, windowDimensions})
|
||||
|
||||
if pidToKillWhenClosed?
|
||||
@pidsToOpenWindows[pidToKillWhenClosed] = openedWindow
|
||||
|
||||
openedWindow.browserWindow.on 'destroyed', =>
|
||||
for pid, trackedWindow of @pidsToOpenWindows when trackedWindow is openedWindow
|
||||
try
|
||||
process.kill(pid)
|
||||
catch error
|
||||
if error.code isnt 'ESRCH'
|
||||
console.log("Killing process #{pid} failed: #{error.code}")
|
||||
delete @pidsToOpenWindows[pid]
|
||||
openedWindow.browserWindow.on 'closed', =>
|
||||
@killProcessForWindow(openedWindow)
|
||||
|
||||
# Kill all processes associated with opened windows.
|
||||
killAllProcesses: ->
|
||||
@killProcess(pid) for pid of @pidsToOpenWindows
|
||||
|
||||
# Kill process associated with the given opened window.
|
||||
killProcessForWindow: (openedWindow) ->
|
||||
for pid, trackedWindow of @pidsToOpenWindows
|
||||
@killProcess(pid) if trackedWindow is openedWindow
|
||||
|
||||
# Kill the process with the given pid.
|
||||
killProcess: (pid) ->
|
||||
try
|
||||
process.kill(pid)
|
||||
catch error
|
||||
if error.code isnt 'ESRCH'
|
||||
console.log("Killing process #{pid} failed: #{error.code}")
|
||||
delete @pidsToOpenWindows[pid]
|
||||
|
||||
# Open an atom:// url.
|
||||
#
|
||||
@@ -367,12 +359,11 @@ class AtomApplication
|
||||
# responsible for opening the URL. A new window will be created with
|
||||
# that package's `urlMain` as the bootstrap script.
|
||||
#
|
||||
# * options
|
||||
# + urlToOpen:
|
||||
# The atom:// url to open.
|
||||
# + devMode:
|
||||
# Boolean to control the opened window's dev mode.
|
||||
openUrl: ({urlToOpen, devMode}) ->
|
||||
# options -
|
||||
# :urlToOpen - The atom:// url to open.
|
||||
# :devMode - Boolean to control the opened window's dev mode.
|
||||
# :safeMode - Boolean to control the opened window's safe mode.
|
||||
openUrl: ({urlToOpen, devMode, safeMode}) ->
|
||||
unless @packages?
|
||||
PackageManager = require '../package-manager'
|
||||
fs = require 'fs-plus'
|
||||
@@ -387,7 +378,8 @@ class AtomApplication
|
||||
if pack.urlMain
|
||||
packagePath = @packages.resolvePackagePath(packageName)
|
||||
bootstrapScript = path.resolve(packagePath, pack.urlMain)
|
||||
new AtomWindow({bootstrapScript, @resourcePath, devMode, urlToOpen, initialSize: @getFocusedWindowSize()})
|
||||
windowDimensions = @focusedWindow()?.getDimensions()
|
||||
new AtomWindow({bootstrapScript, @resourcePath, devMode, safeMode, urlToOpen, windowDimensions})
|
||||
else
|
||||
console.log "Package '#{pack.name}' does not have a url main: #{urlToOpen}"
|
||||
else
|
||||
@@ -395,13 +387,11 @@ class AtomApplication
|
||||
|
||||
# Opens up a new {AtomWindow} to run specs within.
|
||||
#
|
||||
# * options
|
||||
# + exitWhenDone:
|
||||
# A Boolean that if true, will close the window upon completion.
|
||||
# + resourcePath:
|
||||
# The path to include specs from.
|
||||
# + specPath:
|
||||
# The directory to load specs from.
|
||||
# options -
|
||||
# :exitWhenDone - A Boolean that if true, will close the window upon
|
||||
# completion.
|
||||
# :resourcePath - The path to include specs from.
|
||||
# :specPath - The directory to load specs from.
|
||||
runSpecs: ({exitWhenDone, resourcePath, specDirectory, logFile}) ->
|
||||
if resourcePath isnt @resourcePath and not fs.existsSync(resourcePath)
|
||||
resourcePath = @resourcePath
|
||||
@@ -428,15 +418,20 @@ class AtomApplication
|
||||
#
|
||||
# Once paths are selected, they're opened in a new or existing {AtomWindow}s.
|
||||
#
|
||||
# * options
|
||||
# + devMode:
|
||||
# A Boolean which controls whether any newly opened windows should be in
|
||||
# dev mode or not.
|
||||
promptForPath: ({devMode}={}) ->
|
||||
dialog.showOpenDialog title: 'Open', properties: ['openFile', 'openDirectory', 'multiSelections', 'createDirectory'], (pathsToOpen) =>
|
||||
@openPaths({pathsToOpen, devMode})
|
||||
|
||||
# Public: If an update is available, it returns the new version string
|
||||
# otherwise it returns null.
|
||||
getUpdateVersion: ->
|
||||
@updateVersion
|
||||
# options -
|
||||
# :type - A String which specifies the type of the dialog, could be 'file',
|
||||
# 'folder' or 'all'. The 'all' is only available on OS X.
|
||||
# :devMode - A Boolean which controls whether any newly opened windows
|
||||
# should be in dev mode or not.
|
||||
# :safeMode - A Boolean which controls whether any newly opened windows
|
||||
# should be in safe mode or not.
|
||||
promptForPath: ({type, devMode, safeMode}={}) ->
|
||||
type ?= 'all'
|
||||
properties =
|
||||
switch type
|
||||
when 'file' then ['openFile']
|
||||
when 'folder' then ['openDirectory']
|
||||
when 'all' then ['openFile', 'openDirectory']
|
||||
else throw new Error("#{type} is an invalid type for promptForPath")
|
||||
dialog.showOpenDialog title: 'Open', properties: properties.concat(['multiSelections', 'createDirectory']), (pathsToOpen) =>
|
||||
@openPaths({pathsToOpen, devMode, safeMode})
|
||||
|
||||
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