Start rebuilding with a cleanly-separated UI framework

This commit is contained in:
Nathan Sobo 2021-02-20 10:02:34 -07:00
commit b400449a58
13 changed files with 1863 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
.DS_Store

938
Cargo.lock generated Normal file
View file

@ -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",
]

2
Cargo.toml Normal file
View file

@ -0,0 +1,2 @@
[workspace]
members = ["zed", "gpui"]

21
gpui/Cargo.toml Normal file
View file

@ -0,0 +1,21 @@
[package]
authors = ["Nathan Sobo <nathansobo@gmail.com>"]
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"

502
gpui/src/keymap.rs Normal file
View file

@ -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<usize, Pending>,
keymap: Keymap,
}
#[derive(Default)]
struct Pending {
keystrokes: Vec<Keystroke>,
context: Option<Context>,
}
pub struct Keymap(Vec<Binding>);
pub struct Binding {
keystrokes: Vec<Keystroke>,
action: String,
action_arg: Option<Box<dyn ActionArg>>,
context: Option<ContextPredicate>,
}
#[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<String>,
pub map: HashMap<String, String>,
}
#[derive(Debug, Eq, PartialEq)]
enum ContextPredicate {
Identifier(String),
Equal(String, String),
NotEqual(String, String),
Not(Box<ContextPredicate>),
And(Box<ContextPredicate>, Box<ContextPredicate>),
Or(Box<ContextPredicate>, Box<ContextPredicate>),
}
trait ActionArg {
fn boxed_clone(&self) -> Box<dyn Any>;
}
impl<T> ActionArg for T
where
T: 'static + Any + Clone,
{
fn boxed_clone(&self) -> Box<dyn Any> {
Box::new(self.clone())
}
}
pub enum MatchResult {
None,
Pending,
Action {
name: String,
arg: Option<Box<dyn Any>>,
},
}
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<T: IntoIterator<Item = Binding>>(&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<Binding>) -> Self {
Self(bindings)
}
fn add_bindings<T: IntoIterator<Item = Binding>>(&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<S: Into<String>>(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<T: 'static + Any + Clone>(mut self, arg: T) -> Self {
self.action_arg = Some(Box::new(arg));
self
}
}
impl Keystroke {
pub fn parse(source: &str) -> anyhow::Result<Self> {
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<Self> {
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<Self> {
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<A: Any + Clone>(
&mut self,
keystroke: &str,
view_id: usize,
ctx: &Context,
) -> Option<(String, Option<A>)> {
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::<A>().cloned())))
} else {
None
}
}
}
}

5
gpui/src/lib.rs Normal file
View file

@ -0,0 +1,5 @@
pub mod keymap;
pub mod platform;
pub use pathfinder_color as color;
pub use pathfinder_geometry as geometry;

View file

@ -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,
},
}

View file

@ -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<Box<dyn FnOnce()>>,
become_active_callback: Option<Box<dyn FnMut()>>,
resign_active_callback: Option<Box<dyn FnMut()>>,
event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
open_files_callback: Option<Box<dyn FnMut(Vec<PathBuf>)>>,
}
const RUST_WRAPPER_IVAR_NAME: &'static str = "rustWrapper";
impl super::App for App {
fn on_finish_launching<F: 'static + FnOnce()>(mut self, callback: F) -> Self {
self.finish_launching_callback = Some(Box::new(callback));
self
}
fn on_become_active<F: 'static + FnMut()>(mut self, callback: F) -> Self {
self.become_active_callback = Some(Box::new(callback));
self
}
fn on_resign_active<F: 'static + FnMut()>(mut self, callback: F) -> Self {
self.resign_active_callback = Some(Box::new(callback));
self
}
fn on_event<F: 'static + FnMut(Event) -> bool>(mut self, callback: F) -> Self {
self.event_callback = Some(Box::new(callback));
self
}
fn on_open_files<F: 'static + FnMut(Vec<PathBuf>)>(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::<Vec<_>>()
};
let app = unsafe { get_app(this) };
if let Some(callback) = app.open_files_callback.as_mut() {
callback(paths);
}
}

View file

@ -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<f32>) -> Option<Self> {
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,
}
}
}

View file

@ -0,0 +1,8 @@
use super::*;
mod app;
mod event;
pub fn app() -> impl App {
app::App::default()
}

20
gpui/src/platform/mod.rs Normal file
View file

@ -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<F: 'static + FnOnce()>(self, callback: F) -> Self where;
fn on_become_active<F: 'static + FnMut()>(self, callback: F) -> Self;
fn on_resign_active<F: 'static + FnMut()>(self, callback: F) -> Self;
fn on_event<F: 'static + FnMut(Event) -> bool>(self, callback: F) -> Self;
fn on_open_files<F: 'static + FnMut(Vec<PathBuf>)>(self, callback: F) -> Self;
fn run(self);
}

12
zed/Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
authors = ["Nathan Sobo <nathansobo@gmail.com>"]
edition = "2018"
name = "zed"
version = "0.1.0"
[dependencies]
dirs = "3.0"
gpui = {path = "../gpui"}
libc = "0.2"
log = "0.4"
simplelog = "0.9"

38
zed/src/main.rs Normal file
View file

@ -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 }
}