commit b400449a58507cca1fa007197929c2cfd6beabbe Author: Nathan Sobo Date: Sat Feb 20 10:02:34 2021 -0700 Start rebuilding with a cleanly-separated UI framework diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..212de442f4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +.DS_Store \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000..67be2f74aa --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,938 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "async-channel" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb877970c7b440ead138f6321a3b5395d6061183af779340b65e20c0fede9146" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "once_cell", + "vec-arena", +] + +[[package]] +name = "async-fs" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b3ca4f8ff117c37c278a2f7415ce9be55560b846b5bc4412aaa5d29c1c3dae2" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9315f8f07556761c3e48fec2e6b276004acf426e6dc068b2c2251854d65ee0fd" +dependencies = [ + "concurrent-queue", + "fastrand", + "futures-lite", + "libc", + "log", + "nb-connect", + "once_cell", + "parking", + "polling", + "vec-arena", + "waker-fn", + "winapi", +] + +[[package]] +name = "async-lock" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1996609732bde4a9988bc42125f55f2af5f3c36370e27c778d5191a4a1b63bfb" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-net" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06de475c85affe184648202401d7622afb32f0f74e02192857d0201a16defbe5" +dependencies = [ + "async-io", + "blocking", + "fastrand", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef37b86e2fa961bae5a4d212708ea0154f904ce31d1a4a7f47e1bbc33a0c040b" +dependencies = [ + "async-io", + "blocking", + "cfg-if 1.0.0", + "event-listener", + "futures-lite", + "once_cell", + "signal-hook", + "winapi", +] + +[[package]] +name = "async-task" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" + +[[package]] +name = "atomic-waker" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "blake2b_simd" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "blocking" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9" +dependencies = [ + "async-channel", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "once_cell", +] + +[[package]] +name = "cache-padded" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" + +[[package]] +name = "cc" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "cocoa" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832" +dependencies = [ + "bitflags", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types 0.3.2", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" +dependencies = [ + "bitflags", + "block", + "core-foundation", + "core-graphics-types", + "foreign-types 0.3.2", + "libc", + "objc", +] + +[[package]] +name = "concurrent-queue" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +dependencies = [ + "cache-padded", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "core-foundation" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" + +[[package]] +name = "core-graphics" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "269f35f69b542b80e736a20a89a05215c0ce80c2c03c514abb2e318b78379d86" +dependencies = [ + "bitflags", + "core-foundation", + "core-graphics-types", + "foreign-types 0.3.2", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +dependencies = [ + "bitflags", + "core-foundation", + "foreign-types 0.3.2", + "libc", +] + +[[package]] +name = "core-text" +version = "19.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types 0.3.2", + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae8f328835f8f5a6ceb6a7842a7f2d0c03692adb5c889347235d59194731fe3" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "lazy_static", + "loom", +] + +[[package]] +name = "dirs" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "event-listener" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" + +[[package]] +name = "fastrand" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5faf057445ce5c9d4329e382b2ce7ca38550ef3b73a5348362d5f24e0c7fe3" +dependencies = [ + "instant", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.0", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63f713f8b2aa9e24fec85b0e290c56caee12e3b6ae0aeeda238a75b28251afd6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7684cf33bb7f28497939e8c7cf17e3e4e3b8d9a0080ffa4f8ae2f515442ee855" + +[[package]] +name = "futures-core" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65" + +[[package]] +name = "futures-io" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28be053525281ad8259d47e4de5de657b25e7bac113458555bb4b70bc6870500" + +[[package]] +name = "futures-lite" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4481d0cd0de1d204a4fa55e7d45f07b1d958abcb06714b3446438e2eff695fb" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "generator" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cdc09201b2e8ca1b19290cf7e65de2246b8e91fb6874279722189c4de7b94dc" +dependencies = [ + "cc", + "libc", + "log", + "rustc_version", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "gpui" +version = "0.1.0" +dependencies = [ + "anyhow", + "cocoa", + "core-foundation", + "core-text", + "foreign-types 0.5.0", + "log", + "metal", + "objc", + "pathfinder_color", + "pathfinder_geometry", + "smol", + "tree-sitter", +] + +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "loom" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d44c73b4636e497b4917eb21c33539efa3816741a2d3ff26c6316f1b529481a4" +dependencies = [ + "cfg-if 1.0.0", + "generator", + "scoped-tls", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "metal" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4598d719460ade24c7d91f335daf055bf2a7eec030728ce751814c50cdd6a26c" +dependencies = [ + "bitflags", + "block", + "cocoa-foundation", + "foreign-types 0.3.2", + "log", + "objc", +] + +[[package]] +name = "nb-connect" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670361df1bc2399ee1ff50406a0d422587dd3bb0da596e1978fe8e05dabddf4f" +dependencies = [ + "libc", + "socket2", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "once_cell" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" + +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + +[[package]] +name = "pathfinder_color" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69bdc0d277d559e35e1b374de56df9262a6b71e091ca04a8831a239f8c7f0c62" +dependencies = [ + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_geometry" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" +dependencies = [ + "log", + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_simd" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b451513912d6b3440e443aa75a73ab22203afedc4a90df8526d008c0f86f7cb3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" + +[[package]] +name = "polling" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "log", + "wepoll-sys", + "winapi", +] + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_users" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +dependencies = [ + "getrandom", + "redox_syscall", + "rust-argon2", +] + +[[package]] +name = "regex" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" + +[[package]] +name = "rust-argon2" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" +dependencies = [ + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "signal-hook" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7f3f92a1da3d6b1d32245d0cbcbbab0cfc45996d8df619c42bccfa6d2bbb5f" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +dependencies = [ + "libc", +] + +[[package]] +name = "simplelog" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bc0ffd69814a9b251d43afcabf96dad1b29f5028378056257be9e3fecc9f720" +dependencies = [ + "chrono", + "log", + "termcolor", +] + +[[package]] +name = "smol" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cf3b5351f3e783c1d79ab5fc604eeed8b8ae9abd36b166e8b87a089efd85e4" +dependencies = [ + "async-channel", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-net", + "async-process", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "socket2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thread_local" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +dependencies = [ + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "tree-sitter" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d18dcb776d3affaba6db04d11d645946d34a69b3172e588af96ce9fecd20faac" +dependencies = [ + "cc", + "regex", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "vec-arena" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eafc1b9b2dfc6f5529177b62cf806484db55b32dc7c9658a118e11bbeb33061d" + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wepoll-sys" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff" +dependencies = [ + "cc", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "zed" +version = "0.1.0" +dependencies = [ + "dirs", + "gpui", + "libc", + "log", + "simplelog", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000..843d72b73e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["zed", "gpui"] diff --git a/gpui/Cargo.toml b/gpui/Cargo.toml new file mode 100644 index 0000000000..04373d80eb --- /dev/null +++ b/gpui/Cargo.toml @@ -0,0 +1,21 @@ +[package] +authors = ["Nathan Sobo "] +edition = "2018" +name = "gpui" +version = "0.1.0" + +[dependencies] +pathfinder_color = "0.5" +pathfinder_geometry = "0.5" +smol = "1.2" +tree-sitter = "0.17" + +[target.'cfg(target_os = "macos")'.dependencies] +anyhow = "1" +cocoa = "0.24" +core-foundation = "0.9" +core-text = "19.2" +foreign-types = "0.5" +log = "0.4" +metal = "0.21" +objc = "0.2" diff --git a/gpui/src/keymap.rs b/gpui/src/keymap.rs new file mode 100644 index 0000000000..9f92964fef --- /dev/null +++ b/gpui/src/keymap.rs @@ -0,0 +1,502 @@ +use anyhow::anyhow; +use std::{ + any::Any, + collections::{HashMap, HashSet}, +}; +use tree_sitter::{Language, Node, Parser}; + +extern "C" { + fn tree_sitter_zed_context_predicate() -> Language; +} + +pub struct Matcher { + pending: HashMap, + keymap: Keymap, +} + +#[derive(Default)] +struct Pending { + keystrokes: Vec, + context: Option, +} + +pub struct Keymap(Vec); + +pub struct Binding { + keystrokes: Vec, + action: String, + action_arg: Option>, + context: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Keystroke { + pub ctrl: bool, + pub alt: bool, + pub shift: bool, + pub cmd: bool, + pub key: String, +} + +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct Context { + pub set: HashSet, + pub map: HashMap, +} + +#[derive(Debug, Eq, PartialEq)] +enum ContextPredicate { + Identifier(String), + Equal(String, String), + NotEqual(String, String), + Not(Box), + And(Box, Box), + Or(Box, Box), +} + +trait ActionArg { + fn boxed_clone(&self) -> Box; +} + +impl ActionArg for T +where + T: 'static + Any + Clone, +{ + fn boxed_clone(&self) -> Box { + Box::new(self.clone()) + } +} + +pub enum MatchResult { + None, + Pending, + Action { + name: String, + arg: Option>, + }, +} + +impl Matcher { + pub fn new(keymap: Keymap) -> Self { + Self { + pending: HashMap::new(), + keymap, + } + } + + pub fn set_keymap(&mut self, keymap: Keymap) { + self.pending.clear(); + self.keymap = keymap; + } + + pub fn add_bindings>(&mut self, bindings: T) { + self.pending.clear(); + self.keymap.add_bindings(bindings); + } + + pub fn push_keystroke( + &mut self, + keystroke: Keystroke, + view_id: usize, + ctx: &Context, + ) -> MatchResult { + let pending = self.pending.entry(view_id).or_default(); + + if let Some(pending_ctx) = pending.context.as_ref() { + if pending_ctx != ctx { + pending.keystrokes.clear(); + } + } + + pending.keystrokes.push(keystroke); + + let mut retain_pending = false; + for binding in self.keymap.0.iter().rev() { + if binding.keystrokes.starts_with(&pending.keystrokes) + && binding + .context + .as_ref() + .map(|c| c.eval(ctx)) + .unwrap_or(true) + { + if binding.keystrokes.len() == pending.keystrokes.len() { + self.pending.remove(&view_id); + return MatchResult::Action { + name: binding.action.clone(), + arg: binding.action_arg.as_ref().map(|arg| (*arg).boxed_clone()), + }; + } else { + retain_pending = true; + pending.context = Some(ctx.clone()); + } + } + } + + if retain_pending { + MatchResult::Pending + } else { + self.pending.remove(&view_id); + MatchResult::None + } + } +} + +impl Default for Matcher { + fn default() -> Self { + Self::new(Keymap::default()) + } +} + +impl Keymap { + pub fn new(bindings: Vec) -> Self { + Self(bindings) + } + + fn add_bindings>(&mut self, bindings: T) { + self.0.extend(bindings.into_iter()); + } +} + +impl Default for Keymap { + fn default() -> Self { + Self(vec![ + Binding::new("up", "menu:select_prev", Some("menu")), + Binding::new("ctrl-p", "menu:select_prev", Some("menu")), + Binding::new("down", "menu:select_next", Some("menu")), + Binding::new("ctrl-n", "menu:select_next", Some("menu")), + ]) + } +} + +impl Binding { + pub fn new>(keystrokes: &str, action: S, context: Option<&str>) -> Self { + let context = if let Some(context) = context { + Some(ContextPredicate::parse(context).unwrap()) + } else { + None + }; + + Self { + keystrokes: keystrokes + .split_whitespace() + .map(|key| Keystroke::parse(key).unwrap()) + .collect(), + action: action.into(), + action_arg: None, + context, + } + } + + pub fn with_arg(mut self, arg: T) -> Self { + self.action_arg = Some(Box::new(arg)); + self + } +} + +impl Keystroke { + pub fn parse(source: &str) -> anyhow::Result { + let mut ctrl = false; + let mut alt = false; + let mut shift = false; + let mut cmd = false; + let mut key = None; + + let mut components = source.split("-").peekable(); + while let Some(component) = components.next() { + match component { + "ctrl" => ctrl = true, + "alt" => alt = true, + "shift" => shift = true, + "cmd" => cmd = true, + _ => { + if let Some(component) = components.peek() { + if component.is_empty() && source.ends_with('-') { + key = Some(String::from("-")); + break; + } else { + return Err(anyhow!("Invalid keystroke `{}`", source)); + } + } else { + key = Some(String::from(component)); + } + } + } + } + + Ok(Keystroke { + ctrl, + alt, + shift, + cmd, + key: key.unwrap(), + }) + } +} + +impl Context { + pub fn extend(&mut self, other: Context) { + for v in other.set { + self.set.insert(v); + } + for (k, v) in other.map { + self.map.insert(k, v); + } + } +} + +impl ContextPredicate { + fn parse(source: &str) -> anyhow::Result { + let mut parser = Parser::new(); + let language = unsafe { tree_sitter_zed_context_predicate() }; + parser.set_language(language).unwrap(); + let source = source.as_bytes(); + let tree = parser.parse(source, None).unwrap(); + Self::from_node(tree.root_node(), source) + } + + fn from_node(node: Node, source: &[u8]) -> anyhow::Result { + let parse_error = "error parsing context predicate"; + let kind = node.kind(); + + match kind { + "source" => Self::from_node(node.child(0).ok_or(anyhow!(parse_error))?, source), + "identifier" => Ok(Self::Identifier(node.utf8_text(source)?.into())), + "not" => { + let child = Self::from_node( + node.child_by_field_name("expression") + .ok_or(anyhow!(parse_error))?, + source, + )?; + Ok(Self::Not(Box::new(child))) + } + "and" | "or" => { + let left = Box::new(Self::from_node( + node.child_by_field_name("left") + .ok_or(anyhow!(parse_error))?, + source, + )?); + let right = Box::new(Self::from_node( + node.child_by_field_name("right") + .ok_or(anyhow!(parse_error))?, + source, + )?); + if kind == "and" { + Ok(Self::And(left, right)) + } else { + Ok(Self::Or(left, right)) + } + } + "equal" | "not_equal" => { + let left = node + .child_by_field_name("left") + .ok_or(anyhow!(parse_error))? + .utf8_text(source)? + .into(); + let right = node + .child_by_field_name("right") + .ok_or(anyhow!(parse_error))? + .utf8_text(source)? + .into(); + if kind == "equal" { + Ok(Self::Equal(left, right)) + } else { + Ok(Self::NotEqual(left, right)) + } + } + "parenthesized" => Self::from_node( + node.child_by_field_name("expression") + .ok_or(anyhow!(parse_error))?, + source, + ), + _ => Err(anyhow!(parse_error)), + } + } + + fn eval(&self, ctx: &Context) -> bool { + match self { + Self::Identifier(name) => ctx.set.contains(name.as_str()), + Self::Equal(left, right) => ctx + .map + .get(left) + .map(|value| value == right) + .unwrap_or(false), + Self::NotEqual(left, right) => ctx + .map + .get(left) + .map(|value| value != right) + .unwrap_or(true), + Self::Not(pred) => !pred.eval(ctx), + Self::And(left, right) => left.eval(ctx) && right.eval(ctx), + Self::Or(left, right) => left.eval(ctx) || right.eval(ctx), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_keystroke_parsing() -> anyhow::Result<()> { + assert_eq!( + Keystroke::parse("ctrl-p")?, + Keystroke { + key: "p".into(), + ctrl: true, + alt: false, + shift: false, + cmd: false, + } + ); + + assert_eq!( + Keystroke::parse("alt-shift-down")?, + Keystroke { + key: "down".into(), + ctrl: false, + alt: true, + shift: true, + cmd: false, + } + ); + + assert_eq!( + Keystroke::parse("shift-cmd--")?, + Keystroke { + key: "-".into(), + ctrl: false, + alt: false, + shift: true, + cmd: true, + } + ); + + Ok(()) + } + + #[test] + fn test_context_predicate_parsing() -> anyhow::Result<()> { + use ContextPredicate::*; + + assert_eq!( + ContextPredicate::parse("a && (b == c || d != e)")?, + And( + Box::new(Identifier("a".into())), + Box::new(Or( + Box::new(Equal("b".into(), "c".into())), + Box::new(NotEqual("d".into(), "e".into())), + )) + ) + ); + + assert_eq!( + ContextPredicate::parse("!a")?, + Not(Box::new(Identifier("a".into())),) + ); + + Ok(()) + } + + #[test] + fn test_context_predicate_eval() -> anyhow::Result<()> { + let predicate = ContextPredicate::parse("a && b || c == d")?; + + let mut context = Context::default(); + context.set.insert("a".into()); + assert!(!predicate.eval(&context)); + + context.set.insert("b".into()); + assert!(predicate.eval(&context)); + + context.set.remove("b"); + context.map.insert("c".into(), "x".into()); + assert!(!predicate.eval(&context)); + + context.map.insert("c".into(), "d".into()); + assert!(predicate.eval(&context)); + + let predicate = ContextPredicate::parse("!a")?; + assert!(predicate.eval(&Context::default())); + + Ok(()) + } + + #[test] + fn test_matcher() -> anyhow::Result<()> { + #[derive(Clone, Debug, Eq, PartialEq)] + struct ActionArg { + a: &'static str, + } + + let keymap = Keymap(vec![ + Binding::new("a", "a", Some("a")).with_arg(ActionArg { a: "b" }), + Binding::new("b", "b", Some("a")), + Binding::new("a b", "a_b", Some("a || b")), + ]); + + let mut ctx_a = Context::default(); + ctx_a.set.insert("a".into()); + + let mut ctx_b = Context::default(); + ctx_b.set.insert("b".into()); + + let mut matcher = Matcher::new(keymap); + + // Basic match + assert_eq!( + matcher.test_keystroke("a", 1, &ctx_a), + Some(("a".to_string(), Some(ActionArg { a: "b" }))) + ); + + // Multi-keystroke match + assert_eq!(matcher.test_keystroke::<()>("a", 1, &ctx_b), None); + assert_eq!( + matcher.test_keystroke::<()>("b", 1, &ctx_b), + Some(("a_b".to_string(), None)) + ); + + // Failed matches don't interfere with matching subsequent keys + assert_eq!(matcher.test_keystroke::<()>("x", 1, &ctx_a), None); + assert_eq!( + matcher.test_keystroke("a", 1, &ctx_a), + Some(("a".to_string(), Some(ActionArg { a: "b" }))) + ); + + // Pending keystrokes are cleared when the context changes + assert_eq!(matcher.test_keystroke::<()>("a", 1, &ctx_b), None); + assert_eq!( + matcher.test_keystroke::<()>("b", 1, &ctx_a), + Some(("b".to_string(), None)) + ); + + let mut ctx_c = Context::default(); + ctx_c.set.insert("c".into()); + + // Pending keystrokes are maintained per-view + assert_eq!(matcher.test_keystroke::<()>("a", 1, &ctx_b), None); + assert_eq!(matcher.test_keystroke::<()>("a", 2, &ctx_c), None); + assert_eq!( + matcher.test_keystroke::<()>("b", 1, &ctx_b), + Some(("a_b".to_string(), None)) + ); + + Ok(()) + } + + impl Matcher { + fn test_keystroke( + &mut self, + keystroke: &str, + view_id: usize, + ctx: &Context, + ) -> Option<(String, Option)> { + if let MatchResult::Action { name, arg } = + self.push_keystroke(Keystroke::parse(keystroke).unwrap(), view_id, ctx) + { + Some((name, arg.and_then(|arg| arg.downcast_ref::().cloned()))) + } else { + None + } + } + } +} diff --git a/gpui/src/lib.rs b/gpui/src/lib.rs new file mode 100644 index 0000000000..ee2a24d465 --- /dev/null +++ b/gpui/src/lib.rs @@ -0,0 +1,5 @@ +pub mod keymap; +pub mod platform; + +pub use pathfinder_color as color; +pub use pathfinder_geometry as geometry; diff --git a/gpui/src/platform/event.rs b/gpui/src/platform/event.rs new file mode 100644 index 0000000000..6a60bd3120 --- /dev/null +++ b/gpui/src/platform/event.rs @@ -0,0 +1,24 @@ +use crate::{geometry::vector::Vector2F, keymap::Keystroke}; + +#[derive(Debug)] +pub enum Event { + KeyDown { + keystroke: Keystroke, + chars: String, + }, + ScrollWheel { + position: Vector2F, + delta: Vector2F, + precise: bool, + }, + LeftMouseDown { + position: Vector2F, + cmd: bool, + }, + LeftMouseUp { + position: Vector2F, + }, + LeftMouseDragged { + position: Vector2F, + }, +} diff --git a/gpui/src/platform/mac/app.rs b/gpui/src/platform/mac/app.rs new file mode 100644 index 0000000000..c0ba2ad7d0 --- /dev/null +++ b/gpui/src/platform/mac/app.rs @@ -0,0 +1,176 @@ +use super::Event; +pub use cocoa::foundation::NSSize; +use cocoa::{ + base::{id, nil}, + foundation::{NSArray, NSAutoreleasePool, NSString}, +}; +use objc::{ + class, + declare::ClassDecl, + msg_send, + runtime::{Class, Object, Sel}, + sel, sel_impl, +}; +use std::{ + ffi::CStr, + os::raw::{c_char, c_void}, + path::PathBuf, +}; + +#[derive(Default)] +pub struct App { + finish_launching_callback: Option>, + become_active_callback: Option>, + resign_active_callback: Option>, + event_callback: Option bool>>, + open_files_callback: Option)>>, +} + +const RUST_WRAPPER_IVAR_NAME: &'static str = "rustWrapper"; + +impl super::App for App { + fn on_finish_launching(mut self, callback: F) -> Self { + self.finish_launching_callback = Some(Box::new(callback)); + self + } + + fn on_become_active(mut self, callback: F) -> Self { + self.become_active_callback = Some(Box::new(callback)); + self + } + + fn on_resign_active(mut self, callback: F) -> Self { + self.resign_active_callback = Some(Box::new(callback)); + self + } + + fn on_event bool>(mut self, callback: F) -> Self { + self.event_callback = Some(Box::new(callback)); + self + } + + fn on_open_files)>(mut self, callback: F) -> Self { + self.open_files_callback = Some(Box::new(callback)); + self + } + + fn run(self) { + unsafe { + let self_ptr = Box::into_raw(Box::new(self)); + + let pool = NSAutoreleasePool::new(nil); + let app: id = msg_send![build_app_class(), sharedApplication]; + (*app).set_ivar(RUST_WRAPPER_IVAR_NAME, self_ptr as *mut c_void); + let app_delegate: id = msg_send![build_app_delegate_class(), new]; + (*app_delegate).set_ivar(RUST_WRAPPER_IVAR_NAME, self_ptr as *mut c_void); + let _: () = msg_send![app, setDelegate: app_delegate]; + let _: () = msg_send![app, run]; + let _: () = msg_send![pool, drain]; + + // App is done running when we get here, so we can reinstantiate the Box and drop it. + Box::from_raw(self_ptr); + } + } +} + +fn build_app_class() -> *const Class { + unsafe { + let mut decl = ClassDecl::new("GPUIApplication", class!(NSApplication)).unwrap(); + decl.add_ivar::<*mut c_void>(RUST_WRAPPER_IVAR_NAME); + decl.add_method( + sel!(sendEvent:), + send_event as extern "C" fn(&Object, Sel, id), + ); + decl.register() + } +} + +fn build_app_delegate_class() -> *const Class { + unsafe { + let superclass = class!(NSResponder); + let mut decl = ClassDecl::new("GPUIApplicationDelegate", superclass).unwrap(); + decl.add_ivar::<*mut c_void>(RUST_WRAPPER_IVAR_NAME); + decl.add_method( + sel!(applicationDidFinishLaunching:), + did_finish_launching as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(applicationDidBecomeActive:), + did_become_active as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(applicationDidResignActive:), + did_resign_active as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(application:openFiles:), + open_files as extern "C" fn(&Object, Sel, id, id), + ); + decl.register() + } +} + +unsafe fn get_app(object: &Object) -> &mut App { + let wrapper_ptr: *mut c_void = *object.get_ivar(RUST_WRAPPER_IVAR_NAME); + &mut *(wrapper_ptr as *mut App) +} + +extern "C" fn send_event(this: &Object, _sel: Sel, native_event: id) { + let event = unsafe { Event::from_native(native_event, None) }; + + if let Some(event) = event { + let app = unsafe { get_app(this) }; + if let Some(callback) = app.event_callback.as_mut() { + if callback(event) { + return; + } + } + } + + unsafe { + let _: () = msg_send![super(this, class!(NSApplication)), sendEvent: native_event]; + } +} + +extern "C" fn did_finish_launching(this: &Object, _: Sel, _: id) { + let app = unsafe { get_app(this) }; + if let Some(callback) = app.finish_launching_callback.take() { + callback(); + } +} + +extern "C" fn did_become_active(this: &Object, _: Sel, _: id) { + let app = unsafe { get_app(this) }; + if let Some(callback) = app.become_active_callback.as_mut() { + callback(); + } +} + +extern "C" fn did_resign_active(this: &Object, _: Sel, _: id) { + let app = unsafe { get_app(this) }; + if let Some(callback) = app.resign_active_callback.as_mut() { + callback(); + } +} + +extern "C" fn open_files(this: &Object, _: Sel, _: id, paths: id) { + let paths = unsafe { + (0..paths.count()) + .into_iter() + .filter_map(|i| { + let path = paths.objectAtIndex(i); + match CStr::from_ptr(path.UTF8String() as *mut c_char).to_str() { + Ok(string) => Some(PathBuf::from(string)), + Err(err) => { + log::error!("error converting path to string: {}", err); + None + } + } + }) + .collect::>() + }; + let app = unsafe { get_app(this) }; + if let Some(callback) = app.open_files_callback.as_mut() { + callback(paths); + } +} diff --git a/gpui/src/platform/mac/event.rs b/gpui/src/platform/mac/event.rs new file mode 100644 index 0000000000..3123961b64 --- /dev/null +++ b/gpui/src/platform/mac/event.rs @@ -0,0 +1,115 @@ +use super::Event; +use crate::{geometry::vector::vec2f, keymap::Keystroke}; +use cocoa::appkit::{ + NSDeleteFunctionKey as DELETE_KEY, NSDownArrowFunctionKey as ARROW_DOWN_KEY, + NSLeftArrowFunctionKey as ARROW_LEFT_KEY, NSPageDownFunctionKey as PAGE_DOWN_KEY, + NSPageUpFunctionKey as PAGE_UP_KEY, NSRightArrowFunctionKey as ARROW_RIGHT_KEY, + NSUpArrowFunctionKey as ARROW_UP_KEY, +}; +use cocoa::{ + appkit::{NSEvent as _, NSEventModifierFlags, NSEventType}, + base::{id, YES}, + foundation::NSString as _, +}; +use std::{ffi::CStr, os::raw::c_char}; + +const BACKSPACE_KEY: u16 = 0x7f; +const ENTER_KEY: u16 = 0x0d; +const ESCAPE_KEY: u16 = 0x1b; + +impl Event { + pub unsafe fn from_native(native_event: id, window_height: Option) -> Option { + let event_type = native_event.eventType(); + + // Filter out event types that aren't in the NSEventType enum. + // See https://github.com/servo/cocoa-rs/issues/155#issuecomment-323482792 for details. + match event_type as u64 { + 0 | 21 | 32 | 33 | 35 | 36 | 37 => { + return None; + } + _ => {} + } + + match event_type { + NSEventType::NSKeyDown => { + let modifiers = native_event.modifierFlags(); + let unmodified_chars = native_event.charactersIgnoringModifiers(); + let unmodified_chars = CStr::from_ptr(unmodified_chars.UTF8String() as *mut c_char) + .to_str() + .unwrap(); + + let unmodified_chars = if let Some(first_char) = unmodified_chars.chars().next() { + match first_char as u16 { + ARROW_UP_KEY => "up", + ARROW_DOWN_KEY => "down", + ARROW_LEFT_KEY => "left", + ARROW_RIGHT_KEY => "right", + PAGE_UP_KEY => "pageup", + PAGE_DOWN_KEY => "pagedown", + BACKSPACE_KEY => "backspace", + ENTER_KEY => "enter", + DELETE_KEY => "delete", + ESCAPE_KEY => "escape", + _ => unmodified_chars, + } + } else { + return None; + }; + + let chars = native_event.characters(); + let chars = CStr::from_ptr(chars.UTF8String() as *mut c_char) + .to_str() + .unwrap() + .into(); + + Some(Self::KeyDown { + keystroke: Keystroke { + ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask), + alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask), + shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask), + cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask), + key: unmodified_chars.into(), + }, + chars, + }) + } + NSEventType::NSLeftMouseDown => { + window_height.map(|window_height| Self::LeftMouseDown { + position: vec2f( + native_event.locationInWindow().x as f32, + window_height - native_event.locationInWindow().y as f32, + ), + cmd: native_event + .modifierFlags() + .contains(NSEventModifierFlags::NSCommandKeyMask), + }) + } + NSEventType::NSLeftMouseUp => window_height.map(|window_height| Self::LeftMouseUp { + position: vec2f( + native_event.locationInWindow().x as f32, + window_height - native_event.locationInWindow().y as f32, + ), + }), + NSEventType::NSLeftMouseDragged => { + window_height.map(|window_height| Self::LeftMouseDragged { + position: vec2f( + native_event.locationInWindow().x as f32, + window_height - native_event.locationInWindow().y as f32, + ), + }) + } + NSEventType::NSScrollWheel => window_height.map(|window_height| Self::ScrollWheel { + position: vec2f( + native_event.locationInWindow().x as f32, + window_height - native_event.locationInWindow().y as f32, + ), + delta: vec2f( + native_event.scrollingDeltaX() as f32, + native_event.scrollingDeltaY() as f32, + ), + precise: native_event.hasPreciseScrollingDeltas() == YES, + }), + _ => None, + } + } +} diff --git a/gpui/src/platform/mac/mod.rs b/gpui/src/platform/mac/mod.rs new file mode 100644 index 0000000000..ad903d444e --- /dev/null +++ b/gpui/src/platform/mac/mod.rs @@ -0,0 +1,8 @@ +use super::*; + +mod app; +mod event; + +pub fn app() -> impl App { + app::App::default() +} diff --git a/gpui/src/platform/mod.rs b/gpui/src/platform/mod.rs new file mode 100644 index 0000000000..6fa70cfc08 --- /dev/null +++ b/gpui/src/platform/mod.rs @@ -0,0 +1,20 @@ +mod event; +#[cfg(target_os = "macos")] +pub mod mac; +pub mod current { + #[cfg(target_os = "macos")] + pub use super::mac::*; +} + +use std::path::PathBuf; + +use event::Event; + +pub trait App { + fn on_finish_launching(self, callback: F) -> Self where; + fn on_become_active(self, callback: F) -> Self; + fn on_resign_active(self, callback: F) -> Self; + fn on_event bool>(self, callback: F) -> Self; + fn on_open_files)>(self, callback: F) -> Self; + fn run(self); +} diff --git a/zed/Cargo.toml b/zed/Cargo.toml new file mode 100644 index 0000000000..5da82a3da5 --- /dev/null +++ b/zed/Cargo.toml @@ -0,0 +1,12 @@ +[package] +authors = ["Nathan Sobo "] +edition = "2018" +name = "zed" +version = "0.1.0" + +[dependencies] +dirs = "3.0" +gpui = {path = "../gpui"} +libc = "0.2" +log = "0.4" +simplelog = "0.9" diff --git a/zed/src/main.rs b/zed/src/main.rs new file mode 100644 index 0000000000..ef373ae5bc --- /dev/null +++ b/zed/src/main.rs @@ -0,0 +1,38 @@ +use std::fs; + +use fs::OpenOptions; +use gpui::platform::{current as platform, App as _}; +use log::LevelFilter; +use simplelog::SimpleLogger; + +fn main() { + init_logger(); + platform::app() + .on_finish_launching(|| log::info!("finish launching")) + .run(); +} + +fn init_logger() { + let level = LevelFilter::Info; + + if stdout_is_a_pty() { + SimpleLogger::init(level, Default::default()).expect("could not initialize logger"); + } else { + let log_dir_path = dirs::home_dir() + .expect("could not locate home directory for logging") + .join("Library/Logs/"); + let log_file_path = log_dir_path.join("Zed.log"); + fs::create_dir_all(&log_dir_path).expect("could not create log directory"); + let log_file = OpenOptions::new() + .create(true) + .append(true) + .open(log_file_path) + .expect("could not open logfile"); + simplelog::WriteLogger::init(level, simplelog::Config::default(), log_file) + .expect("could not initialize logger"); + } +} + +fn stdout_is_a_pty() -> bool { + unsafe { libc::isatty(libc::STDOUT_FILENO as i32) != 0 } +}