Merge branch 'main' into collab-panel
This commit is contained in:
commit
a555fa1ada
30 changed files with 1795 additions and 332 deletions
5
.zed/settings.json
Normal file
5
.zed/settings.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"JSON": {
|
||||
"tab_size": 4
|
||||
}
|
||||
}
|
232
Cargo.lock
generated
232
Cargo.lock
generated
|
@ -141,7 +141,7 @@ source = "git+https://github.com/alacritty/alacritty?rev=7b9f32300ee0a249c087230
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -187,9 +187,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
|||
|
||||
[[package]]
|
||||
name = "alsa"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8512c9117059663fb5606788fbca3619e2a91dac0e3fe516242eab1fa6be5e44"
|
||||
checksum = "e2562ad8dcf0f789f65c6fdaad8a8a9708ed6b488e649da28c01656ad66b8b47"
|
||||
dependencies = [
|
||||
"alsa-sys",
|
||||
"bitflags 1.3.2",
|
||||
|
@ -215,9 +215,9 @@ checksum = "ec8ad6edb4840b78c5c3d88de606b22252d552b55f3a4699fbb10fc070ec3049"
|
|||
|
||||
[[package]]
|
||||
name = "android-activity"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40bc1575e653f158cbdc6ebcd917b9564e66321c5325c232c3591269c257be69"
|
||||
checksum = "64529721f27c2314ced0890ce45e469574a73e5e6fdd6e9da1860eb29285f5e0"
|
||||
dependencies = [
|
||||
"android-properties",
|
||||
"bitflags 1.3.2",
|
||||
|
@ -507,7 +507,7 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -555,7 +555,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -598,7 +598,7 @@ checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -856,7 +856,7 @@ dependencies = [
|
|||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
"which",
|
||||
]
|
||||
|
||||
|
@ -1040,7 +1040,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"regex-automata 0.3.3",
|
||||
"regex-automata 0.3.4",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
@ -1358,7 +1358,7 @@ dependencies = [
|
|||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1425,7 +1425,7 @@ dependencies = [
|
|||
"sum_tree",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"time 0.3.23",
|
||||
"time 0.3.24",
|
||||
"tiny_http",
|
||||
"url",
|
||||
"util",
|
||||
|
@ -1527,7 +1527,7 @@ dependencies = [
|
|||
"sha-1 0.9.8",
|
||||
"sqlx",
|
||||
"theme",
|
||||
"time 0.3.23",
|
||||
"time 0.3.24",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"toml 0.5.11",
|
||||
|
@ -2044,9 +2044,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "curl-sys"
|
||||
version = "0.4.64+curl-8.2.0"
|
||||
version = "0.4.65+curl-8.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f96069f0b1cb1241c838740659a771ef143363f52772a9ce1bd9c04c75eee0dc"
|
||||
checksum = "961ba061c9ef2fe34bbd12b807152d96f0badd2bebe7b90ce6c8c8b7572a0986"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
|
@ -2126,6 +2126,15 @@ dependencies = [
|
|||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8810e7e2cf385b1e9b50d68264908ec367ba642c96d02edfe61c39e88e2a3c01"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dhat"
|
||||
version = "0.3.2"
|
||||
|
@ -2343,7 +2352,7 @@ dependencies = [
|
|||
"tree-sitter",
|
||||
"tree-sitter-html",
|
||||
"tree-sitter-rust",
|
||||
"tree-sitter-typescript 0.20.2 (git+https://github.com/tree-sitter/tree-sitter-typescript?rev=5d20856f34315b068c41edaee2ac8a100081d259)",
|
||||
"tree-sitter-typescript",
|
||||
"unindent",
|
||||
"util",
|
||||
"workspace",
|
||||
|
@ -2427,9 +2436,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
|
||||
checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
|
@ -2733,7 +2742,7 @@ dependencies = [
|
|||
"smol",
|
||||
"sum_tree",
|
||||
"tempfile",
|
||||
"time 0.3.23",
|
||||
"time 0.3.24",
|
||||
"util",
|
||||
]
|
||||
|
||||
|
@ -2883,7 +2892,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3035,9 +3044,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
|||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.11"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df"
|
||||
checksum = "aca8bbd8e0707c1887a8bbb7e6b40e228f251ff5d62c8220a4a7a53c73aff006"
|
||||
dependencies = [
|
||||
"aho-corasick 1.0.2",
|
||||
"bstr",
|
||||
|
@ -3123,7 +3132,7 @@ dependencies = [
|
|||
"smol",
|
||||
"sqlez",
|
||||
"sum_tree",
|
||||
"time 0.3.23",
|
||||
"time 0.3.24",
|
||||
"tiny-skia",
|
||||
"usvg",
|
||||
"util",
|
||||
|
@ -3853,7 +3862,7 @@ dependencies = [
|
|||
"text",
|
||||
"theme",
|
||||
"tree-sitter",
|
||||
"tree-sitter-elixir 0.1.0 (git+https://github.com/elixir-lang/tree-sitter-elixir?rev=a2861e88a730287a60c11ea9299c033c7d076e30)",
|
||||
"tree-sitter-elixir",
|
||||
"tree-sitter-embedded-template",
|
||||
"tree-sitter-heex",
|
||||
"tree-sitter-html",
|
||||
|
@ -3862,7 +3871,7 @@ dependencies = [
|
|||
"tree-sitter-python",
|
||||
"tree-sitter-ruby",
|
||||
"tree-sitter-rust",
|
||||
"tree-sitter-typescript 0.20.2 (git+https://github.com/tree-sitter/tree-sitter-typescript?rev=5d20856f34315b068c41edaee2ac8a100081d259)",
|
||||
"tree-sitter-typescript",
|
||||
"unicase",
|
||||
"unindent",
|
||||
"util",
|
||||
|
@ -4036,9 +4045,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
|
|||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.3"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0"
|
||||
checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
|
||||
|
||||
[[package]]
|
||||
name = "lipsum"
|
||||
|
@ -4746,7 +4755,7 @@ dependencies = [
|
|||
"proc-macro-crate 1.3.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4897,7 +4906,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5136,9 +5145,9 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
|
|||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.7.1"
|
||||
version = "2.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d2d1d55045829d65aad9d389139882ad623b33b904e7c9f1b10c5b8927298e5"
|
||||
checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"ucd-trie",
|
||||
|
@ -5194,7 +5203,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5232,7 +5241,7 @@ dependencies = [
|
|||
"line-wrap",
|
||||
"quick-xml",
|
||||
"serde",
|
||||
"time 0.3.23",
|
||||
"time 0.3.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5347,7 +5356,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5907,7 +5916,7 @@ checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
|
|||
dependencies = [
|
||||
"aho-corasick 1.0.2",
|
||||
"memchr",
|
||||
"regex-automata 0.3.3",
|
||||
"regex-automata 0.3.4",
|
||||
"regex-syntax 0.7.4",
|
||||
]
|
||||
|
||||
|
@ -5922,9 +5931,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310"
|
||||
checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294"
|
||||
dependencies = [
|
||||
"aho-corasick 1.0.2",
|
||||
"memchr",
|
||||
|
@ -6219,7 +6228,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
|
@ -6236,13 +6245,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rust_decimal"
|
||||
version = "1.30.0"
|
||||
version = "1.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0446843641c69436765a35a5a77088e28c2e6a12da93e84aa3ab1cd4aa5a042"
|
||||
checksum = "4a2ab0025103a60ecaaf3abf24db1db240a4e1c15837090d2c32f625ac98abea"
|
||||
dependencies = [
|
||||
"arrayvec 0.7.4",
|
||||
"borsh",
|
||||
"bytecheck",
|
||||
"byteorder",
|
||||
"bytes 1.4.0",
|
||||
"num-traits",
|
||||
|
@ -6296,7 +6304,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"errno 0.3.1",
|
||||
"errno 0.3.2",
|
||||
"io-lifetimes 1.0.11",
|
||||
"libc",
|
||||
"linux-raw-sys 0.3.8",
|
||||
|
@ -6310,9 +6318,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5"
|
||||
dependencies = [
|
||||
"bitflags 2.3.3",
|
||||
"errno 0.3.1",
|
||||
"errno 0.3.2",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.3",
|
||||
"linux-raw-sys 0.4.5",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
|
@ -6511,7 +6519,7 @@ dependencies = [
|
|||
"serde_json",
|
||||
"sqlx",
|
||||
"thiserror",
|
||||
"time 0.3.23",
|
||||
"time 0.3.24",
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid 1.4.1",
|
||||
|
@ -6539,7 +6547,7 @@ dependencies = [
|
|||
"rust_decimal",
|
||||
"sea-query-derive",
|
||||
"serde_json",
|
||||
"time 0.3.23",
|
||||
"time 0.3.24",
|
||||
"uuid 1.4.1",
|
||||
]
|
||||
|
||||
|
@ -6554,7 +6562,7 @@ dependencies = [
|
|||
"sea-query",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"time 0.3.23",
|
||||
"time 0.3.24",
|
||||
"uuid 1.4.1",
|
||||
]
|
||||
|
||||
|
@ -6687,12 +6695,15 @@ dependencies = [
|
|||
"theme",
|
||||
"tiktoken-rs 0.5.0",
|
||||
"tree-sitter",
|
||||
"tree-sitter-cpp 0.20.2",
|
||||
"tree-sitter-elixir 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tree-sitter-json 0.19.0",
|
||||
"tree-sitter-cpp",
|
||||
"tree-sitter-elixir",
|
||||
"tree-sitter-json 0.20.0",
|
||||
"tree-sitter-lua",
|
||||
"tree-sitter-php",
|
||||
"tree-sitter-ruby",
|
||||
"tree-sitter-rust",
|
||||
"tree-sitter-toml 0.20.0",
|
||||
"tree-sitter-typescript 0.20.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tree-sitter-toml",
|
||||
"tree-sitter-typescript",
|
||||
"unindent",
|
||||
"util",
|
||||
"workspace",
|
||||
|
@ -6724,22 +6735,22 @@ checksum = "5a9f47faea3cad316faa914d013d24f471cd90bfca1a0c70f05a3f42c6441e99"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.175"
|
||||
version = "1.0.180"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d25439cd7397d044e2748a6fe2432b5e85db703d6d097bd014b3c0ad1ebff0b"
|
||||
checksum = "0ea67f183f058fe88a4e3ec6e2788e003840893b91bac4559cabedd00863b3ed"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.175"
|
||||
version = "1.0.180"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b23f7ade6f110613c0d63858ddb8b94c1041f550eab58a16b371bdf2c9c80ab4"
|
||||
checksum = "24e744d7782b686ab3b73267ef05697159cc0e5abbed3f47f9933165e5219036"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -6764,9 +6775,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.103"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b"
|
||||
checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
|
||||
dependencies = [
|
||||
"indexmap 2.0.0",
|
||||
"itoa 1.0.9",
|
||||
|
@ -6788,13 +6799,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.15"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e168eaaf71e8f9bd6037feb05190485708e019f4fd87d161b3c0a0d37daf85e5"
|
||||
checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -7240,7 +7251,7 @@ dependencies = [
|
|||
"sqlx-rt",
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"time 0.3.23",
|
||||
"time 0.3.24",
|
||||
"tokio-stream",
|
||||
"url",
|
||||
"uuid 1.4.1",
|
||||
|
@ -7489,9 +7500,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.27"
|
||||
version = "2.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0"
|
||||
checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -7559,9 +7570,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
|||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.10"
|
||||
version = "0.12.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d2faeef5759ab89935255b1a4cd98e0baf99d1085e37d36599c625dac49ae8e"
|
||||
checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a"
|
||||
|
||||
[[package]]
|
||||
name = "tempdir"
|
||||
|
@ -7744,7 +7755,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -7817,10 +7828,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.23"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446"
|
||||
checksum = "b79eabcd964882a646b3584543ccabeae7869e9ac32a46f6f22b7a5bd405308b"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa 1.0.9",
|
||||
"serde",
|
||||
"time-core",
|
||||
|
@ -7835,9 +7847,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
|
|||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.10"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4"
|
||||
checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
@ -7933,7 +7945,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -8155,7 +8167,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -8257,16 +8269,6 @@ dependencies = [
|
|||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-cpp"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c88fd925d0333e63ac64e521f5bd79c53019e569ffbbccfeef346a326f459e9"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-css"
|
||||
version = "0.19.0"
|
||||
|
@ -8276,16 +8278,6 @@ dependencies = [
|
|||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-elixir"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a9916f3e1c80b3c8aab8582604e97e8720cb9b893489b347cf999f80f9d469e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-elixir"
|
||||
version = "0.1.0"
|
||||
|
@ -8473,26 +8465,6 @@ dependencies = [
|
|||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-toml"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca517f578a98b23d20780247cc2688407fa81effad5b627a5a364ec3339b53e8"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-typescript"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "079c695c32d39ad089101c66393aeaca30e967fba3486a91f573d2f0e12d290a"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree-sitter-typescript"
|
||||
version = "0.20.2"
|
||||
|
@ -8995,7 +8967,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
|
@ -9029,7 +9001,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
@ -9042,9 +9014,9 @@ checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
|
|||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.31.0"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06a3d1b4a575ffb873679402b2aedb3117555eb65c27b1b86c8a91e574bc2a2a"
|
||||
checksum = "41763f20eafed1399fff1afb466496d3a959f58241436cfdc17e3f5ca954de16"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
|
@ -9266,9 +9238,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wast"
|
||||
version = "62.0.0"
|
||||
version = "62.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7f7ee878019d69436895f019b65f62c33da63595d8e857cbdc87c13ecb29a32"
|
||||
checksum = "b8ae06f09dbe377b889fbd620ff8fa21e1d49d1d9d364983c0cdbf9870cb9f1f"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
"memchr",
|
||||
|
@ -9278,11 +9250,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wat"
|
||||
version = "1.0.68"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "295572bf24aa5b685a971a83ad3e8b6e684aaad8a9be24bc7bf59bed84cc1c08"
|
||||
checksum = "842e15861d203fb4a96d314b0751cdeaf0f6f8b35e8d81d2953af2af5e44e637"
|
||||
dependencies = [
|
||||
"wast 62.0.0",
|
||||
"wast 62.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -9659,9 +9631,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25b5872fa2e10bd067ae946f927e726d7d603eaeb6e02fa6a350e0722d2b8c11"
|
||||
checksum = "8bd122eb777186e60c3fdf765a58ac76e41c582f1f535fbf3314434c6b58f3f7"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
@ -9932,9 +9904,9 @@ dependencies = [
|
|||
"tree-sitter",
|
||||
"tree-sitter-bash",
|
||||
"tree-sitter-c",
|
||||
"tree-sitter-cpp 0.20.0",
|
||||
"tree-sitter-cpp",
|
||||
"tree-sitter-css",
|
||||
"tree-sitter-elixir 0.1.0 (git+https://github.com/elixir-lang/tree-sitter-elixir?rev=a2861e88a730287a60c11ea9299c033c7d076e30)",
|
||||
"tree-sitter-elixir",
|
||||
"tree-sitter-elm",
|
||||
"tree-sitter-embedded-template",
|
||||
"tree-sitter-glsl",
|
||||
|
@ -9952,8 +9924,8 @@ dependencies = [
|
|||
"tree-sitter-rust",
|
||||
"tree-sitter-scheme",
|
||||
"tree-sitter-svelte",
|
||||
"tree-sitter-toml 0.5.1",
|
||||
"tree-sitter-typescript 0.20.2 (git+https://github.com/tree-sitter/tree-sitter-typescript?rev=5d20856f34315b068c41edaee2ac8a100081d259)",
|
||||
"tree-sitter-toml",
|
||||
"tree-sitter-typescript",
|
||||
"tree-sitter-yaml",
|
||||
"unindent",
|
||||
"url",
|
||||
|
@ -9990,7 +9962,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# syntax = docker/dockerfile:1.2
|
||||
|
||||
FROM rust:1.70-bullseye as builder
|
||||
FROM rust:1.71-bullseye as builder
|
||||
WORKDIR app
|
||||
COPY . .
|
||||
|
||||
|
|
|
@ -1,159 +1,179 @@
|
|||
{
|
||||
"suffixes": {
|
||||
"aac": "audio",
|
||||
"bash": "terminal",
|
||||
"bmp": "image",
|
||||
"c": "code",
|
||||
"conf": "settings",
|
||||
"cpp": "code",
|
||||
"cc": "code",
|
||||
"css": "code",
|
||||
"doc": "document",
|
||||
"docx": "document",
|
||||
"eslintrc": "eslint",
|
||||
"eslintrc.js": "eslint",
|
||||
"eslintrc.json": "eslint",
|
||||
"flac": "audio",
|
||||
"fish": "terminal",
|
||||
"gitattributes": "vcs",
|
||||
"gitignore": "vcs",
|
||||
"gitmodules": "vcs",
|
||||
"gif": "image",
|
||||
"go": "code",
|
||||
"h": "code",
|
||||
"handlebars": "code",
|
||||
"hbs": "template",
|
||||
"htm": "template",
|
||||
"html": "template",
|
||||
"svelte": "template",
|
||||
"hpp": "code",
|
||||
"ico": "image",
|
||||
"ini": "settings",
|
||||
"java": "code",
|
||||
"jpeg": "image",
|
||||
"jpg": "image",
|
||||
"js": "code",
|
||||
"json": "storage",
|
||||
"lock": "lock",
|
||||
"log": "log",
|
||||
"md": "document",
|
||||
"mdx": "document",
|
||||
"mp3": "audio",
|
||||
"mp4": "video",
|
||||
"ods": "document",
|
||||
"odp": "document",
|
||||
"odt": "document",
|
||||
"ogg": "video",
|
||||
"pdf": "document",
|
||||
"php": "code",
|
||||
"png": "image",
|
||||
"ppt": "document",
|
||||
"pptx": "document",
|
||||
"prettierrc": "prettier",
|
||||
"prettierignore": "prettier",
|
||||
"ps1": "terminal",
|
||||
"psd": "image",
|
||||
"py": "code",
|
||||
"rb": "code",
|
||||
"rkt": "code",
|
||||
"rs": "rust",
|
||||
"rtf": "document",
|
||||
"scm": "code",
|
||||
"sh": "terminal",
|
||||
"bashrc": "terminal",
|
||||
"bash_profile": "terminal",
|
||||
"bash_aliases": "terminal",
|
||||
"bash_logout": "terminal",
|
||||
"profile": "terminal",
|
||||
"zshrc": "terminal",
|
||||
"zshenv": "terminal",
|
||||
"zsh_profile": "terminal",
|
||||
"zsh_aliases": "terminal",
|
||||
"zsh_histfile": "terminal",
|
||||
"zlogin": "terminal",
|
||||
"sql": "code",
|
||||
"svg": "image",
|
||||
"swift": "code",
|
||||
"tiff": "image",
|
||||
"toml": "toml",
|
||||
"ts": "typescript",
|
||||
"tsx": "code",
|
||||
"txt": "document",
|
||||
"wav": "audio",
|
||||
"webm": "video",
|
||||
"xls": "document",
|
||||
"xlsx": "document",
|
||||
"xml": "template",
|
||||
"yaml": "settings",
|
||||
"yml": "settings",
|
||||
"zsh": "terminal"
|
||||
},
|
||||
"types": {
|
||||
"audio": {
|
||||
"icon": "icons/file_icons/audio.svg"
|
||||
"suffixes": {
|
||||
"aac": "audio",
|
||||
"accdb": "storage",
|
||||
"bak": "backup",
|
||||
"bash": "terminal",
|
||||
"bash_aliases": "terminal",
|
||||
"bash_logout": "terminal",
|
||||
"bash_profile": "terminal",
|
||||
"bashrc": "terminal",
|
||||
"bmp": "image",
|
||||
"c": "code",
|
||||
"cc": "code",
|
||||
"conf": "settings",
|
||||
"cpp": "code",
|
||||
"css": "code",
|
||||
"csv": "storage",
|
||||
"dat": "storage",
|
||||
"db": "storage",
|
||||
"dbf": "storage",
|
||||
"dll": "storage",
|
||||
"doc": "document",
|
||||
"docx": "document",
|
||||
"eslintrc": "eslint",
|
||||
"eslintrc.js": "eslint",
|
||||
"eslintrc.json": "eslint",
|
||||
"fmp": "storage",
|
||||
"fp7": "storage",
|
||||
"flac": "audio",
|
||||
"fish": "terminal",
|
||||
"frm": "storage",
|
||||
"gdb": "storage",
|
||||
"gitattributes": "vcs",
|
||||
"gitignore": "vcs",
|
||||
"gitmodules": "vcs",
|
||||
"gif": "image",
|
||||
"go": "code",
|
||||
"h": "code",
|
||||
"handlebars": "code",
|
||||
"hbs": "template",
|
||||
"htm": "template",
|
||||
"html": "template",
|
||||
"ib": "storage",
|
||||
"ico": "image",
|
||||
"ini": "settings",
|
||||
"java": "code",
|
||||
"jpeg": "image",
|
||||
"jpg": "image",
|
||||
"js": "code",
|
||||
"json": "storage",
|
||||
"ldf": "storage",
|
||||
"lock": "lock",
|
||||
"log": "log",
|
||||
"mdb": "storage",
|
||||
"md": "document",
|
||||
"mdf": "storage",
|
||||
"mdx": "document",
|
||||
"mp3": "audio",
|
||||
"mp4": "video",
|
||||
"myd": "storage",
|
||||
"myi": "storage",
|
||||
"ods": "document",
|
||||
"odp": "document",
|
||||
"odt": "document",
|
||||
"ogg": "video",
|
||||
"pdb": "storage",
|
||||
"pdf": "document",
|
||||
"php": "code",
|
||||
"png": "image",
|
||||
"ppt": "document",
|
||||
"pptx": "document",
|
||||
"prettierignore": "prettier",
|
||||
"prettierrc": "prettier",
|
||||
"profile": "terminal",
|
||||
"ps1": "terminal",
|
||||
"psd": "image",
|
||||
"py": "code",
|
||||
"rb": "code",
|
||||
"rkt": "code",
|
||||
"rs": "rust",
|
||||
"rtf": "document",
|
||||
"sav": "storage",
|
||||
"scm": "code",
|
||||
"sh": "terminal",
|
||||
"sqlite": "storage",
|
||||
"sdf": "storage",
|
||||
"svelte": "template",
|
||||
"svg": "image",
|
||||
"swift": "code",
|
||||
"ts": "typescript",
|
||||
"tsx": "code",
|
||||
"tiff": "image",
|
||||
"toml": "toml",
|
||||
"tsv": "storage",
|
||||
"txt": "document",
|
||||
"wav": "audio",
|
||||
"webm": "video",
|
||||
"xls": "document",
|
||||
"xlsx": "document",
|
||||
"xml": "template",
|
||||
"yaml": "settings",
|
||||
"yml": "settings",
|
||||
"zlogin": "terminal",
|
||||
"zsh": "terminal",
|
||||
"zsh_aliases": "terminal",
|
||||
"zshenv": "terminal",
|
||||
"zsh_histfile": "terminal",
|
||||
"zsh_profile": "terminal",
|
||||
"zshrc": "terminal"
|
||||
},
|
||||
"code": {
|
||||
"icon": "icons/file_icons/code.svg"
|
||||
},
|
||||
"collapsed_chevron": {
|
||||
"icon": "icons/file_icons/chevron_right.svg"
|
||||
},
|
||||
"collapsed_folder": {
|
||||
"icon": "icons/file_icons/folder.svg"
|
||||
},
|
||||
"default": {
|
||||
"icon": "icons/file_icons/file.svg"
|
||||
},
|
||||
"document": {
|
||||
"icon": "icons/file_icons/book.svg"
|
||||
},
|
||||
"eslint": {
|
||||
"icon": "icons/file_icons/eslint.svg"
|
||||
},
|
||||
"expanded_chevron": {
|
||||
"icon": "icons/file_icons/chevron_down.svg"
|
||||
},
|
||||
"expanded_folder": {
|
||||
"icon": "icons/file_icons/folder_open.svg"
|
||||
},
|
||||
"image": {
|
||||
"icon": "icons/file_icons/image.svg"
|
||||
},
|
||||
"lock": {
|
||||
"icon": "icons/file_icons/lock.svg"
|
||||
},
|
||||
"log": {
|
||||
"icon": "icons/file_icons/info.svg"
|
||||
},
|
||||
"prettier": {
|
||||
"icon": "icons/file_icons/prettier.svg"
|
||||
},
|
||||
"rust": {
|
||||
"icon": "icons/file_icons/rust.svg"
|
||||
},
|
||||
"settings": {
|
||||
"icon": "icons/file_icons/settings.svg"
|
||||
},
|
||||
"storage": {
|
||||
"icon": "icons/file_icons/database.svg"
|
||||
},
|
||||
"template": {
|
||||
"icon": "icons/file_icons/html.svg"
|
||||
},
|
||||
"terminal": {
|
||||
"icon": "icons/file_icons/terminal.svg"
|
||||
},
|
||||
"toml": {
|
||||
"icon": "icons/file_icons/toml.svg"
|
||||
},
|
||||
"typescript": {
|
||||
"icon": "icons/file_icons/typescript.svg"
|
||||
},
|
||||
"vcs": {
|
||||
"icon": "icons/file_icons/git.svg"
|
||||
},
|
||||
"video": {
|
||||
"icon": "icons/file_icons/video.svg"
|
||||
"types": {
|
||||
"audio": {
|
||||
"icon": "icons/file_icons/audio.svg"
|
||||
},
|
||||
"code": {
|
||||
"icon": "icons/file_icons/code.svg"
|
||||
},
|
||||
"collapsed_chevron": {
|
||||
"icon": "icons/file_icons/chevron_right.svg"
|
||||
},
|
||||
"collapsed_folder": {
|
||||
"icon": "icons/file_icons/folder.svg"
|
||||
},
|
||||
"default": {
|
||||
"icon": "icons/file_icons/file.svg"
|
||||
},
|
||||
"document": {
|
||||
"icon": "icons/file_icons/book.svg"
|
||||
},
|
||||
"eslint": {
|
||||
"icon": "icons/file_icons/eslint.svg"
|
||||
},
|
||||
"expanded_chevron": {
|
||||
"icon": "icons/file_icons/chevron_down.svg"
|
||||
},
|
||||
"expanded_folder": {
|
||||
"icon": "icons/file_icons/folder_open.svg"
|
||||
},
|
||||
"image": {
|
||||
"icon": "icons/file_icons/image.svg"
|
||||
},
|
||||
"lock": {
|
||||
"icon": "icons/file_icons/lock.svg"
|
||||
},
|
||||
"log": {
|
||||
"icon": "icons/file_icons/info.svg"
|
||||
},
|
||||
"prettier": {
|
||||
"icon": "icons/file_icons/prettier.svg"
|
||||
},
|
||||
"rust": {
|
||||
"icon": "icons/file_icons/rust.svg"
|
||||
},
|
||||
"settings": {
|
||||
"icon": "icons/file_icons/settings.svg"
|
||||
},
|
||||
"storage": {
|
||||
"icon": "icons/file_icons/database.svg"
|
||||
},
|
||||
"template": {
|
||||
"icon": "icons/file_icons/html.svg"
|
||||
},
|
||||
"terminal": {
|
||||
"icon": "icons/file_icons/terminal.svg"
|
||||
},
|
||||
"toml": {
|
||||
"icon": "icons/file_icons/toml.svg"
|
||||
},
|
||||
"typescript": {
|
||||
"icon": "icons/file_icons/typescript.svg"
|
||||
},
|
||||
"vcs": {
|
||||
"icon": "icons/file_icons/git.svg"
|
||||
},
|
||||
"video": {
|
||||
"icon": "icons/file_icons/video.svg"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -227,12 +227,26 @@
|
|||
"alt-enter": "search::SelectAllMatches"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "BufferSearchBar > Editor",
|
||||
"bindings": {
|
||||
"up": "search::PreviousHistoryQuery",
|
||||
"down": "search::NextHistoryQuery"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "ProjectSearchBar",
|
||||
"bindings": {
|
||||
"escape": "project_search::ToggleFocus"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "ProjectSearchBar > Editor",
|
||||
"bindings": {
|
||||
"up": "search::PreviousHistoryQuery",
|
||||
"down": "search::NextHistoryQuery"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "ProjectSearchView",
|
||||
"bindings": {
|
||||
|
|
|
@ -1637,6 +1637,7 @@ impl ConversationEditor {
|
|||
let mut editor = Editor::for_buffer(conversation.read(cx).buffer.clone(), None, cx);
|
||||
editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
|
||||
editor.set_show_gutter(false, cx);
|
||||
editor.set_show_wrap_guides(false, cx);
|
||||
editor
|
||||
});
|
||||
|
||||
|
|
|
@ -543,6 +543,7 @@ pub struct Editor {
|
|||
show_local_selections: bool,
|
||||
mode: EditorMode,
|
||||
show_gutter: bool,
|
||||
show_wrap_guides: Option<bool>,
|
||||
placeholder_text: Option<Arc<str>>,
|
||||
highlighted_rows: Option<Range<u32>>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
|
@ -1375,6 +1376,7 @@ impl Editor {
|
|||
show_local_selections: true,
|
||||
mode,
|
||||
show_gutter: mode == EditorMode::Full,
|
||||
show_wrap_guides: None,
|
||||
placeholder_text: None,
|
||||
highlighted_rows: None,
|
||||
background_highlights: Default::default(),
|
||||
|
@ -4219,7 +4221,7 @@ impl Editor {
|
|||
_: &SortLinesCaseSensitive,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.manipulate_lines(cx, |text| text.sort())
|
||||
self.manipulate_lines(cx, |lines| lines.sort())
|
||||
}
|
||||
|
||||
pub fn sort_lines_case_insensitive(
|
||||
|
@ -4227,7 +4229,7 @@ impl Editor {
|
|||
_: &SortLinesCaseInsensitive,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.manipulate_lines(cx, |text| text.sort_by_key(|line| line.to_lowercase()))
|
||||
self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase()))
|
||||
}
|
||||
|
||||
pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext<Self>) {
|
||||
|
@ -4265,19 +4267,19 @@ impl Editor {
|
|||
let text = buffer
|
||||
.text_for_range(start_point..end_point)
|
||||
.collect::<String>();
|
||||
let mut text = text.split("\n").collect_vec();
|
||||
let mut lines = text.split("\n").collect_vec();
|
||||
|
||||
let text_len = text.len();
|
||||
callback(&mut text);
|
||||
let lines_len = lines.len();
|
||||
callback(&mut lines);
|
||||
|
||||
// This is a current limitation with selections.
|
||||
// If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections.
|
||||
debug_assert!(
|
||||
text.len() == text_len,
|
||||
lines.len() == lines_len,
|
||||
"callback should not change the number of lines"
|
||||
);
|
||||
|
||||
edits.push((start_point..end_point, text.join("\n")));
|
||||
edits.push((start_point..end_point, lines.join("\n")));
|
||||
let start_anchor = buffer.anchor_after(start_point);
|
||||
let end_anchor = buffer.anchor_before(end_point);
|
||||
|
||||
|
@ -7187,6 +7189,10 @@ impl Editor {
|
|||
pub fn wrap_guides(&self, cx: &AppContext) -> SmallVec<[(usize, bool); 2]> {
|
||||
let mut wrap_guides = smallvec::smallvec![];
|
||||
|
||||
if self.show_wrap_guides == Some(false) {
|
||||
return wrap_guides;
|
||||
}
|
||||
|
||||
let settings = self.buffer.read(cx).settings_at(0, cx);
|
||||
if settings.show_wrap_guides {
|
||||
if let SoftWrap::Column(soft_wrap) = self.soft_wrap_mode(cx) {
|
||||
|
@ -7244,6 +7250,11 @@ impl Editor {
|
|||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn set_show_wrap_guides(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
|
||||
self.show_wrap_guides = Some(show_gutter);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn reveal_in_finder(&mut self, _: &RevealInFinder, cx: &mut ViewContext<Self>) {
|
||||
if let Some(buffer) = self.buffer().read(cx).as_singleton() {
|
||||
if let Some(file) = buffer.read(cx).file().and_then(|f| f.as_local()) {
|
||||
|
|
|
@ -546,8 +546,20 @@ impl EditorElement {
|
|||
});
|
||||
}
|
||||
|
||||
let scroll_left =
|
||||
layout.position_map.snapshot.scroll_position().x() * layout.position_map.em_width;
|
||||
|
||||
for (wrap_position, active) in layout.wrap_guides.iter() {
|
||||
let x = text_bounds.origin_x() + wrap_position + layout.position_map.em_width / 2.;
|
||||
let x =
|
||||
(text_bounds.origin_x() + wrap_position + layout.position_map.em_width / 2.)
|
||||
- scroll_left;
|
||||
|
||||
if x < text_bounds.origin_x()
|
||||
|| (layout.show_scrollbars && x > self.scrollbar_left(&bounds))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let color = if *active {
|
||||
self.style.active_wrap_guide
|
||||
} else {
|
||||
|
@ -1036,6 +1048,10 @@ impl EditorElement {
|
|||
scene.pop_layer();
|
||||
}
|
||||
|
||||
fn scrollbar_left(&self, bounds: &RectF) -> f32 {
|
||||
bounds.max_x() - self.style.theme.scrollbar.width
|
||||
}
|
||||
|
||||
fn paint_scrollbar(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
|
@ -1054,7 +1070,7 @@ impl EditorElement {
|
|||
let top = bounds.min_y();
|
||||
let bottom = bounds.max_y();
|
||||
let right = bounds.max_x();
|
||||
let left = right - style.width;
|
||||
let left = self.scrollbar_left(&bounds);
|
||||
let row_range = &layout.scrollbar_row_range;
|
||||
let max_row = layout.max_row as f32 + (row_range.end - row_range.start);
|
||||
|
||||
|
|
|
@ -1128,6 +1128,12 @@ impl AppContext {
|
|||
self.keystroke_matcher.clear_bindings();
|
||||
}
|
||||
|
||||
pub fn binding_for_action(&self, action: &dyn Action) -> Option<&Binding> {
|
||||
self.keystroke_matcher
|
||||
.bindings_for_action(action.id())
|
||||
.find(|binding| binding.action().eq(action))
|
||||
}
|
||||
|
||||
pub fn default_global<T: 'static + Default>(&mut self) -> &T {
|
||||
let type_id = TypeId::of::<T>();
|
||||
self.update(|this| {
|
||||
|
|
|
@ -58,11 +58,14 @@ fn build_bridge(swift_target: &SwiftTarget) {
|
|||
"cargo:rerun-if-changed={}/Package.resolved",
|
||||
SWIFT_PACKAGE_NAME
|
||||
);
|
||||
|
||||
let swift_package_root = swift_package_root();
|
||||
let swift_target_folder = swift_target_folder();
|
||||
if !Command::new("swift")
|
||||
.arg("build")
|
||||
.args(["--configuration", &env::var("PROFILE").unwrap()])
|
||||
.args(["--triple", &swift_target.target.triple])
|
||||
.args(["--build-path".into(), swift_target_folder])
|
||||
.current_dir(&swift_package_root)
|
||||
.status()
|
||||
.unwrap()
|
||||
|
@ -128,6 +131,12 @@ fn swift_package_root() -> PathBuf {
|
|||
env::current_dir().unwrap().join(SWIFT_PACKAGE_NAME)
|
||||
}
|
||||
|
||||
fn swift_target_folder() -> PathBuf {
|
||||
env::current_dir()
|
||||
.unwrap()
|
||||
.join(format!("../../target/{SWIFT_PACKAGE_NAME}"))
|
||||
}
|
||||
|
||||
fn copy_dir(source: &Path, destination: &Path) {
|
||||
assert!(
|
||||
Command::new("rm")
|
||||
|
@ -155,8 +164,7 @@ fn copy_dir(source: &Path, destination: &Path) {
|
|||
|
||||
impl SwiftTarget {
|
||||
fn out_dir_path(&self) -> PathBuf {
|
||||
swift_package_root()
|
||||
.join(".build")
|
||||
swift_target_folder()
|
||||
.join(&self.target.unversioned_triple)
|
||||
.join(env::var("PROFILE").unwrap())
|
||||
}
|
||||
|
|
|
@ -115,6 +115,7 @@ actions!(
|
|||
[
|
||||
ExpandSelectedEntry,
|
||||
CollapseSelectedEntry,
|
||||
CollapseAllEntries,
|
||||
NewDirectory,
|
||||
NewFile,
|
||||
Copy,
|
||||
|
@ -140,6 +141,7 @@ pub fn init(assets: impl AssetSource, cx: &mut AppContext) {
|
|||
file_associations::init(assets, cx);
|
||||
cx.add_action(ProjectPanel::expand_selected_entry);
|
||||
cx.add_action(ProjectPanel::collapse_selected_entry);
|
||||
cx.add_action(ProjectPanel::collapse_all_entries);
|
||||
cx.add_action(ProjectPanel::select_prev);
|
||||
cx.add_action(ProjectPanel::select_next);
|
||||
cx.add_action(ProjectPanel::new_file);
|
||||
|
@ -514,6 +516,12 @@ impl ProjectPanel {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn collapse_all_entries(&mut self, _: &CollapseAllEntries, cx: &mut ViewContext<Self>) {
|
||||
self.expanded_dir_ids.clear();
|
||||
self.update_visible_entries(None, cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn toggle_expanded(&mut self, entry_id: ProjectEntryId, cx: &mut ViewContext<Self>) {
|
||||
if let Some(worktree_id) = self.project.read(cx).worktree_id_for_entry(entry_id, cx) {
|
||||
if let Some(expanded_dir_ids) = self.expanded_dir_ids.get_mut(&worktree_id) {
|
||||
|
@ -2654,6 +2662,63 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_collapse_all_entries(cx: &mut gpui::TestAppContext) {
|
||||
init_test_with_editor(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
"/project_root",
|
||||
json!({
|
||||
"dir_1": {
|
||||
"nested_dir": {
|
||||
"file_a.py": "# File contents",
|
||||
"file_b.py": "# File contents",
|
||||
"file_c.py": "# File contents",
|
||||
},
|
||||
"file_1.py": "# File contents",
|
||||
"file_2.py": "# File contents",
|
||||
"file_3.py": "# File contents",
|
||||
},
|
||||
"dir_2": {
|
||||
"file_1.py": "# File contents",
|
||||
"file_2.py": "# File contents",
|
||||
"file_3.py": "# File contents",
|
||||
}
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs.clone(), ["/project_root".as_ref()], cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
|
||||
|
||||
panel.update(cx, |panel, cx| {
|
||||
panel.collapse_all_entries(&CollapseAllEntries, cx)
|
||||
});
|
||||
cx.foreground().run_until_parked();
|
||||
assert_eq!(
|
||||
visible_entries_as_strings(&panel, 0..10, cx),
|
||||
&["v project_root", " > dir_1", " > dir_2",]
|
||||
);
|
||||
|
||||
// Open dir_1 and make sure nested_dir was collapsed when running collapse_all_entries
|
||||
toggle_expand_dir(&panel, "project_root/dir_1", cx);
|
||||
cx.foreground().run_until_parked();
|
||||
assert_eq!(
|
||||
visible_entries_as_strings(&panel, 0..10, cx),
|
||||
&[
|
||||
"v project_root",
|
||||
" v dir_1 <== selected",
|
||||
" > nested_dir",
|
||||
" file_1.py",
|
||||
" file_2.py",
|
||||
" file_3.py",
|
||||
" > dir_2",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
fn toggle_expand_dir(
|
||||
panel: &ViewHandle<ProjectPanel>,
|
||||
path: impl AsRef<Path>,
|
||||
|
@ -2854,3 +2919,4 @@ mod tests {
|
|||
});
|
||||
}
|
||||
}
|
||||
// TODO - a workspace command?
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
SearchOptions, SelectAllMatches, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive,
|
||||
ToggleRegex, ToggleWholeWord,
|
||||
NextHistoryQuery, PreviousHistoryQuery, SearchHistory, SearchOptions, SelectAllMatches,
|
||||
SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleRegex, ToggleWholeWord,
|
||||
};
|
||||
use collections::HashMap;
|
||||
use editor::Editor;
|
||||
|
@ -46,6 +46,8 @@ pub fn init(cx: &mut AppContext) {
|
|||
cx.add_action(BufferSearchBar::select_prev_match_on_pane);
|
||||
cx.add_action(BufferSearchBar::select_all_matches_on_pane);
|
||||
cx.add_action(BufferSearchBar::handle_editor_cancel);
|
||||
cx.add_action(BufferSearchBar::next_history_query);
|
||||
cx.add_action(BufferSearchBar::previous_history_query);
|
||||
add_toggle_option_action::<ToggleCaseSensitive>(SearchOptions::CASE_SENSITIVE, cx);
|
||||
add_toggle_option_action::<ToggleWholeWord>(SearchOptions::WHOLE_WORD, cx);
|
||||
add_toggle_option_action::<ToggleRegex>(SearchOptions::REGEX, cx);
|
||||
|
@ -65,7 +67,7 @@ fn add_toggle_option_action<A: Action>(option: SearchOptions, cx: &mut AppContex
|
|||
}
|
||||
|
||||
pub struct BufferSearchBar {
|
||||
pub query_editor: ViewHandle<Editor>,
|
||||
query_editor: ViewHandle<Editor>,
|
||||
active_searchable_item: Option<Box<dyn SearchableItemHandle>>,
|
||||
active_match_index: Option<usize>,
|
||||
active_searchable_item_subscription: Option<Subscription>,
|
||||
|
@ -76,6 +78,7 @@ pub struct BufferSearchBar {
|
|||
default_options: SearchOptions,
|
||||
query_contains_error: bool,
|
||||
dismissed: bool,
|
||||
search_history: SearchHistory,
|
||||
}
|
||||
|
||||
impl Entity for BufferSearchBar {
|
||||
|
@ -106,6 +109,48 @@ impl View for BufferSearchBar {
|
|||
.map(|active_searchable_item| active_searchable_item.supported_options())
|
||||
.unwrap_or_default();
|
||||
|
||||
let previous_query_keystrokes =
|
||||
cx.binding_for_action(&PreviousHistoryQuery {})
|
||||
.map(|binding| {
|
||||
binding
|
||||
.keystrokes()
|
||||
.iter()
|
||||
.map(|k| k.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
let next_query_keystrokes = cx.binding_for_action(&NextHistoryQuery {}).map(|binding| {
|
||||
binding
|
||||
.keystrokes()
|
||||
.iter()
|
||||
.map(|k| k.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
let new_placeholder_text = match (previous_query_keystrokes, next_query_keystrokes) {
|
||||
(Some(previous_query_keystrokes), Some(next_query_keystrokes)) => {
|
||||
format!(
|
||||
"Search ({}/{} for previous/next query)",
|
||||
previous_query_keystrokes.join(" "),
|
||||
next_query_keystrokes.join(" ")
|
||||
)
|
||||
}
|
||||
(None, Some(next_query_keystrokes)) => {
|
||||
format!(
|
||||
"Search ({} for next query)",
|
||||
next_query_keystrokes.join(" ")
|
||||
)
|
||||
}
|
||||
(Some(previous_query_keystrokes), None) => {
|
||||
format!(
|
||||
"Search ({} for previous query)",
|
||||
previous_query_keystrokes.join(" ")
|
||||
)
|
||||
}
|
||||
(None, None) => String::new(),
|
||||
};
|
||||
self.query_editor.update(cx, |editor, cx| {
|
||||
editor.set_placeholder_text(new_placeholder_text, cx);
|
||||
});
|
||||
|
||||
Flex::row()
|
||||
.with_child(
|
||||
Flex::row()
|
||||
|
@ -258,6 +303,7 @@ impl BufferSearchBar {
|
|||
pending_search: None,
|
||||
query_contains_error: false,
|
||||
dismissed: true,
|
||||
search_history: SearchHistory::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -341,7 +387,7 @@ impl BufferSearchBar {
|
|||
cx: &mut ViewContext<Self>,
|
||||
) -> oneshot::Receiver<()> {
|
||||
let options = options.unwrap_or(self.default_options);
|
||||
if query != self.query_editor.read(cx).text(cx) || self.search_options != options {
|
||||
if query != self.query(cx) || self.search_options != options {
|
||||
self.query_editor.update(cx, |query_editor, cx| {
|
||||
query_editor.buffer().update(cx, |query_buffer, cx| {
|
||||
let len = query_buffer.len(cx);
|
||||
|
@ -674,7 +720,7 @@ impl BufferSearchBar {
|
|||
|
||||
fn update_matches(&mut self, cx: &mut ViewContext<Self>) -> oneshot::Receiver<()> {
|
||||
let (done_tx, done_rx) = oneshot::channel();
|
||||
let query = self.query_editor.read(cx).text(cx);
|
||||
let query = self.query(cx);
|
||||
self.pending_search.take();
|
||||
if let Some(active_searchable_item) = self.active_searchable_item.as_ref() {
|
||||
if query.is_empty() {
|
||||
|
@ -707,6 +753,7 @@ impl BufferSearchBar {
|
|||
)
|
||||
};
|
||||
|
||||
let query_text = query.as_str().to_string();
|
||||
let matches = active_searchable_item.find_matches(query, cx);
|
||||
|
||||
let active_searchable_item = active_searchable_item.downgrade();
|
||||
|
@ -720,6 +767,7 @@ impl BufferSearchBar {
|
|||
.insert(active_searchable_item.downgrade(), matches);
|
||||
|
||||
this.update_match_index(cx);
|
||||
this.search_history.add(query_text);
|
||||
if !this.dismissed {
|
||||
let matches = this
|
||||
.searchable_items_with_matches
|
||||
|
@ -753,6 +801,28 @@ impl BufferSearchBar {
|
|||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
fn next_history_query(&mut self, _: &NextHistoryQuery, cx: &mut ViewContext<Self>) {
|
||||
if let Some(new_query) = self.search_history.next().map(str::to_string) {
|
||||
let _ = self.search(&new_query, Some(self.search_options), cx);
|
||||
} else {
|
||||
self.search_history.reset_selection();
|
||||
let _ = self.search("", Some(self.search_options), cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn previous_history_query(&mut self, _: &PreviousHistoryQuery, cx: &mut ViewContext<Self>) {
|
||||
if self.query(cx).is_empty() {
|
||||
if let Some(new_query) = self.search_history.current().map(str::to_string) {
|
||||
let _ = self.search(&new_query, Some(self.search_options), cx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(new_query) = self.search_history.previous().map(str::to_string) {
|
||||
let _ = self.search(&new_query, Some(self.search_options), cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1333,4 +1403,154 @@ mod tests {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_search_query_history(cx: &mut TestAppContext) {
|
||||
crate::project_search::tests::init_test(cx);
|
||||
|
||||
let buffer_text = r#"
|
||||
A regular expression (shortened as regex or regexp;[1] also referred to as
|
||||
rational expression[2][3]) is a sequence of characters that specifies a search
|
||||
pattern in text. Usually such patterns are used by string-searching algorithms
|
||||
for "find" or "find and replace" operations on strings, or for input validation.
|
||||
"#
|
||||
.unindent();
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx));
|
||||
let (window_id, _root_view) = cx.add_window(|_| EmptyView);
|
||||
|
||||
let editor = cx.add_view(window_id, |cx| Editor::for_buffer(buffer.clone(), None, cx));
|
||||
|
||||
let search_bar = cx.add_view(window_id, |cx| {
|
||||
let mut search_bar = BufferSearchBar::new(cx);
|
||||
search_bar.set_active_pane_item(Some(&editor), cx);
|
||||
search_bar.show(cx);
|
||||
search_bar
|
||||
});
|
||||
|
||||
// Add 3 search items into the history.
|
||||
search_bar
|
||||
.update(cx, |search_bar, cx| search_bar.search("a", None, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
search_bar
|
||||
.update(cx, |search_bar, cx| search_bar.search("b", None, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
search_bar
|
||||
.update(cx, |search_bar, cx| {
|
||||
search_bar.search("c", Some(SearchOptions::CASE_SENSITIVE), cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
// Ensure that the latest search is active.
|
||||
search_bar.read_with(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.query(cx), "c");
|
||||
assert_eq!(search_bar.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
|
||||
// Next history query after the latest should set the query to the empty string.
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.next_history_query(&NextHistoryQuery, cx);
|
||||
});
|
||||
search_bar.read_with(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.query(cx), "");
|
||||
assert_eq!(search_bar.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.next_history_query(&NextHistoryQuery, cx);
|
||||
});
|
||||
search_bar.read_with(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.query(cx), "");
|
||||
assert_eq!(search_bar.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
|
||||
// First previous query for empty current query should set the query to the latest.
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.previous_history_query(&PreviousHistoryQuery, cx);
|
||||
});
|
||||
search_bar.read_with(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.query(cx), "c");
|
||||
assert_eq!(search_bar.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
|
||||
// Further previous items should go over the history in reverse order.
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.previous_history_query(&PreviousHistoryQuery, cx);
|
||||
});
|
||||
search_bar.read_with(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.query(cx), "b");
|
||||
assert_eq!(search_bar.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
|
||||
// Previous items should never go behind the first history item.
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.previous_history_query(&PreviousHistoryQuery, cx);
|
||||
});
|
||||
search_bar.read_with(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.query(cx), "a");
|
||||
assert_eq!(search_bar.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.previous_history_query(&PreviousHistoryQuery, cx);
|
||||
});
|
||||
search_bar.read_with(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.query(cx), "a");
|
||||
assert_eq!(search_bar.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
|
||||
// Next items should go over the history in the original order.
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.next_history_query(&NextHistoryQuery, cx);
|
||||
});
|
||||
search_bar.read_with(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.query(cx), "b");
|
||||
assert_eq!(search_bar.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
|
||||
search_bar
|
||||
.update(cx, |search_bar, cx| search_bar.search("ba", None, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
search_bar.read_with(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.query(cx), "ba");
|
||||
assert_eq!(search_bar.search_options, SearchOptions::NONE);
|
||||
});
|
||||
|
||||
// New search input should add another entry to history and move the selection to the end of the history.
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.previous_history_query(&PreviousHistoryQuery, cx);
|
||||
});
|
||||
search_bar.read_with(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.query(cx), "c");
|
||||
assert_eq!(search_bar.search_options, SearchOptions::NONE);
|
||||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.previous_history_query(&PreviousHistoryQuery, cx);
|
||||
});
|
||||
search_bar.read_with(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.query(cx), "b");
|
||||
assert_eq!(search_bar.search_options, SearchOptions::NONE);
|
||||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.next_history_query(&NextHistoryQuery, cx);
|
||||
});
|
||||
search_bar.read_with(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.query(cx), "c");
|
||||
assert_eq!(search_bar.search_options, SearchOptions::NONE);
|
||||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.next_history_query(&NextHistoryQuery, cx);
|
||||
});
|
||||
search_bar.read_with(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.query(cx), "ba");
|
||||
assert_eq!(search_bar.search_options, SearchOptions::NONE);
|
||||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.next_history_query(&NextHistoryQuery, cx);
|
||||
});
|
||||
search_bar.read_with(cx, |search_bar, cx| {
|
||||
assert_eq!(search_bar.query(cx), "");
|
||||
assert_eq!(search_bar.search_options, SearchOptions::NONE);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
SearchOptions, SelectNextMatch, SelectPrevMatch, ToggleCaseSensitive, ToggleRegex,
|
||||
ToggleWholeWord,
|
||||
NextHistoryQuery, PreviousHistoryQuery, SearchHistory, SearchOptions, SelectNextMatch,
|
||||
SelectPrevMatch, ToggleCaseSensitive, ToggleRegex, ToggleWholeWord,
|
||||
};
|
||||
use anyhow::Context;
|
||||
use collections::HashMap;
|
||||
|
@ -56,6 +56,8 @@ pub fn init(cx: &mut AppContext) {
|
|||
cx.add_action(ProjectSearchBar::search_in_new);
|
||||
cx.add_action(ProjectSearchBar::select_next_match);
|
||||
cx.add_action(ProjectSearchBar::select_prev_match);
|
||||
cx.add_action(ProjectSearchBar::next_history_query);
|
||||
cx.add_action(ProjectSearchBar::previous_history_query);
|
||||
cx.capture_action(ProjectSearchBar::tab);
|
||||
cx.capture_action(ProjectSearchBar::tab_previous);
|
||||
add_toggle_option_action::<ToggleCaseSensitive>(SearchOptions::CASE_SENSITIVE, cx);
|
||||
|
@ -83,6 +85,7 @@ struct ProjectSearch {
|
|||
match_ranges: Vec<Range<Anchor>>,
|
||||
active_query: Option<SearchQuery>,
|
||||
search_id: usize,
|
||||
search_history: SearchHistory,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
@ -131,6 +134,7 @@ impl ProjectSearch {
|
|||
match_ranges: Default::default(),
|
||||
active_query: None,
|
||||
search_id: 0,
|
||||
search_history: SearchHistory::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,6 +148,7 @@ impl ProjectSearch {
|
|||
match_ranges: self.match_ranges.clone(),
|
||||
active_query: self.active_query.clone(),
|
||||
search_id: self.search_id,
|
||||
search_history: self.search_history.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -152,6 +157,7 @@ impl ProjectSearch {
|
|||
.project
|
||||
.update(cx, |project, cx| project.search(query.clone(), cx));
|
||||
self.search_id += 1;
|
||||
self.search_history.add(query.as_str().to_string());
|
||||
self.active_query = Some(query);
|
||||
self.match_ranges.clear();
|
||||
self.pending_search = Some(cx.spawn_weak(|this, mut cx| async move {
|
||||
|
@ -202,6 +208,7 @@ impl ProjectSearch {
|
|||
});
|
||||
self.search_id += 1;
|
||||
self.match_ranges.clear();
|
||||
self.search_history.add(query.as_str().to_string());
|
||||
self.pending_search = Some(cx.spawn(|this, mut cx| async move {
|
||||
let results = search?.await.log_err()?;
|
||||
|
||||
|
@ -278,6 +285,49 @@ impl View for ProjectSearchView {
|
|||
Cow::Borrowed("No results")
|
||||
};
|
||||
|
||||
let previous_query_keystrokes =
|
||||
cx.binding_for_action(&PreviousHistoryQuery {})
|
||||
.map(|binding| {
|
||||
binding
|
||||
.keystrokes()
|
||||
.iter()
|
||||
.map(|k| k.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
let next_query_keystrokes =
|
||||
cx.binding_for_action(&NextHistoryQuery {}).map(|binding| {
|
||||
binding
|
||||
.keystrokes()
|
||||
.iter()
|
||||
.map(|k| k.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
let new_placeholder_text = match (previous_query_keystrokes, next_query_keystrokes) {
|
||||
(Some(previous_query_keystrokes), Some(next_query_keystrokes)) => {
|
||||
format!(
|
||||
"Search ({}/{} for previous/next query)",
|
||||
previous_query_keystrokes.join(" "),
|
||||
next_query_keystrokes.join(" ")
|
||||
)
|
||||
}
|
||||
(None, Some(next_query_keystrokes)) => {
|
||||
format!(
|
||||
"Search ({} for next query)",
|
||||
next_query_keystrokes.join(" ")
|
||||
)
|
||||
}
|
||||
(Some(previous_query_keystrokes), None) => {
|
||||
format!(
|
||||
"Search ({} for previous query)",
|
||||
previous_query_keystrokes.join(" ")
|
||||
)
|
||||
}
|
||||
(None, None) => String::new(),
|
||||
};
|
||||
self.query_editor.update(cx, |editor, cx| {
|
||||
editor.set_placeholder_text(new_placeholder_text, cx);
|
||||
});
|
||||
|
||||
MouseEventHandler::<Status, _>::new(0, cx, |_, _| {
|
||||
Label::new(text, theme.search.results_status.clone())
|
||||
.aligned()
|
||||
|
@ -1152,6 +1202,47 @@ impl ProjectSearchBar {
|
|||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn next_history_query(&mut self, _: &NextHistoryQuery, cx: &mut ViewContext<Self>) {
|
||||
if let Some(search_view) = self.active_project_search.as_ref() {
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
let new_query = search_view.model.update(cx, |model, _| {
|
||||
if let Some(new_query) = model.search_history.next().map(str::to_string) {
|
||||
new_query
|
||||
} else {
|
||||
model.search_history.reset_selection();
|
||||
String::new()
|
||||
}
|
||||
});
|
||||
search_view.set_query(&new_query, cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn previous_history_query(&mut self, _: &PreviousHistoryQuery, cx: &mut ViewContext<Self>) {
|
||||
if let Some(search_view) = self.active_project_search.as_ref() {
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
if search_view.query_editor.read(cx).text(cx).is_empty() {
|
||||
if let Some(new_query) = search_view
|
||||
.model
|
||||
.read(cx)
|
||||
.search_history
|
||||
.current()
|
||||
.map(str::to_string)
|
||||
{
|
||||
search_view.set_query(&new_query, cx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(new_query) = search_view.model.update(cx, |model, _| {
|
||||
model.search_history.previous().map(str::to_string)
|
||||
}) {
|
||||
search_view.set_query(&new_query, cx);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for ProjectSearchBar {
|
||||
|
@ -1333,6 +1424,7 @@ pub mod tests {
|
|||
use editor::DisplayPoint;
|
||||
use gpui::{color::Color, executor::Deterministic, TestAppContext};
|
||||
use project::FakeFs;
|
||||
use semantic_index::semantic_index_settings::SemanticIndexSettings;
|
||||
use serde_json::json;
|
||||
use settings::SettingsStore;
|
||||
use std::sync::Arc;
|
||||
|
@ -1758,6 +1850,192 @@ pub mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_search_query_history(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
"one.rs": "const ONE: usize = 1;",
|
||||
"two.rs": "const TWO: usize = one::ONE + one::ONE;",
|
||||
"three.rs": "const THREE: usize = one::ONE + two::TWO;",
|
||||
"four.rs": "const FOUR: usize = one::ONE + three::THREE;",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx)
|
||||
});
|
||||
|
||||
let search_view = cx.read(|cx| {
|
||||
workspace
|
||||
.read(cx)
|
||||
.active_pane()
|
||||
.read(cx)
|
||||
.active_item()
|
||||
.and_then(|item| item.downcast::<ProjectSearchView>())
|
||||
.expect("Search view expected to appear after new search event trigger")
|
||||
});
|
||||
|
||||
let search_bar = cx.add_view(window_id, |cx| {
|
||||
let mut search_bar = ProjectSearchBar::new();
|
||||
search_bar.set_active_pane_item(Some(&search_view), cx);
|
||||
// search_bar.show(cx);
|
||||
search_bar
|
||||
});
|
||||
|
||||
// Add 3 search items into the history + another unsubmitted one.
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
search_view.search_options = SearchOptions::CASE_SENSITIVE;
|
||||
search_view
|
||||
.query_editor
|
||||
.update(cx, |query_editor, cx| query_editor.set_text("ONE", cx));
|
||||
search_view.search(cx);
|
||||
});
|
||||
cx.foreground().run_until_parked();
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
search_view
|
||||
.query_editor
|
||||
.update(cx, |query_editor, cx| query_editor.set_text("TWO", cx));
|
||||
search_view.search(cx);
|
||||
});
|
||||
cx.foreground().run_until_parked();
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
search_view
|
||||
.query_editor
|
||||
.update(cx, |query_editor, cx| query_editor.set_text("THREE", cx));
|
||||
search_view.search(cx);
|
||||
});
|
||||
cx.foreground().run_until_parked();
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
search_view.query_editor.update(cx, |query_editor, cx| {
|
||||
query_editor.set_text("JUST_TEXT_INPUT", cx)
|
||||
});
|
||||
});
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
// Ensure that the latest input with search settings is active.
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert_eq!(
|
||||
search_view.query_editor.read(cx).text(cx),
|
||||
"JUST_TEXT_INPUT"
|
||||
);
|
||||
assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
|
||||
// Next history query after the latest should set the query to the empty string.
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.next_history_query(&NextHistoryQuery, cx);
|
||||
});
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert_eq!(search_view.query_editor.read(cx).text(cx), "");
|
||||
assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.next_history_query(&NextHistoryQuery, cx);
|
||||
});
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert_eq!(search_view.query_editor.read(cx).text(cx), "");
|
||||
assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
|
||||
// First previous query for empty current query should set the query to the latest submitted one.
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.previous_history_query(&PreviousHistoryQuery, cx);
|
||||
});
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert_eq!(search_view.query_editor.read(cx).text(cx), "THREE");
|
||||
assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
|
||||
// Further previous items should go over the history in reverse order.
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.previous_history_query(&PreviousHistoryQuery, cx);
|
||||
});
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO");
|
||||
assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
|
||||
// Previous items should never go behind the first history item.
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.previous_history_query(&PreviousHistoryQuery, cx);
|
||||
});
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert_eq!(search_view.query_editor.read(cx).text(cx), "ONE");
|
||||
assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.previous_history_query(&PreviousHistoryQuery, cx);
|
||||
});
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert_eq!(search_view.query_editor.read(cx).text(cx), "ONE");
|
||||
assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
|
||||
// Next items should go over the history in the original order.
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.next_history_query(&NextHistoryQuery, cx);
|
||||
});
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO");
|
||||
assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
search_view
|
||||
.query_editor
|
||||
.update(cx, |query_editor, cx| query_editor.set_text("TWO_NEW", cx));
|
||||
search_view.search(cx);
|
||||
});
|
||||
cx.foreground().run_until_parked();
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO_NEW");
|
||||
assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
|
||||
// New search input should add another entry to history and move the selection to the end of the history.
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.previous_history_query(&PreviousHistoryQuery, cx);
|
||||
});
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert_eq!(search_view.query_editor.read(cx).text(cx), "THREE");
|
||||
assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.previous_history_query(&PreviousHistoryQuery, cx);
|
||||
});
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO");
|
||||
assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.next_history_query(&NextHistoryQuery, cx);
|
||||
});
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert_eq!(search_view.query_editor.read(cx).text(cx), "THREE");
|
||||
assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.next_history_query(&NextHistoryQuery, cx);
|
||||
});
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO_NEW");
|
||||
assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.next_history_query(&NextHistoryQuery, cx);
|
||||
});
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert_eq!(search_view.query_editor.read(cx).text(cx), "");
|
||||
assert_eq!(search_view.search_options, SearchOptions::CASE_SENSITIVE);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn init_test(cx: &mut TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
let fonts = cx.font_cache();
|
||||
|
@ -1767,6 +2045,7 @@ pub mod tests {
|
|||
cx.update(|cx| {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
cx.set_global(ActiveSearches::default());
|
||||
settings::register::<SemanticIndexSettings>(cx);
|
||||
|
||||
theme::init((), cx);
|
||||
cx.update_global::<SettingsStore, _, _>(|store, _| {
|
||||
|
|
|
@ -3,6 +3,7 @@ pub use buffer_search::BufferSearchBar;
|
|||
use gpui::{actions, Action, AppContext};
|
||||
use project::search::SearchQuery;
|
||||
pub use project_search::{ProjectSearchBar, ProjectSearchView};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
pub mod buffer_search;
|
||||
pub mod project_search;
|
||||
|
@ -21,6 +22,8 @@ actions!(
|
|||
SelectNextMatch,
|
||||
SelectPrevMatch,
|
||||
SelectAllMatches,
|
||||
NextHistoryQuery,
|
||||
PreviousHistoryQuery,
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -65,3 +68,187 @@ impl SearchOptions {
|
|||
options
|
||||
}
|
||||
}
|
||||
|
||||
const SEARCH_HISTORY_LIMIT: usize = 20;
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct SearchHistory {
|
||||
history: SmallVec<[String; SEARCH_HISTORY_LIMIT]>,
|
||||
selected: Option<usize>,
|
||||
}
|
||||
|
||||
impl SearchHistory {
|
||||
pub fn add(&mut self, search_string: String) {
|
||||
if let Some(i) = self.selected {
|
||||
if search_string == self.history[i] {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(previously_searched) = self.history.last_mut() {
|
||||
if search_string.find(previously_searched.as_str()).is_some() {
|
||||
*previously_searched = search_string;
|
||||
self.selected = Some(self.history.len() - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.history.push(search_string);
|
||||
if self.history.len() > SEARCH_HISTORY_LIMIT {
|
||||
self.history.remove(0);
|
||||
}
|
||||
self.selected = Some(self.history.len() - 1);
|
||||
}
|
||||
|
||||
pub fn next(&mut self) -> Option<&str> {
|
||||
let history_size = self.history.len();
|
||||
if history_size == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let selected = self.selected?;
|
||||
if selected == history_size - 1 {
|
||||
return None;
|
||||
}
|
||||
let next_index = selected + 1;
|
||||
self.selected = Some(next_index);
|
||||
Some(&self.history[next_index])
|
||||
}
|
||||
|
||||
pub fn current(&self) -> Option<&str> {
|
||||
Some(&self.history[self.selected?])
|
||||
}
|
||||
|
||||
pub fn previous(&mut self) -> Option<&str> {
|
||||
let history_size = self.history.len();
|
||||
if history_size == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let prev_index = match self.selected {
|
||||
Some(selected_index) => {
|
||||
if selected_index == 0 {
|
||||
return None;
|
||||
} else {
|
||||
selected_index - 1
|
||||
}
|
||||
}
|
||||
None => history_size - 1,
|
||||
};
|
||||
|
||||
self.selected = Some(prev_index);
|
||||
Some(&self.history[prev_index])
|
||||
}
|
||||
|
||||
pub fn reset_selection(&mut self) {
|
||||
self.selected = None;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
let mut search_history = SearchHistory::default();
|
||||
assert_eq!(
|
||||
search_history.current(),
|
||||
None,
|
||||
"No current selection should be set fo the default search history"
|
||||
);
|
||||
|
||||
search_history.add("rust".to_string());
|
||||
assert_eq!(
|
||||
search_history.current(),
|
||||
Some("rust"),
|
||||
"Newly added item should be selected"
|
||||
);
|
||||
|
||||
// check if duplicates are not added
|
||||
search_history.add("rust".to_string());
|
||||
assert_eq!(
|
||||
search_history.history.len(),
|
||||
1,
|
||||
"Should not add a duplicate"
|
||||
);
|
||||
assert_eq!(search_history.current(), Some("rust"));
|
||||
|
||||
// check if new string containing the previous string replaces it
|
||||
search_history.add("rustlang".to_string());
|
||||
assert_eq!(
|
||||
search_history.history.len(),
|
||||
1,
|
||||
"Should replace previous item if it's a substring"
|
||||
);
|
||||
assert_eq!(search_history.current(), Some("rustlang"));
|
||||
|
||||
// push enough items to test SEARCH_HISTORY_LIMIT
|
||||
for i in 0..SEARCH_HISTORY_LIMIT * 2 {
|
||||
search_history.add(format!("item{i}"));
|
||||
}
|
||||
assert!(search_history.history.len() <= SEARCH_HISTORY_LIMIT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_and_previous() {
|
||||
let mut search_history = SearchHistory::default();
|
||||
assert_eq!(
|
||||
search_history.next(),
|
||||
None,
|
||||
"Default search history should not have a next item"
|
||||
);
|
||||
|
||||
search_history.add("Rust".to_string());
|
||||
assert_eq!(search_history.next(), None);
|
||||
search_history.add("JavaScript".to_string());
|
||||
assert_eq!(search_history.next(), None);
|
||||
search_history.add("TypeScript".to_string());
|
||||
assert_eq!(search_history.next(), None);
|
||||
|
||||
assert_eq!(search_history.current(), Some("TypeScript"));
|
||||
|
||||
assert_eq!(search_history.previous(), Some("JavaScript"));
|
||||
assert_eq!(search_history.current(), Some("JavaScript"));
|
||||
|
||||
assert_eq!(search_history.previous(), Some("Rust"));
|
||||
assert_eq!(search_history.current(), Some("Rust"));
|
||||
|
||||
assert_eq!(search_history.previous(), None);
|
||||
assert_eq!(search_history.current(), Some("Rust"));
|
||||
|
||||
assert_eq!(search_history.next(), Some("JavaScript"));
|
||||
assert_eq!(search_history.current(), Some("JavaScript"));
|
||||
|
||||
assert_eq!(search_history.next(), Some("TypeScript"));
|
||||
assert_eq!(search_history.current(), Some("TypeScript"));
|
||||
|
||||
assert_eq!(search_history.next(), None);
|
||||
assert_eq!(search_history.current(), Some("TypeScript"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reset_selection() {
|
||||
let mut search_history = SearchHistory::default();
|
||||
search_history.add("Rust".to_string());
|
||||
search_history.add("JavaScript".to_string());
|
||||
search_history.add("TypeScript".to_string());
|
||||
|
||||
assert_eq!(search_history.current(), Some("TypeScript"));
|
||||
search_history.reset_selection();
|
||||
assert_eq!(search_history.current(), None);
|
||||
assert_eq!(
|
||||
search_history.previous(),
|
||||
Some("TypeScript"),
|
||||
"Should start from the end after reset on previous item query"
|
||||
);
|
||||
|
||||
search_history.previous();
|
||||
assert_eq!(search_history.current(), Some("JavaScript"));
|
||||
search_history.previous();
|
||||
assert_eq!(search_history.current(), Some("Rust"));
|
||||
|
||||
search_history.reset_selection();
|
||||
assert_eq!(search_history.current(), None);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,9 +54,12 @@ tempdir.workspace = true
|
|||
ctor.workspace = true
|
||||
env_logger.workspace = true
|
||||
|
||||
tree-sitter-typescript = "*"
|
||||
tree-sitter-json = "*"
|
||||
tree-sitter-rust = "*"
|
||||
tree-sitter-toml = "*"
|
||||
tree-sitter-cpp = "*"
|
||||
tree-sitter-elixir = "*"
|
||||
tree-sitter-typescript.workspace = true
|
||||
tree-sitter-json.workspace = true
|
||||
tree-sitter-rust.workspace = true
|
||||
tree-sitter-toml.workspace = true
|
||||
tree-sitter-cpp.workspace = true
|
||||
tree-sitter-elixir.workspace = true
|
||||
tree-sitter-lua.workspace = true
|
||||
tree-sitter-ruby.workspace = true
|
||||
tree-sitter-php.workspace = true
|
||||
|
|
|
@ -21,7 +21,9 @@ const CODE_CONTEXT_TEMPLATE: &str =
|
|||
"The below code snippet is from file '<path>'\n\n```<language>\n<item>\n```";
|
||||
const ENTIRE_FILE_TEMPLATE: &str =
|
||||
"The below snippet is from file '<path>'\n\n```<language>\n<item>\n```";
|
||||
pub const PARSEABLE_ENTIRE_FILE_TYPES: &[&str] = &["TOML", "YAML", "CSS"];
|
||||
const MARKDOWN_CONTEXT_TEMPLATE: &str = "The below file contents is from file '<path>'\n\n<item>";
|
||||
pub const PARSEABLE_ENTIRE_FILE_TYPES: &[&str] =
|
||||
&["TOML", "YAML", "CSS", "HEEX", "ERB", "SVELTE", "HTML"];
|
||||
|
||||
pub struct CodeContextRetriever {
|
||||
pub parser: Parser,
|
||||
|
@ -59,7 +61,7 @@ impl CodeContextRetriever {
|
|||
let document_span = ENTIRE_FILE_TEMPLATE
|
||||
.replace("<path>", relative_path.to_string_lossy().as_ref())
|
||||
.replace("<language>", language_name.as_ref())
|
||||
.replace("item", &content);
|
||||
.replace("<item>", &content);
|
||||
|
||||
Ok(vec![Document {
|
||||
range: 0..content.len(),
|
||||
|
@ -69,6 +71,19 @@ impl CodeContextRetriever {
|
|||
}])
|
||||
}
|
||||
|
||||
fn parse_markdown_file(&self, relative_path: &Path, content: &str) -> Result<Vec<Document>> {
|
||||
let document_span = MARKDOWN_CONTEXT_TEMPLATE
|
||||
.replace("<path>", relative_path.to_string_lossy().as_ref())
|
||||
.replace("<item>", &content);
|
||||
|
||||
Ok(vec![Document {
|
||||
range: 0..content.len(),
|
||||
content: document_span,
|
||||
embedding: Vec::new(),
|
||||
name: "Markdown".to_string(),
|
||||
}])
|
||||
}
|
||||
|
||||
fn get_matches_in_file(
|
||||
&mut self,
|
||||
content: &str,
|
||||
|
@ -135,6 +150,8 @@ impl CodeContextRetriever {
|
|||
|
||||
if PARSEABLE_ENTIRE_FILE_TYPES.contains(&language_name.as_ref()) {
|
||||
return self.parse_entire_file(relative_path, language_name, &content);
|
||||
} else if &language_name.to_string() == &"Markdown".to_string() {
|
||||
return self.parse_markdown_file(relative_path, &content);
|
||||
}
|
||||
|
||||
let mut documents = self.parse_file(content, language)?;
|
||||
|
@ -200,7 +217,12 @@ impl CodeContextRetriever {
|
|||
|
||||
let mut document_content = String::new();
|
||||
for context_range in &context_match.context_ranges {
|
||||
document_content.push_str(&content[context_range.clone()]);
|
||||
add_content_from_range(
|
||||
&mut document_content,
|
||||
content,
|
||||
context_range.clone(),
|
||||
context_match.start_col,
|
||||
);
|
||||
document_content.push_str("\n");
|
||||
}
|
||||
|
||||
|
|
|
@ -612,6 +612,7 @@ impl SemanticIndex {
|
|||
.await
|
||||
{
|
||||
if !PARSEABLE_ENTIRE_FILE_TYPES.contains(&language.name().as_ref())
|
||||
&& &language.name().as_ref() != &"Markdown"
|
||||
&& language
|
||||
.grammar()
|
||||
.and_then(|grammar| grammar.embedding_config.as_ref())
|
||||
|
|
|
@ -485,6 +485,79 @@ async fn test_code_context_retrieval_javascript() {
|
|||
)
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_code_context_retrieval_lua() {
|
||||
let language = lua_lang();
|
||||
let mut retriever = CodeContextRetriever::new();
|
||||
|
||||
let text = r#"
|
||||
-- Creates a new class
|
||||
-- @param baseclass The Baseclass of this class, or nil.
|
||||
-- @return A new class reference.
|
||||
function classes.class(baseclass)
|
||||
-- Create the class definition and metatable.
|
||||
local classdef = {}
|
||||
-- Find the super class, either Object or user-defined.
|
||||
baseclass = baseclass or classes.Object
|
||||
-- If this class definition does not know of a function, it will 'look up' to the Baseclass via the __index of the metatable.
|
||||
setmetatable(classdef, { __index = baseclass })
|
||||
-- All class instances have a reference to the class object.
|
||||
classdef.class = classdef
|
||||
--- Recursivly allocates the inheritance tree of the instance.
|
||||
-- @param mastertable The 'root' of the inheritance tree.
|
||||
-- @return Returns the instance with the allocated inheritance tree.
|
||||
function classdef.alloc(mastertable)
|
||||
-- All class instances have a reference to a superclass object.
|
||||
local instance = { super = baseclass.alloc(mastertable) }
|
||||
-- Any functions this instance does not know of will 'look up' to the superclass definition.
|
||||
setmetatable(instance, { __index = classdef, __newindex = mastertable })
|
||||
return instance
|
||||
end
|
||||
end
|
||||
"#.unindent();
|
||||
|
||||
let documents = retriever.parse_file(&text, language.clone()).unwrap();
|
||||
|
||||
assert_documents_eq(
|
||||
&documents,
|
||||
&[
|
||||
(r#"
|
||||
-- Creates a new class
|
||||
-- @param baseclass The Baseclass of this class, or nil.
|
||||
-- @return A new class reference.
|
||||
function classes.class(baseclass)
|
||||
-- Create the class definition and metatable.
|
||||
local classdef = {}
|
||||
-- Find the super class, either Object or user-defined.
|
||||
baseclass = baseclass or classes.Object
|
||||
-- If this class definition does not know of a function, it will 'look up' to the Baseclass via the __index of the metatable.
|
||||
setmetatable(classdef, { __index = baseclass })
|
||||
-- All class instances have a reference to the class object.
|
||||
classdef.class = classdef
|
||||
--- Recursivly allocates the inheritance tree of the instance.
|
||||
-- @param mastertable The 'root' of the inheritance tree.
|
||||
-- @return Returns the instance with the allocated inheritance tree.
|
||||
function classdef.alloc(mastertable)
|
||||
--[ ... ]--
|
||||
--[ ... ]--
|
||||
end
|
||||
end"#.unindent(),
|
||||
114),
|
||||
(r#"
|
||||
--- Recursivly allocates the inheritance tree of the instance.
|
||||
-- @param mastertable The 'root' of the inheritance tree.
|
||||
-- @return Returns the instance with the allocated inheritance tree.
|
||||
function classdef.alloc(mastertable)
|
||||
-- All class instances have a reference to a superclass object.
|
||||
local instance = { super = baseclass.alloc(mastertable) }
|
||||
-- Any functions this instance does not know of will 'look up' to the superclass definition.
|
||||
setmetatable(instance, { __index = classdef, __newindex = mastertable })
|
||||
return instance
|
||||
end"#.unindent(), 809),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_code_context_retrieval_elixir() {
|
||||
let language = elixir_lang();
|
||||
|
@ -753,6 +826,346 @@ async fn test_code_context_retrieval_cpp() {
|
|||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_code_context_retrieval_ruby() {
|
||||
let language = ruby_lang();
|
||||
let mut retriever = CodeContextRetriever::new();
|
||||
|
||||
let text = r#"
|
||||
# This concern is inspired by "sudo mode" on GitHub. It
|
||||
# is a way to re-authenticate a user before allowing them
|
||||
# to see or perform an action.
|
||||
#
|
||||
# Add `before_action :require_challenge!` to actions you
|
||||
# want to protect.
|
||||
#
|
||||
# The user will be shown a page to enter the challenge (which
|
||||
# is either the password, or just the username when no
|
||||
# password exists). Upon passing, there is a grace period
|
||||
# during which no challenge will be asked from the user.
|
||||
#
|
||||
# Accessing challenge-protected resources during the grace
|
||||
# period will refresh the grace period.
|
||||
module ChallengableConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
CHALLENGE_TIMEOUT = 1.hour.freeze
|
||||
|
||||
def require_challenge!
|
||||
return if skip_challenge?
|
||||
|
||||
if challenge_passed_recently?
|
||||
session[:challenge_passed_at] = Time.now.utc
|
||||
return
|
||||
end
|
||||
|
||||
@challenge = Form::Challenge.new(return_to: request.url)
|
||||
|
||||
if params.key?(:form_challenge)
|
||||
if challenge_passed?
|
||||
session[:challenge_passed_at] = Time.now.utc
|
||||
else
|
||||
flash.now[:alert] = I18n.t('challenge.invalid_password')
|
||||
render_challenge
|
||||
end
|
||||
else
|
||||
render_challenge
|
||||
end
|
||||
end
|
||||
|
||||
def challenge_passed?
|
||||
current_user.valid_password?(challenge_params[:current_password])
|
||||
end
|
||||
end
|
||||
|
||||
class Animal
|
||||
include Comparable
|
||||
|
||||
attr_reader :legs
|
||||
|
||||
def initialize(name, legs)
|
||||
@name, @legs = name, legs
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
legs <=> other.legs
|
||||
end
|
||||
end
|
||||
|
||||
# Singleton method for car object
|
||||
def car.wheels
|
||||
puts "There are four wheels"
|
||||
end"#
|
||||
.unindent();
|
||||
|
||||
let documents = retriever.parse_file(&text, language.clone()).unwrap();
|
||||
|
||||
assert_documents_eq(
|
||||
&documents,
|
||||
&[
|
||||
(
|
||||
r#"
|
||||
# This concern is inspired by "sudo mode" on GitHub. It
|
||||
# is a way to re-authenticate a user before allowing them
|
||||
# to see or perform an action.
|
||||
#
|
||||
# Add `before_action :require_challenge!` to actions you
|
||||
# want to protect.
|
||||
#
|
||||
# The user will be shown a page to enter the challenge (which
|
||||
# is either the password, or just the username when no
|
||||
# password exists). Upon passing, there is a grace period
|
||||
# during which no challenge will be asked from the user.
|
||||
#
|
||||
# Accessing challenge-protected resources during the grace
|
||||
# period will refresh the grace period.
|
||||
module ChallengableConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
CHALLENGE_TIMEOUT = 1.hour.freeze
|
||||
|
||||
def require_challenge!
|
||||
# ...
|
||||
end
|
||||
|
||||
def challenge_passed?
|
||||
# ...
|
||||
end
|
||||
end"#
|
||||
.unindent(),
|
||||
558,
|
||||
),
|
||||
(
|
||||
r#"
|
||||
def require_challenge!
|
||||
return if skip_challenge?
|
||||
|
||||
if challenge_passed_recently?
|
||||
session[:challenge_passed_at] = Time.now.utc
|
||||
return
|
||||
end
|
||||
|
||||
@challenge = Form::Challenge.new(return_to: request.url)
|
||||
|
||||
if params.key?(:form_challenge)
|
||||
if challenge_passed?
|
||||
session[:challenge_passed_at] = Time.now.utc
|
||||
else
|
||||
flash.now[:alert] = I18n.t('challenge.invalid_password')
|
||||
render_challenge
|
||||
end
|
||||
else
|
||||
render_challenge
|
||||
end
|
||||
end"#
|
||||
.unindent(),
|
||||
663,
|
||||
),
|
||||
(
|
||||
r#"
|
||||
def challenge_passed?
|
||||
current_user.valid_password?(challenge_params[:current_password])
|
||||
end"#
|
||||
.unindent(),
|
||||
1254,
|
||||
),
|
||||
(
|
||||
r#"
|
||||
class Animal
|
||||
include Comparable
|
||||
|
||||
attr_reader :legs
|
||||
|
||||
def initialize(name, legs)
|
||||
# ...
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
# ...
|
||||
end
|
||||
end"#
|
||||
.unindent(),
|
||||
1363,
|
||||
),
|
||||
(
|
||||
r#"
|
||||
def initialize(name, legs)
|
||||
@name, @legs = name, legs
|
||||
end"#
|
||||
.unindent(),
|
||||
1427,
|
||||
),
|
||||
(
|
||||
r#"
|
||||
def <=>(other)
|
||||
legs <=> other.legs
|
||||
end"#
|
||||
.unindent(),
|
||||
1501,
|
||||
),
|
||||
(
|
||||
r#"
|
||||
# Singleton method for car object
|
||||
def car.wheels
|
||||
puts "There are four wheels"
|
||||
end"#
|
||||
.unindent(),
|
||||
1591,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_code_context_retrieval_php() {
|
||||
let language = php_lang();
|
||||
let mut retriever = CodeContextRetriever::new();
|
||||
|
||||
let text = r#"
|
||||
<?php
|
||||
|
||||
namespace LevelUp\Experience\Concerns;
|
||||
|
||||
/*
|
||||
This is a multiple-lines comment block
|
||||
that spans over multiple
|
||||
lines
|
||||
*/
|
||||
function functionName() {
|
||||
echo "Hello world!";
|
||||
}
|
||||
|
||||
trait HasAchievements
|
||||
{
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function grantAchievement(Achievement $achievement, $progress = null): void
|
||||
{
|
||||
if ($progress > 100) {
|
||||
throw new Exception(message: 'Progress cannot be greater than 100');
|
||||
}
|
||||
|
||||
if ($this->achievements()->find($achievement->id)) {
|
||||
throw new Exception(message: 'User already has this Achievement');
|
||||
}
|
||||
|
||||
$this->achievements()->attach($achievement, [
|
||||
'progress' => $progress ?? null,
|
||||
]);
|
||||
|
||||
$this->when(value: ($progress === null) || ($progress === 100), callback: fn (): ?array => event(new AchievementAwarded(achievement: $achievement, user: $this)));
|
||||
}
|
||||
|
||||
public function achievements(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(related: Achievement::class)
|
||||
->withPivot(columns: 'progress')
|
||||
->where('is_secret', false)
|
||||
->using(AchievementUser::class);
|
||||
}
|
||||
}
|
||||
|
||||
interface Multiplier
|
||||
{
|
||||
public function qualifies(array $data): bool;
|
||||
|
||||
public function setMultiplier(): int;
|
||||
}
|
||||
|
||||
enum AuditType: string
|
||||
{
|
||||
case Add = 'add';
|
||||
case Remove = 'remove';
|
||||
case Reset = 'reset';
|
||||
case LevelUp = 'level_up';
|
||||
}
|
||||
|
||||
?>"#
|
||||
.unindent();
|
||||
|
||||
let documents = retriever.parse_file(&text, language.clone()).unwrap();
|
||||
|
||||
assert_documents_eq(
|
||||
&documents,
|
||||
&[
|
||||
(
|
||||
r#"
|
||||
/*
|
||||
This is a multiple-lines comment block
|
||||
that spans over multiple
|
||||
lines
|
||||
*/
|
||||
function functionName() {
|
||||
echo "Hello world!";
|
||||
}"#
|
||||
.unindent(),
|
||||
123,
|
||||
),
|
||||
(
|
||||
r#"
|
||||
trait HasAchievements
|
||||
{
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function grantAchievement(Achievement $achievement, $progress = null): void
|
||||
{/* ... */}
|
||||
|
||||
public function achievements(): BelongsToMany
|
||||
{/* ... */}
|
||||
}"#
|
||||
.unindent(),
|
||||
177,
|
||||
),
|
||||
(r#"
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function grantAchievement(Achievement $achievement, $progress = null): void
|
||||
{
|
||||
if ($progress > 100) {
|
||||
throw new Exception(message: 'Progress cannot be greater than 100');
|
||||
}
|
||||
|
||||
if ($this->achievements()->find($achievement->id)) {
|
||||
throw new Exception(message: 'User already has this Achievement');
|
||||
}
|
||||
|
||||
$this->achievements()->attach($achievement, [
|
||||
'progress' => $progress ?? null,
|
||||
]);
|
||||
|
||||
$this->when(value: ($progress === null) || ($progress === 100), callback: fn (): ?array => event(new AchievementAwarded(achievement: $achievement, user: $this)));
|
||||
}"#.unindent(), 245),
|
||||
(r#"
|
||||
public function achievements(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(related: Achievement::class)
|
||||
->withPivot(columns: 'progress')
|
||||
->where('is_secret', false)
|
||||
->using(AchievementUser::class);
|
||||
}"#.unindent(), 902),
|
||||
(r#"
|
||||
interface Multiplier
|
||||
{
|
||||
public function qualifies(array $data): bool;
|
||||
|
||||
public function setMultiplier(): int;
|
||||
}"#.unindent(),
|
||||
1146),
|
||||
(r#"
|
||||
enum AuditType: string
|
||||
{
|
||||
case Add = 'add';
|
||||
case Remove = 'remove';
|
||||
case Reset = 'reset';
|
||||
case LevelUp = 'level_up';
|
||||
}"#.unindent(), 1265)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_dot_product(mut rng: StdRng) {
|
||||
assert_eq!(dot(&[1., 0., 0., 0., 0.], &[0., 1., 0., 0., 0.]), 0.);
|
||||
|
@ -1083,6 +1496,131 @@ fn cpp_lang() -> Arc<Language> {
|
|||
)
|
||||
}
|
||||
|
||||
fn lua_lang() -> Arc<Language> {
|
||||
Arc::new(
|
||||
Language::new(
|
||||
LanguageConfig {
|
||||
name: "Lua".into(),
|
||||
path_suffixes: vec!["lua".into()],
|
||||
collapsed_placeholder: "--[ ... ]--".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_lua::language()),
|
||||
)
|
||||
.with_embedding_query(
|
||||
r#"
|
||||
(
|
||||
(comment)* @context
|
||||
.
|
||||
(function_declaration
|
||||
"function" @name
|
||||
name: (_) @name
|
||||
(comment)* @collapse
|
||||
body: (block) @collapse
|
||||
) @item
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn php_lang() -> Arc<Language> {
|
||||
Arc::new(
|
||||
Language::new(
|
||||
LanguageConfig {
|
||||
name: "PHP".into(),
|
||||
path_suffixes: vec!["php".into()],
|
||||
collapsed_placeholder: "/* ... */".into(),
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_php::language()),
|
||||
)
|
||||
.with_embedding_query(
|
||||
r#"
|
||||
(
|
||||
(comment)* @context
|
||||
.
|
||||
[
|
||||
(function_definition
|
||||
"function" @name
|
||||
name: (_) @name
|
||||
body: (_
|
||||
"{" @keep
|
||||
"}" @keep) @collapse
|
||||
)
|
||||
|
||||
(trait_declaration
|
||||
"trait" @name
|
||||
name: (_) @name)
|
||||
|
||||
(method_declaration
|
||||
"function" @name
|
||||
name: (_) @name
|
||||
body: (_
|
||||
"{" @keep
|
||||
"}" @keep) @collapse
|
||||
)
|
||||
|
||||
(interface_declaration
|
||||
"interface" @name
|
||||
name: (_) @name
|
||||
)
|
||||
|
||||
(enum_declaration
|
||||
"enum" @name
|
||||
name: (_) @name
|
||||
)
|
||||
|
||||
] @item
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn ruby_lang() -> Arc<Language> {
|
||||
Arc::new(
|
||||
Language::new(
|
||||
LanguageConfig {
|
||||
name: "Ruby".into(),
|
||||
path_suffixes: vec!["rb".into()],
|
||||
collapsed_placeholder: "# ...".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_ruby::language()),
|
||||
)
|
||||
.with_embedding_query(
|
||||
r#"
|
||||
(
|
||||
(comment)* @context
|
||||
.
|
||||
[
|
||||
(module
|
||||
"module" @name
|
||||
name: (_) @name)
|
||||
(method
|
||||
"def" @name
|
||||
name: (_) @name
|
||||
body: (body_statement) @collapse)
|
||||
(class
|
||||
"class" @name
|
||||
name: (_) @name)
|
||||
(singleton_method
|
||||
"def" @name
|
||||
object: (_) @name
|
||||
"." @name
|
||||
name: (_) @name
|
||||
body: (body_statement) @collapse)
|
||||
] @item
|
||||
)
|
||||
"#,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn elixir_lang() -> Arc<Language> {
|
||||
Arc::new(
|
||||
Language::new(
|
||||
|
|
|
@ -222,7 +222,7 @@ mod test {
|
|||
});
|
||||
|
||||
search_bar.read_with(cx.cx, |bar, cx| {
|
||||
assert_eq!(bar.query_editor.read(cx).text(cx), "cc");
|
||||
assert_eq!(bar.query(cx), "cc");
|
||||
});
|
||||
|
||||
deterministic.run_until_parked();
|
||||
|
|
|
@ -99,7 +99,7 @@ async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
|
|||
});
|
||||
|
||||
search_bar.read_with(cx.cx, |bar, cx| {
|
||||
assert_eq!(bar.query_editor.read(cx).text(cx), "");
|
||||
assert_eq!(bar.query(cx), "");
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -175,7 +175,7 @@ async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
|
|||
});
|
||||
|
||||
search_bar.read_with(cx.cx, |bar, cx| {
|
||||
assert_eq!(bar.query_editor.read(cx).text(cx), "cc");
|
||||
assert_eq!(bar.query(cx), "cc");
|
||||
});
|
||||
|
||||
// wait for the query editor change event to fire.
|
||||
|
|
|
@ -7,3 +7,4 @@ brackets = [
|
|||
{ start = "[", end = "]", close = true, newline = true },
|
||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
|
||||
]
|
||||
collapsed_placeholder = "--[ ... ]--"
|
||||
|
|
10
crates/zed/src/languages/lua/embedding.scm
Normal file
10
crates/zed/src/languages/lua/embedding.scm
Normal file
|
@ -0,0 +1,10 @@
|
|||
(
|
||||
(comment)* @context
|
||||
.
|
||||
(function_declaration
|
||||
"function" @name
|
||||
name: (_) @name
|
||||
(comment)* @collapse
|
||||
body: (block) @collapse
|
||||
) @item
|
||||
)
|
|
@ -9,3 +9,4 @@ brackets = [
|
|||
{ start = "(", end = ")", close = true, newline = true },
|
||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
|
||||
]
|
||||
collapsed_placeholder = "/* ... */"
|
||||
|
|
36
crates/zed/src/languages/php/embedding.scm
Normal file
36
crates/zed/src/languages/php/embedding.scm
Normal file
|
@ -0,0 +1,36 @@
|
|||
(
|
||||
(comment)* @context
|
||||
.
|
||||
[
|
||||
(function_definition
|
||||
"function" @name
|
||||
name: (_) @name
|
||||
body: (_
|
||||
"{" @keep
|
||||
"}" @keep) @collapse
|
||||
)
|
||||
|
||||
(trait_declaration
|
||||
"trait" @name
|
||||
name: (_) @name)
|
||||
|
||||
(method_declaration
|
||||
"function" @name
|
||||
name: (_) @name
|
||||
body: (_
|
||||
"{" @keep
|
||||
"}" @keep) @collapse
|
||||
)
|
||||
|
||||
(interface_declaration
|
||||
"interface" @name
|
||||
name: (_) @name
|
||||
)
|
||||
|
||||
(enum_declaration
|
||||
"enum" @name
|
||||
name: (_) @name
|
||||
)
|
||||
|
||||
] @item
|
||||
)
|
|
@ -8,8 +8,6 @@
|
|||
name: (_) @name
|
||||
) @item
|
||||
|
||||
|
||||
|
||||
(method_declaration
|
||||
"function" @context
|
||||
name: (_) @name
|
||||
|
@ -24,3 +22,8 @@
|
|||
"enum" @context
|
||||
name: (_) @name
|
||||
) @item
|
||||
|
||||
(trait_declaration
|
||||
"trait" @context
|
||||
name: (_) @name
|
||||
) @item
|
||||
|
|
|
@ -10,3 +10,4 @@ brackets = [
|
|||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["comment", "string"] },
|
||||
{ start = "'", end = "'", close = true, newline = false, not_in = ["comment", "string"] },
|
||||
]
|
||||
collapsed_placeholder = "# ..."
|
||||
|
|
22
crates/zed/src/languages/ruby/embedding.scm
Normal file
22
crates/zed/src/languages/ruby/embedding.scm
Normal file
|
@ -0,0 +1,22 @@
|
|||
(
|
||||
(comment)* @context
|
||||
.
|
||||
[
|
||||
(module
|
||||
"module" @name
|
||||
name: (_) @name)
|
||||
(method
|
||||
"def" @name
|
||||
name: (_) @name
|
||||
body: (body_statement) @collapse)
|
||||
(class
|
||||
"class" @name
|
||||
name: (_) @name)
|
||||
(singleton_method
|
||||
"def" @name
|
||||
object: (_) @name
|
||||
"." @name
|
||||
name: (_) @name
|
||||
body: (body_statement) @collapse)
|
||||
] @item
|
||||
)
|
|
@ -47,6 +47,7 @@ use std::{
|
|||
use sum_tree::Bias;
|
||||
use terminal_view::{get_working_directory, TerminalSettings, TerminalView};
|
||||
use util::{
|
||||
channel::ReleaseChannel,
|
||||
http::{self, HttpClient},
|
||||
paths::PathLikeWithPosition,
|
||||
};
|
||||
|
@ -420,22 +421,41 @@ fn init_panic_hook(app: &App, installation_id: Option<String>) {
|
|||
panic::set_hook(Box::new(move |info| {
|
||||
let prior_panic_count = PANIC_COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
if prior_panic_count > 0 {
|
||||
std::panic::resume_unwind(Box::new(()));
|
||||
// Give the panic-ing thread time to write the panic file
|
||||
loop {
|
||||
std::thread::yield_now();
|
||||
}
|
||||
}
|
||||
|
||||
let thread = thread::current();
|
||||
let thread_name = thread.name().unwrap_or("<unnamed>");
|
||||
|
||||
let payload = info
|
||||
.payload()
|
||||
.downcast_ref::<&str>()
|
||||
.map(|s| s.to_string())
|
||||
.or_else(|| info.payload().downcast_ref::<String>().map(|s| s.clone()))
|
||||
.unwrap_or_else(|| "Box<Any>".to_string());
|
||||
|
||||
if *util::channel::RELEASE_CHANNEL == ReleaseChannel::Dev {
|
||||
let location = info.location().unwrap();
|
||||
let backtrace = Backtrace::new();
|
||||
eprintln!(
|
||||
"Thread {:?} panicked with {:?} at {}:{}:{}\n{:?}",
|
||||
thread_name,
|
||||
payload,
|
||||
location.file(),
|
||||
location.line(),
|
||||
location.column(),
|
||||
backtrace,
|
||||
);
|
||||
std::process::exit(-1);
|
||||
}
|
||||
|
||||
let app_version = ZED_APP_VERSION
|
||||
.or_else(|| platform.app_version().ok())
|
||||
.map_or("dev".to_string(), |v| v.to_string());
|
||||
|
||||
let thread = thread::current();
|
||||
let thread = thread.name().unwrap_or("<unnamed>");
|
||||
|
||||
let payload = info.payload();
|
||||
let payload = None
|
||||
.or_else(|| payload.downcast_ref::<&str>().map(|s| s.to_string()))
|
||||
.or_else(|| payload.downcast_ref::<String>().map(|s| s.clone()))
|
||||
.unwrap_or_else(|| "Box<Any>".to_string());
|
||||
|
||||
let backtrace = Backtrace::new();
|
||||
let mut backtrace = backtrace
|
||||
.frames()
|
||||
|
@ -452,7 +472,7 @@ fn init_panic_hook(app: &App, installation_id: Option<String>) {
|
|||
}
|
||||
|
||||
let panic_data = Panic {
|
||||
thread: thread.into(),
|
||||
thread: thread_name.into(),
|
||||
payload: payload.into(),
|
||||
location_data: info.location().map(|location| LocationData {
|
||||
file: location.file().into(),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[toolchain]
|
||||
channel = "1.70"
|
||||
channel = "1.71"
|
||||
components = [ "rustfmt" ]
|
||||
targets = [ "x86_64-apple-darwin", "aarch64-apple-darwin", "wasm32-wasi" ]
|
||||
|
|
|
@ -182,8 +182,8 @@ export default function editor(): any {
|
|||
line_number: with_opacity(foreground(layer), 0.35),
|
||||
line_number_active: foreground(layer),
|
||||
rename_fade: 0.6,
|
||||
wrap_guide: with_opacity(foreground(layer), 0.1),
|
||||
active_wrap_guide: with_opacity(foreground(layer), 0.2),
|
||||
wrap_guide: with_opacity(foreground(layer), 0.05),
|
||||
active_wrap_guide: with_opacity(foreground(layer), 0.1),
|
||||
unnecessary_code_fade: 0.5,
|
||||
selection: theme.players[0],
|
||||
whitespace: theme.ramps.neutral(0.5).hex(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue