Compare commits

..

2 Commits

Author SHA1 Message Date
Conrad Irwin
db28a0ecde Format markdowns 2024-12-03 09:58:15 -07:00
Conrad Irwin
2223e6e5e1 Format markdown by default 2024-12-03 09:09:59 -07:00
141 changed files with 1239 additions and 3674 deletions

View File

@@ -113,12 +113,6 @@ jobs:
script/check-licenses script/check-licenses
script/generate-licenses /tmp/zed_licenses_output script/generate-licenses /tmp/zed_licenses_output
- name: Check for new vulnerable dependencies
if: github.event_name == 'pull_request'
uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4
with:
license-check: false
- name: Run tests - name: Run tests
uses: ./.github/actions/run_tests uses: ./.github/actions/run_tests

View File

@@ -2,7 +2,8 @@
"languages": { "languages": {
"Markdown": { "Markdown": {
"tab_size": 2, "tab_size": 2,
"formatter": "prettier" "formatter": "prettier",
"format_on_save": "on"
}, },
"TOML": { "TOML": {
"formatter": "prettier", "formatter": "prettier",

261
Cargo.lock generated
View File

@@ -464,12 +464,10 @@ dependencies = [
"feature_flags", "feature_flags",
"futures 0.3.31", "futures 0.3.31",
"gpui", "gpui",
"language",
"language_model", "language_model",
"language_model_selector", "language_model_selector",
"language_models", "language_models",
"log", "log",
"markdown",
"project", "project",
"proto", "proto",
"serde", "serde",
@@ -932,6 +930,20 @@ version = "4.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
[[package]]
name = "async-tls"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfeefd0ca297cbbb3bd34fd6b228401c2a5177038257afd751bc29f0a2da4795"
dependencies = [
"futures-core",
"futures-io",
"rustls 0.20.9",
"rustls-pemfile 1.0.4",
"webpki",
"webpki-roots 0.22.6",
]
[[package]] [[package]]
name = "async-tls" name = "async-tls"
version = "0.13.0" version = "0.13.0"
@@ -956,6 +968,21 @@ dependencies = [
"syn 2.0.87", "syn 2.0.87",
] ]
[[package]]
name = "async-tungstenite"
version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce01ac37fdc85f10a43c43bc582cbd566720357011578a935761075f898baf58"
dependencies = [
"async-std",
"async-tls 0.12.0",
"futures-io",
"futures-util",
"log",
"pin-project-lite",
"tungstenite 0.19.0",
]
[[package]] [[package]]
name = "async-tungstenite" name = "async-tungstenite"
version = "0.28.0" version = "0.28.0"
@@ -963,7 +990,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e661b6cb0a6eb34d02c520b052daa3aa9ac0cc02495c9d066bbce13ead132b" checksum = "90e661b6cb0a6eb34d02c520b052daa3aa9ac0cc02495c9d066bbce13ead132b"
dependencies = [ dependencies = [
"async-std", "async-std",
"async-tls", "async-tls 0.13.0",
"futures-io", "futures-io",
"futures-util", "futures-util",
"log", "log",
@@ -1133,7 +1160,7 @@ dependencies = [
"fastrand 2.2.0", "fastrand 2.2.0",
"hex", "hex",
"http 0.2.12", "http 0.2.12",
"ring", "ring 0.17.8",
"time", "time",
"tokio", "tokio",
"tracing", "tracing",
@@ -1323,7 +1350,7 @@ dependencies = [
"once_cell", "once_cell",
"p256", "p256",
"percent-encoding", "percent-encoding",
"ring", "ring 0.17.8",
"sha2", "sha2",
"subtle", "subtle",
"time", "time",
@@ -2480,7 +2507,7 @@ dependencies = [
"anyhow", "anyhow",
"async-native-tls", "async-native-tls",
"async-recursion 0.3.2", "async-recursion 0.3.2",
"async-tungstenite", "async-tungstenite 0.28.0",
"chrono", "chrono",
"clock", "clock",
"cocoa 0.26.0", "cocoa 0.26.0",
@@ -2612,7 +2639,7 @@ dependencies = [
"assistant_tool", "assistant_tool",
"async-stripe", "async-stripe",
"async-trait", "async-trait",
"async-tungstenite", "async-tungstenite 0.28.0",
"audio", "audio",
"aws-config", "aws-config",
"aws-sdk-kinesis", "aws-sdk-kinesis",
@@ -3418,9 +3445,9 @@ dependencies = [
[[package]] [[package]]
name = "ctor" name = "ctor"
version = "0.2.8" version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
dependencies = [ dependencies = [
"quote", "quote",
"syn 2.0.87", "syn 2.0.87",
@@ -4513,7 +4540,7 @@ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"nanorand", "nanorand",
"spin", "spin 0.9.8",
] ]
[[package]] [[package]]
@@ -5774,7 +5801,7 @@ dependencies = [
"http 1.1.0", "http 1.1.0",
"hyper 1.5.0", "hyper 1.5.0",
"hyper-util", "hyper-util",
"rustls 0.23.18", "rustls 0.23.16",
"rustls-native-certs 0.8.0", "rustls-native-certs 0.8.0",
"rustls-pki-types", "rustls-pki-types",
"tokio", "tokio",
@@ -6426,7 +6453,7 @@ dependencies = [
"base64 0.21.7", "base64 0.21.7",
"js-sys", "js-sys",
"pem", "pem",
"ring", "ring 0.17.8",
"serde", "serde",
"serde_json", "serde_json",
"simple_asn1", "simple_asn1",
@@ -6434,31 +6461,47 @@ dependencies = [
[[package]] [[package]]
name = "jupyter-protocol" name = "jupyter-protocol"
version = "0.5.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "503458f8125fd9047ed0a9d95d7a93adc5eaf8bce48757c6d401e09f71ad3407" checksum = "3d4d496ac890e14efc12c5289818b3c39e3026a7bb02d5576b011e1a062d4bcc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
"bytes 1.8.0", "bytes 1.8.0",
"chrono", "chrono",
"futures 0.3.31", "futures 0.3.31",
"jupyter-serde",
"rand 0.8.5",
"serde", "serde",
"serde_json", "serde_json",
"uuid", "uuid",
] ]
[[package]] [[package]]
name = "jupyter-websocket-client" name = "jupyter-serde"
version = "0.8.0" version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58d9afa5bc6eeafb78f710a2efc585f69099f8b6a99dc7eb826581e3773a6e31" checksum = "32aa595c3912167b7eafcaa822b767ad1fa9605a18127fc9ac741241b796410e"
dependencies = [
"anyhow",
"serde",
"serde_json",
"thiserror 1.0.69",
"uuid",
]
[[package]]
name = "jupyter-websocket-client"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5850894210a3f033ff730d6f956b0335db38573ce7bb61c6abbf69dcbe284ba7"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
"async-tungstenite", "async-tungstenite 0.22.2",
"futures 0.3.31", "futures 0.3.31",
"jupyter-protocol", "jupyter-protocol",
"jupyter-serde",
"serde", "serde",
"serde_json", "serde_json",
"url", "url",
@@ -6774,7 +6817,7 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
dependencies = [ dependencies = [
"spin", "spin 0.9.8",
] ]
[[package]] [[package]]
@@ -6791,9 +6834,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.162" version = "0.2.164"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
[[package]] [[package]]
name = "libdbus-sys" name = "libdbus-sys"
@@ -6866,9 +6909,9 @@ dependencies = [
[[package]] [[package]]
name = "libsqlite3-sys" name = "libsqlite3-sys"
version = "0.30.1" version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f"
dependencies = [ dependencies = [
"cc", "cc",
"pkg-config", "pkg-config",
@@ -7496,13 +7539,13 @@ dependencies = [
[[package]] [[package]]
name = "nbformat" name = "nbformat"
version = "0.9.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19835ad46507d80d9671e10a1c7c335655f4f3033aeb066fe025f14e070c2e66" checksum = "aa6827a3881aa100bb2241cd2633b3c79474dbc93704f1f2cf5cc85064cda4be"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
"jupyter-protocol", "jupyter-serde",
"serde", "serde",
"serde_json", "serde_json",
"thiserror 1.0.69", "thiserror 1.0.69",
@@ -9512,7 +9555,7 @@ dependencies = [
"quinn-proto", "quinn-proto",
"quinn-udp", "quinn-udp",
"rustc-hash 2.0.0", "rustc-hash 2.0.0",
"rustls 0.23.18", "rustls 0.23.16",
"socket2 0.5.7", "socket2 0.5.7",
"thiserror 2.0.3", "thiserror 2.0.3",
"tokio", "tokio",
@@ -9528,9 +9571,9 @@ dependencies = [
"bytes 1.8.0", "bytes 1.8.0",
"getrandom 0.2.15", "getrandom 0.2.15",
"rand 0.8.5", "rand 0.8.5",
"ring", "ring 0.17.8",
"rustc-hash 2.0.0", "rustc-hash 2.0.0",
"rustls 0.23.18", "rustls 0.23.16",
"rustls-pki-types", "rustls-pki-types",
"slab", "slab",
"thiserror 2.0.3", "thiserror 2.0.3",
@@ -10085,7 +10128,7 @@ dependencies = [
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"quinn", "quinn",
"rustls 0.23.18", "rustls 0.23.16",
"rustls-native-certs 0.8.0", "rustls-native-certs 0.8.0",
"rustls-pemfile 2.2.0", "rustls-pemfile 2.2.0",
"rustls-pki-types", "rustls-pki-types",
@@ -10171,6 +10214,21 @@ dependencies = [
"util", "util",
] ]
[[package]]
name = "ring"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
"cc",
"libc",
"once_cell",
"spin 0.5.2",
"untrusted 0.7.1",
"web-sys",
"winapi",
]
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.17.8" version = "0.17.8"
@@ -10181,8 +10239,8 @@ dependencies = [
"cfg-if", "cfg-if",
"getrandom 0.2.15", "getrandom 0.2.15",
"libc", "libc",
"spin", "spin 0.9.8",
"untrusted", "untrusted 0.9.0",
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
@@ -10275,7 +10333,7 @@ name = "rpc"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-tungstenite", "async-tungstenite 0.28.0",
"base64 0.22.1", "base64 0.22.1",
"chrono", "chrono",
"collections", "collections",
@@ -10317,9 +10375,9 @@ dependencies = [
[[package]] [[package]]
name = "runtimelib" name = "runtimelib"
version = "0.24.0" version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "445ff0ee3d5c832cdd27efadd004a741423db1f91bd1de593a14b21211ea084c" checksum = "b3a8ab675beb5cf25c28f9c6ddb8f47bcf73b43872797e6ab6157865f44d1e19"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-dispatcher", "async-dispatcher",
@@ -10332,7 +10390,8 @@ dependencies = [
"futures 0.3.31", "futures 0.3.31",
"glob", "glob",
"jupyter-protocol", "jupyter-protocol",
"ring", "jupyter-serde",
"ring 0.17.8",
"serde", "serde",
"serde_json", "serde_json",
"shellexpand 3.1.0", "shellexpand 3.1.0",
@@ -10459,6 +10518,18 @@ dependencies = [
"rustix 0.38.40", "rustix 0.38.40",
] ]
[[package]]
name = "rustls"
version = "0.20.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99"
dependencies = [
"log",
"ring 0.16.20",
"sct",
"webpki",
]
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.21.12" version = "0.21.12"
@@ -10466,19 +10537,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
dependencies = [ dependencies = [
"log", "log",
"ring", "ring 0.17.8",
"rustls-webpki 0.101.7", "rustls-webpki 0.101.7",
"sct", "sct",
] ]
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.23.18" version = "0.23.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f" checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"ring", "ring 0.17.8",
"rustls-pki-types", "rustls-pki-types",
"rustls-webpki 0.102.8", "rustls-webpki 0.102.8",
"subtle", "subtle",
@@ -10543,8 +10614,8 @@ version = "0.101.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
dependencies = [ dependencies = [
"ring", "ring 0.17.8",
"untrusted", "untrusted 0.9.0",
] ]
[[package]] [[package]]
@@ -10553,9 +10624,9 @@ version = "0.102.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
dependencies = [ dependencies = [
"ring", "ring 0.17.8",
"rustls-pki-types", "rustls-pki-types",
"untrusted", "untrusted 0.9.0",
] ]
[[package]] [[package]]
@@ -10669,8 +10740,8 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
dependencies = [ dependencies = [
"ring", "ring 0.17.8",
"untrusted", "untrusted 0.9.0",
] ]
[[package]] [[package]]
@@ -10958,9 +11029,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.132" version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
dependencies = [ dependencies = [
"indexmap 2.6.0", "indexmap 2.6.0",
"itoa", "itoa",
@@ -11432,6 +11503,12 @@ dependencies = [
"smallvec", "smallvec",
] ]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]] [[package]]
name = "spin" name = "spin"
version = "0.9.8" version = "0.9.8"
@@ -11514,9 +11591,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx" name = "sqlx"
version = "0.8.1" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcfa89bea9500db4a0d038513d7a060566bfc51d46d1c014847049a45cce85e8" checksum = "27144619c6e5802f1380337a209d2ac1c431002dd74c6e60aebff3c506dc4f0c"
dependencies = [ dependencies = [
"sqlx-core", "sqlx-core",
"sqlx-macros", "sqlx-macros",
@@ -11527,9 +11604,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-core" name = "sqlx-core"
version = "0.8.1" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d06e2f2bd861719b1f3f0c7dbe1d80c30bf59e76cf019f07d9014ed7eefb8e08" checksum = "a999083c1af5b5d6c071d34a708a19ba3e02106ad82ef7bbd69f5e48266b613b"
dependencies = [ dependencies = [
"atoi", "atoi",
"bigdecimal", "bigdecimal",
@@ -11555,8 +11632,8 @@ dependencies = [
"paste", "paste",
"percent-encoding", "percent-encoding",
"rust_decimal", "rust_decimal",
"rustls 0.23.18", "rustls 0.21.12",
"rustls-pemfile 2.2.0", "rustls-pemfile 1.0.4",
"serde", "serde",
"serde_json", "serde_json",
"sha2", "sha2",
@@ -11569,14 +11646,14 @@ dependencies = [
"tracing", "tracing",
"url", "url",
"uuid", "uuid",
"webpki-roots 0.26.7", "webpki-roots 0.25.4",
] ]
[[package]] [[package]]
name = "sqlx-macros" name = "sqlx-macros"
version = "0.8.1" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f998a9defdbd48ed005a89362bd40dd2117502f15294f61c8d47034107dbbdc" checksum = "a23217eb7d86c584b8cbe0337b9eacf12ab76fe7673c513141ec42565698bb88"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -11587,9 +11664,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-macros-core" name = "sqlx-macros-core"
version = "0.8.1" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d100558134176a2629d46cec0c8891ba0be8910f7896abfdb75ef4ab6f4e7ce" checksum = "1a099220ae541c5db479c6424bdf1b200987934033c2584f79a0e1693601e776"
dependencies = [ dependencies = [
"dotenvy", "dotenvy",
"either", "either",
@@ -11613,9 +11690,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-mysql" name = "sqlx-mysql"
version = "0.8.1" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936cac0ab331b14cb3921c62156d913e4c15b74fb6ec0f3146bd4ef6e4fb3c12" checksum = "5afe4c38a9b417b6a9a5eeffe7235d0a106716495536e7727d1c7f4b1ff3eba6"
dependencies = [ dependencies = [
"atoi", "atoi",
"base64 0.22.1", "base64 0.22.1",
@@ -11660,9 +11737,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-postgres" name = "sqlx-postgres"
version = "0.8.1" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9734dbce698c67ecf67c442f768a5e90a49b2a4d61a9f1d59f73874bd4cf0710" checksum = "b1dbb157e65f10dbe01f729339c06d239120221c9ad9fa0ba8408c4cc18ecf21"
dependencies = [ dependencies = [
"atoi", "atoi",
"base64 0.22.1", "base64 0.22.1",
@@ -11704,9 +11781,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlx-sqlite" name = "sqlx-sqlite"
version = "0.8.1" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75b419c3c1b1697833dd927bdc4c6545a620bc1bbafabd44e1efbe9afcd337e" checksum = "9b2cdd83c008a622d94499c0006d8ee5f821f36c89b7d625c900e5dc30b5c5ee"
dependencies = [ dependencies = [
"atoi", "atoi",
"chrono", "chrono",
@@ -12780,7 +12857,7 @@ version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
dependencies = [ dependencies = [
"rustls 0.23.18", "rustls 0.23.16",
"rustls-pki-types", "rustls-pki-types",
"tokio", "tokio",
] ]
@@ -13312,6 +13389,25 @@ version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8"
[[package]]
name = "tungstenite"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15fba1a6d6bb030745759a9a2a588bfe8490fc8b4751a277db3a0be1c9ebbf67"
dependencies = [
"byteorder",
"bytes 1.8.0",
"data-encoding",
"http 0.2.12",
"httparse",
"log",
"rand 0.8.5",
"sha1",
"thiserror 1.0.69",
"url",
"utf-8",
]
[[package]] [[package]]
name = "tungstenite" name = "tungstenite"
version = "0.20.1" version = "0.20.1"
@@ -13523,6 +13619,12 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c"
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]] [[package]]
name = "untrusted" name = "untrusted"
version = "0.9.0" version = "0.9.0"
@@ -14433,8 +14535,8 @@ version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53"
dependencies = [ dependencies = [
"ring", "ring 0.17.8",
"untrusted", "untrusted 0.9.0",
] ]
[[package]] [[package]]
@@ -14448,12 +14550,9 @@ dependencies = [
[[package]] [[package]]
name = "webpki-roots" name = "webpki-roots"
version = "0.26.7" version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
dependencies = [
"rustls-pki-types",
]
[[package]] [[package]]
name = "weezl" name = "weezl"
@@ -15773,7 +15872,7 @@ dependencies = [
[[package]] [[package]]
name = "zed_erlang" name = "zed_erlang"
version = "0.1.1" version = "0.1.0"
dependencies = [ dependencies = [
"zed_extension_api 0.1.0", "zed_extension_api 0.1.0",
] ]
@@ -15807,7 +15906,7 @@ dependencies = [
[[package]] [[package]]
name = "zed_haskell" name = "zed_haskell"
version = "0.1.2" version = "0.1.1"
dependencies = [ dependencies = [
"zed_extension_api 0.1.0", "zed_extension_api 0.1.0",
] ]
@@ -15821,28 +15920,28 @@ dependencies = [
[[package]] [[package]]
name = "zed_lua" name = "zed_lua"
version = "0.1.1" version = "0.1.0"
dependencies = [ dependencies = [
"zed_extension_api 0.1.0", "zed_extension_api 0.1.0",
] ]
[[package]] [[package]]
name = "zed_php" name = "zed_php"
version = "0.2.3" version = "0.2.2"
dependencies = [ dependencies = [
"zed_extension_api 0.1.0", "zed_extension_api 0.1.0",
] ]
[[package]] [[package]]
name = "zed_prisma" name = "zed_prisma"
version = "0.0.4" version = "0.0.3"
dependencies = [ dependencies = [
"zed_extension_api 0.1.0", "zed_extension_api 0.1.0",
] ]
[[package]] [[package]]
name = "zed_proto" name = "zed_proto"
version = "0.2.1" version = "0.2.0"
dependencies = [ dependencies = [
"zed_extension_api 0.1.0", "zed_extension_api 0.1.0",
] ]
@@ -15885,7 +15984,7 @@ dependencies = [
[[package]] [[package]]
name = "zed_toml" name = "zed_toml"
version = "0.1.2" version = "0.1.1"
dependencies = [ dependencies = [
"zed_extension_api 0.1.0", "zed_extension_api 0.1.0",
] ]
@@ -15899,7 +15998,7 @@ dependencies = [
[[package]] [[package]]
name = "zed_zig" name = "zed_zig"
version = "0.3.2" version = "0.3.1"
dependencies = [ dependencies = [
"zed_extension_api 0.1.0", "zed_extension_api 0.1.0",
] ]

View File

@@ -388,15 +388,14 @@ indexmap = { version = "1.6.2", features = ["serde"] }
indoc = "2" indoc = "2"
itertools = "0.13.0" itertools = "0.13.0"
jsonwebtoken = "9.3" jsonwebtoken = "9.3"
jupyter-protocol = { version = "0.5.0" } jupyter-protocol = { version = "0.3.0" }
jupyter-websocket-client = { version = "0.8.0" } jupyter-websocket-client = { version = "0.5.0" }
libc = "0.2" libc = "0.2"
libsqlite3-sys = { version = "0.30.1", features = ["bundled"] }
linkify = "0.10.0" linkify = "0.10.0"
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] } log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
markup5ever_rcdom = "0.3.0" markup5ever_rcdom = "0.3.0"
nanoid = "0.4" nanoid = "0.4"
nbformat = { version = "0.9.0" } nbformat = { version = "0.7.0" }
nix = "0.29" nix = "0.29"
num-format = "0.4.4" num-format = "0.4.4"
once_cell = "1.19.0" once_cell = "1.19.0"
@@ -430,7 +429,7 @@ reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "fd110f
"stream", "stream",
] } ] }
rsa = "0.9.6" rsa = "0.9.6"
runtimelib = { version = "0.24.0", default-features = false, features = [ runtimelib = { version = "0.22.0", default-features = false, features = [
"async-dispatcher-runtime", "async-dispatcher-runtime",
] } ] }
rustc-demangle = "0.1.23" rustc-demangle = "0.1.23"

View File

@@ -1,8 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.5 6.66666V8.66666" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M9.20721 10.8551C7.67694 10.8551 6.33401 11.0424 5.52243 11.1878C5.15088 11.2543 4.808 10.9114 4.87454 10.5399C5.01987 9.72825 5.20721 8.38532 5.20721 6.85505C5.20721 5.69375 5.09932 4.64034 4.98377 3.84516C4.9431 3.56522 5.40267 3.3722 5.5684 3.60145C6.30333 4.61809 7.44022 6.08806 8.70721 7.35505C9.9742 8.62204 11.4442 9.75893 12.4608 10.4939C12.69 10.6596 12.497 11.1192 12.2171 11.0785C11.4219 10.963 10.3685 10.8551 9.20721 10.8551Z" fill="black" stroke="black" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.5 5V11" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path d="M7.11129 10.6816L5.28324 8.85355C5.08798 8.65829 4.7714 8.65829 4.57613 8.85355L3.35355 10.0761C3.15829 10.2714 3.15829 10.588 3.35355 10.7832L5.1816 12.6113C5.37686 12.8066 5.69345 12.8066 5.88871 12.6113L7.11129 11.3887C7.30655 11.1934 7.30655 10.8769 7.11129 10.6816Z" fill="black"/>
<path d="M7.5 3V13" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.5 5.33334V10" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.5 4V12" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M13.5 6.66666V8.66666" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 751 B

After

Width:  |  Height:  |  Size: 946 B

View File

@@ -93,6 +93,8 @@
"ctrl-e": "editor::MoveToEndOfLine", "ctrl-e": "editor::MoveToEndOfLine",
"cmd-up": "editor::MoveToBeginning", "cmd-up": "editor::MoveToBeginning",
"cmd-down": "editor::MoveToEnd", "cmd-down": "editor::MoveToEnd",
"ctrl-home": "editor::MoveToBeginning",
"ctrl-end": "editor::MoveToEnd",
"shift-up": "editor::SelectUp", "shift-up": "editor::SelectUp",
"ctrl-shift-p": "editor::SelectUp", "ctrl-shift-p": "editor::SelectUp",
"shift-down": "editor::SelectDown", "shift-down": "editor::SelectDown",

View File

@@ -33,18 +33,6 @@
"(": "vim::SentenceBackward", "(": "vim::SentenceBackward",
")": "vim::SentenceForward", ")": "vim::SentenceForward",
"|": "vim::GoToColumn", "|": "vim::GoToColumn",
"] ]": "vim::NextSectionStart",
"] [": "vim::NextSectionEnd",
"[ [": "vim::PreviousSectionStart",
"[ ]": "vim::PreviousSectionEnd",
"] m": "vim::NextMethodStart",
"] M": "vim::NextMethodEnd",
"[ m": "vim::PreviousMethodStart",
"[ M": "vim::PreviousMethodEnd",
"[ *": "vim::PreviousComment",
"[ /": "vim::PreviousComment",
"] *": "vim::NextComment",
"] /": "vim::NextComment",
// Word motions // Word motions
"w": "vim::NextWordStart", "w": "vim::NextWordStart",
"e": "vim::NextWordEnd", "e": "vim::NextWordEnd",
@@ -67,10 +55,10 @@
"n": "vim::MoveToNextMatch", "n": "vim::MoveToNextMatch",
"shift-n": "vim::MoveToPrevMatch", "shift-n": "vim::MoveToPrevMatch",
"%": "vim::Matching", "%": "vim::Matching",
"] }": ["vim::UnmatchedForward", { "char": "}" }], "] }": ["vim::UnmatchedForward", { "char": "}" } ],
"[ {": ["vim::UnmatchedBackward", { "char": "{" }], "[ {": ["vim::UnmatchedBackward", { "char": "{" } ],
"] )": ["vim::UnmatchedForward", { "char": ")" }], "] )": ["vim::UnmatchedForward", { "char": ")" } ],
"[ (": ["vim::UnmatchedBackward", { "char": "(" }], "[ (": ["vim::UnmatchedBackward", { "char": "(" } ],
"f": ["vim::PushOperator", { "FindForward": { "before": false } }], "f": ["vim::PushOperator", { "FindForward": { "before": false } }],
"t": ["vim::PushOperator", { "FindForward": { "before": true } }], "t": ["vim::PushOperator", { "FindForward": { "before": true } }],
"shift-f": ["vim::PushOperator", { "FindBackward": { "after": false } }], "shift-f": ["vim::PushOperator", { "FindBackward": { "after": false } }],
@@ -221,7 +209,6 @@
"shift-s": "vim::SubstituteLine", "shift-s": "vim::SubstituteLine",
">": ["vim::PushOperator", "Indent"], ">": ["vim::PushOperator", "Indent"],
"<": ["vim::PushOperator", "Outdent"], "<": ["vim::PushOperator", "Outdent"],
"=": ["vim::PushOperator", "AutoIndent"],
"g u": ["vim::PushOperator", "Lowercase"], "g u": ["vim::PushOperator", "Lowercase"],
"g shift-u": ["vim::PushOperator", "Uppercase"], "g shift-u": ["vim::PushOperator", "Uppercase"],
"g ~": ["vim::PushOperator", "OppositeCase"], "g ~": ["vim::PushOperator", "OppositeCase"],
@@ -288,7 +275,6 @@
"ctrl-[": ["vim::SwitchMode", "Normal"], "ctrl-[": ["vim::SwitchMode", "Normal"],
">": "vim::Indent", ">": "vim::Indent",
"<": "vim::Outdent", "<": "vim::Outdent",
"=": "vim::AutoIndent",
"i": ["vim::PushOperator", { "Object": { "around": false } }], "i": ["vim::PushOperator", { "Object": { "around": false } }],
"a": ["vim::PushOperator", { "Object": { "around": true } }], "a": ["vim::PushOperator", { "Object": { "around": true } }],
"g c": "vim::ToggleComments", "g c": "vim::ToggleComments",
@@ -326,22 +312,6 @@
"ctrl-o": "vim::TemporaryNormal" "ctrl-o": "vim::TemporaryNormal"
} }
}, },
{
"context": "vim_mode == helix_normal",
"bindings": {
"i": "vim::InsertBefore",
"a": "vim::InsertAfter",
"w": "vim::NextWordStart",
"e": "vim::NextWordEnd",
"b": "vim::PreviousWordStart",
"h": "vim::Left",
"j": "vim::Down",
"k": "vim::Up",
"l": "vim::Right"
}
},
{ {
"context": "vim_mode == insert && !(showing_code_actions || showing_completions)", "context": "vim_mode == insert && !(showing_code_actions || showing_completions)",
"use_layout_keys": true, "use_layout_keys": true,
@@ -388,8 +358,7 @@
"bindings": { "bindings": {
"escape": "vim::ClearOperators", "escape": "vim::ClearOperators",
"ctrl-c": "vim::ClearOperators", "ctrl-c": "vim::ClearOperators",
"ctrl-[": "vim::ClearOperators", "ctrl-[": "vim::ClearOperators"
"g c": "vim::Comment"
} }
}, },
{ {
@@ -418,9 +387,7 @@
">": "vim::AngleBrackets", ">": "vim::AngleBrackets",
"a": "vim::Argument", "a": "vim::Argument",
"i": "vim::IndentObj", "i": "vim::IndentObj",
"shift-i": ["vim::IndentObj", { "includeBelow": true }], "shift-i": ["vim::IndentObj", { "includeBelow": true }]
"f": "vim::Method",
"c": "vim::Class"
} }
}, },
{ {
@@ -505,13 +472,6 @@
"<": "vim::CurrentLine" "<": "vim::CurrentLine"
} }
}, },
{
"context": "vim_operator == eq",
"use_layout_keys": true,
"bindings": {
"=": "vim::CurrentLine"
}
},
{ {
"context": "vim_operator == gc", "context": "vim_operator == gc",
"use_layout_keys": true, "use_layout_keys": true,

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "https://zed.dev/schema/themes/v0.2.0.json", "$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "Andromeda", "name": "Andromeda",
"author": "Zed Industries", "author": "Zed Industries",
"themes": [ "themes": [

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "https://zed.dev/schema/themes/v0.2.0.json", "$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "Atelier", "name": "Atelier",
"author": "Zed Industries", "author": "Zed Industries",
"themes": [ "themes": [

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "https://zed.dev/schema/themes/v0.2.0.json", "$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "Ayu", "name": "Ayu",
"author": "Zed Industries", "author": "Zed Industries",
"themes": [ "themes": [

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "https://zed.dev/schema/themes/v0.2.0.json", "$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "Gruvbox", "name": "Gruvbox",
"author": "Zed Industries", "author": "Zed Industries",
"themes": [ "themes": [

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "https://zed.dev/schema/themes/v0.2.0.json", "$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "One", "name": "One",
"author": "Zed Industries", "author": "Zed Industries",
"themes": [ "themes": [

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "https://zed.dev/schema/themes/v0.2.0.json", "$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "Rosé Pine", "name": "Rosé Pine",
"author": "Zed Industries", "author": "Zed Industries",
"themes": [ "themes": [

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "https://zed.dev/schema/themes/v0.2.0.json", "$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "Sandcastle", "name": "Sandcastle",
"author": "Zed Industries", "author": "Zed Industries",
"themes": [ "themes": [

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "https://zed.dev/schema/themes/v0.2.0.json", "$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "Solarized", "name": "Solarized",
"author": "Zed Industries", "author": "Zed Industries",
"themes": [ "themes": [

View File

@@ -1,5 +1,5 @@
{ {
"$schema": "https://zed.dev/schema/themes/v0.2.0.json", "$schema": "https://zed.dev/schema/themes/v0.1.0.json",
"name": "Summercamp", "name": "Summercamp",
"author": "Zed Industries", "author": "Zed Industries",
"themes": [ "themes": [

View File

@@ -33,7 +33,7 @@ use editor::{
}, },
scroll::{Autoscroll, AutoscrollStrategy}, scroll::{Autoscroll, AutoscrollStrategy},
Anchor, Editor, EditorEvent, ProposedChangeLocation, ProposedChangesEditor, RowExt, Anchor, Editor, EditorEvent, ProposedChangeLocation, ProposedChangesEditor, RowExt,
ToMultiBufferPoint, ToOffset as _, ToOffset as _, ToPoint,
}; };
use editor::{display_map::CreaseId, FoldPlaceholder}; use editor::{display_map::CreaseId, FoldPlaceholder};
use fs::Fs; use fs::Fs;

View File

@@ -14,7 +14,7 @@ use editor::{
}, },
Anchor, AnchorRangeExt, CodeActionProvider, Editor, EditorElement, EditorEvent, EditorMode, Anchor, AnchorRangeExt, CodeActionProvider, Editor, EditorElement, EditorEvent, EditorMode,
EditorStyle, ExcerptId, ExcerptRange, GutterDimensions, MultiBuffer, MultiBufferSnapshot, EditorStyle, ExcerptId, ExcerptRange, GutterDimensions, MultiBuffer, MultiBufferSnapshot,
ToMultiBufferPoint, ToOffset as _, ToOffset as _, ToPoint,
}; };
use feature_flags::{FeatureFlagAppExt as _, ZedPro}; use feature_flags::{FeatureFlagAppExt as _, ZedPro};
use fs::Fs; use fs::Fs;

View File

@@ -2,7 +2,7 @@ use std::sync::Arc;
use gpui::{AnyElement, DismissEvent, SharedString, Task, WeakView}; use gpui::{AnyElement, DismissEvent, SharedString, Task, WeakView};
use picker::{Picker, PickerDelegate, PickerEditorPosition}; use picker::{Picker, PickerDelegate, PickerEditorPosition};
use ui::{prelude::*, ListItem, ListItemSpacing, PopoverMenu, PopoverTrigger, Tooltip}; use ui::{prelude::*, ListItem, ListItemSpacing, PopoverMenu, PopoverTrigger};
use crate::assistant_panel::ContextEditor; use crate::assistant_panel::ContextEditor;
use crate::SlashCommandWorkingSet; use crate::SlashCommandWorkingSet;
@@ -177,17 +177,11 @@ impl PickerDelegate for SlashCommandDelegate {
.inset(true) .inset(true)
.spacing(ListItemSpacing::Dense) .spacing(ListItemSpacing::Dense)
.selected(selected) .selected(selected)
.tooltip({
let description = info.description.clone();
move |cx| cx.new_view(|_| Tooltip::new(description.clone())).into()
})
.child( .child(
v_flex() v_flex()
.group(format!("command-entry-label-{ix}")) .group(format!("command-entry-label-{ix}"))
.w_full() .w_full()
.py_0p5()
.min_w(px(250.)) .min_w(px(250.))
.max_w(px(400.))
.child( .child(
h_flex() h_flex()
.gap_1p5() .gap_1p5()
@@ -198,7 +192,7 @@ impl PickerDelegate for SlashCommandDelegate {
{ {
label.push_str(&args); label.push_str(&args);
} }
Label::new(label).single_line().size(LabelSize::Small) Label::new(label).size(LabelSize::Small)
})) }))
.children(info.args.clone().filter(|_| !selected).map( .children(info.args.clone().filter(|_| !selected).map(
|args| { |args| {
@@ -206,7 +200,6 @@ impl PickerDelegate for SlashCommandDelegate {
.font_buffer(cx) .font_buffer(cx)
.child( .child(
Label::new(args) Label::new(args)
.single_line()
.size(LabelSize::Small) .size(LabelSize::Small)
.color(Color::Muted), .color(Color::Muted),
) )
@@ -217,11 +210,9 @@ impl PickerDelegate for SlashCommandDelegate {
)), )),
) )
.child( .child(
div().overflow_hidden().text_ellipsis().child( Label::new(info.description.clone())
Label::new(info.description.clone()) .size(LabelSize::Small)
.size(LabelSize::Small) .color(Color::Muted),
.color(Color::Muted),
),
), ),
), ),
), ),

View File

@@ -23,17 +23,15 @@ editor.workspace = true
feature_flags.workspace = true feature_flags.workspace = true
futures.workspace = true futures.workspace = true
gpui.workspace = true gpui.workspace = true
language.workspace = true
language_model.workspace = true language_model.workspace = true
language_model_selector.workspace = true language_model_selector.workspace = true
language_models.workspace = true language_models.workspace = true
log.workspace = true log.workspace = true
markdown.workspace = true
project.workspace = true project.workspace = true
proto.workspace = true proto.workspace = true
settings.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
settings.workspace = true
smol.workspace = true smol.workspace = true
theme.workspace = true theme.workspace = true
ui.workspace = true ui.workspace = true

View File

@@ -3,25 +3,19 @@ use std::sync::Arc;
use anyhow::Result; use anyhow::Result;
use assistant_tool::ToolWorkingSet; use assistant_tool::ToolWorkingSet;
use client::zed_urls; use client::zed_urls;
use collections::HashMap;
use gpui::{ use gpui::{
list, prelude::*, px, Action, AnyElement, AppContext, AsyncWindowContext, Empty, EventEmitter, prelude::*, px, Action, AnyElement, AppContext, AsyncWindowContext, EventEmitter, FocusHandle,
FocusHandle, FocusableView, FontWeight, ListAlignment, ListState, Model, Pixels, FocusableView, FontWeight, Model, Pixels, Subscription, Task, View, ViewContext, WeakView,
StyleRefinement, Subscription, Task, TextStyleRefinement, View, ViewContext, WeakView,
WindowContext, WindowContext,
}; };
use language::LanguageRegistry;
use language_model::{LanguageModelRegistry, Role}; use language_model::{LanguageModelRegistry, Role};
use language_model_selector::LanguageModelSelector; use language_model_selector::LanguageModelSelector;
use markdown::{Markdown, MarkdownStyle};
use settings::Settings;
use theme::ThemeSettings;
use ui::{prelude::*, ButtonLike, Divider, IconButtonShape, Tab, Tooltip}; use ui::{prelude::*, ButtonLike, Divider, IconButtonShape, Tab, Tooltip};
use workspace::dock::{DockPosition, Panel, PanelEvent}; use workspace::dock::{DockPosition, Panel, PanelEvent};
use workspace::Workspace; use workspace::Workspace;
use crate::message_editor::MessageEditor; use crate::message_editor::MessageEditor;
use crate::thread::{MessageId, Thread, ThreadError, ThreadEvent}; use crate::thread::{Message, Thread, ThreadError, ThreadEvent};
use crate::thread_store::ThreadStore; use crate::thread_store::ThreadStore;
use crate::{NewThread, ToggleFocus, ToggleModelSelector}; use crate::{NewThread, ToggleFocus, ToggleModelSelector};
@@ -38,13 +32,9 @@ pub fn init(cx: &mut AppContext) {
pub struct AssistantPanel { pub struct AssistantPanel {
workspace: WeakView<Workspace>, workspace: WeakView<Workspace>,
language_registry: Arc<LanguageRegistry>,
#[allow(unused)] #[allow(unused)]
thread_store: Model<ThreadStore>, thread_store: Model<ThreadStore>,
thread: Model<Thread>, thread: Model<Thread>,
thread_messages: Vec<MessageId>,
rendered_messages_by_id: HashMap<MessageId, View<Markdown>>,
thread_list_state: ListState,
message_editor: View<MessageEditor>, message_editor: View<MessageEditor>,
tools: Arc<ToolWorkingSet>, tools: Arc<ToolWorkingSet>,
last_error: Option<ThreadError>, last_error: Option<ThreadError>,
@@ -85,18 +75,8 @@ impl AssistantPanel {
Self { Self {
workspace: workspace.weak_handle(), workspace: workspace.weak_handle(),
language_registry: workspace.project().read(cx).languages().clone(),
thread_store, thread_store,
thread: thread.clone(), thread: thread.clone(),
thread_messages: Vec::new(),
rendered_messages_by_id: HashMap::default(),
thread_list_state: ListState::new(0, ListAlignment::Bottom, px(1024.), {
let this = cx.view().downgrade();
move |ix, cx: &mut WindowContext| {
this.update(cx, |this, cx| this.render_message(ix, cx))
.unwrap()
}
}),
message_editor: cx.new_view(|cx| MessageEditor::new(thread, cx)), message_editor: cx.new_view(|cx| MessageEditor::new(thread, cx)),
tools, tools,
last_error: None, last_error: None,
@@ -114,9 +94,6 @@ impl AssistantPanel {
self.message_editor = cx.new_view(|cx| MessageEditor::new(thread.clone(), cx)); self.message_editor = cx.new_view(|cx| MessageEditor::new(thread.clone(), cx));
self.thread = thread; self.thread = thread;
self.thread_messages.clear();
self.thread_list_state.reset(0);
self.rendered_messages_by_id.clear();
self._subscriptions = subscriptions; self._subscriptions = subscriptions;
self.message_editor.focus_handle(cx).focus(cx); self.message_editor.focus_handle(cx).focus(cx);
@@ -133,63 +110,6 @@ impl AssistantPanel {
self.last_error = Some(error.clone()); self.last_error = Some(error.clone());
} }
ThreadEvent::StreamedCompletion => {} ThreadEvent::StreamedCompletion => {}
ThreadEvent::StreamedAssistantText(message_id, text) => {
if let Some(markdown) = self.rendered_messages_by_id.get_mut(&message_id) {
markdown.update(cx, |markdown, cx| {
markdown.append(text, cx);
});
}
}
ThreadEvent::MessageAdded(message_id) => {
let old_len = self.thread_messages.len();
self.thread_messages.push(*message_id);
self.thread_list_state.splice(old_len..old_len, 1);
if let Some(message_text) = self
.thread
.read(cx)
.message(*message_id)
.map(|message| message.text.clone())
{
let theme_settings = ThemeSettings::get_global(cx);
let mut text_style = cx.text_style();
text_style.refine(&TextStyleRefinement {
font_family: Some(theme_settings.ui_font.family.clone()),
font_size: Some(TextSize::Default.rems(cx).into()),
color: Some(cx.theme().colors().text),
..Default::default()
});
let markdown_style = MarkdownStyle {
base_text_style: text_style,
syntax: cx.theme().syntax().clone(),
selection_background_color: cx.theme().players().local().selection,
code_block: StyleRefinement {
text: Some(TextStyleRefinement {
font_family: Some(theme_settings.buffer_font.family.clone()),
font_size: Some(theme_settings.buffer_font_size.into()),
..Default::default()
}),
..Default::default()
},
..Default::default()
};
let markdown = cx.new_view(|cx| {
Markdown::new(
message_text,
markdown_style,
Some(self.language_registry.clone()),
None,
cx,
)
});
self.rendered_messages_by_id.insert(*message_id, markdown);
}
cx.notify();
}
ThreadEvent::UsePendingTools => { ThreadEvent::UsePendingTools => {
let pending_tool_uses = self let pending_tool_uses = self
.thread .thread
@@ -381,46 +301,31 @@ impl AssistantPanel {
) )
} }
fn render_message(&self, ix: usize, cx: &mut ViewContext<Self>) -> AnyElement { fn render_message(&self, message: Message, cx: &mut ViewContext<Self>) -> impl IntoElement {
let message_id = self.thread_messages[ix];
let Some(message) = self.thread.read(cx).message(message_id) else {
return Empty.into_any();
};
let Some(markdown) = self.rendered_messages_by_id.get(&message_id) else {
return Empty.into_any();
};
let (role_icon, role_name) = match message.role { let (role_icon, role_name) = match message.role {
Role::User => (IconName::Person, "You"), Role::User => (IconName::Person, "You"),
Role::Assistant => (IconName::ZedAssistant, "Assistant"), Role::Assistant => (IconName::ZedAssistant, "Assistant"),
Role::System => (IconName::Settings, "System"), Role::System => (IconName::Settings, "System"),
}; };
div() v_flex()
.id(("message-container", ix)) .border_1()
.p_2() .border_color(cx.theme().colors().border_variant)
.rounded_md()
.child( .child(
v_flex() h_flex()
.border_1() .justify_between()
.p_1p5()
.border_b_1()
.border_color(cx.theme().colors().border_variant) .border_color(cx.theme().colors().border_variant)
.rounded_md()
.child( .child(
h_flex() h_flex()
.justify_between() .gap_2()
.p_1p5() .child(Icon::new(role_icon).size(IconSize::Small))
.border_b_1() .child(Label::new(role_name).size(LabelSize::Small)),
.border_color(cx.theme().colors().border_variant) ),
.child(
h_flex()
.gap_2()
.child(Icon::new(role_icon).size(IconSize::Small))
.child(Label::new(role_name).size(LabelSize::Small)),
),
)
.child(v_flex().p_1p5().text_ui(cx).child(markdown.clone())),
) )
.into_any() .child(v_flex().p_1p5().child(Label::new(message.text.clone())))
} }
fn render_last_error(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> { fn render_last_error(&self, cx: &mut ViewContext<Self>) -> Option<AnyElement> {
@@ -572,6 +477,8 @@ impl AssistantPanel {
impl Render for AssistantPanel { impl Render for AssistantPanel {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let messages = self.thread.read(cx).messages().cloned().collect::<Vec<_>>();
v_flex() v_flex()
.key_context("AssistantPanel2") .key_context("AssistantPanel2")
.justify_between() .justify_between()
@@ -580,7 +487,20 @@ impl Render for AssistantPanel {
this.new_thread(cx); this.new_thread(cx);
})) }))
.child(self.render_toolbar(cx)) .child(self.render_toolbar(cx))
.child(list(self.thread_list_state.clone()).flex_1()) .child(
v_flex()
.id("message-list")
.gap_2()
.size_full()
.p_2()
.overflow_y_scroll()
.bg(cx.theme().colors().panel_background)
.children(
messages
.into_iter()
.map(|message| self.render_message(message, cx)),
),
)
.child( .child(
h_flex() h_flex()
.border_t_1() .border_t_1()

View File

@@ -56,7 +56,7 @@ impl MessageEditor {
}); });
self.thread.update(cx, |thread, cx| { self.thread.update(cx, |thread, cx| {
thread.insert_user_message(user_message, cx); thread.insert_user_message(user_message);
let mut request = thread.to_completion_request(request_kind, cx); let mut request = thread.to_completion_request(request_kind, cx);
if self.use_tools { if self.use_tools {

View File

@@ -63,8 +63,8 @@ impl Thread {
} }
} }
pub fn message(&self, id: MessageId) -> Option<&Message> { pub fn messages(&self) -> impl Iterator<Item = &Message> {
self.messages.iter().find(|message| message.id == id) self.messages.iter()
} }
pub fn tools(&self) -> &Arc<ToolWorkingSet> { pub fn tools(&self) -> &Arc<ToolWorkingSet> {
@@ -75,14 +75,12 @@ impl Thread {
self.pending_tool_uses_by_id.values().collect() self.pending_tool_uses_by_id.values().collect()
} }
pub fn insert_user_message(&mut self, text: impl Into<String>, cx: &mut ModelContext<Self>) { pub fn insert_user_message(&mut self, text: impl Into<String>) {
let id = self.next_message_id.post_inc();
self.messages.push(Message { self.messages.push(Message {
id, id: self.next_message_id.post_inc(),
role: Role::User, role: Role::User,
text: text.into(), text: text.into(),
}); });
cx.emit(ThreadEvent::MessageAdded(id));
} }
pub fn to_completion_request( pub fn to_completion_request(
@@ -152,13 +150,11 @@ impl Thread {
thread.update(&mut cx, |thread, cx| { thread.update(&mut cx, |thread, cx| {
match event { match event {
LanguageModelCompletionEvent::StartMessage { .. } => { LanguageModelCompletionEvent::StartMessage { .. } => {
let id = thread.next_message_id.post_inc();
thread.messages.push(Message { thread.messages.push(Message {
id, id: thread.next_message_id.post_inc(),
role: Role::Assistant, role: Role::Assistant,
text: String::new(), text: String::new(),
}); });
cx.emit(ThreadEvent::MessageAdded(id));
} }
LanguageModelCompletionEvent::Stop(reason) => { LanguageModelCompletionEvent::Stop(reason) => {
stop_reason = reason; stop_reason = reason;
@@ -167,10 +163,6 @@ impl Thread {
if let Some(last_message) = thread.messages.last_mut() { if let Some(last_message) = thread.messages.last_mut() {
if last_message.role == Role::Assistant { if last_message.role == Role::Assistant {
last_message.text.push_str(&chunk); last_message.text.push_str(&chunk);
cx.emit(ThreadEvent::StreamedAssistantText(
last_message.id,
chunk,
));
} }
} }
} }
@@ -324,8 +316,6 @@ pub enum ThreadError {
pub enum ThreadEvent { pub enum ThreadEvent {
ShowError(ThreadError), ShowError(ThreadError),
StreamedCompletion, StreamedCompletion,
StreamedAssistantText(MessageId, String),
MessageAdded(MessageId),
UsePendingTools, UsePendingTools,
ToolFinished { ToolFinished {
#[allow(unused)] #[allow(unused)]

View File

@@ -1,9 +1,7 @@
use std::time::Duration;
use editor::Editor; use editor::Editor;
use gpui::{ use gpui::{
EventEmitter, IntoElement, ParentElement, Render, Styled, Subscription, Task, View, EventEmitter, IntoElement, ParentElement, Render, Styled, Subscription, View, ViewContext,
ViewContext, WeakView, WeakView,
}; };
use language::Diagnostic; use language::Diagnostic;
use ui::{h_flex, prelude::*, Button, ButtonLike, Color, Icon, IconName, Label, Tooltip}; use ui::{h_flex, prelude::*, Button, ButtonLike, Color, Icon, IconName, Label, Tooltip};
@@ -17,7 +15,6 @@ pub struct DiagnosticIndicator {
workspace: WeakView<Workspace>, workspace: WeakView<Workspace>,
current_diagnostic: Option<Diagnostic>, current_diagnostic: Option<Diagnostic>,
_observe_active_editor: Option<Subscription>, _observe_active_editor: Option<Subscription>,
diagnostics_update: Task<()>,
} }
impl Render for DiagnosticIndicator { impl Render for DiagnosticIndicator {
@@ -129,7 +126,6 @@ impl DiagnosticIndicator {
workspace: workspace.weak_handle(), workspace: workspace.weak_handle(),
current_diagnostic: None, current_diagnostic: None,
_observe_active_editor: None, _observe_active_editor: None,
diagnostics_update: Task::ready(()),
} }
} }
@@ -153,17 +149,8 @@ impl DiagnosticIndicator {
.min_by_key(|entry| (entry.diagnostic.severity, entry.range.len())) .min_by_key(|entry| (entry.diagnostic.severity, entry.range.len()))
.map(|entry| entry.diagnostic); .map(|entry| entry.diagnostic);
if new_diagnostic != self.current_diagnostic { if new_diagnostic != self.current_diagnostic {
self.diagnostics_update = cx.spawn(|diagnostics_indicator, mut cx| async move { self.current_diagnostic = new_diagnostic;
cx.background_executor() cx.notify();
.timer(Duration::from_millis(50))
.await;
diagnostics_indicator
.update(&mut cx, |diagnostics_indicator, cx| {
diagnostics_indicator.current_diagnostic = new_diagnostic;
cx.notify();
})
.ok();
});
} }
} }
} }

View File

@@ -248,7 +248,6 @@ gpui::actions!(
FindAllReferences, FindAllReferences,
Fold, Fold,
FoldAll, FoldAll,
FoldFunctionBodies,
FoldRecursive, FoldRecursive,
FoldSelectedRanges, FoldSelectedRanges,
ToggleFold, ToggleFold,
@@ -304,7 +303,6 @@ gpui::actions!(
OpenPermalinkToLine, OpenPermalinkToLine,
OpenUrl, OpenUrl,
Outdent, Outdent,
AutoIndent,
PageDown, PageDown,
PageUp, PageUp,
Paste, Paste,

View File

@@ -51,7 +51,7 @@ use language::{
use lsp::DiagnosticSeverity; use lsp::DiagnosticSeverity;
use multi_buffer::{ use multi_buffer::{
Anchor, AnchorRangeExt, MultiBuffer, MultiBufferPoint, MultiBufferRow, MultiBufferSnapshot, Anchor, AnchorRangeExt, MultiBuffer, MultiBufferPoint, MultiBufferRow, MultiBufferSnapshot,
ToMultiBufferPoint, ToOffset, ToOffset, ToPoint,
}; };
use serde::Deserialize; use serde::Deserialize;
use std::{ use std::{
@@ -688,10 +688,7 @@ impl DisplaySnapshot {
self.buffer_snapshot.max_buffer_row() self.buffer_snapshot.max_buffer_row()
} }
pub fn prev_line_boundary( pub fn prev_line_boundary(&self, mut point: MultiBufferPoint) -> (Point, DisplayPoint) {
&self,
mut point: MultiBufferPoint,
) -> (MultiBufferPoint, DisplayPoint) {
loop { loop {
let mut inlay_point = self.inlay_snapshot.to_inlay_point(point); let mut inlay_point = self.inlay_snapshot.to_inlay_point(point);
let mut fold_point = self.fold_snapshot.to_fold_point(inlay_point, Bias::Left); let mut fold_point = self.fold_snapshot.to_fold_point(inlay_point, Bias::Left);

View File

@@ -7,8 +7,7 @@ use collections::{Bound, HashMap, HashSet};
use gpui::{AnyElement, EntityId, Pixels, WindowContext}; use gpui::{AnyElement, EntityId, Pixels, WindowContext};
use language::{Chunk, Patch, Point}; use language::{Chunk, Patch, Point};
use multi_buffer::{ use multi_buffer::{
Anchor, ExcerptId, ExcerptInfo, MultiBufferPoint, MultiBufferRow, MultiBufferSnapshot, Anchor, ExcerptId, ExcerptInfo, MultiBufferRow, MultiBufferSnapshot, ToOffset, ToPoint as _,
ToMultiBufferPoint as _, ToOffset,
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{ use std::{
@@ -739,13 +738,13 @@ impl BlockMap {
let wrap_row; let wrap_row;
if excerpt_boundary.next.is_some() { if excerpt_boundary.next.is_some() {
wrap_row = wrap_snapshot wrap_row = wrap_snapshot
.make_wrap_point(MultiBufferPoint::new(excerpt_boundary.row, 0), Bias::Left) .make_wrap_point(Point::new(excerpt_boundary.row.0, 0), Bias::Left)
.row(); .row();
} else { } else {
wrap_row = wrap_snapshot wrap_row = wrap_snapshot
.make_wrap_point( .make_wrap_point(
MultiBufferPoint::new( Point::new(
excerpt_boundary.row, excerpt_boundary.row.0,
buffer.line_len(excerpt_boundary.row), buffer.line_len(excerpt_boundary.row),
), ),
Bias::Left, Bias::Left,
@@ -1367,7 +1366,7 @@ impl BlockSnapshot {
pub(super) fn is_line_replaced(&self, row: MultiBufferRow) -> bool { pub(super) fn is_line_replaced(&self, row: MultiBufferRow) -> bool {
let wrap_point = self let wrap_point = self
.wrap_snapshot .wrap_snapshot
.make_wrap_point(MultiBufferPoint::new(row, 0), Bias::Left); .make_wrap_point(Point::new(row.0, 0), Bias::Left);
let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&()); let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &()); cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
cursor.item().map_or(false, |transform| { cursor.item().map_or(false, |transform| {
@@ -2512,7 +2511,7 @@ mod tests {
let wrap_row = wrap_row as u32; let wrap_row = wrap_row as u32;
let multibuffer_row = wraps_snapshot let multibuffer_row = wraps_snapshot
.to_point(WrapPoint::new(wrap_row, 0), Bias::Left) .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
.row(); .row;
// Create empty lines for the above block // Create empty lines for the above block
while let Some((placement, block)) = sorted_blocks_iter.peek() { while let Some((placement, block)) = sorted_blocks_iter.peek() {
@@ -2544,8 +2543,7 @@ mod tests {
is_in_replace_block = true; is_in_replace_block = true;
if wrap_row == replace_range.start.0 { if wrap_row == replace_range.start.0 {
expected_buffer_rows expected_buffer_rows.push(input_buffer_rows[multibuffer_row as usize]);
.push(input_buffer_rows[multibuffer_row.0 as usize]);
} }
if wrap_row == replace_range.end.0 { if wrap_row == replace_range.end.0 {
@@ -2567,9 +2565,9 @@ mod tests {
} }
if is_in_replace_block { if is_in_replace_block {
expected_replaced_buffer_rows.insert(multibuffer_row); expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
} else { } else {
let buffer_row = input_buffer_rows[multibuffer_row.0 as usize]; let buffer_row = input_buffer_rows[multibuffer_row as usize];
let soft_wrapped = wraps_snapshot let soft_wrapped = wraps_snapshot
.to_tab_point(WrapPoint::new(wrap_row, 0)) .to_tab_point(WrapPoint::new(wrap_row, 0))
.column() .column()

View File

@@ -1,8 +1,6 @@
use collections::HashMap; use collections::HashMap;
use gpui::{AnyElement, IntoElement}; use gpui::{AnyElement, IntoElement};
use multi_buffer::{ use multi_buffer::{Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, ToPoint};
Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, ToMultiBufferPoint,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{cmp::Ordering, fmt::Debug, ops::Range, sync::Arc}; use std::{cmp::Ordering, fmt::Debug, ops::Range, sync::Arc};
use sum_tree::{Bias, SeekTarget, SumTree}; use sum_tree::{Bias, SeekTarget, SumTree};

View File

@@ -1380,7 +1380,7 @@ pub type FoldEdit = Edit<FoldOffset>;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{display_map::inlay_map::InlayMap, MultiBuffer, ToMultiBufferPoint}; use crate::{display_map::inlay_map::InlayMap, MultiBuffer, ToPoint};
use collections::HashSet; use collections::HashSet;
use rand::prelude::*; use rand::prelude::*;
use settings::SettingsStore; use settings::SettingsStore;

View File

@@ -3,8 +3,7 @@ use collections::{BTreeMap, BTreeSet};
use gpui::HighlightStyle; use gpui::HighlightStyle;
use language::{Chunk, Edit, Point, TextSummary}; use language::{Chunk, Edit, Point, TextSummary};
use multi_buffer::{ use multi_buffer::{
Anchor, MultiBufferChunks, MultiBufferPoint, MultiBufferRow, MultiBufferRows, Anchor, MultiBufferChunks, MultiBufferRow, MultiBufferRows, MultiBufferSnapshot, ToOffset,
MultiBufferSnapshot, ToOffset,
}; };
use std::{ use std::{
any::TypeId, any::TypeId,
@@ -778,7 +777,7 @@ impl InlaySnapshot {
None => self.len(), None => self.len(),
} }
} }
pub fn to_buffer_point(&self, point: InlayPoint) -> MultiBufferPoint { pub fn to_buffer_point(&self, point: InlayPoint) -> Point {
let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>(&()); let mut cursor = self.transforms.cursor::<(InlayPoint, Point)>(&());
cursor.seek(&point, Bias::Right, &()); cursor.seek(&point, Bias::Right, &());
match cursor.item() { match cursor.item() {
@@ -836,7 +835,7 @@ impl InlaySnapshot {
} }
} }
} }
pub fn to_inlay_point(&self, point: MultiBufferPoint) -> InlayPoint { pub fn to_inlay_point(&self, point: Point) -> InlayPoint {
let mut cursor = self.transforms.cursor::<(Point, InlayPoint)>(&()); let mut cursor = self.transforms.cursor::<(Point, InlayPoint)>(&());
cursor.seek(&point, Bias::Left, &()); cursor.seek(&point, Bias::Left, &());
loop { loop {

View File

@@ -3,7 +3,7 @@ use super::{
Highlights, Highlights,
}; };
use language::{Chunk, Point}; use language::{Chunk, Point};
use multi_buffer::{MultiBufferPoint, MultiBufferSnapshot}; use multi_buffer::MultiBufferSnapshot;
use std::{cmp, mem, num::NonZeroU32, ops::Range}; use std::{cmp, mem, num::NonZeroU32, ops::Range};
use sum_tree::Bias; use sum_tree::Bias;
@@ -316,13 +316,13 @@ impl TabSnapshot {
) )
} }
pub fn make_tab_point(&self, point: MultiBufferPoint, bias: Bias) -> TabPoint { pub fn make_tab_point(&self, point: Point, bias: Bias) -> TabPoint {
let inlay_point = self.fold_snapshot.inlay_snapshot.to_inlay_point(point); let inlay_point = self.fold_snapshot.inlay_snapshot.to_inlay_point(point);
let fold_point = self.fold_snapshot.to_fold_point(inlay_point, bias); let fold_point = self.fold_snapshot.to_fold_point(inlay_point, bias);
self.to_tab_point(fold_point) self.to_tab_point(fold_point)
} }
pub fn to_point(&self, point: TabPoint, bias: Bias) -> MultiBufferPoint { pub fn to_point(&self, point: TabPoint, bias: Bias) -> Point {
let fold_point = self.to_fold_point(point, bias).0; let fold_point = self.to_fold_point(point, bias).0;
let inlay_point = fold_point.to_inlay_point(&self.fold_snapshot); let inlay_point = fold_point.to_inlay_point(&self.fold_snapshot);
self.fold_snapshot self.fold_snapshot

View File

@@ -5,7 +5,7 @@ use super::{
}; };
use gpui::{AppContext, Context, Font, LineWrapper, Model, ModelContext, Pixels, Task}; use gpui::{AppContext, Context, Font, LineWrapper, Model, ModelContext, Pixels, Task};
use language::{Chunk, Point}; use language::{Chunk, Point};
use multi_buffer::{MultiBufferPoint, MultiBufferSnapshot}; use multi_buffer::MultiBufferSnapshot;
use smol::future::yield_now; use smol::future::yield_now;
use std::sync::LazyLock; use std::sync::LazyLock;
use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration}; use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration};
@@ -747,11 +747,11 @@ impl WrapSnapshot {
TabPoint(tab_point) TabPoint(tab_point)
} }
pub fn to_point(&self, point: WrapPoint, bias: Bias) -> MultiBufferPoint { pub fn to_point(&self, point: WrapPoint, bias: Bias) -> Point {
self.tab_snapshot.to_point(self.to_tab_point(point), bias) self.tab_snapshot.to_point(self.to_tab_point(point), bias)
} }
pub fn make_wrap_point(&self, point: MultiBufferPoint, bias: Bias) -> WrapPoint { pub fn make_wrap_point(&self, point: Point, bias: Bias) -> WrapPoint {
self.tab_point_to_wrap_point(self.tab_snapshot.make_tab_point(point, bias)) self.tab_point_to_wrap_point(self.tab_snapshot.make_tab_point(point, bias))
} }

View File

@@ -114,8 +114,8 @@ use lsp::{
use mouse_context_menu::MouseContextMenu; use mouse_context_menu::MouseContextMenu;
use movement::TextLayoutDetails; use movement::TextLayoutDetails;
pub use multi_buffer::{ pub use multi_buffer::{
Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
ToMultiBufferPoint, ToOffset, ToPoint,
}; };
use multi_buffer::{ use multi_buffer::{
ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16, ExpandExcerptDirection, MultiBufferDiffHunk, MultiBufferPoint, MultiBufferRow, ToOffsetUtf16,
@@ -4098,10 +4098,8 @@ impl Editor {
if buffer.contains_str_at(selection.start, &pair.end) { if buffer.contains_str_at(selection.start, &pair.end) {
let pair_start_len = pair.start.len(); let pair_start_len = pair.start.len();
if buffer.contains_str_at( if buffer.contains_str_at(selection.start - pair_start_len, &pair.start)
selection.start.saturating_sub(pair_start_len), {
&pair.start,
) {
selection.start -= pair_start_len; selection.start -= pair_start_len;
selection.end += pair.end.len(); selection.end += pair.end.len();
@@ -6299,25 +6297,6 @@ impl Editor {
}); });
} }
pub fn autoindent(&mut self, _: &AutoIndent, cx: &mut ViewContext<Self>) {
if self.read_only(cx) {
return;
}
let selections = self
.selections
.all::<usize>(cx)
.into_iter()
.map(|s| s.range());
self.transact(cx, |this, cx| {
this.buffer.update(cx, |buffer, cx| {
buffer.autoindent_ranges(selections, cx);
});
let selections = this.selections.all::<usize>(cx);
this.change_selections(Some(Autoscroll::fit()), cx, |s| s.select(selections));
});
}
pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) { pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let selections = self.selections.all::<Point>(cx); let selections = self.selections.all::<Point>(cx);
@@ -11097,23 +11076,6 @@ impl Editor {
self.fold_creases(fold_ranges, true, cx); self.fold_creases(fold_ranges, true, cx);
} }
pub fn fold_function_bodies(
&mut self,
_: &actions::FoldFunctionBodies,
cx: &mut ViewContext<Self>,
) {
let snapshot = self.buffer.read(cx).snapshot(cx);
let Some((_, _, buffer)) = snapshot.as_singleton() else {
return;
};
let creases = buffer
.function_body_fold_ranges(0..buffer.len())
.map(|range| Crease::simple(range, self.display_map.read(cx).fold_placeholder.clone()))
.collect();
self.fold_creases(creases, true, cx);
}
pub fn fold_recursive(&mut self, _: &actions::FoldRecursive, cx: &mut ViewContext<Self>) { pub fn fold_recursive(&mut self, _: &actions::FoldRecursive, cx: &mut ViewContext<Self>) {
let mut to_fold = Vec::new(); let mut to_fold = Vec::new();
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
@@ -12852,48 +12814,14 @@ impl Editor {
}; };
for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer { for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
let editor = buffer let editor =
.read(cx) workspace.open_project_item::<Self>(pane.clone(), buffer, true, true, cx);
.file()
.is_none()
.then(|| {
// Handle file-less buffers separately: those are not really the project items, so won't have a paroject path or entity id,
// so `workspace.open_project_item` will never find them, always opening a new editor.
// Instead, we try to activate the existing editor in the pane first.
let (editor, pane_item_index) =
pane.read(cx).items().enumerate().find_map(|(i, item)| {
let editor = item.downcast::<Editor>()?;
let singleton_buffer =
editor.read(cx).buffer().read(cx).as_singleton()?;
if singleton_buffer == buffer {
Some((editor, i))
} else {
None
}
})?;
pane.update(cx, |pane, cx| {
pane.activate_item(pane_item_index, true, true, cx)
});
Some(editor)
})
.flatten()
.unwrap_or_else(|| {
workspace.open_project_item::<Self>(
pane.clone(),
buffer,
true,
true,
cx,
)
});
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
let autoscroll = match scroll_offset { let autoscroll = match scroll_offset {
Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize), Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
None => Autoscroll::newest(), None => Autoscroll::newest(),
}; };
let nav_history = editor.nav_history.take(); let nav_history = editor.nav_history.take();
editor.unfold_ranges(&ranges, false, true, cx);
editor.change_selections(Some(autoscroll), cx, |s| { editor.change_selections(Some(autoscroll), cx, |s| {
s.select_ranges(ranges); s.select_ranges(ranges);
}); });
@@ -14703,7 +14631,7 @@ trait SelectionExt {
) -> Range<MultiBufferRow>; ) -> Range<MultiBufferRow>;
} }
impl<T: ToMultiBufferPoint + ToOffset> SelectionExt for Selection<T> { impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> { fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
let start = self let start = self
.start .start

View File

@@ -34,7 +34,6 @@ use serde_json::{self, json};
use std::sync::atomic::AtomicUsize; use std::sync::atomic::AtomicUsize;
use std::sync::atomic::{self, AtomicBool}; use std::sync::atomic::{self, AtomicBool};
use std::{cell::RefCell, future::Future, rc::Rc, time::Instant}; use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
use test::editor_lsp_test_context::rust_lang;
use unindent::Unindent; use unindent::Unindent;
use util::{ use util::{
assert_set_eq, assert_set_eq,
@@ -5459,7 +5458,7 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
} }
#[gpui::test] #[gpui::test]
async fn test_autoindent(cx: &mut gpui::TestAppContext) { async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let language = Arc::new( let language = Arc::new(
@@ -5521,89 +5520,6 @@ async fn test_autoindent(cx: &mut gpui::TestAppContext) {
}); });
} }
#[gpui::test]
async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
{
let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
cx.set_state(indoc! {"
impl A {
fn b() {}
«fn c() {
}ˇ»
}
"});
cx.update_editor(|editor, cx| {
editor.autoindent(&Default::default(), cx);
});
cx.assert_editor_state(indoc! {"
impl A {
fn b() {}
«fn c() {
}ˇ»
}
"});
}
{
let mut cx = EditorTestContext::new_multibuffer(
cx,
[indoc! { "
impl A {
«
// a
fn b(){}
»
«
}
fn c(){}
»
"}],
);
let buffer = cx.update_editor(|editor, cx| {
let buffer = editor.buffer().update(cx, |buffer, _| {
buffer.all_buffers().iter().next().unwrap().clone()
});
buffer.update(cx, |buffer, cx| buffer.set_language(Some(rust_lang()), cx));
buffer
});
cx.run_until_parked();
cx.update_editor(|editor, cx| {
editor.select_all(&Default::default(), cx);
editor.autoindent(&Default::default(), cx)
});
cx.run_until_parked();
cx.update(|cx| {
pretty_assertions::assert_eq!(
buffer.read(cx).text(),
indoc! { "
impl A {
// a
fn b(){}
}
fn c(){}
" }
)
});
}
}
#[gpui::test] #[gpui::test]
async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) { async fn test_autoclose_and_auto_surround_pairs(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
@@ -11567,7 +11483,7 @@ async fn test_multibuffer_reverts(cx: &mut gpui::TestAppContext) {
} }
#[gpui::test] #[gpui::test]
async fn test_multibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) { async fn test_mutlibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
let cols = 4; let cols = 4;
@@ -11805,7 +11721,7 @@ async fn test_multibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
multi_buffer_editor.update(cx, |editor, cx| { multi_buffer_editor.update(cx, |editor, cx| {
editor.change_selections(Some(Autoscroll::Next), cx, |s| { editor.change_selections(Some(Autoscroll::Next), cx, |s| {
s.select_ranges(Some(70..70)) s.select_ranges(Some(60..70))
}); });
editor.open_excerpts(&OpenExcerpts, cx); editor.open_excerpts(&OpenExcerpts, cx);
}); });
@@ -11856,74 +11772,6 @@ async fn test_multibuffer_in_navigation_history(cx: &mut gpui::TestAppContext) {
.unwrap(); .unwrap();
} }
#[gpui::test]
async fn test_multibuffer_unfold_on_jump(cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {});
let texts = ["{\n\tx\n}".to_owned(), "y".to_owned()];
let buffers = texts
.clone()
.map(|txt| cx.new_model(|cx| Buffer::local(txt, cx)));
let multi_buffer = cx.new_model(|cx| {
let mut multi_buffer = MultiBuffer::new(ReadWrite);
for i in 0..2 {
multi_buffer.push_excerpts(
buffers[i].clone(),
[ExcerptRange {
context: 0..texts[i].len(),
primary: None,
}],
cx,
);
}
multi_buffer
});
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
"/project",
json!({
"x": &texts[0],
"y": &texts[1],
}),
)
.await;
let project = Project::test(fs, ["/project".as_ref()], cx).await;
let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
let multi_buffer_editor = cx.new_view(|cx| {
Editor::for_multibuffer(multi_buffer.clone(), Some(project.clone()), true, cx)
});
let buffer_editor =
cx.new_view(|cx| Editor::for_buffer(buffers[0].clone(), Some(project.clone()), cx));
workspace
.update(cx, |workspace, cx| {
workspace.add_item_to_active_pane(
Box::new(multi_buffer_editor.clone()),
None,
true,
cx,
);
workspace.add_item_to_active_pane(Box::new(buffer_editor.clone()), None, false, cx);
})
.unwrap();
cx.executor().run_until_parked();
buffer_editor.update(cx, |buffer_editor, cx| {
buffer_editor.fold_at_level(&FoldAtLevel { level: 1 }, cx);
assert!(buffer_editor.snapshot(cx).fold_count() == 1);
});
cx.executor().run_until_parked();
multi_buffer_editor.update(cx, |multi_buffer_editor, cx| {
multi_buffer_editor.change_selections(None, cx, |s| s.select_ranges([3..4]));
multi_buffer_editor.open_excerpts(&OpenExcerpts, cx);
});
cx.executor().run_until_parked();
buffer_editor.update(cx, |buffer_editor, cx| {
assert!(buffer_editor.snapshot(cx).fold_count() == 0);
});
}
#[gpui::test] #[gpui::test]
async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) { async fn test_toggle_hunk_diff(executor: BackgroundExecutor, cx: &mut gpui::TestAppContext) {
init_test(cx, |_| {}); init_test(cx, |_| {});
@@ -14085,6 +13933,20 @@ pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsC
update_test_language_settings(cx, f); update_test_language_settings(cx, f);
} }
pub(crate) fn rust_lang() -> Arc<Language> {
Arc::new(Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
..Default::default()
},
Some(tree_sitter_rust::LANGUAGE.into()),
))
}
#[track_caller] #[track_caller]
fn assert_hunk_revert( fn assert_hunk_revert(
not_reverted_text_with_selections: &str, not_reverted_text_with_selections: &str,

View File

@@ -20,9 +20,9 @@ use crate::{
DisplayRow, DocumentHighlightRead, DocumentHighlightWrite, Editor, EditorMode, EditorSettings, DisplayRow, DocumentHighlightRead, DocumentHighlightWrite, Editor, EditorMode, EditorSettings,
EditorSnapshot, EditorStyle, ExpandExcerpts, FocusedBlock, GutterDimensions, HalfPageDown, EditorSnapshot, EditorStyle, ExpandExcerpts, FocusedBlock, GutterDimensions, HalfPageDown,
HalfPageUp, HandleInput, HoveredCursor, HoveredHunk, JumpData, LineDown, LineUp, OpenExcerpts, HalfPageUp, HandleInput, HoveredCursor, HoveredHunk, JumpData, LineDown, LineUp, OpenExcerpts,
PageDown, PageUp, Point, RowExt, RowRangeExt, SelectPhase, Selection, SoftWrap, PageDown, PageUp, Point, RowExt, RowRangeExt, SelectPhase, Selection, SoftWrap, ToPoint,
ToMultiBufferPoint, CURSORS_VISIBLE_FOR, FILE_HEADER_HEIGHT, CURSORS_VISIBLE_FOR, FILE_HEADER_HEIGHT, GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN,
GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED, MAX_LINE_LEN, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT, MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
}; };
use client::ParticipantIndex; use client::ParticipantIndex;
use collections::{BTreeMap, HashMap, HashSet}; use collections::{BTreeMap, HashMap, HashSet};
@@ -84,7 +84,7 @@ struct SelectionLayout {
} }
impl SelectionLayout { impl SelectionLayout {
fn new<T: ToMultiBufferPoint + ToDisplayPoint + Clone>( fn new<T: ToPoint + ToDisplayPoint + Clone>(
selection: Selection<T>, selection: Selection<T>,
line_mode: bool, line_mode: bool,
cursor_shape: CursorShape, cursor_shape: CursorShape,
@@ -189,7 +189,6 @@ impl EditorElement {
register_action(view, cx, Editor::tab_prev); register_action(view, cx, Editor::tab_prev);
register_action(view, cx, Editor::indent); register_action(view, cx, Editor::indent);
register_action(view, cx, Editor::outdent); register_action(view, cx, Editor::outdent);
register_action(view, cx, Editor::autoindent);
register_action(view, cx, Editor::delete_line); register_action(view, cx, Editor::delete_line);
register_action(view, cx, Editor::join_lines); register_action(view, cx, Editor::join_lines);
register_action(view, cx, Editor::sort_lines_case_sensitive); register_action(view, cx, Editor::sort_lines_case_sensitive);
@@ -342,7 +341,6 @@ impl EditorElement {
register_action(view, cx, Editor::fold); register_action(view, cx, Editor::fold);
register_action(view, cx, Editor::fold_at_level); register_action(view, cx, Editor::fold_at_level);
register_action(view, cx, Editor::fold_all); register_action(view, cx, Editor::fold_all);
register_action(view, cx, Editor::fold_function_bodies);
register_action(view, cx, Editor::fold_at); register_action(view, cx, Editor::fold_at);
register_action(view, cx, Editor::fold_recursive); register_action(view, cx, Editor::fold_recursive);
register_action(view, cx, Editor::toggle_fold); register_action(view, cx, Editor::toggle_fold);

View File

@@ -378,7 +378,7 @@ fn show_hover(
}, },
..Default::default() ..Default::default()
}; };
Markdown::new_text(text, markdown_style.clone(), None, None, cx) Markdown::new_text(text, markdown_style.clone(), None, cx, None)
}) })
.ok(); .ok();
@@ -593,8 +593,8 @@ async fn parse_blocks(
combined_text, combined_text,
markdown_style.clone(), markdown_style.clone(),
Some(language_registry.clone()), Some(language_registry.clone()),
fallback_language_name,
cx, cx,
fallback_language_name,
) )
}) })
.ok(); .ok();

View File

@@ -4,7 +4,7 @@ use gpui::{Action, AnchorCorner, AppContext, CursorStyle, Hsla, Model, MouseButt
use language::{Buffer, BufferId, Point}; use language::{Buffer, BufferId, Point};
use multi_buffer::{ use multi_buffer::{
Anchor, AnchorRangeExt, ExcerptRange, MultiBuffer, MultiBufferDiffHunk, MultiBufferRow, Anchor, AnchorRangeExt, ExcerptRange, MultiBuffer, MultiBufferDiffHunk, MultiBufferRow,
MultiBufferSnapshot, ToMultiBufferPoint, MultiBufferSnapshot, ToPoint,
}; };
use std::{ops::Range, sync::Arc}; use std::{ops::Range, sync::Arc};
use text::OffsetRangeExt; use text::OffsetRangeExt;
@@ -399,12 +399,6 @@ impl Editor {
} }
} }
fn has_multiple_hunks(&self, cx: &AppContext) -> bool {
let snapshot = self.buffer.read(cx).snapshot(cx);
let mut hunks = snapshot.git_diff_hunks_in_range(MultiBufferRow::MIN..MultiBufferRow::MAX);
hunks.nth(1).is_some()
}
fn hunk_header_block( fn hunk_header_block(
&self, &self,
hunk: &HoveredHunk, hunk: &HoveredHunk,
@@ -434,7 +428,6 @@ impl Editor {
render: Arc::new({ render: Arc::new({
let editor = cx.view().clone(); let editor = cx.view().clone();
let hunk = hunk.clone(); let hunk = hunk.clone();
let has_multiple_hunks = self.has_multiple_hunks(cx);
move |cx| { move |cx| {
let hunk_controls_menu_handle = let hunk_controls_menu_handle =
@@ -478,7 +471,6 @@ impl Editor {
IconButton::new("next-hunk", IconName::ArrowDown) IconButton::new("next-hunk", IconName::ArrowDown)
.shape(IconButtonShape::Square) .shape(IconButtonShape::Square)
.icon_size(IconSize::Small) .icon_size(IconSize::Small)
.disabled(!has_multiple_hunks)
.tooltip({ .tooltip({
let focus_handle = editor.focus_handle(cx); let focus_handle = editor.focus_handle(cx);
move |cx| { move |cx| {
@@ -507,7 +499,6 @@ impl Editor {
IconButton::new("prev-hunk", IconName::ArrowUp) IconButton::new("prev-hunk", IconName::ArrowUp)
.shape(IconButtonShape::Square) .shape(IconButtonShape::Square)
.icon_size(IconSize::Small) .icon_size(IconSize::Small)
.disabled(!has_multiple_hunks)
.tooltip({ .tooltip({
let focus_handle = editor.focus_handle(cx); let focus_handle = editor.focus_handle(cx);
move |cx| { move |cx| {

View File

@@ -1258,7 +1258,6 @@ pub mod tests {
use crate::{ use crate::{
scroll::{scroll_amount::ScrollAmount, Autoscroll}, scroll::{scroll_amount::ScrollAmount, Autoscroll},
test::editor_lsp_test_context::rust_lang,
ExcerptRange, ExcerptRange,
}; };
use futures::StreamExt; use futures::StreamExt;
@@ -2275,7 +2274,7 @@ pub mod tests {
let project = Project::test(fs, ["/a".as_ref()], cx).await; let project = Project::test(fs, ["/a".as_ref()], cx).await;
let language_registry = project.read_with(cx, |project, _| project.languages().clone()); let language_registry = project.read_with(cx, |project, _| project.languages().clone());
language_registry.add(rust_lang()); language_registry.add(crate::editor_tests::rust_lang());
let mut fake_servers = language_registry.register_fake_lsp( let mut fake_servers = language_registry.register_fake_lsp(
"Rust", "Rust",
FakeLspAdapter { FakeLspAdapter {
@@ -2571,7 +2570,7 @@ pub mod tests {
let project = Project::test(fs, ["/a".as_ref()], cx).await; let project = Project::test(fs, ["/a".as_ref()], cx).await;
let language_registry = project.read_with(cx, |project, _| project.languages().clone()); let language_registry = project.read_with(cx, |project, _| project.languages().clone());
let language = rust_lang(); let language = crate::editor_tests::rust_lang();
language_registry.add(language); language_registry.add(language);
let mut fake_servers = language_registry.register_fake_lsp( let mut fake_servers = language_registry.register_fake_lsp(
"Rust", "Rust",
@@ -2923,7 +2922,7 @@ pub mod tests {
let project = Project::test(fs, ["/a".as_ref()], cx).await; let project = Project::test(fs, ["/a".as_ref()], cx).await;
let language_registry = project.read_with(cx, |project, _| project.languages().clone()); let language_registry = project.read_with(cx, |project, _| project.languages().clone());
language_registry.add(rust_lang()); language_registry.add(crate::editor_tests::rust_lang());
let mut fake_servers = language_registry.register_fake_lsp( let mut fake_servers = language_registry.register_fake_lsp(
"Rust", "Rust",
FakeLspAdapter { FakeLspAdapter {
@@ -3154,7 +3153,7 @@ pub mod tests {
let project = Project::test(fs, ["/a".as_ref()], cx).await; let project = Project::test(fs, ["/a".as_ref()], cx).await;
let language_registry = project.read_with(cx, |project, _| project.languages().clone()); let language_registry = project.read_with(cx, |project, _| project.languages().clone());
language_registry.add(rust_lang()); language_registry.add(crate::editor_tests::rust_lang());
let mut fake_servers = language_registry.register_fake_lsp( let mut fake_servers = language_registry.register_fake_lsp(
"Rust", "Rust",
FakeLspAdapter { FakeLspAdapter {
@@ -3397,7 +3396,7 @@ pub mod tests {
let project = Project::test(fs, ["/a".as_ref()], cx).await; let project = Project::test(fs, ["/a".as_ref()], cx).await;
let language_registry = project.read_with(cx, |project, _| project.languages().clone()); let language_registry = project.read_with(cx, |project, _| project.languages().clone());
language_registry.add(rust_lang()); language_registry.add(crate::editor_tests::rust_lang());
let mut fake_servers = language_registry.register_fake_lsp( let mut fake_servers = language_registry.register_fake_lsp(
"Rust", "Rust",
FakeLspAdapter { FakeLspAdapter {

View File

@@ -3,7 +3,7 @@ use crate::{
persistence::{SerializedEditor, DB}, persistence::{SerializedEditor, DB},
scroll::ScrollAnchor, scroll::ScrollAnchor,
Anchor, Autoscroll, Editor, EditorEvent, EditorSettings, ExcerptId, ExcerptRange, MultiBuffer, Anchor, Autoscroll, Editor, EditorEvent, EditorSettings, ExcerptId, ExcerptRange, MultiBuffer,
MultiBufferSnapshot, NavigationData, SearchWithinRange, ToMultiBufferPoint as _, MultiBufferSnapshot, NavigationData, SearchWithinRange, ToPoint as _,
}; };
use anyhow::{anyhow, Context as _, Result}; use anyhow::{anyhow, Context as _, Result};
use collections::HashSet; use collections::HashSet;

View File

@@ -2,9 +2,7 @@
//! in editor given a given motion (e.g. it handles converting a "move left" command into coordinates in editor). It is exposed mostly for use by vim crate. //! in editor given a given motion (e.g. it handles converting a "move left" command into coordinates in editor). It is exposed mostly for use by vim crate.
use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint}; use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
use crate::{ use crate::{scroll::ScrollAnchor, CharKind, DisplayRow, EditorStyle, RowExt, ToOffset, ToPoint};
scroll::ScrollAnchor, CharKind, DisplayRow, EditorStyle, RowExt, ToMultiBufferPoint, ToOffset,
};
use gpui::{Pixels, WindowTextSystem}; use gpui::{Pixels, WindowTextSystem};
use language::Point; use language::Point;
use multi_buffer::{MultiBufferRow, MultiBufferSnapshot}; use multi_buffer::{MultiBufferRow, MultiBufferSnapshot};
@@ -490,101 +488,6 @@ pub fn find_boundary_point(
map.clip_point(offset.to_display_point(map), Bias::Right) map.clip_point(offset.to_display_point(map), Bias::Right)
} }
pub fn find_preceding_boundary_trail(
map: &DisplaySnapshot,
head: DisplayPoint,
mut is_boundary: impl FnMut(char, char) -> bool,
) -> (Option<DisplayPoint>, DisplayPoint) {
let mut offset = head.to_offset(map, Bias::Left);
let mut trail_offset = None;
let mut prev_ch = map.buffer_snapshot.chars_at(offset).next();
let mut forward = map.buffer_snapshot.reversed_chars_at(offset).peekable();
// Skip newlines
while let Some(&ch) = forward.peek() {
if ch == '\n' {
prev_ch = forward.next();
offset -= ch.len_utf8();
trail_offset = Some(offset);
} else {
break;
}
}
// Find the boundary
let start_offset = offset;
for ch in forward {
if let Some(prev_ch) = prev_ch {
if is_boundary(prev_ch, ch) {
if start_offset == offset {
trail_offset = Some(offset);
} else {
break;
}
}
}
offset -= ch.len_utf8();
prev_ch = Some(ch);
}
let trail = trail_offset
.map(|trail_offset: usize| map.clip_point(trail_offset.to_display_point(map), Bias::Left));
(
trail,
map.clip_point(offset.to_display_point(map), Bias::Left),
)
}
/// Finds the location of a boundary
pub fn find_boundary_trail(
map: &DisplaySnapshot,
head: DisplayPoint,
mut is_boundary: impl FnMut(char, char) -> bool,
) -> (Option<DisplayPoint>, DisplayPoint) {
let mut offset = head.to_offset(map, Bias::Right);
let mut trail_offset = None;
let mut prev_ch = map.buffer_snapshot.reversed_chars_at(offset).next();
let mut forward = map.buffer_snapshot.chars_at(offset).peekable();
// Skip newlines
while let Some(&ch) = forward.peek() {
if ch == '\n' {
prev_ch = forward.next();
offset += ch.len_utf8();
trail_offset = Some(offset);
} else {
break;
}
}
// Find the boundary
let start_offset = offset;
for ch in forward {
if let Some(prev_ch) = prev_ch {
if is_boundary(prev_ch, ch) {
if start_offset == offset {
trail_offset = Some(offset);
} else {
break;
}
}
}
offset += ch.len_utf8();
prev_ch = Some(ch);
}
let trail = trail_offset
.map(|trail_offset: usize| map.clip_point(trail_offset.to_display_point(map), Bias::Right));
(
trail,
map.clip_point(offset.to_display_point(map), Bias::Right),
)
}
pub fn find_boundary( pub fn find_boundary(
map: &DisplaySnapshot, map: &DisplaySnapshot,
from: DisplayPoint, from: DisplayPoint,

View File

@@ -8,7 +8,7 @@ use crate::{
hover_popover::hide_hover, hover_popover::hide_hover,
persistence::DB, persistence::DB,
Anchor, DisplayPoint, DisplayRow, Editor, EditorEvent, EditorMode, EditorSettings, Anchor, DisplayPoint, DisplayRow, Editor, EditorEvent, EditorMode, EditorSettings,
InlayHintRefreshReason, MultiBufferSnapshot, RowExt, ToMultiBufferPoint, InlayHintRefreshReason, MultiBufferSnapshot, RowExt, ToPoint,
}; };
pub use autoscroll::{Autoscroll, AutoscrollStrategy}; pub use autoscroll::{Autoscroll, AutoscrollStrategy};
use gpui::{point, px, AppContext, Entity, Global, Pixels, Task, ViewContext, WindowContext}; use gpui::{point, px, AppContext, Entity, Global, Pixels, Task, ViewContext, WindowContext};

View File

@@ -15,7 +15,7 @@ use crate::{
display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint}, display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
movement::TextLayoutDetails, movement::TextLayoutDetails,
Anchor, DisplayPoint, DisplayRow, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, Anchor, DisplayPoint, DisplayRow, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode,
ToMultiBufferPoint, ToOffset, ToOffset, ToPoint,
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -502,13 +502,7 @@ impl<'a> MutableSelectionsCollection<'a> {
pub fn insert_range<T>(&mut self, range: Range<T>) pub fn insert_range<T>(&mut self, range: Range<T>)
where where
T: 'a T: 'a + ToOffset + ToPoint + TextDimension + Ord + Sub<T, Output = T> + std::marker::Copy,
+ ToOffset
+ ToMultiBufferPoint
+ TextDimension
+ Ord
+ Sub<T, Output = T>
+ std::marker::Copy,
{ {
let mut selections = self.collection.all(self.cx); let mut selections = self.collection.all(self.cx);
let mut start = range.start.to_offset(&self.buffer()); let mut start = range.start.to_offset(&self.buffer());
@@ -531,7 +525,7 @@ impl<'a> MutableSelectionsCollection<'a> {
pub fn select<T>(&mut self, mut selections: Vec<Selection<T>>) pub fn select<T>(&mut self, mut selections: Vec<Selection<T>>)
where where
T: ToOffset + ToMultiBufferPoint + Ord + std::marker::Copy + std::fmt::Debug, T: ToOffset + ToPoint + Ord + std::marker::Copy + std::fmt::Debug,
{ {
let buffer = self.buffer.read(self.cx).snapshot(self.cx); let buffer = self.buffer.read(self.cx).snapshot(self.cx);
selections.sort_unstable_by_key(|s| s.start); selections.sort_unstable_by_key(|s| s.start);

View File

@@ -8,7 +8,7 @@ use std::{
use anyhow::Result; use anyhow::Result;
use serde_json::json; use serde_json::json;
use crate::{Editor, ToMultiBufferPoint}; use crate::{Editor, ToPoint};
use collections::HashSet; use collections::HashSet;
use futures::Future; use futures::Future;
use gpui::{View, ViewContext, VisualTestContext}; use gpui::{View, ViewContext, VisualTestContext};
@@ -31,47 +31,6 @@ pub struct EditorLspTestContext {
pub buffer_lsp_url: lsp::Url, pub buffer_lsp_url: lsp::Url,
} }
pub(crate) fn rust_lang() -> Arc<Language> {
let language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
line_comments: vec!["// ".into(), "/// ".into(), "//! ".into()],
..Default::default()
},
Some(tree_sitter_rust::LANGUAGE.into()),
)
.with_queries(LanguageQueries {
indents: Some(Cow::from(indoc! {r#"
[
((where_clause) _ @end)
(field_expression)
(call_expression)
(assignment_expression)
(let_declaration)
(let_chain)
(await_expression)
] @indent
(_ "[" "]" @end) @indent
(_ "<" ">" @end) @indent
(_ "{" "}" @end) @indent
(_ "(" ")" @end) @indent"#})),
brackets: Some(Cow::from(indoc! {r#"
("(" @open ")" @close)
("[" @open "]" @close)
("{" @open "}" @close)
("<" @open ">" @close)
("\"" @open "\"" @close)
(closure_parameters "|" @open "|" @close)"#})),
..Default::default()
})
.expect("Could not parse queries");
Arc::new(language)
}
impl EditorLspTestContext { impl EditorLspTestContext {
pub async fn new( pub async fn new(
language: Language, language: Language,
@@ -160,7 +119,46 @@ impl EditorLspTestContext {
capabilities: lsp::ServerCapabilities, capabilities: lsp::ServerCapabilities,
cx: &mut gpui::TestAppContext, cx: &mut gpui::TestAppContext,
) -> EditorLspTestContext { ) -> EditorLspTestContext {
Self::new(Arc::into_inner(rust_lang()).unwrap(), capabilities, cx).await let language = Language::new(
LanguageConfig {
name: "Rust".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["rs".to_string()],
..Default::default()
},
line_comments: vec!["// ".into(), "/// ".into(), "//! ".into()],
..Default::default()
},
Some(tree_sitter_rust::LANGUAGE.into()),
)
.with_queries(LanguageQueries {
indents: Some(Cow::from(indoc! {r#"
[
((where_clause) _ @end)
(field_expression)
(call_expression)
(assignment_expression)
(let_declaration)
(let_chain)
(await_expression)
] @indent
(_ "[" "]" @end) @indent
(_ "<" ">" @end) @indent
(_ "{" "}" @end) @indent
(_ "(" ")" @end) @indent"#})),
brackets: Some(Cow::from(indoc! {r#"
("(" @open ")" @close)
("[" @open "]" @close)
("{" @open "}" @close)
("<" @open ">" @close)
("\"" @open "\"" @close)
(closure_parameters "|" @open "|" @close)"#})),
..Default::default()
})
.expect("Could not parse queries");
Self::new(language, capabilities, cx).await
} }
pub async fn new_typescript( pub async fn new_typescript(

View File

@@ -11,7 +11,7 @@ use gpui::{
}; };
use itertools::Itertools; use itertools::Itertools;
use language::{Buffer, BufferSnapshot, LanguageRegistry}; use language::{Buffer, BufferSnapshot, LanguageRegistry};
use multi_buffer::{ExcerptRange, ToMultiBufferPoint}; use multi_buffer::{ExcerptRange, ToPoint};
use parking_lot::RwLock; use parking_lot::RwLock;
use project::{FakeFs, Project}; use project::{FakeFs, Project};
use std::{ use std::{

View File

@@ -1,4 +1,4 @@
use editor::{Editor, ToMultiBufferPoint}; use editor::{Editor, ToPoint};
use gpui::{AppContext, FocusHandle, FocusableView, Subscription, Task, View, WeakView}; use gpui::{AppContext, FocusHandle, FocusableView, Subscription, Task, View, WeakView};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@@ -1111,16 +1111,10 @@ impl PlatformWindow for MacWindow {
} }
fn update_ime_position(&self, _bounds: Bounds<ScaledPixels>) { fn update_ime_position(&self, _bounds: Bounds<ScaledPixels>) {
let executor = self.0.lock().executor.clone(); unsafe {
executor let input_context: id = msg_send![class!(NSTextInputContext), currentInputContext];
.spawn(async move { let _: () = msg_send![input_context, invalidateCharacterCoordinates];
unsafe { }
let input_context: id =
msg_send![class!(NSTextInputContext), currentInputContext];
let _: () = msg_send![input_context, invalidateCharacterCoordinates];
}
})
.detach()
} }
} }
@@ -1259,10 +1253,7 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
// otherwise we only send to the input handler if we don't have a matching binding. // otherwise we only send to the input handler if we don't have a matching binding.
// The input handler may call `do_command_by_selector` if it doesn't know how to handle // The input handler may call `do_command_by_selector` if it doesn't know how to handle
// a key. If it does so, it will return YES so we won't send the key twice. // a key. If it does so, it will return YES so we won't send the key twice.
// We also do this for non-printing keys (like arrow keys and escape) as the IME menu if is_composing || event.keystroke.key_char.is_none() {
// may need them even if there is no marked text;
// however we skip keys with control or the input handler adds control-characters to the buffer.
if is_composing || (event.keystroke.key_char.is_none() && !event.keystroke.modifiers.control) {
{ {
let mut lock = window_state.as_ref().lock(); let mut lock = window_state.as_ref().lock();
lock.keystroke_for_do_command = Some(event.keystroke.clone()); lock.keystroke_for_do_command = Some(event.keystroke.clone());

View File

@@ -128,15 +128,13 @@ impl Scene {
} }
pub fn finish(&mut self) { pub fn finish(&mut self) {
self.shadows.sort_by_key(|shadow| shadow.order); self.shadows.sort();
self.quads.sort_by_key(|quad| quad.order); self.quads.sort();
self.paths.sort_by_key(|path| path.order); self.paths.sort();
self.underlines.sort_by_key(|underline| underline.order); self.underlines.sort();
self.monochrome_sprites self.monochrome_sprites.sort();
.sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id)); self.polychrome_sprites.sort();
self.polychrome_sprites self.surfaces.sort();
.sort_by_key(|sprite| (sprite.order, sprite.tile.tile_id));
self.surfaces.sort_by_key(|surface| surface.order);
} }
#[cfg_attr( #[cfg_attr(
@@ -198,7 +196,7 @@ pub(crate) enum PaintOperation {
EndLayer, EndLayer,
} }
#[derive(Clone)] #[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
pub(crate) enum Primitive { pub(crate) enum Primitive {
Shadow(Shadow), Shadow(Shadow),
Quad(Quad), Quad(Quad),
@@ -451,7 +449,7 @@ pub(crate) enum PrimitiveBatch<'a> {
Surfaces(&'a [PaintSurface]), Surfaces(&'a [PaintSurface]),
} }
#[derive(Default, Debug, Clone)] #[derive(Default, Debug, Clone, Eq, PartialEq)]
#[repr(C)] #[repr(C)]
pub(crate) struct Quad { pub(crate) struct Quad {
pub order: DrawOrder, pub order: DrawOrder,
@@ -464,13 +462,25 @@ pub(crate) struct Quad {
pub border_widths: Edges<ScaledPixels>, pub border_widths: Edges<ScaledPixels>,
} }
impl Ord for Quad {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.order.cmp(&other.order)
}
}
impl PartialOrd for Quad {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl From<Quad> for Primitive { impl From<Quad> for Primitive {
fn from(quad: Quad) -> Self { fn from(quad: Quad) -> Self {
Primitive::Quad(quad) Primitive::Quad(quad)
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Eq, PartialEq)]
#[repr(C)] #[repr(C)]
pub(crate) struct Underline { pub(crate) struct Underline {
pub order: DrawOrder, pub order: DrawOrder,
@@ -482,13 +492,25 @@ pub(crate) struct Underline {
pub wavy: bool, pub wavy: bool,
} }
impl Ord for Underline {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.order.cmp(&other.order)
}
}
impl PartialOrd for Underline {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl From<Underline> for Primitive { impl From<Underline> for Primitive {
fn from(underline: Underline) -> Self { fn from(underline: Underline) -> Self {
Primitive::Underline(underline) Primitive::Underline(underline)
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Eq, PartialEq)]
#[repr(C)] #[repr(C)]
pub(crate) struct Shadow { pub(crate) struct Shadow {
pub order: DrawOrder, pub order: DrawOrder,
@@ -499,6 +521,18 @@ pub(crate) struct Shadow {
pub color: Hsla, pub color: Hsla,
} }
impl Ord for Shadow {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.order.cmp(&other.order)
}
}
impl PartialOrd for Shadow {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl From<Shadow> for Primitive { impl From<Shadow> for Primitive {
fn from(shadow: Shadow) -> Self { fn from(shadow: Shadow) -> Self {
Primitive::Shadow(shadow) Primitive::Shadow(shadow)
@@ -608,7 +642,7 @@ impl Default for TransformationMatrix {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, Eq, PartialEq)]
#[repr(C)] #[repr(C)]
pub(crate) struct MonochromeSprite { pub(crate) struct MonochromeSprite {
pub order: DrawOrder, pub order: DrawOrder,
@@ -620,13 +654,28 @@ pub(crate) struct MonochromeSprite {
pub transformation: TransformationMatrix, pub transformation: TransformationMatrix,
} }
impl Ord for MonochromeSprite {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match self.order.cmp(&other.order) {
std::cmp::Ordering::Equal => self.tile.tile_id.cmp(&other.tile.tile_id),
order => order,
}
}
}
impl PartialOrd for MonochromeSprite {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl From<MonochromeSprite> for Primitive { impl From<MonochromeSprite> for Primitive {
fn from(sprite: MonochromeSprite) -> Self { fn from(sprite: MonochromeSprite) -> Self {
Primitive::MonochromeSprite(sprite) Primitive::MonochromeSprite(sprite)
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
#[repr(C)] #[repr(C)]
pub(crate) struct PolychromeSprite { pub(crate) struct PolychromeSprite {
pub order: DrawOrder, pub order: DrawOrder,
@@ -638,6 +687,22 @@ pub(crate) struct PolychromeSprite {
pub corner_radii: Corners<ScaledPixels>, pub corner_radii: Corners<ScaledPixels>,
pub tile: AtlasTile, pub tile: AtlasTile,
} }
impl Eq for PolychromeSprite {}
impl Ord for PolychromeSprite {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match self.order.cmp(&other.order) {
std::cmp::Ordering::Equal => self.tile.tile_id.cmp(&other.tile.tile_id),
order => order,
}
}
}
impl PartialOrd for PolychromeSprite {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl From<PolychromeSprite> for Primitive { impl From<PolychromeSprite> for Primitive {
fn from(sprite: PolychromeSprite) -> Self { fn from(sprite: PolychromeSprite) -> Self {
@@ -645,7 +710,7 @@ impl From<PolychromeSprite> for Primitive {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct PaintSurface { pub(crate) struct PaintSurface {
pub order: DrawOrder, pub order: DrawOrder,
pub bounds: Bounds<ScaledPixels>, pub bounds: Bounds<ScaledPixels>,
@@ -654,6 +719,18 @@ pub(crate) struct PaintSurface {
pub image_buffer: media::core_video::CVImageBuffer, pub image_buffer: media::core_video::CVImageBuffer,
} }
impl Ord for PaintSurface {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.order.cmp(&other.order)
}
}
impl PartialOrd for PaintSurface {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl From<PaintSurface> for Primitive { impl From<PaintSurface> for Primitive {
fn from(surface: PaintSurface) -> Self { fn from(surface: PaintSurface) -> Self {
Primitive::Surface(surface) Primitive::Surface(surface)
@@ -782,6 +859,26 @@ impl Path<Pixels> {
} }
} }
impl Eq for Path<ScaledPixels> {}
impl PartialEq for Path<ScaledPixels> {
fn eq(&self, other: &Self) -> bool {
self.order == other.order
}
}
impl Ord for Path<ScaledPixels> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.order.cmp(&other.order)
}
}
impl PartialOrd for Path<ScaledPixels> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl From<Path<ScaledPixels>> for Primitive { impl From<Path<ScaledPixels>> for Primitive {
fn from(path: Path<ScaledPixels>) -> Self { fn from(path: Path<ScaledPixels>) -> Self {
Primitive::Path(path) Primitive::Path(path)

View File

@@ -14,8 +14,7 @@ use crate::{
SyntaxMapMatches, SyntaxSnapshot, ToTreeSitterPoint, SyntaxMapMatches, SyntaxSnapshot, ToTreeSitterPoint,
}, },
task_context::RunnableRange, task_context::RunnableRange,
LanguageScope, Outline, OutlineConfig, RunnableCapture, RunnableTag, TextObject, LanguageScope, Outline, OutlineConfig, RunnableCapture, RunnableTag,
TreeSitterOptions,
}; };
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use async_watch as watch; use async_watch as watch;
@@ -468,7 +467,6 @@ struct AutoindentRequest {
before_edit: BufferSnapshot, before_edit: BufferSnapshot,
entries: Vec<AutoindentRequestEntry>, entries: Vec<AutoindentRequestEntry>,
is_block_mode: bool, is_block_mode: bool,
ignore_empty_lines: bool,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -1383,7 +1381,7 @@ impl Buffer {
let autoindent_requests = self.autoindent_requests.clone(); let autoindent_requests = self.autoindent_requests.clone();
Some(async move { Some(async move {
let mut indent_sizes = BTreeMap::<u32, (IndentSize, bool)>::new(); let mut indent_sizes = BTreeMap::new();
for request in autoindent_requests { for request in autoindent_requests {
// Resolve each edited range to its row in the current buffer and in the // Resolve each edited range to its row in the current buffer and in the
// buffer before this batch of edits. // buffer before this batch of edits.
@@ -1477,12 +1475,10 @@ impl Buffer {
let suggested_indent = indent_sizes let suggested_indent = indent_sizes
.get(&suggestion.basis_row) .get(&suggestion.basis_row)
.copied() .copied()
.map(|e| e.0)
.unwrap_or_else(|| { .unwrap_or_else(|| {
snapshot.indent_size_for_line(suggestion.basis_row) snapshot.indent_size_for_line(suggestion.basis_row)
}) })
.with_delta(suggestion.delta, language_indent_size); .with_delta(suggestion.delta, language_indent_size);
if old_suggestions.get(&new_row).map_or( if old_suggestions.get(&new_row).map_or(
true, true,
|(old_indentation, was_within_error)| { |(old_indentation, was_within_error)| {
@@ -1490,10 +1486,7 @@ impl Buffer {
&& (!suggestion.within_error || *was_within_error) && (!suggestion.within_error || *was_within_error)
}, },
) { ) {
indent_sizes.insert( indent_sizes.insert(new_row, suggested_indent);
new_row,
(suggested_indent, request.ignore_empty_lines),
);
} }
} }
} }
@@ -1501,12 +1494,10 @@ impl Buffer {
if let (true, Some(original_indent_column)) = if let (true, Some(original_indent_column)) =
(request.is_block_mode, original_indent_column) (request.is_block_mode, original_indent_column)
{ {
let new_indent = let new_indent = indent_sizes
if let Some((indent, _)) = indent_sizes.get(&row_range.start) { .get(&row_range.start)
*indent .copied()
} else { .unwrap_or_else(|| snapshot.indent_size_for_line(row_range.start));
snapshot.indent_size_for_line(row_range.start)
};
let delta = new_indent.len as i64 - original_indent_column as i64; let delta = new_indent.len as i64 - original_indent_column as i64;
if delta != 0 { if delta != 0 {
for row in row_range.skip(1) { for row in row_range.skip(1) {
@@ -1521,7 +1512,7 @@ impl Buffer {
Ordering::Equal => {} Ordering::Equal => {}
} }
} }
(size, request.ignore_empty_lines) size
}); });
} }
} }
@@ -1532,15 +1523,6 @@ impl Buffer {
} }
indent_sizes indent_sizes
.into_iter()
.filter_map(|(row, (indent, ignore_empty_lines))| {
if ignore_empty_lines && snapshot.line_len(row) == 0 {
None
} else {
Some((row, indent))
}
})
.collect()
}) })
} }
@@ -2085,7 +2067,6 @@ impl Buffer {
before_edit, before_edit,
entries, entries,
is_block_mode: matches!(mode, AutoindentMode::Block { .. }), is_block_mode: matches!(mode, AutoindentMode::Block { .. }),
ignore_empty_lines: false,
})); }));
} }
@@ -2113,30 +2094,6 @@ impl Buffer {
cx.notify(); cx.notify();
} }
pub fn autoindent_ranges<I, T>(&mut self, ranges: I, cx: &mut ModelContext<Self>)
where
I: IntoIterator<Item = Range<T>>,
T: ToOffset + Copy,
{
let before_edit = self.snapshot();
let entries = ranges
.into_iter()
.map(|range| AutoindentRequestEntry {
range: before_edit.anchor_before(range.start)..before_edit.anchor_after(range.end),
first_line_is_new: true,
indent_size: before_edit.language_indent_size_at(range.start, cx),
original_indent_column: None,
})
.collect();
self.autoindent_requests.push(Arc::new(AutoindentRequest {
before_edit,
entries,
is_block_mode: false,
ignore_empty_lines: true,
}));
self.request_autoindent(cx);
}
// Inserts newlines at the given position to create an empty line, returning the start of the new line. // Inserts newlines at the given position to create an empty line, returning the start of the new line.
// You can also request the insertion of empty lines above and below the line starting at the returned point. // You can also request the insertion of empty lines above and below the line starting at the returned point.
pub fn insert_empty_line( pub fn insert_empty_line(
@@ -3355,14 +3312,6 @@ impl BufferSnapshot {
}) })
} }
pub fn function_body_fold_ranges<T: ToOffset>(
&self,
within: Range<T>,
) -> impl Iterator<Item = Range<usize>> + '_ {
self.text_object_ranges(within, TreeSitterOptions::default())
.filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
}
/// For each grammar in the language, runs the provided /// For each grammar in the language, runs the provided
/// [`tree_sitter::Query`] against the given range. /// [`tree_sitter::Query`] against the given range.
pub fn matches( pub fn matches(
@@ -3421,72 +3370,6 @@ impl BufferSnapshot {
}) })
} }
pub fn text_object_ranges<T: ToOffset>(
&self,
range: Range<T>,
options: TreeSitterOptions,
) -> impl Iterator<Item = (Range<usize>, TextObject)> + '_ {
let range = range.start.to_offset(self).saturating_sub(1)
..self.len().min(range.end.to_offset(self) + 1);
let mut matches =
self.syntax
.matches_with_options(range.clone(), &self.text, options, |grammar| {
grammar.text_object_config.as_ref().map(|c| &c.query)
});
let configs = matches
.grammars()
.iter()
.map(|grammar| grammar.text_object_config.as_ref())
.collect::<Vec<_>>();
let mut captures = Vec::<(Range<usize>, TextObject)>::new();
iter::from_fn(move || loop {
while let Some(capture) = captures.pop() {
if capture.0.overlaps(&range) {
return Some(capture);
}
}
let mat = matches.peek()?;
let Some(config) = configs[mat.grammar_index].as_ref() else {
matches.advance();
continue;
};
for capture in mat.captures {
let Some(ix) = config
.text_objects_by_capture_ix
.binary_search_by_key(&capture.index, |e| e.0)
.ok()
else {
continue;
};
let text_object = config.text_objects_by_capture_ix[ix].1;
let byte_range = capture.node.byte_range();
let mut found = false;
for (range, existing) in captures.iter_mut() {
if existing == &text_object {
range.start = range.start.min(byte_range.start);
range.end = range.end.max(byte_range.end);
found = true;
break;
}
}
if !found {
captures.push((byte_range, text_object));
}
}
matches.advance();
})
}
/// Returns enclosing bracket ranges containing the given range /// Returns enclosing bracket ranges containing the given range
pub fn enclosing_bracket_ranges<T: ToOffset>( pub fn enclosing_bracket_ranges<T: ToOffset>(
&self, &self,
@@ -4632,7 +4515,7 @@ impl CharClassifier {
self.kind(c) == CharKind::Punctuation self.kind(c) == CharKind::Punctuation
} }
pub fn kind_with(&self, c: char, ignore_punctuation: bool) -> CharKind { pub fn kind(&self, c: char) -> CharKind {
if c.is_whitespace() { if c.is_whitespace() {
return CharKind::Whitespace; return CharKind::Whitespace;
} else if c.is_alphanumeric() || c == '_' { } else if c.is_alphanumeric() || c == '_' {
@@ -4642,7 +4525,7 @@ impl CharClassifier {
if let Some(scope) = &self.scope { if let Some(scope) = &self.scope {
if let Some(characters) = scope.word_characters() { if let Some(characters) = scope.word_characters() {
if characters.contains(&c) { if characters.contains(&c) {
if c == '-' && !self.for_completion && !ignore_punctuation { if c == '-' && !self.for_completion && !self.ignore_punctuation {
return CharKind::Punctuation; return CharKind::Punctuation;
} }
return CharKind::Word; return CharKind::Word;
@@ -4650,16 +4533,12 @@ impl CharClassifier {
} }
} }
if ignore_punctuation { if self.ignore_punctuation {
CharKind::Word CharKind::Word
} else { } else {
CharKind::Punctuation CharKind::Punctuation
} }
} }
pub fn kind(&self, c: char) -> CharKind {
self.kind_with(c, self.ignore_punctuation)
}
} }
/// Find all of the ranges of whitespace that occur at the ends of lines /// Find all of the ranges of whitespace that occur at the ends of lines

View File

@@ -20,7 +20,6 @@ use std::{
sync::LazyLock, sync::LazyLock,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use syntax_map::TreeSitterOptions;
use text::network::Network; use text::network::Network;
use text::{BufferId, LineEnding, LineIndent}; use text::{BufferId, LineEnding, LineIndent};
use text::{Point, ToPoint}; use text::{Point, ToPoint};
@@ -916,39 +915,6 @@ async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
} }
} }
#[gpui::test]
fn test_text_objects(cx: &mut AppContext) {
let (text, ranges) = marked_text_ranges(
indoc! {r#"
impl Hello {
fn say() -> u8 { return /* ˇhi */ 1 }
}"#
},
false,
);
let buffer =
cx.new_model(|cx| Buffer::local(text.clone(), cx).with_language(Arc::new(rust_lang()), cx));
let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
let matches = snapshot
.text_object_ranges(ranges[0].clone(), TreeSitterOptions::default())
.map(|(range, text_object)| (&text[range], text_object))
.collect::<Vec<_>>();
assert_eq!(
matches,
&[
("/* hi */", TextObject::AroundComment),
("return /* hi */ 1", TextObject::InsideFunction),
(
"fn say() -> u8 { return /* hi */ 1 }",
TextObject::AroundFunction
),
],
)
}
#[gpui::test] #[gpui::test]
fn test_enclosing_bracket_ranges(cx: &mut AppContext) { fn test_enclosing_bracket_ranges(cx: &mut AppContext) {
let mut assert = |selection_text, range_markers| { let mut assert = |selection_text, range_markers| {
@@ -3216,20 +3182,6 @@ fn rust_lang() -> Language {
"#, "#,
) )
.unwrap() .unwrap()
.with_text_object_query(
r#"
(function_item
body: (_
"{"
(_)* @function.inside
"}" )) @function.around
(line_comment)+ @comment.around
(block_comment) @comment.around
"#,
)
.unwrap()
.with_outline_query( .with_outline_query(
r#" r#"
(line_comment) @annotation (line_comment) @annotation

View File

@@ -78,7 +78,7 @@ pub use language_registry::{
}; };
pub use lsp::LanguageServerId; pub use lsp::LanguageServerId;
pub use outline::*; pub use outline::*;
pub use syntax_map::{OwnedSyntaxLayer, SyntaxLayer, TreeSitterOptions}; pub use syntax_map::{OwnedSyntaxLayer, SyntaxLayer};
pub use text::{AnchorRangeExt, LineEnding}; pub use text::{AnchorRangeExt, LineEnding};
pub use tree_sitter::{Node, Parser, Tree, TreeCursor}; pub use tree_sitter::{Node, Parser, Tree, TreeCursor};
@@ -848,7 +848,6 @@ pub struct Grammar {
pub(crate) runnable_config: Option<RunnableConfig>, pub(crate) runnable_config: Option<RunnableConfig>,
pub(crate) indents_config: Option<IndentConfig>, pub(crate) indents_config: Option<IndentConfig>,
pub outline_config: Option<OutlineConfig>, pub outline_config: Option<OutlineConfig>,
pub text_object_config: Option<TextObjectConfig>,
pub embedding_config: Option<EmbeddingConfig>, pub embedding_config: Option<EmbeddingConfig>,
pub(crate) injection_config: Option<InjectionConfig>, pub(crate) injection_config: Option<InjectionConfig>,
pub(crate) override_config: Option<OverrideConfig>, pub(crate) override_config: Option<OverrideConfig>,
@@ -874,44 +873,6 @@ pub struct OutlineConfig {
pub annotation_capture_ix: Option<u32>, pub annotation_capture_ix: Option<u32>,
} }
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TextObject {
InsideFunction,
AroundFunction,
InsideClass,
AroundClass,
InsideComment,
AroundComment,
}
impl TextObject {
pub fn from_capture_name(name: &str) -> Option<TextObject> {
match name {
"function.inside" => Some(TextObject::InsideFunction),
"function.around" => Some(TextObject::AroundFunction),
"class.inside" => Some(TextObject::InsideClass),
"class.around" => Some(TextObject::AroundClass),
"comment.inside" => Some(TextObject::InsideComment),
"comment.around" => Some(TextObject::AroundComment),
_ => None,
}
}
pub fn around(&self) -> Option<Self> {
match self {
TextObject::InsideFunction => Some(TextObject::AroundFunction),
TextObject::InsideClass => Some(TextObject::AroundClass),
TextObject::InsideComment => Some(TextObject::AroundComment),
_ => None,
}
}
}
pub struct TextObjectConfig {
pub query: Query,
pub text_objects_by_capture_ix: Vec<(u32, TextObject)>,
}
#[derive(Debug)] #[derive(Debug)]
pub struct EmbeddingConfig { pub struct EmbeddingConfig {
pub query: Query, pub query: Query,
@@ -989,7 +950,6 @@ impl Language {
highlights_query: None, highlights_query: None,
brackets_config: None, brackets_config: None,
outline_config: None, outline_config: None,
text_object_config: None,
embedding_config: None, embedding_config: None,
indents_config: None, indents_config: None,
injection_config: None, injection_config: None,
@@ -1060,12 +1020,7 @@ impl Language {
if let Some(query) = queries.runnables { if let Some(query) = queries.runnables {
self = self self = self
.with_runnable_query(query.as_ref()) .with_runnable_query(query.as_ref())
.context("Error loading runnables query")?; .context("Error loading tests query")?;
}
if let Some(query) = queries.text_objects {
self = self
.with_text_object_query(query.as_ref())
.context("Error loading textobject query")?;
} }
Ok(self) Ok(self)
} }
@@ -1142,26 +1097,6 @@ impl Language {
Ok(self) Ok(self)
} }
pub fn with_text_object_query(mut self, source: &str) -> Result<Self> {
let grammar = self
.grammar_mut()
.ok_or_else(|| anyhow!("cannot mutate grammar"))?;
let query = Query::new(&grammar.ts_language, source)?;
let mut text_objects_by_capture_ix = Vec::new();
for (ix, name) in query.capture_names().iter().enumerate() {
if let Some(text_object) = TextObject::from_capture_name(name) {
text_objects_by_capture_ix.push((ix as u32, text_object));
}
}
grammar.text_object_config = Some(TextObjectConfig {
query,
text_objects_by_capture_ix,
});
Ok(self)
}
pub fn with_embedding_query(mut self, source: &str) -> Result<Self> { pub fn with_embedding_query(mut self, source: &str) -> Result<Self> {
let grammar = self let grammar = self
.grammar_mut() .grammar_mut()

View File

@@ -181,7 +181,6 @@ pub const QUERY_FILENAME_PREFIXES: &[(
("overrides", |q| &mut q.overrides), ("overrides", |q| &mut q.overrides),
("redactions", |q| &mut q.redactions), ("redactions", |q| &mut q.redactions),
("runnables", |q| &mut q.runnables), ("runnables", |q| &mut q.runnables),
("textobjects", |q| &mut q.text_objects),
]; ];
/// Tree-sitter language queries for a given language. /// Tree-sitter language queries for a given language.
@@ -196,7 +195,6 @@ pub struct LanguageQueries {
pub overrides: Option<Cow<'static, str>>, pub overrides: Option<Cow<'static, str>>,
pub redactions: Option<Cow<'static, str>>, pub redactions: Option<Cow<'static, str>>,
pub runnables: Option<Cow<'static, str>>, pub runnables: Option<Cow<'static, str>>,
pub text_objects: Option<Cow<'static, str>>,
} }
#[derive(Clone, Default)] #[derive(Clone, Default)]

View File

@@ -814,23 +814,6 @@ impl SyntaxSnapshot {
buffer.as_rope(), buffer.as_rope(),
self.layers_for_range(range, buffer, true), self.layers_for_range(range, buffer, true),
query, query,
TreeSitterOptions::default(),
)
}
pub fn matches_with_options<'a>(
&'a self,
range: Range<usize>,
buffer: &'a BufferSnapshot,
options: TreeSitterOptions,
query: fn(&Grammar) -> Option<&Query>,
) -> SyntaxMapMatches<'a> {
SyntaxMapMatches::new(
range.clone(),
buffer.as_rope(),
self.layers_for_range(range, buffer, true),
query,
options,
) )
} }
@@ -1018,25 +1001,12 @@ impl<'a> SyntaxMapCaptures<'a> {
} }
} }
#[derive(Default)]
pub struct TreeSitterOptions {
max_start_depth: Option<u32>,
}
impl TreeSitterOptions {
pub fn max_start_depth(max_start_depth: u32) -> Self {
Self {
max_start_depth: Some(max_start_depth),
}
}
}
impl<'a> SyntaxMapMatches<'a> { impl<'a> SyntaxMapMatches<'a> {
fn new( fn new(
range: Range<usize>, range: Range<usize>,
text: &'a Rope, text: &'a Rope,
layers: impl Iterator<Item = SyntaxLayer<'a>>, layers: impl Iterator<Item = SyntaxLayer<'a>>,
query: fn(&Grammar) -> Option<&Query>, query: fn(&Grammar) -> Option<&Query>,
options: TreeSitterOptions,
) -> Self { ) -> Self {
let mut result = Self::default(); let mut result = Self::default();
for layer in layers { for layer in layers {
@@ -1057,7 +1027,6 @@ impl<'a> SyntaxMapMatches<'a> {
query_cursor.deref_mut(), query_cursor.deref_mut(),
) )
}; };
cursor.set_max_start_depth(options.max_start_depth);
cursor.set_byte_range(range.clone()); cursor.set_byte_range(range.clone());
let matches = cursor.matches(query, layer.node(), TextProvider(text)); let matches = cursor.matches(query, layer.node(), TextProvider(text));

View File

@@ -1,7 +0,0 @@
(function_definition
body: (_
"{"
(_)* @function.inside
"}" )) @function.around
(comment) @comment.around

View File

@@ -1,25 +0,0 @@
(declaration
declarator: (function_declarator)) @function.around
(function_definition
body: (_
"{"
(_)* @function.inside
"}" )) @function.around
(preproc_function_def
value: (_) @function.inside) @function.around
(comment) @comment.around
(struct_specifier
body: (_
"{"
(_)* @class.inside
"}")) @class.around
(enum_specifier
body: (_
"{"
[(_) ","?]* @class.inside
"}")) @class.around

View File

@@ -1,31 +0,0 @@
(declaration
declarator: (function_declarator)) @function.around
(function_definition
body: (_
"{"
(_)* @function.inside
"}" )) @function.around
(preproc_function_def
value: (_) @function.inside) @function.around
(comment) @comment.around
(struct_specifier
body: (_
"{"
(_)* @class.inside
"}")) @class.around
(enum_specifier
body: (_
"{"
[(_) ","?]* @class.inside
"}")) @class.around
(class_specifier
body: (_
"{"
[(_) ":"? ";"?]* @class.inside
"}"?)) @class.around

View File

@@ -1,30 +0,0 @@
(comment) @comment.around
(rule_set
(block (
"{"
(_)* @function.inside
"}" ))) @function.around
(keyframe_block
(block (
"{"
(_)* @function.inside
"}" ))) @function.around
(media_statement
(block (
"{"
(_)* @class.inside
"}" ))) @class.around
(supports_statement
(block (
"{"
(_)* @class.inside
"}" ))) @class.around
(keyframes_statement
(keyframe_block_list (
"{"
(_)* @class.inside
"}" ))) @class.around

View File

@@ -1,25 +0,0 @@
(function_declaration
body: (_
"{"
(_)* @function.inside
"}")) @function.around
(method_declaration
body: (_
"{"
(_)* @function.inside
"}")) @function.around
(type_declaration
(type_spec (struct_type (field_declaration_list (
"{"
(_)* @class.inside
"}")?)))) @class.around
(type_declaration
(type_spec (interface_type
(_)* @class.inside))) @class.around
(type_declaration) @class.around
(comment)+ @comment.around

View File

@@ -1,51 +0,0 @@
(comment)+ @comment.around
(function_declaration
body: (_
"{"
(_)* @function.inside
"}")) @function.around
(method_definition
body: (_
"{"
(_)* @function.inside
"}")) @function.around
(function_expression
body: (_
"{"
(_)* @function.inside
"}")) @function.around
(arrow_function
body: (statement_block
"{"
(_)* @function.inside
"}")) @function.around
(arrow_function) @function.around
(generator_function
body: (_
"{"
(_)* @function.inside
"}")) @function.around
(generator_function_declaration
body: (_
"{"
(_)* @function.inside
"}")) @function.around
(class_declaration
body: (_
"{"
[(_) ";"?]* @class.inside
"}" )) @class.around
(class
body: (_
"{"
[(_) ";"?]* @class.inside
"}" )) @class.around

View File

@@ -1 +0,0 @@
(comment)+ @comment.around

View File

@@ -1 +0,0 @@
(comment)+ @comment.around

View File

@@ -1,3 +0,0 @@
(section
(atx_heading)
(_)* @class.inside) @class.around

View File

@@ -1,7 +0,0 @@
(comment)+ @comment.around
(function_definition
body: (_) @function.inside) @function.around
(class_definition
body: (_) @class.inside) @class.around

View File

@@ -15,7 +15,11 @@
(visibility_modifier)? @context (visibility_modifier)? @context
name: (_) @name) @item name: (_) @name) @item
(function_item (impl_item
"impl" @context
trait: (_)? @name
"for"? @context
type: (_) @name
body: (_ "{" @open (_)* "}" @close)) @item body: (_ "{" @open (_)* "}" @close)) @item
(trait_item (trait_item

View File

@@ -1,51 +0,0 @@
; functions
(function_signature_item) @function.around
(function_item
body: (_
"{"
(_)* @function.inside
"}" )) @function.around
; classes
(struct_item
body: (_
["{" "("]?
[(_) ","?]* @class.inside
["}" ")"]? )) @class.around
(enum_item
body: (_
"{"
[(_) ","?]* @class.inside
"}" )) @class.around
(union_item
body: (_
"{"
[(_) ","?]* @class.inside
"}" )) @class.around
(trait_item
body: (_
"{"
[(_) ","?]* @class.inside
"}" )) @class.around
(impl_item
body: (_
"{"
[(_) ","?]* @class.inside
"}" )) @class.around
(mod_item
body: (_
"{"
[(_) ","?]* @class.inside
"}" )) @class.around
; comments
(line_comment)+ @comment.around
(block_comment) @comment.around

View File

@@ -1,79 +0,0 @@
(comment)+ @comment.around
(function_declaration
body: (_
"{"
(_)* @function.inside
"}")) @function.around
(method_definition
body: (_
"{"
(_)* @function.inside
"}")) @function.around
(function_expression
body: (_
"{"
(_)* @function.inside
"}")) @function.around
(arrow_function
body: (statement_block
"{"
(_)* @function.inside
"}")) @function.around
(arrow_function) @function.around
(function_signature) @function.around
(generator_function
body: (_
"{"
(_)* @function.inside
"}")) @function.around
(generator_function_declaration
body: (_
"{"
(_)* @function.inside
"}")) @function.around
(class_declaration
body: (_
"{"
[(_) ";"?]* @class.inside
"}" )) @class.around
(class
body: (_
"{"
(_)* @class.inside
"}" )) @class.around
(interface_declaration
body: (_
"{"
[(_) ";"?]* @class.inside
"}" )) @class.around
(enum_declaration
body: (_
"{"
[(_) ","?]* @class.inside
"}" )) @class.around
(ambient_declaration
(module
body: (_
"{"
[(_) ";"?]* @class.inside
"}" ))) @class.around
(internal_module
body: (_
"{"
[(_) ";"?]* @class.inside
"}" )) @class.around
(type_alias_declaration) @class.around

View File

@@ -1,79 +0,0 @@
(comment)+ @comment.around
(function_declaration
body: (_
"{"
(_)* @function.inside
"}")) @function.around
(method_definition
body: (_
"{"
(_)* @function.inside
"}")) @function.around
(function_expression
body: (_
"{"
(_)* @function.inside
"}")) @function.around
(arrow_function
body: (statement_block
"{"
(_)* @function.inside
"}")) @function.around
(arrow_function) @function.around
(function_signature) @function.around
(generator_function
body: (_
"{"
(_)* @function.inside
"}")) @function.around
(generator_function_declaration
body: (_
"{"
(_)* @function.inside
"}")) @function.around
(class_declaration
body: (_
"{"
[(_) ";"?]* @class.inside
"}" )) @class.around
(class
body: (_
"{"
(_)* @class.inside
"}" )) @class.around
(interface_declaration
body: (_
"{"
[(_) ";"?]* @class.inside
"}" )) @class.around
(enum_declaration
body: (_
"{"
[(_) ","?]* @class.inside
"}" )) @class.around
(ambient_declaration
(module
body: (_
"{"
[(_) ";"?]* @class.inside
"}" ))) @class.around
(internal_module
body: (_
"{"
[(_) ";"?]* @class.inside
"}" )) @class.around
(type_alias_declaration) @class.around

View File

@@ -1 +0,0 @@
(comment)+ @comment

View File

@@ -178,7 +178,7 @@ impl MarkdownExample {
cx: &mut WindowContext, cx: &mut WindowContext,
) -> Self { ) -> Self {
let markdown = let markdown =
cx.new_view(|cx| Markdown::new(text, style, Some(language_registry), None, cx)); cx.new_view(|cx| Markdown::new(text, style, Some(language_registry), cx, None));
Self { markdown } Self { markdown }
} }
} }

View File

@@ -87,7 +87,7 @@ pub fn main() {
heading: Default::default(), heading: Default::default(),
}; };
let markdown = cx.new_view(|cx| { let markdown = cx.new_view(|cx| {
Markdown::new(MARKDOWN_EXAMPLE.into(), markdown_style, None, None, cx) Markdown::new(MARKDOWN_EXAMPLE.into(), markdown_style, None, cx, None)
}); });
HelloWorld { markdown } HelloWorld { markdown }

View File

@@ -71,8 +71,8 @@ impl Markdown {
source: String, source: String,
style: MarkdownStyle, style: MarkdownStyle,
language_registry: Option<Arc<LanguageRegistry>>, language_registry: Option<Arc<LanguageRegistry>>,
fallback_code_block_language: Option<String>,
cx: &ViewContext<Self>, cx: &ViewContext<Self>,
fallback_code_block_language: Option<String>,
) -> Self { ) -> Self {
let focus_handle = cx.focus_handle(); let focus_handle = cx.focus_handle();
let mut this = Self { let mut this = Self {
@@ -97,8 +97,8 @@ impl Markdown {
source: String, source: String,
style: MarkdownStyle, style: MarkdownStyle,
language_registry: Option<Arc<LanguageRegistry>>, language_registry: Option<Arc<LanguageRegistry>>,
fallback_code_block_language: Option<String>,
cx: &ViewContext<Self>, cx: &ViewContext<Self>,
fallback_code_block_language: Option<String>,
) -> Self { ) -> Self {
let focus_handle = cx.focus_handle(); let focus_handle = cx.focus_handle();
let mut this = Self { let mut this = Self {

View File

@@ -1,7 +1,5 @@
use crate::MultiBufferPoint; use super::{ExcerptId, MultiBufferSnapshot, ToOffset, ToOffsetUtf16, ToPoint};
use language::{OffsetUtf16, Point, TextDimension};
use super::{ExcerptId, MultiBufferSnapshot, ToMultiBufferPoint, ToOffset, ToOffsetUtf16};
use language::{OffsetUtf16, TextDimension};
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
ops::{Range, Sub}, ops::{Range, Sub},
@@ -111,9 +109,9 @@ impl ToOffsetUtf16 for Anchor {
} }
} }
impl ToMultiBufferPoint for Anchor { impl ToPoint for Anchor {
fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferPoint { fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
MultiBufferPoint(self.summary(snapshot)) self.summary(snapshot)
} }
} }
@@ -121,7 +119,7 @@ pub trait AnchorRangeExt {
fn cmp(&self, b: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Ordering; fn cmp(&self, b: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> Ordering;
fn overlaps(&self, b: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool; fn overlaps(&self, b: &Range<Anchor>, buffer: &MultiBufferSnapshot) -> bool;
fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<usize>; fn to_offset(&self, content: &MultiBufferSnapshot) -> Range<usize>;
fn to_point(&self, content: &MultiBufferSnapshot) -> Range<MultiBufferPoint>; fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point>;
} }
impl AnchorRangeExt for Range<Anchor> { impl AnchorRangeExt for Range<Anchor> {
@@ -140,7 +138,7 @@ impl AnchorRangeExt for Range<Anchor> {
self.start.to_offset(content)..self.end.to_offset(content) self.start.to_offset(content)..self.end.to_offset(content)
} }
fn to_point(&self, content: &MultiBufferSnapshot) -> Range<MultiBufferPoint> { fn to_point(&self, content: &MultiBufferSnapshot) -> Range<Point> {
self.start.to_point(content)..self.end.to_point(content) self.start.to_point(content)..self.end.to_point(content)
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,8 @@
use crate::Project; use crate::Project;
use anyhow::{Context as _, Result}; use anyhow::Context as _;
use collections::HashMap; use collections::HashMap;
use gpui::{AnyWindowHandle, AppContext, Context, Entity, Model, ModelContext, Task, WeakModel}; use gpui::{AnyWindowHandle, AppContext, Context, Entity, Model, ModelContext, WeakModel};
use itertools::Itertools; use itertools::Itertools;
use language::LanguageName;
use settings::{Settings, SettingsLocation}; use settings::{Settings, SettingsLocation};
use smol::channel::bounded; use smol::channel::bounded;
use std::{ use std::{
@@ -11,11 +10,10 @@ use std::{
env::{self}, env::{self},
iter, iter,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Arc,
}; };
use task::{Shell, SpawnInTerminal}; use task::{Shell, SpawnInTerminal};
use terminal::{ use terminal::{
terminal_settings::{self, TerminalSettings, VenvSettings}, terminal_settings::{self, TerminalSettings},
TaskState, TaskStatus, Terminal, TerminalBuilder, TaskState, TaskStatus, Terminal, TerminalBuilder,
}; };
use util::ResultExt; use util::ResultExt;
@@ -44,7 +42,7 @@ pub struct SshCommand {
} }
impl Project { impl Project {
pub fn active_project_directory(&self, cx: &AppContext) -> Option<Arc<Path>> { pub fn active_project_directory(&self, cx: &AppContext) -> Option<PathBuf> {
let worktree = self let worktree = self
.active_entry() .active_entry()
.and_then(|entry_id| self.worktree_for_entry(entry_id, cx)) .and_then(|entry_id| self.worktree_for_entry(entry_id, cx))
@@ -55,7 +53,7 @@ impl Project {
worktree worktree
.root_entry() .root_entry()
.filter(|entry| entry.is_dir()) .filter(|entry| entry.is_dir())
.map(|_| worktree.abs_path().clone()) .map(|_| worktree.abs_path().to_path_buf())
}); });
worktree worktree
} }
@@ -89,12 +87,12 @@ impl Project {
kind: TerminalKind, kind: TerminalKind,
window: AnyWindowHandle, window: AnyWindowHandle,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Task<Result<Model<Terminal>>> { ) -> anyhow::Result<Model<Terminal>> {
let path: Option<Arc<Path>> = match &kind { let path = match &kind {
TerminalKind::Shell(path) => path.as_ref().map(|path| Arc::from(path.as_ref())), TerminalKind::Shell(path) => path.as_ref().map(|path| path.to_path_buf()),
TerminalKind::Task(spawn_task) => { TerminalKind::Task(spawn_task) => {
if let Some(cwd) = &spawn_task.cwd { if let Some(cwd) = &spawn_task.cwd {
Some(Arc::from(cwd.as_ref())) Some(cwd.clone())
} else { } else {
self.active_project_directory(cx) self.active_project_directory(cx)
} }
@@ -111,7 +109,7 @@ impl Project {
}); });
} }
} }
let settings = TerminalSettings::get(settings_location, cx).clone(); let settings = TerminalSettings::get(settings_location, cx);
let (completion_tx, completion_rx) = bounded(1); let (completion_tx, completion_rx) = bounded(1);
@@ -130,206 +128,160 @@ impl Project {
} else { } else {
None None
}; };
let python_venv_directory = path
.as_ref()
.and_then(|path| self.python_venv_directory(path, settings, cx));
let mut python_venv_activate_command = None;
cx.spawn(move |this, mut cx| async move { let (spawn_task, shell) = match kind {
let python_venv_directory = if let Some(path) = path.clone() { TerminalKind::Shell(_) => {
this.update(&mut cx, |this, cx| { if let Some(python_venv_directory) = python_venv_directory {
this.python_venv_directory(path, settings.detect_venv.clone(), cx) python_venv_activate_command =
})? self.python_activate_command(&python_venv_directory, settings);
.await
} else {
None
};
let mut python_venv_activate_command = None;
let (spawn_task, shell) = match kind {
TerminalKind::Shell(_) => {
if let Some(python_venv_directory) = python_venv_directory {
python_venv_activate_command = this
.update(&mut cx, |this, _| {
this.python_activate_command(
&python_venv_directory,
&settings.detect_venv,
)
})
.ok()
.flatten();
}
match &ssh_details {
Some((host, ssh_command)) => {
log::debug!("Connecting to a remote server: {ssh_command:?}");
// Alacritty sets its terminfo to `alacritty`, this requiring hosts to have it installed
// to properly display colors.
// We do not have the luxury of assuming the host has it installed,
// so we set it to a default that does not break the highlighting via ssh.
env.entry("TERM".to_string())
.or_insert_with(|| "xterm-256color".to_string());
let (program, args) =
wrap_for_ssh(ssh_command, None, path.as_deref(), env, None);
env = HashMap::default();
(
Option::<TaskState>::None,
Shell::WithArguments {
program,
args,
title_override: Some(format!("{} — Terminal", host).into()),
},
)
}
None => (None, settings.shell.clone()),
}
} }
TerminalKind::Task(spawn_task) => {
let task_state = Some(TaskState {
id: spawn_task.id,
full_label: spawn_task.full_label,
label: spawn_task.label,
command_label: spawn_task.command_label,
hide: spawn_task.hide,
status: TaskStatus::Running,
show_summary: spawn_task.show_summary,
show_command: spawn_task.show_command,
completion_rx,
});
env.extend(spawn_task.env); match &ssh_details {
Some((host, ssh_command)) => {
log::debug!("Connecting to a remote server: {ssh_command:?}");
if let Some(venv_path) = &python_venv_directory { // Alacritty sets its terminfo to `alacritty`, this requiring hosts to have it installed
env.insert( // to properly display colors.
"VIRTUAL_ENV".to_string(), // We do not have the luxury of assuming the host has it installed,
venv_path.to_string_lossy().to_string(), // so we set it to a default that does not break the highlighting via ssh.
); env.entry("TERM".to_string())
} .or_insert_with(|| "xterm-256color".to_string());
match &ssh_details { let (program, args) =
Some((host, ssh_command)) => { wrap_for_ssh(ssh_command, None, path.as_deref(), env, None);
log::debug!("Connecting to a remote server: {ssh_command:?}"); env = HashMap::default();
env.entry("TERM".to_string()) (
.or_insert_with(|| "xterm-256color".to_string()); None,
let (program, args) = wrap_for_ssh( Shell::WithArguments {
ssh_command, program,
Some((&spawn_task.command, &spawn_task.args)), args,
path.as_deref(), title_override: Some(format!("{} — Terminal", host).into()),
env, },
python_venv_directory,
);
env = HashMap::default();
(
task_state,
Shell::WithArguments {
program,
args,
title_override: Some(format!("{} — Terminal", host).into()),
},
)
}
None => {
if let Some(venv_path) = &python_venv_directory {
add_environment_path(&mut env, &venv_path.join("bin")).log_err();
}
(
task_state,
Shell::WithArguments {
program: spawn_task.command,
args: spawn_task.args,
title_override: None,
},
)
}
}
}
};
let terminal = this.update(&mut cx, |this, cx| {
TerminalBuilder::new(
local_path.map(|path| path.to_path_buf()),
spawn_task,
shell,
env,
settings.cursor_shape.unwrap_or_default(),
settings.alternate_scroll,
settings.max_scroll_history_lines,
ssh_details.is_some(),
window,
completion_tx,
cx,
)
.map(|builder| {
let terminal_handle = cx.new_model(|cx| builder.subscribe(cx));
this.terminals
.local_handles
.push(terminal_handle.downgrade());
let id = terminal_handle.entity_id();
cx.observe_release(&terminal_handle, move |project, _terminal, cx| {
let handles = &mut project.terminals.local_handles;
if let Some(index) = handles
.iter()
.position(|terminal| terminal.entity_id() == id)
{
handles.remove(index);
cx.notify();
}
})
.detach();
if let Some(activate_command) = python_venv_activate_command {
this.activate_python_virtual_environment(
activate_command,
&terminal_handle,
cx,
);
}
terminal_handle
})
})?;
terminal
})
}
fn python_venv_directory(
&self,
abs_path: Arc<Path>,
venv_settings: VenvSettings,
cx: &ModelContext<Project>,
) -> Task<Option<PathBuf>> {
cx.spawn(move |this, mut cx| async move {
if let Some((worktree, _)) = this
.update(&mut cx, |this, cx| this.find_worktree(&abs_path, cx))
.ok()?
{
let toolchain = this
.update(&mut cx, |this, cx| {
this.active_toolchain(
worktree.read(cx).id(),
LanguageName::new("Python"),
cx,
) )
}) }
.ok()? None => (None, settings.shell.clone()),
.await;
if let Some(toolchain) = toolchain {
let toolchain_path = Path::new(toolchain.path.as_ref());
return Some(toolchain_path.parent()?.parent()?.to_path_buf());
} }
} }
let venv_settings = venv_settings.as_option()?; TerminalKind::Task(spawn_task) => {
this.update(&mut cx, move |this, cx| { let task_state = Some(TaskState {
if let Some(path) = this.find_venv_in_worktree(&abs_path, &venv_settings, cx) { id: spawn_task.id,
return Some(path); full_label: spawn_task.full_label,
label: spawn_task.label,
command_label: spawn_task.command_label,
hide: spawn_task.hide,
status: TaskStatus::Running,
show_summary: spawn_task.show_summary,
show_command: spawn_task.show_command,
completion_rx,
});
env.extend(spawn_task.env);
if let Some(venv_path) = &python_venv_directory {
env.insert(
"VIRTUAL_ENV".to_string(),
venv_path.to_string_lossy().to_string(),
);
}
match &ssh_details {
Some((host, ssh_command)) => {
log::debug!("Connecting to a remote server: {ssh_command:?}");
env.entry("TERM".to_string())
.or_insert_with(|| "xterm-256color".to_string());
let (program, args) = wrap_for_ssh(
ssh_command,
Some((&spawn_task.command, &spawn_task.args)),
path.as_deref(),
env,
python_venv_directory,
);
env = HashMap::default();
(
task_state,
Shell::WithArguments {
program,
args,
title_override: Some(format!("{} — Terminal", host).into()),
},
)
}
None => {
if let Some(venv_path) = &python_venv_directory {
add_environment_path(&mut env, &venv_path.join("bin")).log_err();
}
(
task_state,
Shell::WithArguments {
program: spawn_task.command,
args: spawn_task.args,
title_override: None,
},
)
}
}
}
};
let terminal = TerminalBuilder::new(
local_path,
spawn_task,
shell,
env,
settings.cursor_shape.unwrap_or_default(),
settings.alternate_scroll,
settings.max_scroll_history_lines,
ssh_details.is_some(),
window,
completion_tx,
cx,
)
.map(|builder| {
let terminal_handle = cx.new_model(|cx| builder.subscribe(cx));
self.terminals
.local_handles
.push(terminal_handle.downgrade());
let id = terminal_handle.entity_id();
cx.observe_release(&terminal_handle, move |project, _terminal, cx| {
let handles = &mut project.terminals.local_handles;
if let Some(index) = handles
.iter()
.position(|terminal| terminal.entity_id() == id)
{
handles.remove(index);
cx.notify();
} }
this.find_venv_on_filesystem(&abs_path, &venv_settings, cx)
}) })
.ok() .detach();
.flatten()
}) if let Some(activate_command) = python_venv_activate_command {
self.activate_python_virtual_environment(activate_command, &terminal_handle, cx);
}
terminal_handle
});
terminal
}
pub fn python_venv_directory(
&self,
abs_path: &Path,
settings: &TerminalSettings,
cx: &AppContext,
) -> Option<PathBuf> {
let venv_settings = settings.detect_venv.as_option()?;
if let Some(path) = self.find_venv_in_worktree(abs_path, &venv_settings, cx) {
return Some(path);
}
self.find_venv_on_filesystem(abs_path, &venv_settings, cx)
} }
fn find_venv_in_worktree( fn find_venv_in_worktree(
@@ -385,9 +337,9 @@ impl Project {
fn python_activate_command( fn python_activate_command(
&self, &self,
venv_base_directory: &Path, venv_base_directory: &Path,
venv_settings: &VenvSettings, settings: &TerminalSettings,
) -> Option<String> { ) -> Option<String> {
let venv_settings = venv_settings.as_option()?; let venv_settings = settings.detect_venv.as_option()?;
let activate_keyword = match venv_settings.activate_script { let activate_keyword = match venv_settings.activate_script {
terminal_settings::ActivateScript::Default => match std::env::consts::OS { terminal_settings::ActivateScript::Default => match std::env::consts::OS {
"windows" => ".", "windows" => ".",
@@ -489,7 +441,7 @@ pub fn wrap_for_ssh(
(program, args) (program, args)
} }
fn add_environment_path(env: &mut HashMap<String, String>, new_path: &Path) -> Result<()> { fn add_environment_path(env: &mut HashMap<String, String>, new_path: &Path) -> anyhow::Result<()> {
let mut env_paths = vec![new_path.to_path_buf()]; let mut env_paths = vec![new_path.to_path_buf()];
if let Some(path) = env.get("PATH").or(env::var("PATH").ok().as_ref()) { if let Some(path) = env.get("PATH").or(env::var("PATH").ok().as_ref()) {
let mut paths = std::env::split_paths(&path).collect::<Vec<_>>(); let mut paths = std::env::split_paths(&path).collect::<Vec<_>>();

View File

@@ -201,7 +201,7 @@ impl SshPrompt {
selection_background_color: cx.theme().players().local().selection, selection_background_color: cx.theme().players().local().selection,
..Default::default() ..Default::default()
}; };
let markdown = cx.new_view(|cx| Markdown::new_text(prompt, markdown_style, None, None, cx)); let markdown = cx.new_view(|cx| Markdown::new_text(prompt, markdown_style, None, cx, None));
self.prompt = Some((markdown, tx)); self.prompt = Some((markdown, tx));
self.status_message.take(); self.status_message.take();
cx.focus_view(&self.editor); cx.focus_view(&self.editor);

View File

@@ -6,12 +6,9 @@ use futures::{
AsyncBufReadExt as _, SinkExt as _, AsyncBufReadExt as _, SinkExt as _,
}; };
use gpui::{EntityId, Task, View, WindowContext}; use gpui::{EntityId, Task, View, WindowContext};
use jupyter_protocol::{ use jupyter_protocol::{JupyterKernelspec, JupyterMessage, JupyterMessageContent, KernelInfoReply};
connection_info::{ConnectionInfo, Transport},
ExecutionState, JupyterKernelspec, JupyterMessage, JupyterMessageContent, KernelInfoReply,
};
use project::Fs; use project::Fs;
use runtimelib::dirs; use runtimelib::{dirs, ConnectionInfo, ExecutionState};
use smol::{net::TcpListener, process::Command}; use smol::{net::TcpListener, process::Command};
use std::{ use std::{
env, env,
@@ -122,7 +119,7 @@ impl NativeRunningKernel {
let ports = peek_ports(ip).await?; let ports = peek_ports(ip).await?;
let connection_info = ConnectionInfo { let connection_info = ConnectionInfo {
transport: Transport::TCP, transport: "tcp".to_string(),
ip: ip.to_string(), ip: ip.to_string(),
stdin_port: ports[0], stdin_port: ports[0],
control_port: ports[1], control_port: ports[1],

View File

@@ -334,11 +334,9 @@ impl ExecutionView {
result.transient.as_ref().and_then(|t| t.display_id.clone()), result.transient.as_ref().and_then(|t| t.display_id.clone()),
cx, cx,
), ),
JupyterMessageContent::DisplayData(result) => Output::new( JupyterMessageContent::DisplayData(result) => {
&result.data, Output::new(&result.data, result.transient.display_id.clone(), cx)
result.transient.as_ref().and_then(|t| t.display_id.clone()), }
cx,
),
JupyterMessageContent::StreamContent(result) => { JupyterMessageContent::StreamContent(result) => {
// Previous stream data will combine together, handling colors, carriage returns, etc // Previous stream data will combine together, handling colors, carriage returns, etc
if let Some(new_terminal) = self.apply_terminal_text(&result.text, cx) { if let Some(new_terminal) = self.apply_terminal_text(&result.text, cx) {

View File

@@ -14,7 +14,7 @@ use editor::{
RenderBlock, RenderBlock,
}, },
scroll::Autoscroll, scroll::Autoscroll,
Anchor, AnchorRangeExt as _, Editor, MultiBuffer, ToMultiBufferPoint, Anchor, AnchorRangeExt as _, Editor, MultiBuffer, ToPoint,
}; };
use futures::FutureExt as _; use futures::FutureExt as _;
use gpui::{ use gpui::{

View File

@@ -13,7 +13,7 @@ anyhow.workspace = true
collections.workspace = true collections.workspace = true
futures.workspace = true futures.workspace = true
indoc.workspace = true indoc.workspace = true
libsqlite3-sys.workspace = true libsqlite3-sys = { version = "0.28", features = ["bundled"] }
parking_lot.workspace = true parking_lot.workspace = true
smol.workspace = true smol.workspace = true
sqlformat.workspace = true sqlformat.workspace = true

View File

@@ -24,7 +24,7 @@ pub struct Toolbar {
pub breadcrumbs: bool, pub breadcrumbs: bool,
} }
#[derive(Clone, Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct TerminalSettings { pub struct TerminalSettings {
pub shell: Shell, pub shell: Shell,
pub working_directory: WorkingDirectory, pub working_directory: WorkingDirectory,

View File

@@ -1,6 +1,7 @@
Design notes: Design notes:
This crate is split into two conceptual halves: This crate is split into two conceptual halves:
- The terminal.rs file and the src/mappings/ folder, these contain the code for interacting with Alacritty and maintaining the pty event loop. Some behavior in this file is constrained by terminal protocols and standards. The Zed init function is also placed here. - The terminal.rs file and the src/mappings/ folder, these contain the code for interacting with Alacritty and maintaining the pty event loop. Some behavior in this file is constrained by terminal protocols and standards. The Zed init function is also placed here.
- Everything else. These other files integrate the `Terminal` struct created in terminal.rs into the rest of GPUI. The main entry point for GPUI is the terminal_view.rs file and the modal.rs file. - Everything else. These other files integrate the `Terminal` struct created in terminal.rs into the rest of GPUI. The main entry point for GPUI is the terminal_view.rs file and the modal.rs file.

View File

@@ -5,7 +5,7 @@ use futures::{stream::FuturesUnordered, StreamExt as _};
use gpui::{AsyncWindowContext, Axis, Model, Task, View, WeakView}; use gpui::{AsyncWindowContext, Axis, Model, Task, View, WeakView};
use project::{terminals::TerminalKind, Project}; use project::{terminals::TerminalKind, Project};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf}; use std::path::PathBuf;
use ui::{Pixels, ViewContext, VisualContext as _, WindowContext}; use ui::{Pixels, ViewContext, VisualContext as _, WindowContext};
use util::ResultExt as _; use util::ResultExt as _;
@@ -219,39 +219,33 @@ async fn deserialize_pane_group(
}) })
.log_err()?; .log_err()?;
let active_item = serialized_pane.active_item; let active_item = serialized_pane.active_item;
pane.update(cx, |pane, cx| {
let terminal = pane populate_pane_items(pane, new_items, active_item, cx);
.update(cx, |pane, cx| { // Avoid blank panes in splits
populate_pane_items(pane, new_items, active_item, cx); if pane.items_len() == 0 {
// Avoid blank panes in splits let working_directory = workspace
if pane.items_len() == 0 { .update(cx, |workspace, cx| default_working_directory(workspace, cx))
let working_directory = workspace .ok()
.update(cx, |workspace, cx| default_working_directory(workspace, cx)) .flatten();
.ok() let kind = TerminalKind::Shell(working_directory);
.flatten(); let window = cx.window_handle();
let kind = TerminalKind::Shell( let terminal = project
working_directory.as_deref().map(Path::to_path_buf), .update(cx, |project, cx| project.create_terminal(kind, window, cx))
); .log_err()?;
let window = cx.window_handle();
let terminal = project
.update(cx, |project, cx| project.create_terminal(kind, window, cx));
Some(Some(terminal))
} else {
Some(None)
}
})
.ok()
.flatten()?;
if let Some(terminal) = terminal {
let terminal = terminal.await.ok()?;
pane.update(cx, |pane, cx| {
let terminal_view = Box::new(cx.new_view(|cx| { let terminal_view = Box::new(cx.new_view(|cx| {
TerminalView::new(terminal, workspace.clone(), Some(workspace_id), cx) TerminalView::new(
terminal.clone(),
workspace.clone(),
Some(workspace_id),
cx,
)
})); }));
pane.add_item(terminal_view, true, false, None, cx); pane.add_item(terminal_view, true, false, None, cx);
}) }
.ok()?; Some(())
} })
.ok()
.flatten()?;
Some((Member::Pane(pane.clone()), active.then_some(pane))) Some((Member::Pane(pane.clone()), active.then_some(pane)))
} }
} }

View File

@@ -318,19 +318,10 @@ impl TerminalPanel {
} }
} }
pane::Event::Split(direction) => { pane::Event::Split(direction) => {
let new_pane = self.new_pane_with_cloned_active_terminal(cx); let Some(new_pane) = self.new_pane_with_cloned_active_terminal(cx) else {
let pane = pane.clone(); return;
let direction = *direction; };
cx.spawn(move |this, mut cx| async move { self.center.split(&pane, &new_pane, *direction).log_err();
let Some(new_pane) = new_pane.await else {
return;
};
this.update(&mut cx, |this, _| {
this.center.split(&pane, &new_pane, direction).log_err();
})
.ok();
})
.detach();
} }
pane::Event::Focus => { pane::Event::Focus => {
self.active_pane = pane.clone(); self.active_pane = pane.clone();
@@ -343,12 +334,8 @@ impl TerminalPanel {
fn new_pane_with_cloned_active_terminal( fn new_pane_with_cloned_active_terminal(
&mut self, &mut self,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Task<Option<View<Pane>>> { ) -> Option<View<Pane>> {
let Some(workspace) = self.workspace.clone().upgrade() else { let workspace = self.workspace.clone().upgrade()?;
return Task::ready(None);
};
let database_id = workspace.read(cx).database_id();
let weak_workspace = self.workspace.clone();
let project = workspace.read(cx).project().clone(); let project = workspace.read(cx).project().clone();
let working_directory = self let working_directory = self
.active_pane .active_pane
@@ -365,37 +352,21 @@ impl TerminalPanel {
.or_else(|| default_working_directory(workspace.read(cx), cx)); .or_else(|| default_working_directory(workspace.read(cx), cx));
let kind = TerminalKind::Shell(working_directory); let kind = TerminalKind::Shell(working_directory);
let window = cx.window_handle(); let window = cx.window_handle();
cx.spawn(move |this, mut cx| async move { let terminal = project
let terminal = project .update(cx, |project, cx| project.create_terminal(kind, window, cx))
.update(&mut cx, |project, cx| { .log_err()?;
project.create_terminal(kind, window, cx) let database_id = workspace.read(cx).database_id();
}) let terminal_view = Box::new(cx.new_view(|cx| {
.log_err()? TerminalView::new(terminal.clone(), self.workspace.clone(), database_id, cx)
.await }));
.log_err()?; let pane = new_terminal_pane(self.workspace.clone(), project, cx);
self.apply_tab_bar_buttons(&pane, cx);
pane.update(cx, |pane, cx| {
pane.add_item(terminal_view, true, true, None, cx);
});
cx.focus_view(&pane);
let terminal_view = Box::new( Some(pane)
cx.new_view(|cx| {
TerminalView::new(terminal.clone(), weak_workspace.clone(), database_id, cx)
})
.ok()?,
);
let pane = this
.update(&mut cx, |this, cx| {
let pane = new_terminal_pane(weak_workspace, project, cx);
this.apply_tab_bar_buttons(&pane, cx);
pane
})
.ok()?;
pane.update(&mut cx, |pane, cx| {
pane.add_item(terminal_view, true, true, None, cx);
})
.ok()?;
cx.focus_view(&pane).ok()?;
Some(pane)
})
} }
pub fn open_terminal( pub fn open_terminal(
@@ -518,58 +489,43 @@ impl TerminalPanel {
.last() .last()
.expect("covered no terminals case above") .expect("covered no terminals case above")
.clone(); .clone();
let id = spawn_in_terminal.id.clone(); if allow_concurrent_runs {
cx.spawn(move |this, mut cx| async move { debug_assert!(
if allow_concurrent_runs { !use_new_terminal,
debug_assert!( "Should have handled 'allow_concurrent_runs && use_new_terminal' case above"
!use_new_terminal, );
"Should have handled 'allow_concurrent_runs && use_new_terminal' case above" self.replace_terminal(
); spawn_task,
this.update(&mut cx, |this, cx| { task_pane,
this.replace_terminal( existing_item_index,
spawn_task, existing_terminal,
task_pane, cx,
existing_item_index, );
existing_terminal, } else {
cx, self.deferred_tasks.insert(
) spawn_in_terminal.id.clone(),
})? cx.spawn(|terminal_panel, mut cx| async move {
.await; wait_for_terminals_tasks(terminals_for_task, &mut cx).await;
} else { terminal_panel
this.update(&mut cx, |this, cx| { .update(&mut cx, |terminal_panel, cx| {
this.deferred_tasks.insert( if use_new_terminal {
id, terminal_panel
cx.spawn(|terminal_panel, mut cx| async move { .spawn_in_new_terminal(spawn_task, cx)
wait_for_terminals_tasks(terminals_for_task, &mut cx).await; .detach_and_log_err(cx);
let Ok(Some(new_terminal_task)) = } else {
terminal_panel.update(&mut cx, |terminal_panel, cx| { terminal_panel.replace_terminal(
if use_new_terminal { spawn_task,
terminal_panel task_pane,
.spawn_in_new_terminal(spawn_task, cx) existing_item_index,
.detach_and_log_err(cx); existing_terminal,
None cx,
} else { );
Some(terminal_panel.replace_terminal( }
spawn_task, })
task_pane, .ok();
existing_item_index, }),
existing_terminal, );
cx, }
))
}
})
else {
return;
};
new_terminal_task.await;
}),
);
})
.ok();
}
anyhow::Result::<_, anyhow::Error>::Ok(())
})
.detach()
} }
pub fn spawn_in_new_terminal( pub fn spawn_in_new_terminal(
@@ -655,14 +611,11 @@ impl TerminalPanel {
cx.spawn(|terminal_panel, mut cx| async move { cx.spawn(|terminal_panel, mut cx| async move {
let pane = terminal_panel.update(&mut cx, |this, _| this.active_pane.clone())?; let pane = terminal_panel.update(&mut cx, |this, _| this.active_pane.clone())?;
let project = workspace.update(&mut cx, |workspace, _| workspace.project().clone())?;
let window = cx.window_handle();
let terminal = project
.update(&mut cx, |project, cx| {
project.create_terminal(kind, window, cx)
})?
.await?;
let result = workspace.update(&mut cx, |workspace, cx| { let result = workspace.update(&mut cx, |workspace, cx| {
let window = cx.window_handle();
let terminal = workspace
.project()
.update(cx, |project, cx| project.create_terminal(kind, window, cx))?;
let terminal_view = Box::new(cx.new_view(|cx| { let terminal_view = Box::new(cx.new_view(|cx| {
TerminalView::new( TerminalView::new(
terminal.clone(), terminal.clone(),
@@ -742,64 +695,48 @@ impl TerminalPanel {
terminal_item_index: usize, terminal_item_index: usize,
terminal_to_replace: View<TerminalView>, terminal_to_replace: View<TerminalView>,
cx: &mut ViewContext<'_, Self>, cx: &mut ViewContext<'_, Self>,
) -> Task<Option<()>> { ) -> Option<()> {
let project = self
.workspace
.update(cx, |workspace, _| workspace.project().clone())
.ok()?;
let reveal = spawn_task.reveal; let reveal = spawn_task.reveal;
let window = cx.window_handle(); let window = cx.window_handle();
let task_workspace = self.workspace.clone(); let new_terminal = project.update(cx, |project, cx| {
cx.spawn(move |this, mut cx| async move { project
let project = this .create_terminal(TerminalKind::Task(spawn_task), window, cx)
.update(&mut cx, |this, cx| { .log_err()
this.workspace })?;
.update(cx, |workspace, _| workspace.project().clone()) terminal_to_replace.update(cx, |terminal_to_replace, cx| {
terminal_to_replace.set_terminal(new_terminal, cx);
});
match reveal {
RevealStrategy::Always => {
self.activate_terminal_view(&task_pane, terminal_item_index, true, cx);
let task_workspace = self.workspace.clone();
cx.spawn(|_, mut cx| async move {
task_workspace
.update(&mut cx, |workspace, cx| workspace.focus_panel::<Self>(cx))
.ok() .ok()
}) })
.ok() .detach();
.flatten()?;
let new_terminal = project
.update(&mut cx, |project, cx| {
project.create_terminal(TerminalKind::Task(spawn_task), window, cx)
})
.ok()?
.await
.log_err()?;
terminal_to_replace
.update(&mut cx, |terminal_to_replace, cx| {
terminal_to_replace.set_terminal(new_terminal, cx);
})
.ok()?;
match reveal {
RevealStrategy::Always => {
this.update(&mut cx, |this, cx| {
this.activate_terminal_view(&task_pane, terminal_item_index, true, cx)
})
.ok()?;
cx.spawn(|mut cx| async move {
task_workspace
.update(&mut cx, |workspace, cx| workspace.focus_panel::<Self>(cx))
.ok()
})
.detach();
}
RevealStrategy::NoFocus => {
this.update(&mut cx, |this, cx| {
this.activate_terminal_view(&task_pane, terminal_item_index, false, cx)
})
.ok()?;
cx.spawn(|mut cx| async move {
task_workspace
.update(&mut cx, |workspace, cx| workspace.open_panel::<Self>(cx))
.ok()
})
.detach();
}
RevealStrategy::Never => {}
} }
RevealStrategy::NoFocus => {
self.activate_terminal_view(&task_pane, terminal_item_index, false, cx);
let task_workspace = self.workspace.clone();
cx.spawn(|_, mut cx| async move {
task_workspace
.update(&mut cx, |workspace, cx| workspace.open_panel::<Self>(cx))
.ok()
})
.detach();
}
RevealStrategy::Never => {}
}
Some(()) Some(())
})
} }
fn has_no_terminals(&self, cx: &WindowContext) -> bool { fn has_no_terminals(&self, cx: &WindowContext) -> bool {
@@ -1061,18 +998,18 @@ impl Render for TerminalPanel {
if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) { if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) {
cx.focus_view(&pane); cx.focus_view(&pane);
} else { } else {
let new_pane = terminal_panel.new_pane_with_cloned_active_terminal(cx); if let Some(new_pane) =
cx.spawn(|this, mut cx| async move { terminal_panel.new_pane_with_cloned_active_terminal(cx)
if let Some(new_pane) = new_pane.await { {
this.update(&mut cx, |this, _| { terminal_panel
this.center .center
.split(&this.active_pane, &new_pane, SplitDirection::Right) .split(
.log_err(); &terminal_panel.active_pane,
}) &new_pane,
.ok(); SplitDirection::Right,
} )
}) .log_err();
.detach(); }
} }
})) }))
.on_action(cx.listener( .on_action(cx.listener(

View File

@@ -136,36 +136,24 @@ impl TerminalView {
let working_directory = default_working_directory(workspace, cx); let working_directory = default_working_directory(workspace, cx);
let window = cx.window_handle(); let window = cx.window_handle();
let project = workspace.project().downgrade(); let terminal = workspace
cx.spawn(move |workspace, mut cx| async move { .project()
let terminal = project .update(cx, |project, cx| {
.update(&mut cx, |project, cx| { project.create_terminal(TerminalKind::Shell(working_directory), window, cx)
project.create_terminal(TerminalKind::Shell(working_directory), window, cx) })
}) .notify_err(workspace, cx);
.ok()?
.await;
let terminal = workspace
.update(&mut cx, |workspace, cx| terminal.notify_err(workspace, cx))
.ok()
.flatten()?;
workspace if let Some(terminal) = terminal {
.update(&mut cx, |workspace, cx| { let view = cx.new_view(|cx| {
let view = cx.new_view(|cx| { TerminalView::new(
TerminalView::new( terminal,
terminal, workspace.weak_handle(),
workspace.weak_handle(), workspace.database_id(),
workspace.database_id(), cx,
cx, )
) });
}); workspace.add_item_to_active_pane(Box::new(view), None, true, cx);
workspace.add_item_to_active_pane(Box::new(view), None, true, cx); }
})
.ok();
Some(())
})
.detach()
} }
pub fn new( pub fn new(
@@ -1243,11 +1231,9 @@ impl SerializableItem for TerminalView {
.ok() .ok()
.flatten(); .flatten();
let terminal = project let terminal = project.update(&mut cx, |project, cx| {
.update(&mut cx, |project, cx| { project.create_terminal(TerminalKind::Shell(cwd), window, cx)
project.create_terminal(TerminalKind::Shell(cwd), window, cx) })??;
})?
.await?;
cx.update(|cx| { cx.update(|cx| {
cx.new_view(|cx| TerminalView::new(terminal, workspace, Some(workspace_id), cx)) cx.new_view(|cx| TerminalView::new(terminal, workspace, Some(workspace_id), cx))
}) })
@@ -1376,14 +1362,11 @@ impl SearchableItem for TerminalView {
///Gets the working directory for the given workspace, respecting the user's settings. ///Gets the working directory for the given workspace, respecting the user's settings.
/// None implies "~" on whichever machine we end up on. /// None implies "~" on whichever machine we end up on.
pub(crate) fn default_working_directory(workspace: &Workspace, cx: &AppContext) -> Option<PathBuf> { pub fn default_working_directory(workspace: &Workspace, cx: &AppContext) -> Option<PathBuf> {
match &TerminalSettings::get_global(cx).working_directory { match &TerminalSettings::get_global(cx).working_directory {
WorkingDirectory::CurrentProjectDirectory => workspace WorkingDirectory::CurrentProjectDirectory => {
.project() workspace.project().read(cx).active_project_directory(cx)
.read(cx) }
.active_project_directory(cx)
.as_deref()
.map(Path::to_path_buf),
WorkingDirectory::FirstProjectDirectory => first_project_directory(workspace, cx), WorkingDirectory::FirstProjectDirectory => first_project_directory(workspace, cx),
WorkingDirectory::AlwaysHome => None, WorkingDirectory::AlwaysHome => None,
WorkingDirectory::Always { directory } => { WorkingDirectory::Always { directory } => {

View File

@@ -84,31 +84,6 @@ impl<T: Copy + Ord> Selection<T> {
} }
self.goal = new_goal; self.goal = new_goal;
} }
pub fn set_tail(&mut self, tail: T, new_goal: SelectionGoal) {
if tail.cmp(&self.head()) <= Ordering::Equal {
if self.reversed {
self.end = self.start;
self.reversed = false;
}
self.start = tail;
} else {
if !self.reversed {
self.start = self.end;
self.reversed = true;
}
self.end = tail;
}
self.goal = new_goal;
}
pub fn swap_head_tail(&mut self) {
if self.reversed {
self.reversed = false;
} else {
std::mem::swap(&mut self.start, &mut self.end);
}
}
} }
impl<T: Copy> Selection<T> { impl<T: Copy> Selection<T> {

View File

@@ -19,8 +19,6 @@ use theme::{Appearance, AppearanceContent, ThemeFamilyContent};
use crate::vscode::VsCodeTheme; use crate::vscode::VsCodeTheme;
use crate::vscode::VsCodeThemeConverter; use crate::vscode::VsCodeThemeConverter;
const ZED_THEME_SCHEMA_URL: &str = "https://zed.dev/public/schema/themes/v0.2.0.json";
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct FamilyMetadata { struct FamilyMetadata {
pub name: String, pub name: String,
@@ -71,53 +69,34 @@ pub struct ThemeMetadata {
#[derive(Parser)] #[derive(Parser)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
struct Args { struct Args {
/// The path to the theme to import.
theme_path: PathBuf,
/// Whether to warn when values are missing from the theme.
#[arg(long)]
warn_on_missing: bool,
/// The path to write the output to.
#[arg(long, short)]
output: Option<PathBuf>,
#[command(subcommand)] #[command(subcommand)]
command: Command, command: Option<Command>,
} }
#[derive(PartialEq, Subcommand)] #[derive(Subcommand)]
enum Command { enum Command {
/// Prints the JSON schema for a theme. /// Prints the JSON schema for a theme.
PrintSchema, PrintSchema,
/// Converts a VSCode theme to Zed format [default]
Convert {
/// The path to the theme to import.
theme_path: PathBuf,
/// Whether to warn when values are missing from the theme.
#[arg(long)]
warn_on_missing: bool,
/// The path to write the output to.
#[arg(long, short)]
output: Option<PathBuf>,
},
} }
fn main() -> Result<()> { fn main() -> Result<()> {
let args = Args::parse(); let args = Args::parse();
match args.command {
Command::PrintSchema => {
let theme_family_schema = schema_for!(ThemeFamilyContent);
println!(
"{}",
serde_json::to_string_pretty(&theme_family_schema).unwrap()
);
Ok(())
}
Command::Convert {
theme_path,
warn_on_missing,
output,
} => convert(theme_path, output, warn_on_missing),
}
}
fn convert(theme_file_path: PathBuf, output: Option<PathBuf>, warn_on_missing: bool) -> Result<()> {
let log_config = { let log_config = {
let mut config = simplelog::ConfigBuilder::new(); let mut config = simplelog::ConfigBuilder::new();
if !warn_on_missing {
if !args.warn_on_missing {
config.add_filter_ignore_str("theme_printer"); config.add_filter_ignore_str("theme_printer");
} }
@@ -132,11 +111,28 @@ fn convert(theme_file_path: PathBuf, output: Option<PathBuf>, warn_on_missing: b
) )
.expect("could not initialize logger"); .expect("could not initialize logger");
if let Some(command) = args.command {
match command {
Command::PrintSchema => {
let theme_family_schema = schema_for!(ThemeFamilyContent);
println!(
"{}",
serde_json::to_string_pretty(&theme_family_schema).unwrap()
);
return Ok(());
}
}
}
let theme_file_path = args.theme_path;
let theme_file = match File::open(&theme_file_path) { let theme_file = match File::open(&theme_file_path) {
Ok(file) => file, Ok(file) => file,
Err(err) => { Err(err) => {
log::info!("Failed to open file at path: {:?}", theme_file_path); log::info!("Failed to open file at path: {:?}", theme_file_path);
return Err(err.into()); return Err(err)?;
} }
}; };
@@ -152,14 +148,10 @@ fn convert(theme_file_path: PathBuf, output: Option<PathBuf>, warn_on_missing: b
let converter = VsCodeThemeConverter::new(vscode_theme, theme_metadata, IndexMap::new()); let converter = VsCodeThemeConverter::new(vscode_theme, theme_metadata, IndexMap::new());
let theme = converter.convert()?; let theme = converter.convert()?;
let mut theme = serde_json::to_value(theme).unwrap();
theme.as_object_mut().unwrap().insert(
"$schema".to_string(),
serde_json::Value::String(ZED_THEME_SCHEMA_URL.to_string()),
);
let theme_json = serde_json::to_string_pretty(&theme).unwrap(); let theme_json = serde_json::to_string_pretty(&theme).unwrap();
if let Some(output) = output { if let Some(output) = args.output {
let mut file = File::create(output)?; let mut file = File::create(output)?;
file.write_all(theme_json.as_bytes())?; file.write_all(theme_json.as_bytes())?;
} else { } else {

View File

@@ -26,21 +26,21 @@ Now let's dive a bit deeper into how to customize `Label` instances:
- **Setting Color:** To set the color of the label using various predefined color options such as `Default`, `Muted`, `Created`, `Modified`, `Deleted`, etc, the `color()` function is called on the `Label` instance: - **Setting Color:** To set the color of the label using various predefined color options such as `Default`, `Muted`, `Created`, `Modified`, `Deleted`, etc, the `color()` function is called on the `Label` instance:
```rust ```rust
Label::new("Hello, world!").color(LabelColor::Default); Label::new("Hello, world!").color(LabelColor::Default);
``` ```
- **Setting Line Height Style:** To set the line height style, the `line_height_style()` function is utilized: - **Setting Line Height Style:** To set the line height style, the `line_height_style()` function is utilized:
```rust ```rust
Label::new("Hello, world!").line_height_style(LineHeightStyle::TextLabel); Label::new("Hello, world!").line_height_style(LineHeightStyle::TextLabel);
``` ```
- **Adding a Strikethrough:** To add a strikethrough in a `Label`, the `set_strikethrough()` function is used: - **Adding a Strikethrough:** To add a strikethrough in a `Label`, the `set_strikethrough()` function is used:
```rust ```rust
Label::new("Hello, world!").set_strikethrough(true); Label::new("Hello, world!").set_strikethrough(true);
``` ```
That's it! Now you can use the `Label` component to create and customize text on your application's interface. That's it! Now you can use the `Label` component to create and customize text on your application's interface.

View File

@@ -6,7 +6,7 @@ Let's work through the prototypical "Build a todo app" example to showcase how w
We'll create a headline, a list of todo items, and a form to add new items. We'll create a headline, a list of todo items, and a form to add new items.
~~~rust ```rust
struct TodoList<V: 'static> { struct TodoList<V: 'static> {
headline: SharedString, headline: SharedString,
items: Vec<TodoItem>, items: Vec<TodoItem>,
@@ -36,7 +36,7 @@ impl<V: 'static> TodoList<V> {
} }
} }
} }
~~~ ```
All of this is relatively straightforward. All of this is relatively straightforward.
@@ -44,7 +44,7 @@ We use [gpui::SharedString] in components instead of [std::string::String]. This
When we want to pass an action we pass a `ClickHandler`. Whenever we want to add an action, the struct it belongs to needs to be generic over the view type `V`. When we want to pass an action we pass a `ClickHandler`. Whenever we want to add an action, the struct it belongs to needs to be generic over the view type `V`.
~~~rust ```rust
use gpui::hsla use gpui::hsla
impl<V: 'static> TodoList<V> { impl<V: 'static> TodoList<V> {
@@ -53,7 +53,7 @@ impl<V: 'static> TodoList<V> {
div().size_4().bg(hsla(50.0/360.0, 1.0, 0.5, 1.0)) div().size_4().bg(hsla(50.0/360.0, 1.0, 0.5, 1.0))
} }
} }
~~~ ```
Every component needs a render method, and it should return `impl Element<V>`. This basic component will render a 16x16px yellow square on the screen. Every component needs a render method, and it should return `impl Element<V>`. This basic component will render a 16x16px yellow square on the screen.
@@ -84,7 +84,7 @@ Let's grab our [theme::colors::ThemeColors] from the theme and start building ou
We can access the current theme's colors like this: We can access the current theme's colors like this:
~~~rust ```rust
impl<V: 'static> TodoList<V> { impl<V: 'static> TodoList<V> {
// ... // ...
fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> { fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
@@ -93,11 +93,11 @@ impl<V: 'static> TodoList<V> {
div().size_4().hsla(50.0/360.0, 1.0, 0.5, 1.0) div().size_4().hsla(50.0/360.0, 1.0, 0.5, 1.0)
} }
} }
~~~ ```
Now we have access to the complete set of colors defined in the theme. Now we have access to the complete set of colors defined in the theme.
~~~rust ```rust
use gpui::hsla use gpui::hsla
impl<V: 'static> TodoList<V> { impl<V: 'static> TodoList<V> {
@@ -108,11 +108,11 @@ impl<V: 'static> TodoList<V> {
div().size_4().bg(color.surface) div().size_4().bg(color.surface)
} }
} }
~~~ ```
Let's finish up some basic styles for the container then move on to adding the other elements. Let's finish up some basic styles for the container then move on to adding the other elements.
~~~rust ```rust
use gpui::hsla use gpui::hsla
impl<V: 'static> TodoList<V> { impl<V: 'static> TodoList<V> {
@@ -140,7 +140,7 @@ impl<V: 'static> TodoList<V> {
) )
} }
} }
~~~ ```
### Headline ### Headline
@@ -154,7 +154,6 @@ TODO
TODO TODO
### End result ### End result
TODO TODO

View File

@@ -65,11 +65,6 @@ impl LabelCommon for HighlightedLabel {
self.base = self.base.underline(underline); self.base = self.base.underline(underline);
self self
} }
fn single_line(mut self) -> Self {
self.base = self.base.single_line();
self
}
} }
pub fn highlight_ranges( pub fn highlight_ranges(

View File

@@ -56,6 +56,20 @@ impl Label {
single_line: false, single_line: false,
} }
} }
/// Make the label display in a single line mode
///
/// # Examples
///
/// ```
/// use ui::prelude::*;
///
/// let my_label = Label::new("Hello, World!").single_line();
/// ```
pub fn single_line(mut self) -> Self {
self.single_line = true;
self
}
} }
// Style methods. // Style methods.
@@ -163,12 +177,6 @@ impl LabelCommon for Label {
self.base = self.base.underline(underline); self.base = self.base.underline(underline);
self self
} }
fn single_line(mut self) -> Self {
self.single_line = true;
self.base = self.base.single_line();
self
}
} }
impl RenderOnce for Label { impl RenderOnce for Label {

View File

@@ -49,9 +49,6 @@ pub trait LabelCommon {
/// Sets the alpha property of the label, overwriting the alpha value of the color. /// Sets the alpha property of the label, overwriting the alpha value of the color.
fn alpha(self, alpha: f32) -> Self; fn alpha(self, alpha: f32) -> Self;
/// Sets the label to render as a single line.
fn single_line(self) -> Self;
} }
#[derive(IntoElement)] #[derive(IntoElement)]
@@ -66,7 +63,6 @@ pub struct LabelLike {
children: SmallVec<[AnyElement; 2]>, children: SmallVec<[AnyElement; 2]>,
alpha: Option<f32>, alpha: Option<f32>,
underline: bool, underline: bool,
single_line: bool,
} }
impl Default for LabelLike { impl Default for LabelLike {
@@ -88,7 +84,6 @@ impl LabelLike {
children: SmallVec::new(), children: SmallVec::new(),
alpha: None, alpha: None,
underline: false, underline: false,
single_line: false,
} }
} }
} }
@@ -144,11 +139,6 @@ impl LabelCommon for LabelLike {
self.alpha = Some(alpha); self.alpha = Some(alpha);
self self
} }
fn single_line(mut self) -> Self {
self.single_line = true;
self
}
} }
impl ParentElement for LabelLike { impl ParentElement for LabelLike {
@@ -188,7 +178,6 @@ impl RenderOnce for LabelLike {
this this
}) })
.when(self.strikethrough, |this| this.line_through()) .when(self.strikethrough, |this| this.line_through())
.when(self.single_line, |this| this.whitespace_nowrap())
.text_color(color) .text_color(color)
.font_weight(self.weight.unwrap_or(settings.ui_font.weight)) .font_weight(self.weight.unwrap_or(settings.ui_font.weight))
.children(self.children) .children(self.children)

View File

@@ -30,7 +30,6 @@ cargo test -p vim --features neovim test_visual_star_hash
This will run your keystrokes against a headless neovim and cache the results in the test_data directory. This will run your keystrokes against a headless neovim and cache the results in the test_data directory.
## Testing zed-only behavior ## Testing zed-only behavior
Zed does more than vim/neovim in their default modes. The `VimTestContext` can be used instead. This lets you test integration with the language server and other parts of zed's UI that don't have a NeoVim equivalent. Zed does more than vim/neovim in their default modes. The `VimTestContext` can be used instead. This lets you test integration with the language server and other parts of zed's UI that don't have a NeoVim equivalent.

View File

@@ -9,7 +9,7 @@ use anyhow::{anyhow, Result};
use command_palette_hooks::CommandInterceptResult; use command_palette_hooks::CommandInterceptResult;
use editor::{ use editor::{
actions::{SortLinesCaseInsensitive, SortLinesCaseSensitive}, actions::{SortLinesCaseInsensitive, SortLinesCaseSensitive},
Editor, ToMultiBufferPoint, Editor, ToPoint,
}; };
use gpui::{actions, impl_actions, Action, AppContext, Global, ViewContext}; use gpui::{actions, impl_actions, Action, AppContext, Global, ViewContext};
use language::Point; use language::Point;

View File

@@ -1,271 +0,0 @@
use editor::{movement, scroll::Autoscroll, DisplayPoint, Editor};
use gpui::{actions, Action};
use language::{CharClassifier, CharKind};
use ui::ViewContext;
use crate::{motion::Motion, state::Mode, Vim};
actions!(vim, [HelixNormalAfter]);
pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, Vim::helix_normal_after);
}
impl Vim {
pub fn helix_normal_after(&mut self, action: &HelixNormalAfter, cx: &mut ViewContext<Self>) {
if self.active_operator().is_some() {
self.operator_stack.clear();
self.sync_vim_settings(cx);
return;
}
self.stop_recording_immediately(action.boxed_clone(), cx);
self.switch_mode(Mode::HelixNormal, false, cx);
return;
}
pub fn helix_normal_motion(
&mut self,
motion: Motion,
times: Option<usize>,
cx: &mut ViewContext<Self>,
) {
self.helix_move_cursor(motion, times, cx);
}
fn helix_find_range_forward(
&mut self,
times: Option<usize>,
cx: &mut ViewContext<Self>,
mut is_boundary: impl FnMut(char, char, &CharClassifier) -> bool,
) {
self.update_editor(cx, |_, editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_with(|map, selection| {
let times = times.unwrap_or(1);
if selection.head() == map.max_point() {
return;
}
// collapse to block cursor
if selection.tail() < selection.head() {
selection.set_tail(movement::left(map, selection.head()), selection.goal);
} else {
selection.set_tail(selection.head(), selection.goal);
selection.set_head(movement::right(map, selection.head()), selection.goal);
}
// create a classifier
let classifier = map
.buffer_snapshot
.char_classifier_at(selection.head().to_point(map));
let mut last_selection = selection.clone();
for _ in 0..times {
let (new_tail, new_head) =
movement::find_boundary_trail(map, selection.head(), |left, right| {
is_boundary(left, right, &classifier)
});
selection.set_head(new_head, selection.goal);
if let Some(new_tail) = new_tail {
selection.set_tail(new_tail, selection.goal);
}
if selection.head() == last_selection.head()
&& selection.tail() == last_selection.tail()
{
break;
}
last_selection = selection.clone();
}
});
});
});
}
fn helix_find_range_backward(
&mut self,
times: Option<usize>,
cx: &mut ViewContext<Self>,
mut is_boundary: impl FnMut(char, char, &CharClassifier) -> bool,
) {
self.update_editor(cx, |_, editor, cx| {
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_with(|map, selection| {
let times = times.unwrap_or(1);
if selection.head() == DisplayPoint::zero() {
return;
}
// collapse to block cursor
if selection.tail() < selection.head() {
selection.set_tail(movement::left(map, selection.head()), selection.goal);
} else {
selection.set_tail(selection.head(), selection.goal);
selection.set_head(movement::right(map, selection.head()), selection.goal);
}
// flip the selection
selection.swap_head_tail();
// create a classifier
let classifier = map
.buffer_snapshot
.char_classifier_at(selection.head().to_point(map));
let mut last_selection = selection.clone();
for _ in 0..times {
let (new_tail, new_head) = movement::find_preceding_boundary_trail(
map,
selection.head(),
|left, right| is_boundary(left, right, &classifier),
);
selection.set_head(new_head, selection.goal);
if let Some(new_tail) = new_tail {
selection.set_tail(new_tail, selection.goal);
}
if selection.head() == last_selection.head()
&& selection.tail() == last_selection.tail()
{
break;
}
last_selection = selection.clone();
}
});
})
});
}
pub fn helix_move_and_collapse(
&mut self,
motion: Motion,
times: Option<usize>,
cx: &mut ViewContext<Self>,
) {
self.update_editor(cx, |_, editor, cx| {
let text_layout_details = editor.text_layout_details(cx);
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_with(|map, selection| {
let goal = selection.goal;
let cursor = if selection.is_empty() || selection.reversed {
selection.head()
} else {
movement::left(map, selection.head())
};
let (point, goal) = motion
.move_point(map, cursor, selection.goal, times, &text_layout_details)
.unwrap_or((cursor, goal));
selection.collapse_to(point, goal)
})
});
});
}
pub fn helix_move_cursor(
&mut self,
motion: Motion,
times: Option<usize>,
cx: &mut ViewContext<Self>,
) {
match motion {
Motion::NextWordStart { ignore_punctuation } => {
self.helix_find_range_forward(times, cx, |left, right, classifier| {
let left_kind = classifier.kind_with(left, ignore_punctuation);
let right_kind = classifier.kind_with(right, ignore_punctuation);
let at_newline = right == '\n';
let found =
left_kind != right_kind && right_kind != CharKind::Whitespace || at_newline;
found
})
}
Motion::NextWordEnd { ignore_punctuation } => {
self.helix_find_range_forward(times, cx, |left, right, classifier| {
let left_kind = classifier.kind_with(left, ignore_punctuation);
let right_kind = classifier.kind_with(right, ignore_punctuation);
let at_newline = right == '\n';
let found = left_kind != right_kind
&& (left_kind != CharKind::Whitespace || at_newline);
found
})
}
Motion::PreviousWordStart { ignore_punctuation } => {
self.helix_find_range_backward(times, cx, |left, right, classifier| {
let left_kind = classifier.kind_with(left, ignore_punctuation);
let right_kind = classifier.kind_with(right, ignore_punctuation);
let at_newline = right == '\n';
let found = left_kind != right_kind
&& (left_kind != CharKind::Whitespace || at_newline);
found
})
}
Motion::PreviousWordEnd { ignore_punctuation } => {
self.helix_find_range_backward(times, cx, |left, right, classifier| {
let left_kind = classifier.kind_with(left, ignore_punctuation);
let right_kind = classifier.kind_with(right, ignore_punctuation);
let at_newline = right == '\n';
let found = left_kind != right_kind
&& right_kind != CharKind::Whitespace
&& !at_newline;
found
})
}
_ => self.helix_move_and_collapse(motion, times, cx),
}
}
}
#[cfg(test)]
mod test {
use indoc::indoc;
use crate::{state::Mode, test::VimTestContext};
#[gpui::test]
async fn test_next_word_start(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
// «
// ˇ
// »
cx.set_state(
indoc! {"
The quˇick brown
fox jumps over
the lazy dog."},
Mode::HelixNormal,
);
cx.simulate_keystrokes("w");
cx.assert_state(
indoc! {"
The qu«ick ˇ»brown
fox jumps over
the lazy dog."},
Mode::HelixNormal,
);
cx.simulate_keystrokes("w");
cx.assert_state(
indoc! {"
The quick «brownˇ»
fox jumps over
the lazy dog."},
Mode::HelixNormal,
);
}
}

View File

@@ -9,10 +9,9 @@ use ui::ViewContext;
pub(crate) enum IndentDirection { pub(crate) enum IndentDirection {
In, In,
Out, Out,
Auto,
} }
actions!(vim, [Indent, Outdent, AutoIndent]); actions!(vim, [Indent, Outdent,]);
pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) { pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, |vim, _: &Indent, cx| { Vim::action(editor, cx, |vim, _: &Indent, cx| {
@@ -50,24 +49,6 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
vim.switch_mode(Mode::Normal, true, cx) vim.switch_mode(Mode::Normal, true, cx)
} }
}); });
Vim::action(editor, cx, |vim, _: &AutoIndent, cx| {
vim.record_current_action(cx);
let count = Vim::take_count(cx).unwrap_or(1);
vim.store_visual_marks(cx);
vim.update_editor(cx, |vim, editor, cx| {
editor.transact(cx, |editor, cx| {
let original_positions = vim.save_selection_starts(editor, cx);
for _ in 0..count {
editor.autoindent(&Default::default(), cx);
}
vim.restore_selection_cursors(editor, cx, original_positions);
});
});
if vim.mode.is_visual() {
vim.switch_mode(Mode::Normal, true, cx)
}
});
} }
impl Vim { impl Vim {
@@ -90,10 +71,10 @@ impl Vim {
motion.expand_selection(map, selection, times, false, &text_layout_details); motion.expand_selection(map, selection, times, false, &text_layout_details);
}); });
}); });
match dir { if dir == IndentDirection::In {
IndentDirection::In => editor.indent(&Default::default(), cx), editor.indent(&Default::default(), cx);
IndentDirection::Out => editor.outdent(&Default::default(), cx), } else {
IndentDirection::Auto => editor.autoindent(&Default::default(), cx), editor.outdent(&Default::default(), cx);
} }
editor.change_selections(None, cx, |s| { editor.change_selections(None, cx, |s| {
s.move_with(|map, selection| { s.move_with(|map, selection| {
@@ -123,10 +104,10 @@ impl Vim {
object.expand_selection(map, selection, around); object.expand_selection(map, selection, around);
}); });
}); });
match dir { if dir == IndentDirection::In {
IndentDirection::In => editor.indent(&Default::default(), cx), editor.indent(&Default::default(), cx);
IndentDirection::Out => editor.outdent(&Default::default(), cx), } else {
IndentDirection::Auto => editor.autoindent(&Default::default(), cx), editor.outdent(&Default::default(), cx);
} }
editor.change_selections(None, cx, |s| { editor.change_selections(None, cx, |s| {
s.move_with(|map, selection| { s.move_with(|map, selection| {
@@ -141,11 +122,7 @@ impl Vim {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::{ use crate::test::NeovimBackedTestContext;
state::Mode,
test::{NeovimBackedTestContext, VimTestContext},
};
use indoc::indoc;
#[gpui::test] #[gpui::test]
async fn test_indent_gv(cx: &mut gpui::TestAppContext) { async fn test_indent_gv(cx: &mut gpui::TestAppContext) {
@@ -158,46 +135,4 @@ mod test {
.await .await
.assert_eq("« hello\n ˇ» world\n"); .assert_eq("« hello\n ˇ» world\n");
} }
#[gpui::test]
async fn test_autoindent_op(cx: &mut gpui::TestAppContext) {
let mut cx = VimTestContext::new(cx, true).await;
cx.set_state(
indoc!(
"
fn a() {
b();
c();
d();
ˇe();
f();
g();
}
"
),
Mode::Normal,
);
cx.simulate_keystrokes("= a p");
cx.assert_state(
indoc!(
"
fn a() {
b();
c();
d();
ˇe();
f();
g();
}
"
),
Mode::Normal,
);
}
} }

View File

@@ -11,7 +11,6 @@ use language::{CharKind, Point, Selection, SelectionGoal};
use multi_buffer::MultiBufferRow; use multi_buffer::MultiBufferRow;
use serde::Deserialize; use serde::Deserialize;
use std::ops::Range; use std::ops::Range;
use workspace::searchable::Direction;
use crate::{ use crate::{
normal::mark, normal::mark,
@@ -105,16 +104,6 @@ pub enum Motion {
WindowTop, WindowTop,
WindowMiddle, WindowMiddle,
WindowBottom, WindowBottom,
NextSectionStart,
NextSectionEnd,
PreviousSectionStart,
PreviousSectionEnd,
NextMethodStart,
NextMethodEnd,
PreviousMethodStart,
PreviousMethodEnd,
NextComment,
PreviousComment,
// we don't have a good way to run a search synchronously, so // we don't have a good way to run a search synchronously, so
// we handle search motions by running the search async and then // we handle search motions by running the search async and then
@@ -280,16 +269,6 @@ actions!(
WindowTop, WindowTop,
WindowMiddle, WindowMiddle,
WindowBottom, WindowBottom,
NextSectionStart,
NextSectionEnd,
PreviousSectionStart,
PreviousSectionEnd,
NextMethodStart,
NextMethodEnd,
PreviousMethodStart,
PreviousMethodEnd,
NextComment,
PreviousComment,
] ]
); );
@@ -475,37 +454,6 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, |vim, &WindowBottom, cx| { Vim::action(editor, cx, |vim, &WindowBottom, cx| {
vim.motion(Motion::WindowBottom, cx) vim.motion(Motion::WindowBottom, cx)
}); });
Vim::action(editor, cx, |vim, &PreviousSectionStart, cx| {
vim.motion(Motion::PreviousSectionStart, cx)
});
Vim::action(editor, cx, |vim, &NextSectionStart, cx| {
vim.motion(Motion::NextSectionStart, cx)
});
Vim::action(editor, cx, |vim, &PreviousSectionEnd, cx| {
vim.motion(Motion::PreviousSectionEnd, cx)
});
Vim::action(editor, cx, |vim, &NextSectionEnd, cx| {
vim.motion(Motion::NextSectionEnd, cx)
});
Vim::action(editor, cx, |vim, &PreviousMethodStart, cx| {
vim.motion(Motion::PreviousMethodStart, cx)
});
Vim::action(editor, cx, |vim, &NextMethodStart, cx| {
vim.motion(Motion::NextMethodStart, cx)
});
Vim::action(editor, cx, |vim, &PreviousMethodEnd, cx| {
vim.motion(Motion::PreviousMethodEnd, cx)
});
Vim::action(editor, cx, |vim, &NextMethodEnd, cx| {
vim.motion(Motion::NextMethodEnd, cx)
});
Vim::action(editor, cx, |vim, &NextComment, cx| {
vim.motion(Motion::NextComment, cx)
});
Vim::action(editor, cx, |vim, &PreviousComment, cx| {
vim.motion(Motion::PreviousComment, cx)
});
} }
impl Vim { impl Vim {
@@ -529,8 +477,6 @@ impl Vim {
return; return;
} }
} }
Mode::HelixNormal => {}
} }
} }
@@ -560,8 +506,6 @@ impl Vim {
Mode::Visual | Mode::VisualLine | Mode::VisualBlock => { Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
self.visual_motion(motion.clone(), count, cx) self.visual_motion(motion.clone(), count, cx)
} }
Mode::HelixNormal => self.helix_normal_motion(motion.clone(), count, cx),
} }
self.clear_operator(cx); self.clear_operator(cx);
if let Some(operator) = waiting_operator { if let Some(operator) = waiting_operator {
@@ -592,16 +536,6 @@ impl Motion {
| WindowTop | WindowTop
| WindowMiddle | WindowMiddle
| WindowBottom | WindowBottom
| NextSectionStart
| NextSectionEnd
| PreviousSectionStart
| PreviousSectionEnd
| NextMethodStart
| NextMethodEnd
| PreviousMethodStart
| PreviousMethodEnd
| NextComment
| PreviousComment
| Jump { line: true, .. } => true, | Jump { line: true, .. } => true,
EndOfLine { .. } EndOfLine { .. }
| Matching | Matching
@@ -673,16 +607,6 @@ impl Motion {
| NextLineStart | NextLineStart
| PreviousLineStart | PreviousLineStart
| ZedSearchResult { .. } | ZedSearchResult { .. }
| NextSectionStart
| NextSectionEnd
| PreviousSectionStart
| PreviousSectionEnd
| NextMethodStart
| NextMethodEnd
| PreviousMethodStart
| PreviousMethodEnd
| NextComment
| PreviousComment
| Jump { .. } => false, | Jump { .. } => false,
} }
} }
@@ -728,16 +652,6 @@ impl Motion {
| FirstNonWhitespace { .. } | FirstNonWhitespace { .. }
| FindBackward { .. } | FindBackward { .. }
| Jump { .. } | Jump { .. }
| NextSectionStart
| NextSectionEnd
| PreviousSectionStart
| PreviousSectionEnd
| NextMethodStart
| NextMethodEnd
| PreviousMethodStart
| PreviousMethodEnd
| NextComment
| PreviousComment
| ZedSearchResult { .. } => false, | ZedSearchResult { .. } => false,
RepeatFind { last_find: motion } | RepeatFindReversed { last_find: motion } => { RepeatFind { last_find: motion } | RepeatFindReversed { last_find: motion } => {
motion.inclusive() motion.inclusive()
@@ -953,47 +867,6 @@ impl Motion {
return None; return None;
} }
} }
NextSectionStart => (
section_motion(map, point, times, Direction::Next, true),
SelectionGoal::None,
),
NextSectionEnd => (
section_motion(map, point, times, Direction::Next, false),
SelectionGoal::None,
),
PreviousSectionStart => (
section_motion(map, point, times, Direction::Prev, true),
SelectionGoal::None,
),
PreviousSectionEnd => (
section_motion(map, point, times, Direction::Prev, false),
SelectionGoal::None,
),
NextMethodStart => (
method_motion(map, point, times, Direction::Next, true),
SelectionGoal::None,
),
NextMethodEnd => (
method_motion(map, point, times, Direction::Next, false),
SelectionGoal::None,
),
PreviousMethodStart => (
method_motion(map, point, times, Direction::Prev, true),
SelectionGoal::None,
),
PreviousMethodEnd => (
method_motion(map, point, times, Direction::Prev, false),
SelectionGoal::None,
),
NextComment => (
comment_motion(map, point, times, Direction::Next),
SelectionGoal::None,
),
PreviousComment => (
comment_motion(map, point, times, Direction::Prev),
SelectionGoal::None,
),
}; };
(new_point != point || infallible).then_some((new_point, goal)) (new_point != point || infallible).then_some((new_point, goal))
@@ -2256,231 +2129,6 @@ fn window_bottom(
} }
} }
fn method_motion(
map: &DisplaySnapshot,
mut display_point: DisplayPoint,
times: usize,
direction: Direction,
is_start: bool,
) -> DisplayPoint {
let Some((_, _, buffer)) = map.buffer_snapshot.as_singleton() else {
return display_point;
};
for _ in 0..times {
let point = map.display_point_to_point(display_point, Bias::Left);
let offset = point.to_offset(&map.buffer_snapshot);
let range = if direction == Direction::Prev {
0..offset
} else {
offset..buffer.len()
};
let possibilities = buffer
.text_object_ranges(range, language::TreeSitterOptions::max_start_depth(4))
.filter_map(|(range, object)| {
if !matches!(object, language::TextObject::AroundFunction) {
return None;
}
let relevant = if is_start { range.start } else { range.end };
if direction == Direction::Prev && relevant < offset {
Some(relevant)
} else if direction == Direction::Next && relevant > offset + 1 {
Some(relevant)
} else {
None
}
});
let dest = if direction == Direction::Prev {
possibilities.max().unwrap_or(offset)
} else {
possibilities.min().unwrap_or(offset)
};
let new_point = map.clip_point(dest.to_display_point(&map), Bias::Left);
if new_point == display_point {
break;
}
display_point = new_point;
}
display_point
}
fn comment_motion(
map: &DisplaySnapshot,
mut display_point: DisplayPoint,
times: usize,
direction: Direction,
) -> DisplayPoint {
let Some((_, _, buffer)) = map.buffer_snapshot.as_singleton() else {
return display_point;
};
for _ in 0..times {
let point = map.display_point_to_point(display_point, Bias::Left);
let offset = point.to_offset(&map.buffer_snapshot);
let range = if direction == Direction::Prev {
0..offset
} else {
offset..buffer.len()
};
let possibilities = buffer
.text_object_ranges(range, language::TreeSitterOptions::max_start_depth(6))
.filter_map(|(range, object)| {
if !matches!(object, language::TextObject::AroundComment) {
return None;
}
let relevant = if direction == Direction::Prev {
range.start
} else {
range.end
};
if direction == Direction::Prev && relevant < offset {
Some(relevant)
} else if direction == Direction::Next && relevant > offset + 1 {
Some(relevant)
} else {
None
}
});
let dest = if direction == Direction::Prev {
possibilities.max().unwrap_or(offset)
} else {
possibilities.min().unwrap_or(offset)
};
let new_point = map.clip_point(dest.to_display_point(&map), Bias::Left);
if new_point == display_point {
break;
}
display_point = new_point;
}
display_point
}
fn section_motion(
map: &DisplaySnapshot,
mut display_point: DisplayPoint,
times: usize,
direction: Direction,
is_start: bool,
) -> DisplayPoint {
if let Some((_, _, buffer)) = map.buffer_snapshot.as_singleton() {
for _ in 0..times {
let offset = map
.display_point_to_point(display_point, Bias::Left)
.to_offset(&map.buffer_snapshot);
let range = if direction == Direction::Prev {
0..offset
} else {
offset..buffer.len()
};
// we set a max start depth here because we want a section to only be "top level"
// similar to vim's default of '{' in the first column.
// (and without it, ]] at the start of editor.rs is -very- slow)
let mut possibilities = buffer
.text_object_ranges(range, language::TreeSitterOptions::max_start_depth(3))
.filter(|(_, object)| {
matches!(
object,
language::TextObject::AroundClass | language::TextObject::AroundFunction
)
})
.collect::<Vec<_>>();
possibilities.sort_by_key(|(range_a, _)| range_a.start);
let mut prev_end = None;
let possibilities = possibilities.into_iter().filter_map(|(range, t)| {
if t == language::TextObject::AroundFunction
&& prev_end.is_some_and(|prev_end| prev_end > range.start)
{
return None;
}
prev_end = Some(range.end);
let relevant = if is_start { range.start } else { range.end };
if direction == Direction::Prev && relevant < offset {
Some(relevant)
} else if direction == Direction::Next && relevant > offset + 1 {
Some(relevant)
} else {
None
}
});
let offset = if direction == Direction::Prev {
possibilities.max().unwrap_or(0)
} else {
possibilities.min().unwrap_or(buffer.len())
};
let new_point = map.clip_point(offset.to_display_point(&map), Bias::Left);
if new_point == display_point {
break;
}
display_point = new_point;
}
return display_point;
};
for _ in 0..times {
let point = map.display_point_to_point(display_point, Bias::Left);
let Some(excerpt) = map.buffer_snapshot.excerpt_containing(point..point) else {
return display_point;
};
let next_point = match (direction, is_start) {
(Direction::Prev, true) => {
let mut start = excerpt.start_anchor().to_display_point(&map);
if start >= display_point && start.row() > DisplayRow(0) {
let Some(excerpt) = map.buffer_snapshot.excerpt_before(excerpt.id()) else {
return display_point;
};
start = excerpt.start_anchor().to_display_point(&map);
}
start
}
(Direction::Prev, false) => {
let mut start = excerpt.start_anchor().to_display_point(&map);
if start.row() > DisplayRow(0) {
*start.row_mut() -= 1;
}
map.clip_point(start, Bias::Left)
}
(Direction::Next, true) => {
let mut end = excerpt.end_anchor().to_display_point(&map);
*end.row_mut() += 1;
map.clip_point(end, Bias::Right)
}
(Direction::Next, false) => {
let mut end = excerpt.end_anchor().to_display_point(&map);
*end.column_mut() = 0;
if end <= display_point {
*end.row_mut() += 1;
let point_end = map.display_point_to_point(end, Bias::Right);
let Some(excerpt) =
map.buffer_snapshot.excerpt_containing(point_end..point_end)
else {
return display_point;
};
end = excerpt.end_anchor().to_display_point(&map);
*end.column_mut() = 0;
}
end
}
};
if next_point == display_point {
break;
}
display_point = next_point;
}
display_point
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {

View File

@@ -170,9 +170,6 @@ impl Vim {
Some(Operator::Indent) => self.indent_motion(motion, times, IndentDirection::In, cx), Some(Operator::Indent) => self.indent_motion(motion, times, IndentDirection::In, cx),
Some(Operator::Rewrap) => self.rewrap_motion(motion, times, cx), Some(Operator::Rewrap) => self.rewrap_motion(motion, times, cx),
Some(Operator::Outdent) => self.indent_motion(motion, times, IndentDirection::Out, cx), Some(Operator::Outdent) => self.indent_motion(motion, times, IndentDirection::Out, cx),
Some(Operator::AutoIndent) => {
self.indent_motion(motion, times, IndentDirection::Auto, cx)
}
Some(Operator::Lowercase) => { Some(Operator::Lowercase) => {
self.change_case_motion(motion, times, CaseTarget::Lowercase, cx) self.change_case_motion(motion, times, CaseTarget::Lowercase, cx)
} }
@@ -205,9 +202,6 @@ impl Vim {
Some(Operator::Outdent) => { Some(Operator::Outdent) => {
self.indent_object(object, around, IndentDirection::Out, cx) self.indent_object(object, around, IndentDirection::Out, cx)
} }
Some(Operator::AutoIndent) => {
self.indent_object(object, around, IndentDirection::Auto, cx)
}
Some(Operator::Rewrap) => self.rewrap_object(object, around, cx), Some(Operator::Rewrap) => self.rewrap_object(object, around, cx),
Some(Operator::Lowercase) => { Some(Operator::Lowercase) => {
self.change_case_object(object, around, CaseTarget::Lowercase, cx) self.change_case_object(object, around, CaseTarget::Lowercase, cx)

View File

@@ -145,8 +145,6 @@ impl Vim {
cursor_positions.push(selection.start..selection.start); cursor_positions.push(selection.start..selection.start);
} }
} }
Mode::HelixNormal => {}
Mode::Insert | Mode::Normal | Mode::Replace => { Mode::Insert | Mode::Normal | Mode::Replace => {
let start = selection.start; let start = selection.start;
let mut end = start; let mut end = start;

View File

@@ -1,6 +1,6 @@
use std::ops::Range; use std::ops::Range;
use editor::{scroll::Autoscroll, Editor, MultiBufferSnapshot, ToMultiBufferPoint, ToOffset}; use editor::{scroll::Autoscroll, Editor, MultiBufferSnapshot, ToOffset, ToPoint};
use gpui::{impl_actions, ViewContext}; use gpui::{impl_actions, ViewContext};
use language::{Bias, Point}; use language::{Bias, Point};
use serde::Deserialize; use serde::Deserialize;

View File

@@ -1,10 +1,6 @@
use std::ops::Range; use std::ops::Range;
use crate::{ use crate::{motion::right, state::Mode, Vim};
motion::right,
state::{Mode, Operator},
Vim,
};
use editor::{ use editor::{
display_map::{DisplaySnapshot, ToDisplayPoint}, display_map::{DisplaySnapshot, ToDisplayPoint},
movement::{self, FindRange}, movement::{self, FindRange},
@@ -14,7 +10,7 @@ use editor::{
use itertools::Itertools; use itertools::Itertools;
use gpui::{actions, impl_actions, ViewContext}; use gpui::{actions, impl_actions, ViewContext};
use language::{BufferSnapshot, CharKind, Point, Selection, TextObject, TreeSitterOptions}; use language::{BufferSnapshot, CharKind, Point, Selection};
use multi_buffer::MultiBufferRow; use multi_buffer::MultiBufferRow;
use serde::Deserialize; use serde::Deserialize;
@@ -34,9 +30,6 @@ pub enum Object {
Argument, Argument,
IndentObj { include_below: bool }, IndentObj { include_below: bool },
Tag, Tag,
Method,
Class,
Comment,
} }
#[derive(Clone, Deserialize, PartialEq)] #[derive(Clone, Deserialize, PartialEq)]
@@ -68,10 +61,7 @@ actions!(
CurlyBrackets, CurlyBrackets,
AngleBrackets, AngleBrackets,
Argument, Argument,
Tag, Tag
Method,
Class,
Comment
] ]
); );
@@ -117,18 +107,6 @@ pub fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
Vim::action(editor, cx, |vim, _: &Argument, cx| { Vim::action(editor, cx, |vim, _: &Argument, cx| {
vim.object(Object::Argument, cx) vim.object(Object::Argument, cx)
}); });
Vim::action(editor, cx, |vim, _: &Method, cx| {
vim.object(Object::Method, cx)
});
Vim::action(editor, cx, |vim, _: &Class, cx| {
vim.object(Object::Class, cx)
});
Vim::action(editor, cx, |vim, _: &Comment, cx| {
if !matches!(vim.active_operator(), Some(Operator::Object { .. })) {
vim.push_operator(Operator::Object { around: true }, cx);
}
vim.object(Object::Comment, cx)
});
Vim::action( Vim::action(
editor, editor,
cx, cx,
@@ -143,7 +121,7 @@ impl Vim {
match self.mode { match self.mode {
Mode::Normal => self.normal_object(object, cx), Mode::Normal => self.normal_object(object, cx),
Mode::Visual | Mode::VisualLine | Mode::VisualBlock => self.visual_object(object, cx), Mode::Visual | Mode::VisualLine | Mode::VisualBlock => self.visual_object(object, cx),
Mode::Insert | Mode::Replace | Mode::HelixNormal => { Mode::Insert | Mode::Replace => {
// Shouldn't execute a text object in insert mode. Ignoring // Shouldn't execute a text object in insert mode. Ignoring
} }
} }
@@ -166,9 +144,6 @@ impl Object {
| Object::CurlyBrackets | Object::CurlyBrackets
| Object::SquareBrackets | Object::SquareBrackets
| Object::Argument | Object::Argument
| Object::Method
| Object::Class
| Object::Comment
| Object::IndentObj { .. } => true, | Object::IndentObj { .. } => true,
} }
} }
@@ -187,15 +162,12 @@ impl Object {
| Object::Parentheses | Object::Parentheses
| Object::SquareBrackets | Object::SquareBrackets
| Object::Tag | Object::Tag
| Object::Method
| Object::Class
| Object::Comment
| Object::CurlyBrackets | Object::CurlyBrackets
| Object::AngleBrackets => true, | Object::AngleBrackets => true,
} }
} }
pub fn target_visual_mode(self, current_mode: Mode, around: bool) -> Mode { pub fn target_visual_mode(self, current_mode: Mode) -> Mode {
match self { match self {
Object::Word { .. } Object::Word { .. }
| Object::Sentence | Object::Sentence
@@ -214,16 +186,8 @@ impl Object {
| Object::AngleBrackets | Object::AngleBrackets
| Object::VerticalBars | Object::VerticalBars
| Object::Tag | Object::Tag
| Object::Comment
| Object::Argument | Object::Argument
| Object::IndentObj { .. } => Mode::Visual, | Object::IndentObj { .. } => Mode::Visual,
Object::Method | Object::Class => {
if around {
Mode::VisualLine
} else {
Mode::Visual
}
}
Object::Paragraph => Mode::VisualLine, Object::Paragraph => Mode::VisualLine,
} }
} }
@@ -274,33 +238,6 @@ impl Object {
Object::AngleBrackets => { Object::AngleBrackets => {
surrounding_markers(map, relative_to, around, self.is_multiline(), '<', '>') surrounding_markers(map, relative_to, around, self.is_multiline(), '<', '>')
} }
Object::Method => text_object(
map,
relative_to,
if around {
TextObject::AroundFunction
} else {
TextObject::InsideFunction
},
),
Object::Comment => text_object(
map,
relative_to,
if around {
TextObject::AroundComment
} else {
TextObject::InsideComment
},
),
Object::Class => text_object(
map,
relative_to,
if around {
TextObject::AroundClass
} else {
TextObject::InsideClass
},
),
Object::Argument => argument(map, relative_to, around), Object::Argument => argument(map, relative_to, around),
Object::IndentObj { include_below } => indent(map, relative_to, around, include_below), Object::IndentObj { include_below } => indent(map, relative_to, around, include_below),
} }
@@ -504,47 +441,6 @@ fn around_next_word(
Some(start..end) Some(start..end)
} }
fn text_object(
map: &DisplaySnapshot,
relative_to: DisplayPoint,
target: TextObject,
) -> Option<Range<DisplayPoint>> {
let snapshot = &map.buffer_snapshot;
let offset = relative_to.to_offset(map, Bias::Left);
let excerpt = snapshot.excerpt_containing(offset..offset)?;
let buffer = excerpt.buffer();
let mut matches: Vec<Range<usize>> = buffer
.text_object_ranges(offset..offset, TreeSitterOptions::default())
.filter_map(|(r, m)| if m == target { Some(r) } else { None })
.collect();
matches.sort_by_key(|r| (r.end - r.start));
if let Some(range) = matches.first() {
return Some(range.start.to_display_point(map)..range.end.to_display_point(map));
}
let around = target.around()?;
let mut matches: Vec<Range<usize>> = buffer
.text_object_ranges(offset..offset, TreeSitterOptions::default())
.filter_map(|(r, m)| if m == around { Some(r) } else { None })
.collect();
matches.sort_by_key(|r| (r.end - r.start));
let around_range = matches.first()?;
let mut matches: Vec<Range<usize>> = buffer
.text_object_ranges(around_range.clone(), TreeSitterOptions::default())
.filter_map(|(r, m)| if m == target { Some(r) } else { None })
.collect();
matches.sort_by_key(|r| r.start);
if let Some(range) = matches.first() {
if !range.is_empty() {
return Some(range.start.to_display_point(map)..range.end.to_display_point(map));
}
}
return Some(around_range.start.to_display_point(map)..around_range.end.to_display_point(map));
}
fn argument( fn argument(
map: &DisplaySnapshot, map: &DisplaySnapshot,
relative_to: DisplayPoint, relative_to: DisplayPoint,

View File

@@ -3,7 +3,7 @@ use crate::{
state::Mode, state::Mode,
Vim, Vim,
}; };
use editor::{display_map::ToDisplayPoint, Bias, Editor, ToMultiBufferPoint}; use editor::{display_map::ToDisplayPoint, Bias, Editor, ToPoint};
use gpui::{actions, ViewContext}; use gpui::{actions, ViewContext};
use language::Point; use language::Point;
use std::ops::Range; use std::ops::Range;

Some files were not shown because too many files have changed in this diff Show More