mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
Merge branch 'main' into vim-visual-selection
This commit is contained in:
commit
fb90eada70
5
.zed/settings.json
Normal file
5
.zed/settings.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"JSON": {
|
||||
"tab_size": 4
|
||||
}
|
||||
}
|
284
Cargo.lock
generated
284
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",
|
||||
@ -1649,6 +1649,21 @@ dependencies = [
|
||||
"theme",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "copilot"
|
||||
version = "0.1.0"
|
||||
@ -2042,9 +2057,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",
|
||||
@ -2124,6 +2139,28 @@ 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 = "derive_more"
|
||||
version = "0.99.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
|
||||
dependencies = [
|
||||
"convert_case 0.4.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version 0.4.0",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dhat"
|
||||
version = "0.3.2"
|
||||
@ -2305,6 +2342,7 @@ dependencies = [
|
||||
"clock",
|
||||
"collections",
|
||||
"context_menu",
|
||||
"convert_case 0.6.0",
|
||||
"copilot",
|
||||
"ctor",
|
||||
"db",
|
||||
@ -2341,7 +2379,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",
|
||||
@ -2425,9 +2463,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",
|
||||
@ -2731,7 +2769,7 @@ dependencies = [
|
||||
"smol",
|
||||
"sum_tree",
|
||||
"tempfile",
|
||||
"time 0.3.23",
|
||||
"time 0.3.24",
|
||||
"util",
|
||||
]
|
||||
|
||||
@ -2881,7 +2919,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3033,9 +3071,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",
|
||||
@ -3087,6 +3125,7 @@ dependencies = [
|
||||
"core-graphics",
|
||||
"core-text",
|
||||
"ctor",
|
||||
"derive_more",
|
||||
"dhat",
|
||||
"env_logger 0.9.3",
|
||||
"etagere",
|
||||
@ -3121,7 +3160,7 @@ dependencies = [
|
||||
"smol",
|
||||
"sqlez",
|
||||
"sum_tree",
|
||||
"time 0.3.23",
|
||||
"time 0.3.24",
|
||||
"tiny-skia",
|
||||
"usvg",
|
||||
"util",
|
||||
@ -3133,6 +3172,7 @@ dependencies = [
|
||||
name = "gpui_macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gpui",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
@ -3851,7 +3891,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",
|
||||
@ -3860,7 +3900,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",
|
||||
@ -4034,9 +4074,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"
|
||||
@ -4744,7 +4784,7 @@ dependencies = [
|
||||
"proc-macro-crate 1.3.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4895,7 +4935,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5097,7 +5137,7 @@ version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
"rustc_version 0.3.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5134,9 +5174,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",
|
||||
@ -5192,7 +5232,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5230,7 +5270,7 @@ dependencies = [
|
||||
"line-wrap",
|
||||
"quick-xml",
|
||||
"serde",
|
||||
"time 0.3.23",
|
||||
"time 0.3.24",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5345,7 +5385,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]]
|
||||
@ -5905,7 +5945,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",
|
||||
]
|
||||
|
||||
@ -5920,9 +5960,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",
|
||||
@ -6217,7 +6257,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@ -6234,13 +6274,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",
|
||||
@ -6268,7 +6307,16 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
|
||||
dependencies = [
|
||||
"semver",
|
||||
"semver 0.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver 1.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6294,7 +6342,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",
|
||||
@ -6308,9 +6356,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",
|
||||
]
|
||||
|
||||
@ -6509,7 +6557,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"thiserror",
|
||||
"time 0.3.23",
|
||||
"time 0.3.24",
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid 1.4.1",
|
||||
@ -6537,7 +6585,7 @@ dependencies = [
|
||||
"rust_decimal",
|
||||
"sea-query-derive",
|
||||
"serde_json",
|
||||
"time 0.3.23",
|
||||
"time 0.3.24",
|
||||
"uuid 1.4.1",
|
||||
]
|
||||
|
||||
@ -6552,7 +6600,7 @@ dependencies = [
|
||||
"sea-query",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"time 0.3.23",
|
||||
"time 0.3.24",
|
||||
"uuid 1.4.1",
|
||||
]
|
||||
|
||||
@ -6685,12 +6733,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",
|
||||
@ -6705,6 +6756,12 @@ dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.10.2"
|
||||
@ -6722,22 +6779,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]]
|
||||
@ -6762,9 +6819,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",
|
||||
@ -6786,13 +6843,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]]
|
||||
@ -7238,7 +7295,7 @@ dependencies = [
|
||||
"sqlx-rt",
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"time 0.3.23",
|
||||
"time 0.3.24",
|
||||
"tokio-stream",
|
||||
"url",
|
||||
"uuid 1.4.1",
|
||||
@ -7487,9 +7544,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",
|
||||
@ -7557,9 +7614,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"
|
||||
@ -7742,7 +7799,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -7815,10 +7872,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",
|
||||
@ -7833,9 +7891,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",
|
||||
]
|
||||
@ -7931,7 +7989,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -8153,7 +8211,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -8255,16 +8313,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"
|
||||
@ -8274,16 +8322,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"
|
||||
@ -8471,26 +8509,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"
|
||||
@ -8993,7 +9011,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@ -9027,7 +9045,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@ -9040,9 +9058,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",
|
||||
]
|
||||
@ -9264,9 +9282,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",
|
||||
@ -9276,11 +9294,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]]
|
||||
@ -9657,9 +9675,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",
|
||||
]
|
||||
@ -9843,7 +9861,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.98.0"
|
||||
version = "0.100.0"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"ai",
|
||||
@ -9930,9 +9948,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",
|
||||
@ -9950,8 +9968,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",
|
||||
@ -9988,7 +10006,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.27",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -79,6 +79,7 @@ resolver = "2"
|
||||
anyhow = { version = "1.0.57" }
|
||||
async-trait = { version = "0.1" }
|
||||
ctor = { version = "0.1" }
|
||||
derive_more = { version = "0.99.17" }
|
||||
env_logger = { version = "0.9" }
|
||||
futures = { version = "0.3" }
|
||||
globset = { version = "0.4" }
|
||||
|
@ -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 . .
|
||||
|
||||
|
BIN
assets/fonts/plex/IBMPlexSans-Bold.ttf
Normal file
BIN
assets/fonts/plex/IBMPlexSans-Bold.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/plex/IBMPlexSans-Italic.ttf
Normal file
BIN
assets/fonts/plex/IBMPlexSans-Italic.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/plex/IBMPlexSans-Regular.ttf
Normal file
BIN
assets/fonts/plex/IBMPlexSans-Regular.ttf
Normal file
Binary file not shown.
93
assets/fonts/plex/LICENSE.txt
Normal file
93
assets/fonts/plex/LICENSE.txt
Normal file
@ -0,0 +1,93 @@
|
||||
Copyright © 2017 IBM Corp. with Reserved Font Name "Plex"
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
|
||||
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
@ -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": {
|
||||
|
@ -362,7 +362,7 @@ impl AssistantPanel {
|
||||
this.set_active_editor_index(this.prev_active_editor_index, cx);
|
||||
}
|
||||
})
|
||||
.with_tooltip::<History>(1, "History".into(), None, tooltip_style, cx)
|
||||
.with_tooltip::<History>(1, "History", None, tooltip_style, cx)
|
||||
}
|
||||
|
||||
fn render_editor_tools(&self, cx: &mut ViewContext<Self>) -> Vec<AnyElement<Self>> {
|
||||
@ -394,7 +394,7 @@ impl AssistantPanel {
|
||||
})
|
||||
.with_tooltip::<Split>(
|
||||
1,
|
||||
"Split Message".into(),
|
||||
"Split Message",
|
||||
Some(Box::new(Split)),
|
||||
tooltip_style,
|
||||
cx,
|
||||
@ -416,13 +416,7 @@ impl AssistantPanel {
|
||||
active_editor.update(cx, |editor, cx| editor.assist(&Default::default(), cx));
|
||||
}
|
||||
})
|
||||
.with_tooltip::<Assist>(
|
||||
1,
|
||||
"Assist".into(),
|
||||
Some(Box::new(Assist)),
|
||||
tooltip_style,
|
||||
cx,
|
||||
)
|
||||
.with_tooltip::<Assist>(1, "Assist", Some(Box::new(Assist)), tooltip_style, cx)
|
||||
}
|
||||
|
||||
fn render_quote_button(cx: &mut ViewContext<Self>) -> impl Element<Self> {
|
||||
@ -446,7 +440,7 @@ impl AssistantPanel {
|
||||
})
|
||||
.with_tooltip::<QuoteSelection>(
|
||||
1,
|
||||
"Quote Selection".into(),
|
||||
"Quote Selection",
|
||||
Some(Box::new(QuoteSelection)),
|
||||
tooltip_style,
|
||||
cx,
|
||||
@ -468,7 +462,7 @@ impl AssistantPanel {
|
||||
})
|
||||
.with_tooltip::<NewConversation>(
|
||||
1,
|
||||
"New Conversation".into(),
|
||||
"New Conversation",
|
||||
Some(Box::new(NewConversation)),
|
||||
tooltip_style,
|
||||
cx,
|
||||
@ -498,11 +492,7 @@ impl AssistantPanel {
|
||||
})
|
||||
.with_tooltip::<ToggleZoom>(
|
||||
0,
|
||||
if self.zoomed {
|
||||
"Zoom Out".into()
|
||||
} else {
|
||||
"Zoom In".into()
|
||||
},
|
||||
if self.zoomed { "Zoom Out" } else { "Zoom In" },
|
||||
Some(Box::new(ToggleZoom)),
|
||||
tooltip_style,
|
||||
cx,
|
||||
@ -1637,6 +1627,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
|
||||
});
|
||||
|
||||
|
@ -12,10 +12,7 @@ use client::{
|
||||
use collections::{HashMap, HashSet};
|
||||
use fs::FakeFs;
|
||||
use futures::{channel::oneshot, StreamExt as _};
|
||||
use gpui::{
|
||||
elements::*, executor::Deterministic, AnyElement, Entity, ModelHandle, TestAppContext, View,
|
||||
ViewContext, ViewHandle, WeakViewHandle,
|
||||
};
|
||||
use gpui::{executor::Deterministic, ModelHandle, TestAppContext, WindowHandle};
|
||||
use language::LanguageRegistry;
|
||||
use parking_lot::Mutex;
|
||||
use project::{Project, WorktreeId};
|
||||
@ -466,42 +463,8 @@ impl TestClient {
|
||||
&self,
|
||||
project: &ModelHandle<Project>,
|
||||
cx: &mut TestAppContext,
|
||||
) -> ViewHandle<Workspace> {
|
||||
struct WorkspaceContainer {
|
||||
workspace: Option<WeakViewHandle<Workspace>>,
|
||||
}
|
||||
|
||||
impl Entity for WorkspaceContainer {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl View for WorkspaceContainer {
|
||||
fn ui_name() -> &'static str {
|
||||
"WorkspaceContainer"
|
||||
}
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
if let Some(workspace) = self
|
||||
.workspace
|
||||
.as_ref()
|
||||
.and_then(|workspace| workspace.upgrade(cx))
|
||||
{
|
||||
ChildView::new(&workspace, cx).into_any()
|
||||
} else {
|
||||
Empty::new().into_any()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We use a workspace container so that we don't need to remove the window in order to
|
||||
// drop the workspace and we can use a ViewHandle instead.
|
||||
let (window_id, container) = cx.add_window(|_| WorkspaceContainer { workspace: None });
|
||||
let workspace = cx.add_view(window_id, |cx| Workspace::test_new(project.clone(), cx));
|
||||
container.update(cx, |container, cx| {
|
||||
container.workspace = Some(workspace.downgrade());
|
||||
cx.notify();
|
||||
});
|
||||
workspace
|
||||
) -> WindowHandle<Workspace> {
|
||||
cx.add_window(|cx| Workspace::test_new(project.clone(), cx))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,7 @@ use client::{User, RECEIVE_TIMEOUT};
|
||||
use collections::HashSet;
|
||||
use editor::{
|
||||
test::editor_test_context::EditorTestContext, ConfirmCodeAction, ConfirmCompletion,
|
||||
ConfirmRename, Editor, ExcerptRange, MultiBuffer, Redo, Rename, ToOffset, ToggleCodeActions,
|
||||
Undo,
|
||||
ConfirmRename, Editor, ExcerptRange, MultiBuffer, Redo, Rename, ToggleCodeActions, Undo,
|
||||
};
|
||||
use fs::{repository::GitFileStatus, FakeFs, Fs as _, LineEnding, RemoveOptions};
|
||||
use futures::StreamExt as _;
|
||||
@ -1208,7 +1207,7 @@ async fn test_share_project(
|
||||
cx_c: &mut TestAppContext,
|
||||
) {
|
||||
deterministic.forbid_parking();
|
||||
let (window_b, _) = cx_b.add_window(|_| EmptyView);
|
||||
let window_b = cx_b.add_window(|_| EmptyView);
|
||||
let mut server = TestServer::start(&deterministic).await;
|
||||
let client_a = server.create_client(cx_a, "user_a").await;
|
||||
let client_b = server.create_client(cx_b, "user_b").await;
|
||||
@ -1316,7 +1315,7 @@ async fn test_share_project(
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let editor_b = cx_b.add_view(window_b, |cx| Editor::for_buffer(buffer_b, None, cx));
|
||||
let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, None, cx));
|
||||
|
||||
// Client A sees client B's selection
|
||||
deterministic.run_until_parked();
|
||||
@ -1499,8 +1498,8 @@ async fn test_host_disconnect(
|
||||
deterministic.run_until_parked();
|
||||
assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
|
||||
|
||||
let (window_id_b, workspace_b) =
|
||||
cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
|
||||
let window_b = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
|
||||
let workspace_b = window_b.root(cx_b);
|
||||
let editor_b = workspace_b
|
||||
.update(cx_b, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "b.txt"), None, true, cx)
|
||||
@ -1509,11 +1508,9 @@ async fn test_host_disconnect(
|
||||
.unwrap()
|
||||
.downcast::<Editor>()
|
||||
.unwrap();
|
||||
assert!(cx_b
|
||||
.read_window(window_id_b, |cx| editor_b.is_focused(cx))
|
||||
.unwrap());
|
||||
assert!(window_b.read_with(cx_b, |cx| editor_b.is_focused(cx)));
|
||||
editor_b.update(cx_b, |editor, cx| editor.insert("X", cx));
|
||||
assert!(cx_b.is_window_edited(workspace_b.window_id()));
|
||||
assert!(window_b.is_edited(cx_b));
|
||||
|
||||
// Drop client A's connection. Collaborators should disappear and the project should not be shown as shared.
|
||||
server.forbid_connections();
|
||||
@ -1525,10 +1522,10 @@ async fn test_host_disconnect(
|
||||
assert!(worktree_a.read_with(cx_a, |tree, _| !tree.as_local().unwrap().is_shared()));
|
||||
|
||||
// Ensure client B's edited state is reset and that the whole window is blurred.
|
||||
cx_b.read_window(window_id_b, |cx| {
|
||||
window_b.read_with(cx_b, |cx| {
|
||||
assert_eq!(cx.focused_view_id(), None);
|
||||
});
|
||||
assert!(!cx_b.is_window_edited(workspace_b.window_id()));
|
||||
assert!(!window_b.is_edited(cx_b));
|
||||
|
||||
// Ensure client B is not prompted to save edits when closing window after disconnecting.
|
||||
let can_close = workspace_b
|
||||
@ -3445,13 +3442,11 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor(
|
||||
.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let (window_a, _) = cx_a.add_window(|_| EmptyView);
|
||||
let editor_a = cx_a.add_view(window_a, |cx| {
|
||||
Editor::for_buffer(buffer_a, Some(project_a), cx)
|
||||
});
|
||||
let window_a = cx_a.add_window(|_| EmptyView);
|
||||
let editor_a = window_a.add_view(cx_a, |cx| Editor::for_buffer(buffer_a, Some(project_a), cx));
|
||||
let mut editor_cx_a = EditorTestContext {
|
||||
cx: cx_a,
|
||||
window_id: window_a,
|
||||
window: window_a.into(),
|
||||
editor: editor_a,
|
||||
};
|
||||
|
||||
@ -3460,13 +3455,11 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor(
|
||||
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let (window_b, _) = cx_b.add_window(|_| EmptyView);
|
||||
let editor_b = cx_b.add_view(window_b, |cx| {
|
||||
Editor::for_buffer(buffer_b, Some(project_b), cx)
|
||||
});
|
||||
let window_b = cx_b.add_window(|_| EmptyView);
|
||||
let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, Some(project_b), cx));
|
||||
let mut editor_cx_b = EditorTestContext {
|
||||
cx: cx_b,
|
||||
window_id: window_b,
|
||||
window: window_b.into(),
|
||||
editor: editor_b,
|
||||
};
|
||||
|
||||
@ -4205,8 +4198,8 @@ async fn test_collaborating_with_completion(
|
||||
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let (window_b, _) = cx_b.add_window(|_| EmptyView);
|
||||
let editor_b = cx_b.add_view(window_b, |cx| {
|
||||
let window_b = cx_b.add_window(|_| EmptyView);
|
||||
let editor_b = window_b.add_view(cx_b, |cx| {
|
||||
Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
|
||||
});
|
||||
|
||||
@ -5316,7 +5309,8 @@ async fn test_collaborating_with_code_actions(
|
||||
|
||||
// Join the project as client B.
|
||||
let project_b = client_b.build_remote_project(project_id, cx_b).await;
|
||||
let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
|
||||
let window_b = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
|
||||
let workspace_b = window_b.root(cx_b);
|
||||
let editor_b = workspace_b
|
||||
.update(cx_b, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "main.rs"), None, true, cx)
|
||||
@ -5540,7 +5534,8 @@ async fn test_collaborating_with_renames(
|
||||
.unwrap();
|
||||
let project_b = client_b.build_remote_project(project_id, cx_b).await;
|
||||
|
||||
let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
|
||||
let window_b = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx));
|
||||
let workspace_b = window_b.root(cx_b);
|
||||
let editor_b = workspace_b
|
||||
.update(cx_b, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "one.rs"), None, true, cx)
|
||||
@ -5571,6 +5566,7 @@ async fn test_collaborating_with_renames(
|
||||
.unwrap();
|
||||
prepare_rename.await.unwrap();
|
||||
editor_b.update(cx_b, |editor, cx| {
|
||||
use editor::ToOffset;
|
||||
let rename = editor.pending_rename().unwrap();
|
||||
let buffer = editor.buffer().read(cx).snapshot(cx);
|
||||
assert_eq!(
|
||||
@ -6445,8 +6441,10 @@ async fn test_basic_following(
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a);
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b);
|
||||
let window_a = client_a.build_workspace(&project_a, cx_a);
|
||||
let workspace_a = window_a.root(cx_a);
|
||||
let window_b = client_b.build_workspace(&project_b, cx_b);
|
||||
let workspace_b = window_b.root(cx_b);
|
||||
|
||||
// Client A opens some editors.
|
||||
let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
|
||||
@ -6529,7 +6527,8 @@ async fn test_basic_following(
|
||||
cx_c.foreground().run_until_parked();
|
||||
let active_call_c = cx_c.read(ActiveCall::global);
|
||||
let project_c = client_c.build_remote_project(project_id, cx_c).await;
|
||||
let workspace_c = client_c.build_workspace(&project_c, cx_c);
|
||||
let window_c = client_c.build_workspace(&project_c, cx_c);
|
||||
let workspace_c = window_c.root(cx_c);
|
||||
active_call_c
|
||||
.update(cx_c, |call, cx| call.set_location(Some(&project_c), cx))
|
||||
.await
|
||||
@ -6547,7 +6546,7 @@ async fn test_basic_following(
|
||||
cx_d.foreground().run_until_parked();
|
||||
let active_call_d = cx_d.read(ActiveCall::global);
|
||||
let project_d = client_d.build_remote_project(project_id, cx_d).await;
|
||||
let workspace_d = client_d.build_workspace(&project_d, cx_d);
|
||||
let workspace_d = client_d.build_workspace(&project_d, cx_d).root(cx_d);
|
||||
active_call_d
|
||||
.update(cx_d, |call, cx| call.set_location(Some(&project_d), cx))
|
||||
.await
|
||||
@ -6645,6 +6644,7 @@ async fn test_basic_following(
|
||||
}
|
||||
|
||||
// Client C closes the project.
|
||||
window_c.remove(cx_c);
|
||||
cx_c.drop_last(workspace_c);
|
||||
|
||||
// Clients A and B see that client B is following A, and client C is not present in the followers.
|
||||
@ -6874,9 +6874,7 @@ async fn test_basic_following(
|
||||
});
|
||||
|
||||
// Client B activates a panel, and the previously-opened screen-sharing item gets activated.
|
||||
let panel = cx_b.add_view(workspace_b.window_id(), |_| {
|
||||
TestPanel::new(DockPosition::Left)
|
||||
});
|
||||
let panel = window_b.add_view(cx_b, |_| TestPanel::new(DockPosition::Left));
|
||||
workspace_b.update(cx_b, |workspace, cx| {
|
||||
workspace.add_panel(panel, cx);
|
||||
workspace.toggle_panel_focus::<TestPanel>(cx);
|
||||
@ -6904,7 +6902,7 @@ async fn test_basic_following(
|
||||
|
||||
// Client B activates an item that doesn't implement following,
|
||||
// so the previously-opened screen-sharing item gets activated.
|
||||
let unfollowable_item = cx_b.add_view(workspace_b.window_id(), |_| TestItem::new());
|
||||
let unfollowable_item = window_b.add_view(cx_b, |_| TestItem::new());
|
||||
workspace_b.update(cx_b, |workspace, cx| {
|
||||
workspace.active_pane().update(cx, |pane, cx| {
|
||||
pane.add_item(Box::new(unfollowable_item), true, true, None, cx)
|
||||
@ -7066,10 +7064,10 @@ async fn test_following_tab_order(
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a);
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
||||
let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b);
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
||||
let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
|
||||
|
||||
let client_b_id = project_a.read_with(cx_a, |project, _| {
|
||||
@ -7192,7 +7190,7 @@ async fn test_peers_following_each_other(
|
||||
.unwrap();
|
||||
|
||||
// Client A opens some editors.
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a);
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
||||
let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
|
||||
let _editor_a1 = workspace_a
|
||||
.update(cx_a, |workspace, cx| {
|
||||
@ -7204,7 +7202,7 @@ async fn test_peers_following_each_other(
|
||||
.unwrap();
|
||||
|
||||
// Client B opens an editor.
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b);
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
||||
let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
|
||||
let _editor_b1 = workspace_b
|
||||
.update(cx_b, |workspace, cx| {
|
||||
@ -7363,7 +7361,7 @@ async fn test_auto_unfollowing(
|
||||
.unwrap();
|
||||
|
||||
// Client A opens some editors.
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a);
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
||||
let _editor_a1 = workspace_a
|
||||
.update(cx_a, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "1.txt"), None, true, cx)
|
||||
@ -7374,7 +7372,7 @@ async fn test_auto_unfollowing(
|
||||
.unwrap();
|
||||
|
||||
// Client B starts following client A.
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b);
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
||||
let pane_b = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
|
||||
let leader_id = project_b.read_with(cx_b, |project, _| {
|
||||
project.collaborators().values().next().unwrap().peer_id
|
||||
@ -7502,14 +7500,14 @@ async fn test_peers_simultaneously_following_each_other(
|
||||
|
||||
client_a.fs.insert_tree("/a", json!({})).await;
|
||||
let (project_a, _) = client_a.build_local_project("/a", cx_a).await;
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a);
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
||||
let project_id = active_call_a
|
||||
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let project_b = client_b.build_remote_project(project_id, cx_b).await;
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b);
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
||||
|
||||
deterministic.run_until_parked();
|
||||
let client_a_id = project_b.read_with(cx_b, |project, _| {
|
||||
@ -7601,8 +7599,8 @@ async fn test_on_input_format_from_host_to_guest(
|
||||
.update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let (window_a, _) = cx_a.add_window(|_| EmptyView);
|
||||
let editor_a = cx_a.add_view(window_a, |cx| {
|
||||
let window_a = cx_a.add_window(|_| EmptyView);
|
||||
let editor_a = window_a.add_view(cx_a, |cx| {
|
||||
Editor::for_buffer(buffer_a, Some(project_a.clone()), cx)
|
||||
});
|
||||
|
||||
@ -7730,8 +7728,8 @@ async fn test_on_input_format_from_guest_to_host(
|
||||
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
let (window_b, _) = cx_b.add_window(|_| EmptyView);
|
||||
let editor_b = cx_b.add_view(window_b, |cx| {
|
||||
let window_b = cx_b.add_window(|_| EmptyView);
|
||||
let editor_b = window_b.add_view(cx_b, |cx| {
|
||||
Editor::for_buffer(buffer_b, Some(project_b.clone()), cx)
|
||||
});
|
||||
|
||||
@ -7891,7 +7889,7 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a);
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
||||
cx_a.foreground().start_waiting();
|
||||
|
||||
let _buffer_a = project_a
|
||||
@ -7955,11 +7953,12 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
);
|
||||
let inlay_cache = editor.inlay_hint_cache();
|
||||
assert_eq!(
|
||||
inlay_cache.version, edits_made,
|
||||
inlay_cache.version(),
|
||||
edits_made,
|
||||
"Host editor update the cache version after every cache/view change",
|
||||
);
|
||||
});
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b);
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
||||
let editor_b = workspace_b
|
||||
.update(cx_b, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "main.rs"), None, true, cx)
|
||||
@ -7978,7 +7977,8 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
);
|
||||
let inlay_cache = editor.inlay_hint_cache();
|
||||
assert_eq!(
|
||||
inlay_cache.version, edits_made,
|
||||
inlay_cache.version(),
|
||||
edits_made,
|
||||
"Guest editor update the cache version after every cache/view change"
|
||||
);
|
||||
});
|
||||
@ -7998,7 +7998,7 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
"Host should get hints from the 1st edit and 1st LSP query"
|
||||
);
|
||||
let inlay_cache = editor.inlay_hint_cache();
|
||||
assert_eq!(inlay_cache.version, edits_made);
|
||||
assert_eq!(inlay_cache.version(), edits_made);
|
||||
});
|
||||
editor_b.update(cx_b, |editor, _| {
|
||||
assert_eq!(
|
||||
@ -8012,7 +8012,7 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
"Guest should get hints the 1st edit and 2nd LSP query"
|
||||
);
|
||||
let inlay_cache = editor.inlay_hint_cache();
|
||||
assert_eq!(inlay_cache.version, edits_made);
|
||||
assert_eq!(inlay_cache.version(), edits_made);
|
||||
});
|
||||
|
||||
editor_a.update(cx_a, |editor, cx| {
|
||||
@ -8037,7 +8037,7 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
4th query was made by guest (but not applied) due to cache invalidation logic"
|
||||
);
|
||||
let inlay_cache = editor.inlay_hint_cache();
|
||||
assert_eq!(inlay_cache.version, edits_made);
|
||||
assert_eq!(inlay_cache.version(), edits_made);
|
||||
});
|
||||
editor_b.update(cx_b, |editor, _| {
|
||||
assert_eq!(
|
||||
@ -8053,7 +8053,7 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
"Guest should get hints from 3rd edit, 6th LSP query"
|
||||
);
|
||||
let inlay_cache = editor.inlay_hint_cache();
|
||||
assert_eq!(inlay_cache.version, edits_made);
|
||||
assert_eq!(inlay_cache.version(), edits_made);
|
||||
});
|
||||
|
||||
fake_language_server
|
||||
@ -8079,7 +8079,8 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
);
|
||||
let inlay_cache = editor.inlay_hint_cache();
|
||||
assert_eq!(
|
||||
inlay_cache.version, edits_made,
|
||||
inlay_cache.version(),
|
||||
edits_made,
|
||||
"Host should accepted all edits and bump its cache version every time"
|
||||
);
|
||||
});
|
||||
@ -8100,7 +8101,7 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
);
|
||||
let inlay_cache = editor.inlay_hint_cache();
|
||||
assert_eq!(
|
||||
inlay_cache.version,
|
||||
inlay_cache.version(),
|
||||
edits_made,
|
||||
"Guest should accepted all edits and bump its cache version every time"
|
||||
);
|
||||
@ -8198,8 +8199,8 @@ async fn test_inlay_hint_refresh_is_forwarded(
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a);
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b);
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
||||
cx_a.foreground().start_waiting();
|
||||
cx_b.foreground().start_waiting();
|
||||
|
||||
@ -8266,7 +8267,8 @@ async fn test_inlay_hint_refresh_is_forwarded(
|
||||
);
|
||||
let inlay_cache = editor.inlay_hint_cache();
|
||||
assert_eq!(
|
||||
inlay_cache.version, 0,
|
||||
inlay_cache.version(),
|
||||
0,
|
||||
"Host should not increment its cache version due to no changes",
|
||||
);
|
||||
});
|
||||
@ -8281,7 +8283,8 @@ async fn test_inlay_hint_refresh_is_forwarded(
|
||||
);
|
||||
let inlay_cache = editor.inlay_hint_cache();
|
||||
assert_eq!(
|
||||
inlay_cache.version, edits_made,
|
||||
inlay_cache.version(),
|
||||
edits_made,
|
||||
"Guest editor update the cache version after every cache/view change"
|
||||
);
|
||||
});
|
||||
@ -8298,7 +8301,8 @@ async fn test_inlay_hint_refresh_is_forwarded(
|
||||
);
|
||||
let inlay_cache = editor.inlay_hint_cache();
|
||||
assert_eq!(
|
||||
inlay_cache.version, 0,
|
||||
inlay_cache.version(),
|
||||
0,
|
||||
"Host should not increment its cache version due to no changes",
|
||||
);
|
||||
});
|
||||
@ -8313,7 +8317,8 @@ async fn test_inlay_hint_refresh_is_forwarded(
|
||||
);
|
||||
let inlay_cache = editor.inlay_hint_cache();
|
||||
assert_eq!(
|
||||
inlay_cache.version, edits_made,
|
||||
inlay_cache.version(),
|
||||
edits_made,
|
||||
"Guest should accepted all edits and bump its cache version every time"
|
||||
);
|
||||
});
|
||||
@ -8345,13 +8350,10 @@ fn room_participants(room: &ModelHandle<Room>, cx: &mut TestAppContext) -> RoomP
|
||||
|
||||
fn extract_hint_labels(editor: &Editor) -> Vec<String> {
|
||||
let mut labels = Vec::new();
|
||||
for (_, excerpt_hints) in &editor.inlay_hint_cache().hints {
|
||||
let excerpt_hints = excerpt_hints.read();
|
||||
for (_, inlay) in excerpt_hints.hints.iter() {
|
||||
match &inlay.label {
|
||||
project::InlayHintLabel::String(s) => labels.push(s.to_string()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
for hint in editor.inlay_hint_cache().hints() {
|
||||
match hint.label {
|
||||
project::InlayHintLabel::String(s) => labels.push(s),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
labels
|
||||
|
@ -183,7 +183,7 @@ async fn apply_server_operation(
|
||||
let username;
|
||||
{
|
||||
let mut plan = plan.lock();
|
||||
let mut user = plan.user(user_id);
|
||||
let user = plan.user(user_id);
|
||||
if user.online {
|
||||
return false;
|
||||
}
|
||||
|
@ -15,8 +15,8 @@ use gpui::{
|
||||
geometry::{rect::RectF, vector::vec2f, PathBuilder},
|
||||
json::{self, ToJson},
|
||||
platform::{CursorStyle, MouseButton},
|
||||
AppContext, Entity, ImageData, LayoutContext, ModelHandle, SceneBuilder, Subscription, View,
|
||||
ViewContext, ViewHandle, WeakViewHandle,
|
||||
AppContext, Entity, ImageData, LayoutContext, ModelHandle, PaintContext, SceneBuilder,
|
||||
Subscription, View, ViewContext, ViewHandle, WeakViewHandle,
|
||||
};
|
||||
use picker::PickerEvent;
|
||||
use project::{Project, RepositoryEntry};
|
||||
@ -238,7 +238,7 @@ impl CollabTitlebarItem {
|
||||
.left()
|
||||
.with_tooltip::<RecentProjectsTooltip>(
|
||||
0,
|
||||
"Recent projects".into(),
|
||||
"Recent projects",
|
||||
Some(Box::new(recent_projects::OpenRecent)),
|
||||
theme.tooltip.clone(),
|
||||
cx,
|
||||
@ -282,7 +282,7 @@ impl CollabTitlebarItem {
|
||||
.left()
|
||||
.with_tooltip::<BranchPopoverTooltip>(
|
||||
0,
|
||||
"Recent branches".into(),
|
||||
"Recent branches",
|
||||
Some(Box::new(ToggleVcsMenu)),
|
||||
theme.tooltip.clone(),
|
||||
cx,
|
||||
@ -582,7 +582,7 @@ impl CollabTitlebarItem {
|
||||
})
|
||||
.with_tooltip::<ToggleContactsMenu>(
|
||||
0,
|
||||
"Show contacts menu".into(),
|
||||
"Show contacts menu",
|
||||
Some(Box::new(ToggleContactsMenu)),
|
||||
theme.tooltip.clone(),
|
||||
cx,
|
||||
@ -633,7 +633,7 @@ impl CollabTitlebarItem {
|
||||
})
|
||||
.with_tooltip::<ToggleScreenSharing>(
|
||||
0,
|
||||
tooltip.into(),
|
||||
tooltip,
|
||||
Some(Box::new(ToggleScreenSharing)),
|
||||
theme.tooltip.clone(),
|
||||
cx,
|
||||
@ -686,7 +686,7 @@ impl CollabTitlebarItem {
|
||||
})
|
||||
.with_tooltip::<ToggleMute>(
|
||||
0,
|
||||
tooltip.into(),
|
||||
tooltip,
|
||||
Some(Box::new(ToggleMute)),
|
||||
theme.tooltip.clone(),
|
||||
cx,
|
||||
@ -734,7 +734,7 @@ impl CollabTitlebarItem {
|
||||
})
|
||||
.with_tooltip::<ToggleDeafen>(
|
||||
0,
|
||||
tooltip.into(),
|
||||
tooltip,
|
||||
Some(Box::new(ToggleDeafen)),
|
||||
theme.tooltip.clone(),
|
||||
cx,
|
||||
@ -768,7 +768,7 @@ impl CollabTitlebarItem {
|
||||
})
|
||||
.with_tooltip::<LeaveCall>(
|
||||
0,
|
||||
tooltip.into(),
|
||||
tooltip,
|
||||
Some(Box::new(LeaveCall)),
|
||||
theme.tooltip.clone(),
|
||||
cx,
|
||||
@ -1312,7 +1312,7 @@ impl Element<CollabTitlebarItem> for AvatarRibbon {
|
||||
_: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
_: &mut CollabTitlebarItem,
|
||||
_: &mut ViewContext<CollabTitlebarItem>,
|
||||
_: &mut PaintContext<CollabTitlebarItem>,
|
||||
) -> Self::PaintState {
|
||||
let mut path = PathBuilder::new();
|
||||
path.reset(bounds.lower_left());
|
||||
|
@ -305,18 +305,18 @@ impl ContactList {
|
||||
github_login
|
||||
);
|
||||
let mut answer = cx.prompt(PromptLevel::Warning, &prompt_message, &["Remove", "Cancel"]);
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
if answer.next().await == Some(0) {
|
||||
if let Err(e) = user_store
|
||||
.update(&mut cx, |store, cx| store.remove_contact(user_id, cx))
|
||||
.await
|
||||
{
|
||||
cx.prompt(
|
||||
window_id,
|
||||
window.prompt(
|
||||
PromptLevel::Info,
|
||||
&format!("Failed to remove contact: {}", e),
|
||||
&["Ok"],
|
||||
&mut cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -837,7 +837,7 @@ impl ContactList {
|
||||
),
|
||||
background: Some(tree_branch.color),
|
||||
border: gpui::Border::default(),
|
||||
corner_radius: 0.,
|
||||
corner_radii: Default::default(),
|
||||
});
|
||||
scene.push_quad(gpui::Quad {
|
||||
bounds: RectF::from_points(
|
||||
@ -846,7 +846,7 @@ impl ContactList {
|
||||
),
|
||||
background: Some(tree_branch.color),
|
||||
border: gpui::Border::default(),
|
||||
corner_radius: 0.,
|
||||
corner_radii: Default::default(),
|
||||
});
|
||||
}))
|
||||
.constrained()
|
||||
@ -934,7 +934,7 @@ impl ContactList {
|
||||
),
|
||||
background: Some(tree_branch.color),
|
||||
border: gpui::Border::default(),
|
||||
corner_radius: 0.,
|
||||
corner_radii: Default::default(),
|
||||
});
|
||||
scene.push_quad(gpui::Quad {
|
||||
bounds: RectF::from_points(
|
||||
@ -943,7 +943,7 @@ impl ContactList {
|
||||
),
|
||||
background: Some(tree_branch.color),
|
||||
border: gpui::Border::default(),
|
||||
corner_radius: 0.,
|
||||
corner_radii: Default::default(),
|
||||
});
|
||||
}))
|
||||
.constrained()
|
||||
@ -1345,7 +1345,7 @@ impl View for ContactList {
|
||||
})
|
||||
.with_tooltip::<AddContact>(
|
||||
0,
|
||||
"Search for new contact".into(),
|
||||
"Search for new contact",
|
||||
None,
|
||||
theme.tooltip.clone(),
|
||||
cx,
|
||||
|
@ -7,7 +7,7 @@ use gpui::{
|
||||
},
|
||||
json::ToJson,
|
||||
serde_json::{self, json},
|
||||
AnyElement, Axis, Element, LayoutContext, SceneBuilder, ViewContext,
|
||||
AnyElement, Axis, Element, LayoutContext, PaintContext, SceneBuilder, ViewContext,
|
||||
};
|
||||
|
||||
use crate::CollabTitlebarItem;
|
||||
@ -54,7 +54,7 @@ impl Element<CollabTitlebarItem> for FacePile {
|
||||
visible_bounds: RectF,
|
||||
_layout: &mut Self::LayoutState,
|
||||
view: &mut CollabTitlebarItem,
|
||||
cx: &mut ViewContext<CollabTitlebarItem>,
|
||||
cx: &mut PaintContext<CollabTitlebarItem>,
|
||||
) -> Self::PaintState {
|
||||
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||
|
||||
|
@ -7,7 +7,7 @@ use gpui::{
|
||||
elements::*,
|
||||
geometry::{rect::RectF, vector::vec2f},
|
||||
platform::{CursorStyle, MouseButton, WindowBounds, WindowKind, WindowOptions},
|
||||
AnyElement, AppContext, Entity, View, ViewContext,
|
||||
AnyElement, AppContext, Entity, View, ViewContext, WindowHandle,
|
||||
};
|
||||
use util::ResultExt;
|
||||
use workspace::AppState;
|
||||
@ -16,10 +16,10 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
|
||||
let app_state = Arc::downgrade(app_state);
|
||||
let mut incoming_call = ActiveCall::global(cx).read(cx).incoming();
|
||||
cx.spawn(|mut cx| async move {
|
||||
let mut notification_windows = Vec::new();
|
||||
let mut notification_windows: Vec<WindowHandle<IncomingCallNotification>> = Vec::new();
|
||||
while let Some(incoming_call) = incoming_call.next().await {
|
||||
for window_id in notification_windows.drain(..) {
|
||||
cx.remove_window(window_id);
|
||||
for window in notification_windows.drain(..) {
|
||||
window.remove(&mut cx);
|
||||
}
|
||||
|
||||
if let Some(incoming_call) = incoming_call {
|
||||
@ -31,7 +31,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
|
||||
|
||||
for screen in cx.platform().screens() {
|
||||
let screen_bounds = screen.bounds();
|
||||
let (window_id, _) = cx.add_window(
|
||||
let window = cx.add_window(
|
||||
WindowOptions {
|
||||
bounds: WindowBounds::Fixed(RectF::new(
|
||||
screen_bounds.upper_right()
|
||||
@ -49,7 +49,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
|
||||
|_| IncomingCallNotification::new(incoming_call.clone(), app_state.clone()),
|
||||
);
|
||||
|
||||
notification_windows.push(window_id);
|
||||
notification_windows.push(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
|
||||
|
||||
for screen in cx.platform().screens() {
|
||||
let screen_bounds = screen.bounds();
|
||||
let (window_id, _) = cx.add_window(
|
||||
let window = cx.add_window(
|
||||
WindowOptions {
|
||||
bounds: WindowBounds::Fixed(RectF::new(
|
||||
screen_bounds.upper_right() - vec2f(PADDING + window_size.x(), PADDING),
|
||||
@ -52,20 +52,20 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
|
||||
notification_windows
|
||||
.entry(*project_id)
|
||||
.or_insert(Vec::new())
|
||||
.push(window_id);
|
||||
.push(window);
|
||||
}
|
||||
}
|
||||
room::Event::RemoteProjectUnshared { project_id } => {
|
||||
if let Some(window_ids) = notification_windows.remove(&project_id) {
|
||||
for window_id in window_ids {
|
||||
cx.update_window(window_id, |cx| cx.remove_window());
|
||||
if let Some(windows) = notification_windows.remove(&project_id) {
|
||||
for window in windows {
|
||||
window.remove(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
room::Event::Left => {
|
||||
for (_, window_ids) in notification_windows.drain() {
|
||||
for window_id in window_ids {
|
||||
cx.update_window(window_id, |cx| cx.remove_window());
|
||||
for (_, windows) in notification_windows.drain() {
|
||||
for window in windows {
|
||||
window.remove(cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,11 @@ pub fn init(cx: &mut AppContext) {
|
||||
{
|
||||
status_indicator = Some(cx.add_status_bar_item(|_| SharingStatusIndicator));
|
||||
}
|
||||
} else if let Some((window_id, _)) = status_indicator.take() {
|
||||
cx.update_window(window_id, |cx| cx.remove_window());
|
||||
} else if let Some(window) = status_indicator.take() {
|
||||
window.update(cx, |cx| cx.remove_window());
|
||||
}
|
||||
} else if let Some((window_id, _)) = status_indicator.take() {
|
||||
cx.update_window(window_id, |cx| cx.remove_window());
|
||||
} else if let Some(window) = status_indicator.take() {
|
||||
window.update(cx, |cx| cx.remove_window());
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
@ -1,8 +1,8 @@
|
||||
use collections::CommandPaletteFilter;
|
||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
use gpui::{
|
||||
actions, elements::*, keymap_matcher::Keystroke, Action, AppContext, Element, MouseState,
|
||||
ViewContext,
|
||||
actions, anyhow::anyhow, elements::*, keymap_matcher::Keystroke, Action, AnyWindowHandle,
|
||||
AppContext, Element, MouseState, ViewContext,
|
||||
};
|
||||
use picker::{Picker, PickerDelegate, PickerEvent};
|
||||
use std::cmp;
|
||||
@ -28,7 +28,7 @@ pub struct CommandPaletteDelegate {
|
||||
pub enum Event {
|
||||
Dismissed,
|
||||
Confirmed {
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
focused_view_id: usize,
|
||||
action: Box<dyn Action>,
|
||||
},
|
||||
@ -80,12 +80,13 @@ impl PickerDelegate for CommandPaletteDelegate {
|
||||
query: String,
|
||||
cx: &mut ViewContext<Picker<Self>>,
|
||||
) -> gpui::Task<()> {
|
||||
let window_id = cx.window_id();
|
||||
let view_id = self.focused_view_id;
|
||||
let window = cx.window();
|
||||
cx.spawn(move |picker, mut cx| async move {
|
||||
let actions = cx
|
||||
.available_actions(window_id, view_id)
|
||||
let actions = window
|
||||
.available_actions(view_id, &cx)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter_map(|(name, action, bindings)| {
|
||||
let filtered = cx.read(|cx| {
|
||||
if cx.has_global::<CommandPaletteFilter>() {
|
||||
@ -162,13 +163,15 @@ impl PickerDelegate for CommandPaletteDelegate {
|
||||
|
||||
fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
|
||||
if !self.matches.is_empty() {
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
let focused_view_id = self.focused_view_id;
|
||||
let action_ix = self.matches[self.selected_ix].candidate_id;
|
||||
let action = self.actions.remove(action_ix).action;
|
||||
cx.app_context()
|
||||
.spawn(move |mut cx| async move {
|
||||
cx.dispatch_action(window_id, focused_view_id, action.as_ref())
|
||||
window
|
||||
.dispatch_action(focused_view_id, action.as_ref(), &mut cx)
|
||||
.ok_or_else(|| anyhow!("window was closed"))
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
@ -295,8 +298,9 @@ mod tests {
|
||||
let app_state = init_test(cx);
|
||||
|
||||
let project = Project::test(app_state.fs.clone(), [], cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let editor = cx.add_view(window_id, |cx| {
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let workspace = window.root(cx);
|
||||
let editor = window.add_view(cx, |cx| {
|
||||
let mut editor = Editor::single_line(None, cx);
|
||||
editor.set_text("abc", cx);
|
||||
editor
|
||||
|
@ -1,5 +1,5 @@
|
||||
use gpui::{
|
||||
anyhow,
|
||||
anyhow::{self, anyhow},
|
||||
elements::*,
|
||||
geometry::vector::Vector2F,
|
||||
keymap_matcher::KeymapContext,
|
||||
@ -218,12 +218,14 @@ impl ContextMenu {
|
||||
if let Some(ContextMenuItem::Item { action, .. }) = self.items.get(ix) {
|
||||
match action {
|
||||
ContextMenuItemAction::Action(action) => {
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
let view_id = self.parent_view_id;
|
||||
let action = action.boxed_clone();
|
||||
cx.app_context()
|
||||
.spawn(|mut cx| async move {
|
||||
cx.dispatch_action(window_id, view_id, action.as_ref())
|
||||
window
|
||||
.dispatch_action(view_id, action.as_ref(), &mut cx)
|
||||
.ok_or_else(|| anyhow!("window was closed"))
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
@ -480,17 +482,19 @@ impl ContextMenu {
|
||||
.on_down(MouseButton::Left, |_, _, _| {}) // Capture these events
|
||||
.on_click(MouseButton::Left, move |_, menu, cx| {
|
||||
menu.cancel(&Default::default(), cx);
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
match &action {
|
||||
ContextMenuItemAction::Action(action) => {
|
||||
let action = action.boxed_clone();
|
||||
cx.app_context()
|
||||
.spawn(|mut cx| async move {
|
||||
cx.dispatch_action(
|
||||
window_id,
|
||||
view_id,
|
||||
action.as_ref(),
|
||||
)
|
||||
window
|
||||
.dispatch_action(
|
||||
view_id,
|
||||
action.as_ref(),
|
||||
&mut cx,
|
||||
)
|
||||
.ok_or_else(|| anyhow!("window was closed"))
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use gpui::{
|
||||
geometry::rect::RectF,
|
||||
platform::{WindowBounds, WindowKind, WindowOptions},
|
||||
AnyElement, AnyViewHandle, AppContext, ClipboardItem, Element, Entity, View, ViewContext,
|
||||
ViewHandle,
|
||||
WindowHandle,
|
||||
};
|
||||
use theme::ui::modal;
|
||||
|
||||
@ -18,43 +18,43 @@ const COPILOT_SIGN_UP_URL: &'static str = "https://github.com/features/copilot";
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
if let Some(copilot) = Copilot::global(cx) {
|
||||
let mut code_verification: Option<ViewHandle<CopilotCodeVerification>> = None;
|
||||
let mut verification_window: Option<WindowHandle<CopilotCodeVerification>> = None;
|
||||
cx.observe(&copilot, move |copilot, cx| {
|
||||
let status = copilot.read(cx).status();
|
||||
|
||||
match &status {
|
||||
crate::Status::SigningIn { prompt } => {
|
||||
if let Some(code_verification_handle) = code_verification.as_mut() {
|
||||
let window_id = code_verification_handle.window_id();
|
||||
let updated = cx.update_window(window_id, |cx| {
|
||||
code_verification_handle.update(cx, |code_verification, cx| {
|
||||
code_verification.set_status(status.clone(), cx)
|
||||
});
|
||||
cx.activate_window();
|
||||
});
|
||||
if updated.is_none() {
|
||||
code_verification = Some(create_copilot_auth_window(cx, &status));
|
||||
if let Some(window) = verification_window.as_mut() {
|
||||
let updated = window
|
||||
.root(cx)
|
||||
.map(|root| {
|
||||
root.update(cx, |verification, cx| {
|
||||
verification.set_status(status.clone(), cx);
|
||||
cx.activate_window();
|
||||
})
|
||||
})
|
||||
.is_some();
|
||||
if !updated {
|
||||
verification_window = Some(create_copilot_auth_window(cx, &status));
|
||||
}
|
||||
} else if let Some(_prompt) = prompt {
|
||||
code_verification = Some(create_copilot_auth_window(cx, &status));
|
||||
verification_window = Some(create_copilot_auth_window(cx, &status));
|
||||
}
|
||||
}
|
||||
Status::Authorized | Status::Unauthorized => {
|
||||
if let Some(code_verification) = code_verification.as_ref() {
|
||||
let window_id = code_verification.window_id();
|
||||
cx.update_window(window_id, |cx| {
|
||||
code_verification.update(cx, |code_verification, cx| {
|
||||
code_verification.set_status(status, cx)
|
||||
if let Some(window) = verification_window.as_ref() {
|
||||
if let Some(verification) = window.root(cx) {
|
||||
verification.update(cx, |verification, cx| {
|
||||
verification.set_status(status, cx);
|
||||
cx.platform().activate(true);
|
||||
cx.activate_window();
|
||||
});
|
||||
|
||||
cx.platform().activate(true);
|
||||
cx.activate_window();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if let Some(code_verification) = code_verification.take() {
|
||||
cx.update_window(code_verification.window_id(), |cx| cx.remove_window());
|
||||
if let Some(code_verification) = verification_window.take() {
|
||||
code_verification.update(cx, |cx| cx.remove_window());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -66,7 +66,7 @@ pub fn init(cx: &mut AppContext) {
|
||||
fn create_copilot_auth_window(
|
||||
cx: &mut AppContext,
|
||||
status: &Status,
|
||||
) -> ViewHandle<CopilotCodeVerification> {
|
||||
) -> WindowHandle<CopilotCodeVerification> {
|
||||
let window_size = theme::current(cx).copilot.modal.dimensions();
|
||||
let window_options = WindowOptions {
|
||||
bounds: WindowBounds::Fixed(RectF::new(Default::default(), window_size)),
|
||||
@ -78,10 +78,9 @@ fn create_copilot_auth_window(
|
||||
is_movable: true,
|
||||
screen: None,
|
||||
};
|
||||
let (_, view) = cx.add_window(window_options, |_cx| {
|
||||
cx.add_window(window_options, |_cx| {
|
||||
CopilotCodeVerification::new(status.clone())
|
||||
});
|
||||
view
|
||||
})
|
||||
}
|
||||
|
||||
pub struct CopilotCodeVerification {
|
||||
|
@ -140,7 +140,7 @@ impl View for CopilotButton {
|
||||
})
|
||||
.with_tooltip::<Self>(
|
||||
0,
|
||||
"GitHub Copilot".into(),
|
||||
"GitHub Copilot",
|
||||
None,
|
||||
theme.tooltip.clone(),
|
||||
cx,
|
||||
|
@ -855,7 +855,8 @@ mod tests {
|
||||
|
||||
let language_server_id = LanguageServerId(0);
|
||||
let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let workspace = window.root(cx);
|
||||
|
||||
// Create some diagnostics
|
||||
project.update(cx, |project, cx| {
|
||||
@ -942,7 +943,7 @@ mod tests {
|
||||
});
|
||||
|
||||
// Open the project diagnostics view while there are already diagnostics.
|
||||
let view = cx.add_view(window_id, |cx| {
|
||||
let view = window.add_view(cx, |cx| {
|
||||
ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)
|
||||
});
|
||||
|
||||
@ -1248,9 +1249,10 @@ mod tests {
|
||||
let server_id_1 = LanguageServerId(100);
|
||||
let server_id_2 = LanguageServerId(101);
|
||||
let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let workspace = window.root(cx);
|
||||
|
||||
let view = cx.add_view(window_id, |cx| {
|
||||
let view = window.add_view(cx, |cx| {
|
||||
ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)
|
||||
});
|
||||
|
||||
|
@ -173,7 +173,7 @@ impl View for DiagnosticIndicator {
|
||||
})
|
||||
.with_tooltip::<Summary>(
|
||||
0,
|
||||
"Project Diagnostics".to_string(),
|
||||
"Project Diagnostics",
|
||||
Some(Box::new(crate::Deploy)),
|
||||
tooltip_style,
|
||||
cx,
|
||||
|
@ -6,7 +6,7 @@ use gpui::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
platform::{CursorStyle, MouseButton},
|
||||
scene::{MouseDown, MouseDrag},
|
||||
AnyElement, Element, View, ViewContext, WeakViewHandle, WindowContext,
|
||||
AnyElement, AnyWindowHandle, Element, View, ViewContext, WeakViewHandle, WindowContext,
|
||||
};
|
||||
|
||||
const DEAD_ZONE: f32 = 4.;
|
||||
@ -21,7 +21,7 @@ enum State<V: View> {
|
||||
region: RectF,
|
||||
},
|
||||
Dragging {
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
position: Vector2F,
|
||||
region_offset: Vector2F,
|
||||
region: RectF,
|
||||
@ -49,14 +49,14 @@ impl<V: View> Clone for State<V> {
|
||||
region,
|
||||
},
|
||||
State::Dragging {
|
||||
window_id,
|
||||
window,
|
||||
position,
|
||||
region_offset,
|
||||
region,
|
||||
payload,
|
||||
render,
|
||||
} => Self::Dragging {
|
||||
window_id: window_id.clone(),
|
||||
window: window.clone(),
|
||||
position: position.clone(),
|
||||
region_offset: region_offset.clone(),
|
||||
region: region.clone(),
|
||||
@ -87,16 +87,16 @@ impl<V: View> DragAndDrop<V> {
|
||||
self.containers.insert(handle);
|
||||
}
|
||||
|
||||
pub fn currently_dragged<T: Any>(&self, window_id: usize) -> Option<(Vector2F, Rc<T>)> {
|
||||
pub fn currently_dragged<T: Any>(&self, window: AnyWindowHandle) -> Option<(Vector2F, Rc<T>)> {
|
||||
self.currently_dragged.as_ref().and_then(|state| {
|
||||
if let State::Dragging {
|
||||
position,
|
||||
payload,
|
||||
window_id: window_dragged_from,
|
||||
window: window_dragged_from,
|
||||
..
|
||||
} = state
|
||||
{
|
||||
if &window_id != window_dragged_from {
|
||||
if &window != window_dragged_from {
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -126,9 +126,9 @@ impl<V: View> DragAndDrop<V> {
|
||||
cx: &mut WindowContext,
|
||||
render: Rc<impl 'static + Fn(&T, &mut ViewContext<V>) -> AnyElement<V>>,
|
||||
) {
|
||||
let window_id = cx.window_id();
|
||||
let window = cx.window();
|
||||
cx.update_global(|this: &mut Self, cx| {
|
||||
this.notify_containers_for_window(window_id, cx);
|
||||
this.notify_containers_for_window(window, cx);
|
||||
|
||||
match this.currently_dragged.as_ref() {
|
||||
Some(&State::Down {
|
||||
@ -141,7 +141,7 @@ impl<V: View> DragAndDrop<V> {
|
||||
}) => {
|
||||
if (event.position - (region.origin() + region_offset)).length() > DEAD_ZONE {
|
||||
this.currently_dragged = Some(State::Dragging {
|
||||
window_id,
|
||||
window,
|
||||
region_offset,
|
||||
region,
|
||||
position: event.position,
|
||||
@ -163,7 +163,7 @@ impl<V: View> DragAndDrop<V> {
|
||||
..
|
||||
}) => {
|
||||
this.currently_dragged = Some(State::Dragging {
|
||||
window_id,
|
||||
window,
|
||||
region_offset,
|
||||
region,
|
||||
position: event.position,
|
||||
@ -188,14 +188,14 @@ impl<V: View> DragAndDrop<V> {
|
||||
State::Down { .. } => None,
|
||||
State::DeadZone { .. } => None,
|
||||
State::Dragging {
|
||||
window_id,
|
||||
window,
|
||||
region_offset,
|
||||
position,
|
||||
region,
|
||||
payload,
|
||||
render,
|
||||
} => {
|
||||
if cx.window_id() != window_id {
|
||||
if cx.window() != window {
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -260,27 +260,27 @@ impl<V: View> DragAndDrop<V> {
|
||||
|
||||
pub fn cancel_dragging<P: Any>(&mut self, cx: &mut WindowContext) {
|
||||
if let Some(State::Dragging {
|
||||
payload, window_id, ..
|
||||
payload, window, ..
|
||||
}) = &self.currently_dragged
|
||||
{
|
||||
if payload.is::<P>() {
|
||||
let window_id = *window_id;
|
||||
let window = *window;
|
||||
self.currently_dragged = Some(State::Canceled);
|
||||
self.notify_containers_for_window(window_id, cx);
|
||||
self.notify_containers_for_window(window, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn finish_dragging(&mut self, cx: &mut WindowContext) {
|
||||
if let Some(State::Dragging { window_id, .. }) = self.currently_dragged.take() {
|
||||
self.notify_containers_for_window(window_id, cx);
|
||||
if let Some(State::Dragging { window, .. }) = self.currently_dragged.take() {
|
||||
self.notify_containers_for_window(window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn notify_containers_for_window(&mut self, window_id: usize, cx: &mut WindowContext) {
|
||||
fn notify_containers_for_window(&mut self, window: AnyWindowHandle, cx: &mut WindowContext) {
|
||||
self.containers.retain(|container| {
|
||||
if let Some(container) = container.upgrade(cx) {
|
||||
if container.window_id() == window_id {
|
||||
if container.window() == window {
|
||||
container.update(cx, |_, cx| cx.notify());
|
||||
}
|
||||
true
|
||||
|
@ -47,6 +47,7 @@ workspace = { path = "../workspace" }
|
||||
|
||||
aho-corasick = "0.7"
|
||||
anyhow.workspace = true
|
||||
convert_case = "0.6.0"
|
||||
futures.workspace = true
|
||||
indoc = "1.0.4"
|
||||
itertools = "0.10"
|
||||
@ -56,12 +57,12 @@ ordered-float.workspace = true
|
||||
parking_lot.workspace = true
|
||||
postage.workspace = true
|
||||
pulldown-cmark = { version = "0.9.2", default-features = false }
|
||||
rand.workspace = true
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
smallvec.workspace = true
|
||||
smol.workspace = true
|
||||
rand.workspace = true
|
||||
|
||||
tree-sitter-rust = { workspace = true, optional = true }
|
||||
tree-sitter-html = { workspace = true, optional = true }
|
||||
|
@ -397,7 +397,7 @@ impl InlayMap {
|
||||
buffer_snapshot: MultiBufferSnapshot,
|
||||
mut buffer_edits: Vec<text::Edit<usize>>,
|
||||
) -> (InlaySnapshot, Vec<InlayEdit>) {
|
||||
let mut snapshot = &mut self.snapshot;
|
||||
let snapshot = &mut self.snapshot;
|
||||
|
||||
if buffer_edits.is_empty() {
|
||||
if snapshot.buffer.trailing_excerpt_update_count()
|
||||
@ -572,7 +572,6 @@ impl InlayMap {
|
||||
})
|
||||
.collect();
|
||||
let buffer_snapshot = snapshot.buffer.clone();
|
||||
drop(snapshot);
|
||||
let (snapshot, edits) = self.sync(buffer_snapshot, buffer_edits);
|
||||
(snapshot, edits)
|
||||
}
|
||||
@ -635,7 +634,6 @@ impl InlayMap {
|
||||
}
|
||||
log::info!("removing inlays: {:?}", to_remove);
|
||||
|
||||
drop(snapshot);
|
||||
let (snapshot, edits) = self.splice(to_remove, to_insert);
|
||||
(snapshot, edits)
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ use blink_manager::BlinkManager;
|
||||
use client::{ClickhouseEvent, TelemetrySettings};
|
||||
use clock::{Global, ReplicaId};
|
||||
use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
|
||||
use convert_case::{Case, Casing};
|
||||
use copilot::Copilot;
|
||||
pub use display_map::DisplayPoint;
|
||||
use display_map::*;
|
||||
@ -89,7 +90,7 @@ use std::{
|
||||
cmp::{self, Ordering, Reverse},
|
||||
mem,
|
||||
num::NonZeroU32,
|
||||
ops::{ControlFlow, Deref, DerefMut, Range},
|
||||
ops::{ControlFlow, Deref, DerefMut, Range, RangeInclusive},
|
||||
path::Path,
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
@ -231,6 +232,13 @@ actions!(
|
||||
SortLinesCaseInsensitive,
|
||||
ReverseLines,
|
||||
ShuffleLines,
|
||||
ConvertToUpperCase,
|
||||
ConvertToLowerCase,
|
||||
ConvertToTitleCase,
|
||||
ConvertToSnakeCase,
|
||||
ConvertToKebabCase,
|
||||
ConvertToUpperCamelCase,
|
||||
ConvertToLowerCamelCase,
|
||||
Transpose,
|
||||
Cut,
|
||||
Copy,
|
||||
@ -353,6 +361,13 @@ pub fn init(cx: &mut AppContext) {
|
||||
cx.add_action(Editor::sort_lines_case_insensitive);
|
||||
cx.add_action(Editor::reverse_lines);
|
||||
cx.add_action(Editor::shuffle_lines);
|
||||
cx.add_action(Editor::convert_to_upper_case);
|
||||
cx.add_action(Editor::convert_to_lower_case);
|
||||
cx.add_action(Editor::convert_to_title_case);
|
||||
cx.add_action(Editor::convert_to_snake_case);
|
||||
cx.add_action(Editor::convert_to_kebab_case);
|
||||
cx.add_action(Editor::convert_to_upper_camel_case);
|
||||
cx.add_action(Editor::convert_to_lower_camel_case);
|
||||
cx.add_action(Editor::delete_to_previous_word_start);
|
||||
cx.add_action(Editor::delete_to_previous_subword_start);
|
||||
cx.add_action(Editor::delete_to_next_word_end);
|
||||
@ -543,6 +558,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 +1391,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(),
|
||||
@ -2706,7 +2723,7 @@ impl Editor {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn excerpt_visible_offsets(
|
||||
pub fn excerpt_visible_offsets(
|
||||
&self,
|
||||
restrict_to_languages: Option<&HashSet<Arc<Language>>>,
|
||||
cx: &mut ViewContext<'_, '_, Editor>,
|
||||
@ -4219,7 +4236,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 +4244,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 +4282,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);
|
||||
|
||||
@ -4304,6 +4321,97 @@ impl Editor {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn convert_to_upper_case(&mut self, _: &ConvertToUpperCase, cx: &mut ViewContext<Self>) {
|
||||
self.manipulate_text(cx, |text| text.to_uppercase())
|
||||
}
|
||||
|
||||
pub fn convert_to_lower_case(&mut self, _: &ConvertToLowerCase, cx: &mut ViewContext<Self>) {
|
||||
self.manipulate_text(cx, |text| text.to_lowercase())
|
||||
}
|
||||
|
||||
pub fn convert_to_title_case(&mut self, _: &ConvertToTitleCase, cx: &mut ViewContext<Self>) {
|
||||
self.manipulate_text(cx, |text| text.to_case(Case::Title))
|
||||
}
|
||||
|
||||
pub fn convert_to_snake_case(&mut self, _: &ConvertToSnakeCase, cx: &mut ViewContext<Self>) {
|
||||
self.manipulate_text(cx, |text| text.to_case(Case::Snake))
|
||||
}
|
||||
|
||||
pub fn convert_to_kebab_case(&mut self, _: &ConvertToKebabCase, cx: &mut ViewContext<Self>) {
|
||||
self.manipulate_text(cx, |text| text.to_case(Case::Kebab))
|
||||
}
|
||||
|
||||
pub fn convert_to_upper_camel_case(
|
||||
&mut self,
|
||||
_: &ConvertToUpperCamelCase,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.manipulate_text(cx, |text| text.to_case(Case::UpperCamel))
|
||||
}
|
||||
|
||||
pub fn convert_to_lower_camel_case(
|
||||
&mut self,
|
||||
_: &ConvertToLowerCamelCase,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.manipulate_text(cx, |text| text.to_case(Case::Camel))
|
||||
}
|
||||
|
||||
fn manipulate_text<Fn>(&mut self, cx: &mut ViewContext<Self>, mut callback: Fn)
|
||||
where
|
||||
Fn: FnMut(&str) -> String,
|
||||
{
|
||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
let buffer = self.buffer.read(cx).snapshot(cx);
|
||||
|
||||
let mut new_selections = Vec::new();
|
||||
let mut edits = Vec::new();
|
||||
let mut selection_adjustment = 0i32;
|
||||
|
||||
for selection in self.selections.all::<usize>(cx) {
|
||||
let selection_is_empty = selection.is_empty();
|
||||
|
||||
let (start, end) = if selection_is_empty {
|
||||
let word_range = movement::surrounding_word(
|
||||
&display_map,
|
||||
selection.start.to_display_point(&display_map),
|
||||
);
|
||||
let start = word_range.start.to_offset(&display_map, Bias::Left);
|
||||
let end = word_range.end.to_offset(&display_map, Bias::Left);
|
||||
(start, end)
|
||||
} else {
|
||||
(selection.start, selection.end)
|
||||
};
|
||||
|
||||
let text = buffer.text_for_range(start..end).collect::<String>();
|
||||
let old_length = text.len() as i32;
|
||||
let text = callback(&text);
|
||||
|
||||
new_selections.push(Selection {
|
||||
start: (start as i32 - selection_adjustment) as usize,
|
||||
end: ((start + text.len()) as i32 - selection_adjustment) as usize,
|
||||
goal: SelectionGoal::None,
|
||||
..selection
|
||||
});
|
||||
|
||||
selection_adjustment += old_length - text.len() as i32;
|
||||
|
||||
edits.push((start..end, text));
|
||||
}
|
||||
|
||||
self.transact(cx, |this, cx| {
|
||||
this.buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit(edits, None, cx);
|
||||
});
|
||||
|
||||
this.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.select(new_selections);
|
||||
});
|
||||
|
||||
this.request_autoscroll(Autoscroll::fit(), cx);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
|
||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
let buffer = &display_map.buffer_snapshot;
|
||||
@ -7187,6 +7295,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 +7356,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()) {
|
||||
@ -7432,6 +7549,78 @@ impl Editor {
|
||||
results
|
||||
}
|
||||
|
||||
pub fn background_highlight_row_ranges<T: 'static>(
|
||||
&self,
|
||||
search_range: Range<Anchor>,
|
||||
display_snapshot: &DisplaySnapshot,
|
||||
count: usize,
|
||||
) -> Vec<RangeInclusive<DisplayPoint>> {
|
||||
let mut results = Vec::new();
|
||||
let buffer = &display_snapshot.buffer_snapshot;
|
||||
let Some((_, ranges)) = self.background_highlights
|
||||
.get(&TypeId::of::<T>()) else {
|
||||
return vec![];
|
||||
};
|
||||
|
||||
let start_ix = match ranges.binary_search_by(|probe| {
|
||||
let cmp = probe.end.cmp(&search_range.start, buffer);
|
||||
if cmp.is_gt() {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Less
|
||||
}
|
||||
}) {
|
||||
Ok(i) | Err(i) => i,
|
||||
};
|
||||
let mut push_region = |start: Option<Point>, end: Option<Point>| {
|
||||
if let (Some(start_display), Some(end_display)) = (start, end) {
|
||||
results.push(
|
||||
start_display.to_display_point(display_snapshot)
|
||||
..=end_display.to_display_point(display_snapshot),
|
||||
);
|
||||
}
|
||||
};
|
||||
let mut start_row: Option<Point> = None;
|
||||
let mut end_row: Option<Point> = None;
|
||||
if ranges.len() > count {
|
||||
return vec![];
|
||||
}
|
||||
for range in &ranges[start_ix..] {
|
||||
if range.start.cmp(&search_range.end, buffer).is_ge() {
|
||||
break;
|
||||
}
|
||||
let end = range.end.to_point(buffer);
|
||||
if let Some(current_row) = &end_row {
|
||||
if end.row == current_row.row {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let start = range.start.to_point(buffer);
|
||||
|
||||
if start_row.is_none() {
|
||||
assert_eq!(end_row, None);
|
||||
start_row = Some(start);
|
||||
end_row = Some(end);
|
||||
continue;
|
||||
}
|
||||
if let Some(current_end) = end_row.as_mut() {
|
||||
if start.row > current_end.row + 1 {
|
||||
push_region(start_row, end_row);
|
||||
start_row = Some(start);
|
||||
end_row = Some(end);
|
||||
} else {
|
||||
// Merge two hunks.
|
||||
*current_end = end;
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
// We might still have a hunk that was not rendered (if there was a search hit on the last line)
|
||||
push_region(start_row, end_row);
|
||||
results
|
||||
}
|
||||
|
||||
pub fn highlight_text<T: 'static>(
|
||||
&mut self,
|
||||
ranges: Vec<Range<Anchor>>,
|
||||
@ -8496,7 +8685,7 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend
|
||||
// We really need to rethink this ID system...
|
||||
.with_tooltip::<BlockContextToolip>(
|
||||
cx.block_id,
|
||||
"Copy diagnostic message".to_string(),
|
||||
"Copy diagnostic message",
|
||||
None,
|
||||
tooltip_style,
|
||||
cx,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -32,7 +32,7 @@ use gpui::{
|
||||
platform::{CursorStyle, Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent},
|
||||
text_layout::{self, Line, RunStyle, TextLayoutCache},
|
||||
AnyElement, Axis, Border, CursorRegion, Element, EventContext, FontCache, LayoutContext,
|
||||
MouseRegion, Quad, SceneBuilder, SizeConstraint, ViewContext, WindowContext,
|
||||
MouseRegion, PaintContext, Quad, SceneBuilder, SizeConstraint, ViewContext, WindowContext,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use json::json;
|
||||
@ -508,13 +508,13 @@ impl EditorElement {
|
||||
bounds: gutter_bounds,
|
||||
background: Some(self.style.gutter_background),
|
||||
border: Border::new(0., Color::transparent_black()),
|
||||
corner_radius: 0.,
|
||||
corner_radii: Default::default(),
|
||||
});
|
||||
scene.push_quad(Quad {
|
||||
bounds: text_bounds,
|
||||
background: Some(self.style.background),
|
||||
border: Border::new(0., Color::transparent_black()),
|
||||
corner_radius: 0.,
|
||||
corner_radii: Default::default(),
|
||||
});
|
||||
|
||||
if let EditorMode::Full = layout.mode {
|
||||
@ -542,7 +542,7 @@ impl EditorElement {
|
||||
bounds: RectF::new(origin, size),
|
||||
background: Some(self.style.active_line_background),
|
||||
border: Border::default(),
|
||||
corner_radius: 0.,
|
||||
corner_radii: Default::default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -562,12 +562,24 @@ impl EditorElement {
|
||||
bounds: RectF::new(origin, size),
|
||||
background: Some(self.style.highlighted_line_background),
|
||||
border: Border::default(),
|
||||
corner_radius: 0.,
|
||||
corner_radii: Default::default(),
|
||||
});
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -580,7 +592,7 @@ impl EditorElement {
|
||||
),
|
||||
background: Some(color),
|
||||
border: Border::new(0., Color::transparent_black()),
|
||||
corner_radius: 0.,
|
||||
corner_radii: Default::default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -681,7 +693,7 @@ impl EditorElement {
|
||||
bounds: highlight_bounds,
|
||||
background: Some(diff_style.modified),
|
||||
border: Border::new(0., Color::transparent_black()),
|
||||
corner_radius: 1. * line_height,
|
||||
corner_radii: (1. * line_height).into(),
|
||||
});
|
||||
|
||||
continue;
|
||||
@ -714,7 +726,7 @@ impl EditorElement {
|
||||
bounds: highlight_bounds,
|
||||
background: Some(diff_style.deleted),
|
||||
border: Border::new(0., Color::transparent_black()),
|
||||
corner_radius: 1. * line_height,
|
||||
corner_radii: (1. * line_height).into(),
|
||||
});
|
||||
|
||||
continue;
|
||||
@ -736,7 +748,7 @@ impl EditorElement {
|
||||
bounds: highlight_bounds,
|
||||
background: Some(color),
|
||||
border: Border::new(0., Color::transparent_black()),
|
||||
corner_radius: diff_style.corner_radius * line_height,
|
||||
corner_radii: (diff_style.corner_radius * line_height).into(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1056,6 +1068,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,
|
||||
@ -1074,7 +1090,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);
|
||||
|
||||
@ -1111,8 +1127,6 @@ impl EditorElement {
|
||||
if layout.is_singleton && scrollbar_settings.selections {
|
||||
let start_anchor = Anchor::min();
|
||||
let end_anchor = Anchor::max();
|
||||
let mut start_row = None;
|
||||
let mut end_row = None;
|
||||
let color = scrollbar_theme.selections;
|
||||
let border = Border {
|
||||
width: 1.,
|
||||
@ -1123,54 +1137,32 @@ impl EditorElement {
|
||||
bottom: false,
|
||||
left: true,
|
||||
};
|
||||
let mut push_region = |start, end| {
|
||||
if let (Some(start_display), Some(end_display)) = (start, end) {
|
||||
let start_y = y_for_row(start_display as f32);
|
||||
let mut end_y = y_for_row(end_display as f32);
|
||||
if end_y - start_y < 1. {
|
||||
end_y = start_y + 1.;
|
||||
}
|
||||
let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y));
|
||||
|
||||
scene.push_quad(Quad {
|
||||
bounds,
|
||||
background: Some(color),
|
||||
border,
|
||||
corner_radius: style.thumb.corner_radius,
|
||||
})
|
||||
let mut push_region = |start: DisplayPoint, end: DisplayPoint| {
|
||||
let start_y = y_for_row(start.row() as f32);
|
||||
let mut end_y = y_for_row(end.row() as f32);
|
||||
if end_y - start_y < 1. {
|
||||
end_y = start_y + 1.;
|
||||
}
|
||||
let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y));
|
||||
|
||||
scene.push_quad(Quad {
|
||||
bounds,
|
||||
background: Some(color),
|
||||
border,
|
||||
corner_radii: style.thumb.corner_radii.into(),
|
||||
})
|
||||
};
|
||||
for (row, _) in &editor
|
||||
.background_highlights_in_range_for::<crate::items::BufferSearchHighlights>(
|
||||
let background_ranges = editor
|
||||
.background_highlight_row_ranges::<crate::items::BufferSearchHighlights>(
|
||||
start_anchor..end_anchor,
|
||||
&layout.position_map.snapshot,
|
||||
&theme,
|
||||
)
|
||||
{
|
||||
let start_display = row.start;
|
||||
let end_display = row.end;
|
||||
|
||||
if start_row.is_none() {
|
||||
assert_eq!(end_row, None);
|
||||
start_row = Some(start_display.row());
|
||||
end_row = Some(end_display.row());
|
||||
continue;
|
||||
}
|
||||
if let Some(current_end) = end_row.as_mut() {
|
||||
if start_display.row() > *current_end + 1 {
|
||||
push_region(start_row, end_row);
|
||||
start_row = Some(start_display.row());
|
||||
end_row = Some(end_display.row());
|
||||
} else {
|
||||
// Merge two hunks.
|
||||
*current_end = end_display.row();
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
50000,
|
||||
);
|
||||
for row in background_ranges {
|
||||
let start = row.start();
|
||||
let end = row.end();
|
||||
push_region(*start, *end);
|
||||
}
|
||||
// We might still have a hunk that was not rendered (if there was a search hit on the last line)
|
||||
push_region(start_row, end_row);
|
||||
}
|
||||
|
||||
if layout.is_singleton && scrollbar_settings.git_diff {
|
||||
@ -1217,7 +1209,7 @@ impl EditorElement {
|
||||
bounds,
|
||||
background: Some(color),
|
||||
border,
|
||||
corner_radius: style.thumb.corner_radius,
|
||||
corner_radii: style.thumb.corner_radii.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1226,7 +1218,7 @@ impl EditorElement {
|
||||
bounds: thumb_bounds,
|
||||
border: style.thumb.border,
|
||||
background: style.thumb.background_color,
|
||||
corner_radius: style.thumb.corner_radius,
|
||||
corner_radii: style.thumb.corner_radii.into(),
|
||||
});
|
||||
}
|
||||
|
||||
@ -2481,7 +2473,7 @@ impl Element<Editor> for EditorElement {
|
||||
visible_bounds: RectF,
|
||||
layout: &mut Self::LayoutState,
|
||||
editor: &mut Editor,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
cx: &mut PaintContext<Editor>,
|
||||
) -> Self::PaintState {
|
||||
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||
scene.push_layer(Some(visible_bounds));
|
||||
@ -2751,14 +2743,14 @@ impl Cursor {
|
||||
bounds,
|
||||
background: None,
|
||||
border: Border::all(1., self.color),
|
||||
corner_radius: 0.,
|
||||
corner_radii: Default::default(),
|
||||
});
|
||||
} else {
|
||||
scene.push_quad(Quad {
|
||||
bounds,
|
||||
background: Some(self.color),
|
||||
border: Default::default(),
|
||||
corner_radius: 0.,
|
||||
corner_radii: Default::default(),
|
||||
});
|
||||
}
|
||||
|
||||
@ -2998,16 +2990,18 @@ mod tests {
|
||||
use language::language_settings;
|
||||
use log::info;
|
||||
use std::{num::NonZeroU32, sync::Arc};
|
||||
use util::test::{generate_marked_text, sample_text};
|
||||
use util::test::sample_text;
|
||||
|
||||
#[gpui::test]
|
||||
fn test_layout_line_numbers(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
|
||||
Editor::new(EditorMode::Full, buffer, None, None, cx)
|
||||
});
|
||||
let editor = cx
|
||||
.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
|
||||
Editor::new(EditorMode::Full, buffer, None, None, cx)
|
||||
})
|
||||
.root(cx);
|
||||
let element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
|
||||
|
||||
let layouts = editor.update(cx, |editor, cx| {
|
||||
@ -3023,10 +3017,12 @@ mod tests {
|
||||
async fn test_vim_visual_selections(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx);
|
||||
Editor::new(EditorMode::Full, buffer, None, None, cx)
|
||||
});
|
||||
let editor = cx
|
||||
.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx);
|
||||
Editor::new(EditorMode::Full, buffer, None, None, cx)
|
||||
})
|
||||
.root(cx);
|
||||
let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
|
||||
let (_, state) = editor.update(cx, |editor, cx| {
|
||||
editor.cursor_shape = CursorShape::Block;
|
||||
@ -3099,25 +3095,27 @@ mod tests {
|
||||
// 13: bbbbbb
|
||||
// 14: cccccc
|
||||
// 15: dddddd
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_multi(
|
||||
[
|
||||
(
|
||||
&(sample_text(8, 6, 'a') + "\n"),
|
||||
vec![
|
||||
Point::new(0, 0)..Point::new(3, 0),
|
||||
Point::new(4, 0)..Point::new(7, 0),
|
||||
],
|
||||
),
|
||||
(
|
||||
&(sample_text(8, 6, 'a') + "\n"),
|
||||
vec![Point::new(1, 0)..Point::new(3, 0)],
|
||||
),
|
||||
],
|
||||
cx,
|
||||
);
|
||||
Editor::new(EditorMode::Full, buffer, None, None, cx)
|
||||
});
|
||||
let editor = cx
|
||||
.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_multi(
|
||||
[
|
||||
(
|
||||
&(sample_text(8, 6, 'a') + "\n"),
|
||||
vec![
|
||||
Point::new(0, 0)..Point::new(3, 0),
|
||||
Point::new(4, 0)..Point::new(7, 0),
|
||||
],
|
||||
),
|
||||
(
|
||||
&(sample_text(8, 6, 'a') + "\n"),
|
||||
vec![Point::new(1, 0)..Point::new(3, 0)],
|
||||
),
|
||||
],
|
||||
cx,
|
||||
);
|
||||
Editor::new(EditorMode::Full, buffer, None, None, cx)
|
||||
})
|
||||
.root(cx);
|
||||
let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
|
||||
let (_, state) = editor.update(cx, |editor, cx| {
|
||||
editor.cursor_shape = CursorShape::Block;
|
||||
@ -3167,10 +3165,12 @@ mod tests {
|
||||
fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("", cx);
|
||||
Editor::new(EditorMode::Full, buffer, None, None, cx)
|
||||
});
|
||||
let editor = cx
|
||||
.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("", cx);
|
||||
Editor::new(EditorMode::Full, buffer, None, None, cx)
|
||||
})
|
||||
.root(cx);
|
||||
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.set_placeholder_text("hello", cx);
|
||||
@ -3221,7 +3221,14 @@ mod tests {
|
||||
let mut scene = SceneBuilder::new(1.0);
|
||||
let bounds = RectF::new(Default::default(), size);
|
||||
editor.update(cx, |editor, cx| {
|
||||
element.paint(&mut scene, bounds, bounds, &mut state, editor, cx);
|
||||
element.paint(
|
||||
&mut scene,
|
||||
bounds,
|
||||
bounds,
|
||||
&mut state,
|
||||
editor,
|
||||
&mut PaintContext::new(cx),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -3377,10 +3384,12 @@ mod tests {
|
||||
info!(
|
||||
"Creating editor with mode {editor_mode:?}, width {editor_width} and text '{input_text}'"
|
||||
);
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&input_text, cx);
|
||||
Editor::new(editor_mode, buffer, None, None, cx)
|
||||
});
|
||||
let editor = cx
|
||||
.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&input_text, cx);
|
||||
Editor::new(editor_mode, buffer, None, None, cx)
|
||||
})
|
||||
.root(cx);
|
||||
|
||||
let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
|
||||
let (_, layout_state) = editor.update(cx, |editor, cx| {
|
||||
|
@ -599,7 +599,7 @@ impl InfoPopover {
|
||||
bounds,
|
||||
background: Some(code_span_background_color),
|
||||
border: Default::default(),
|
||||
corner_radius: 2.0,
|
||||
corner_radii: (2.0).into(),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -28,7 +28,10 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use text::Selection;
|
||||
use util::{paths::FILE_ROW_COLUMN_DELIMITER, ResultExt, TryFutureExt};
|
||||
use util::{
|
||||
paths::{PathExt, FILE_ROW_COLUMN_DELIMITER},
|
||||
ResultExt, TryFutureExt,
|
||||
};
|
||||
use workspace::item::{BreadcrumbText, FollowableItemHandle};
|
||||
use workspace::{
|
||||
item::{FollowableItem, Item, ItemEvent, ItemHandle, ProjectItem},
|
||||
@ -546,9 +549,7 @@ impl Item for Editor {
|
||||
.and_then(|f| f.as_local())?
|
||||
.abs_path(cx);
|
||||
|
||||
let file_path = util::paths::compact(&file_path)
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
let file_path = file_path.compact().to_string_lossy().to_string();
|
||||
|
||||
Some(file_path.into())
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ use gpui::{
|
||||
};
|
||||
use language::{Bias, Point};
|
||||
use util::ResultExt;
|
||||
use workspace::{item::Item, WorkspaceId};
|
||||
use workspace::WorkspaceId;
|
||||
|
||||
use crate::{
|
||||
display_map::{DisplaySnapshot, ToDisplayPoint},
|
||||
@ -333,9 +333,7 @@ impl Editor {
|
||||
cx,
|
||||
);
|
||||
|
||||
if !self.is_singleton(cx) {
|
||||
self.refresh_inlays(InlayRefreshReason::NewLinesShown, cx);
|
||||
}
|
||||
self.refresh_inlays(InlayRefreshReason::NewLinesShown, cx);
|
||||
}
|
||||
|
||||
pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Vector2F {
|
||||
|
@ -69,7 +69,8 @@ impl<'a> EditorLspTestContext<'a> {
|
||||
.insert_tree("/root", json!({ "dir": { file_name.clone(): "" }}))
|
||||
.await;
|
||||
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let workspace = window.root(cx);
|
||||
project
|
||||
.update(cx, |project, cx| {
|
||||
project.find_or_create_local_worktree("/root", true, cx)
|
||||
@ -98,7 +99,7 @@ impl<'a> EditorLspTestContext<'a> {
|
||||
Self {
|
||||
cx: EditorTestContext {
|
||||
cx,
|
||||
window_id,
|
||||
window: window.into(),
|
||||
editor,
|
||||
},
|
||||
lsp,
|
||||
|
@ -3,7 +3,8 @@ use crate::{
|
||||
};
|
||||
use futures::Future;
|
||||
use gpui::{
|
||||
keymap_matcher::Keystroke, AppContext, ContextHandle, ModelContext, ViewContext, ViewHandle,
|
||||
keymap_matcher::Keystroke, AnyWindowHandle, AppContext, ContextHandle, ModelContext,
|
||||
ViewContext, ViewHandle,
|
||||
};
|
||||
use indoc::indoc;
|
||||
use language::{Buffer, BufferSnapshot};
|
||||
@ -21,7 +22,7 @@ use super::build_editor;
|
||||
|
||||
pub struct EditorTestContext<'a> {
|
||||
pub cx: &'a mut gpui::TestAppContext,
|
||||
pub window_id: usize,
|
||||
pub window: AnyWindowHandle,
|
||||
pub editor: ViewHandle<Editor>,
|
||||
}
|
||||
|
||||
@ -32,16 +33,14 @@ impl<'a> EditorTestContext<'a> {
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| project.create_buffer("", None, cx))
|
||||
.unwrap();
|
||||
let (window_id, editor) = cx.update(|cx| {
|
||||
cx.add_window(Default::default(), |cx| {
|
||||
cx.focus_self();
|
||||
build_editor(MultiBuffer::build_from_buffer(buffer, cx), cx)
|
||||
})
|
||||
let window = cx.add_window(|cx| {
|
||||
cx.focus_self();
|
||||
build_editor(MultiBuffer::build_from_buffer(buffer, cx), cx)
|
||||
});
|
||||
|
||||
let editor = window.root(cx);
|
||||
Self {
|
||||
cx,
|
||||
window_id,
|
||||
window: window.into(),
|
||||
editor,
|
||||
}
|
||||
}
|
||||
@ -113,7 +112,8 @@ impl<'a> EditorTestContext<'a> {
|
||||
let keystroke_under_test_handle =
|
||||
self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text));
|
||||
let keystroke = Keystroke::parse(keystroke_text).unwrap();
|
||||
self.cx.dispatch_keystroke(self.window_id, keystroke, false);
|
||||
|
||||
self.cx.dispatch_keystroke(self.window, keystroke, false);
|
||||
keystroke_under_test_handle
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ impl View for DeployFeedbackButton {
|
||||
})
|
||||
.with_tooltip::<Self>(
|
||||
0,
|
||||
"Send Feedback".into(),
|
||||
"Send Feedback",
|
||||
Some(Box::new(GiveFeedback)),
|
||||
theme.tooltip.clone(),
|
||||
cx,
|
||||
|
@ -80,7 +80,7 @@ impl View for SubmitFeedbackButton {
|
||||
.with_margin_left(theme.feedback.button_margin)
|
||||
.with_tooltip::<Self>(
|
||||
0,
|
||||
"cmd-s".into(),
|
||||
"cmd-s",
|
||||
Some(Box::new(SubmitFeedback)),
|
||||
theme.tooltip.clone(),
|
||||
cx,
|
||||
|
@ -617,8 +617,9 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
cx.dispatch_action(window_id, Toggle);
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let workspace = window.root(cx);
|
||||
cx.dispatch_action(window.into(), Toggle);
|
||||
|
||||
let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
|
||||
finder
|
||||
@ -631,8 +632,8 @@ mod tests {
|
||||
});
|
||||
|
||||
let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
|
||||
cx.dispatch_action(window_id, SelectNext);
|
||||
cx.dispatch_action(window_id, Confirm);
|
||||
cx.dispatch_action(window.into(), SelectNext);
|
||||
cx.dispatch_action(window.into(), Confirm);
|
||||
active_pane
|
||||
.condition(cx, |pane, _| pane.active_item().is_some())
|
||||
.await;
|
||||
@ -671,8 +672,9 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
cx.dispatch_action(window_id, Toggle);
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let workspace = window.root(cx);
|
||||
cx.dispatch_action(window.into(), Toggle);
|
||||
let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
|
||||
|
||||
let file_query = &first_file_name[..3];
|
||||
@ -704,8 +706,8 @@ mod tests {
|
||||
});
|
||||
|
||||
let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
|
||||
cx.dispatch_action(window_id, SelectNext);
|
||||
cx.dispatch_action(window_id, Confirm);
|
||||
cx.dispatch_action(window.into(), SelectNext);
|
||||
cx.dispatch_action(window.into(), Confirm);
|
||||
active_pane
|
||||
.condition(cx, |pane, _| pane.active_item().is_some())
|
||||
.await;
|
||||
@ -754,8 +756,9 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
cx.dispatch_action(window_id, Toggle);
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let workspace = window.root(cx);
|
||||
cx.dispatch_action(window.into(), Toggle);
|
||||
let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
|
||||
|
||||
let file_query = &first_file_name[..3];
|
||||
@ -787,8 +790,8 @@ mod tests {
|
||||
});
|
||||
|
||||
let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
|
||||
cx.dispatch_action(window_id, SelectNext);
|
||||
cx.dispatch_action(window_id, Confirm);
|
||||
cx.dispatch_action(window.into(), SelectNext);
|
||||
cx.dispatch_action(window.into(), Confirm);
|
||||
active_pane
|
||||
.condition(cx, |pane, _| pane.active_item().is_some())
|
||||
.await;
|
||||
@ -837,19 +840,23 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let project = Project::test(app_state.fs.clone(), ["/dir".as_ref()], cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let (_, finder) = cx.add_window(|cx| {
|
||||
Picker::new(
|
||||
FileFinderDelegate::new(
|
||||
workspace.downgrade(),
|
||||
workspace.read(cx).project().clone(),
|
||||
None,
|
||||
Vec::new(),
|
||||
let workspace = cx
|
||||
.add_window(|cx| Workspace::test_new(project, cx))
|
||||
.root(cx);
|
||||
let finder = cx
|
||||
.add_window(|cx| {
|
||||
Picker::new(
|
||||
FileFinderDelegate::new(
|
||||
workspace.downgrade(),
|
||||
workspace.read(cx).project().clone(),
|
||||
None,
|
||||
Vec::new(),
|
||||
cx,
|
||||
),
|
||||
cx,
|
||||
),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
)
|
||||
})
|
||||
.root(cx);
|
||||
|
||||
let query = test_path_like("hi");
|
||||
finder
|
||||
@ -931,19 +938,23 @@ mod tests {
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let (_, finder) = cx.add_window(|cx| {
|
||||
Picker::new(
|
||||
FileFinderDelegate::new(
|
||||
workspace.downgrade(),
|
||||
workspace.read(cx).project().clone(),
|
||||
None,
|
||||
Vec::new(),
|
||||
let workspace = cx
|
||||
.add_window(|cx| Workspace::test_new(project, cx))
|
||||
.root(cx);
|
||||
let finder = cx
|
||||
.add_window(|cx| {
|
||||
Picker::new(
|
||||
FileFinderDelegate::new(
|
||||
workspace.downgrade(),
|
||||
workspace.read(cx).project().clone(),
|
||||
None,
|
||||
Vec::new(),
|
||||
cx,
|
||||
),
|
||||
cx,
|
||||
),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
)
|
||||
})
|
||||
.root(cx);
|
||||
finder
|
||||
.update(cx, |f, cx| {
|
||||
f.delegate_mut().spawn_search(test_path_like("hi"), cx)
|
||||
@ -967,19 +978,23 @@ mod tests {
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let (_, finder) = cx.add_window(|cx| {
|
||||
Picker::new(
|
||||
FileFinderDelegate::new(
|
||||
workspace.downgrade(),
|
||||
workspace.read(cx).project().clone(),
|
||||
None,
|
||||
Vec::new(),
|
||||
let workspace = cx
|
||||
.add_window(|cx| Workspace::test_new(project, cx))
|
||||
.root(cx);
|
||||
let finder = cx
|
||||
.add_window(|cx| {
|
||||
Picker::new(
|
||||
FileFinderDelegate::new(
|
||||
workspace.downgrade(),
|
||||
workspace.read(cx).project().clone(),
|
||||
None,
|
||||
Vec::new(),
|
||||
cx,
|
||||
),
|
||||
cx,
|
||||
),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
)
|
||||
})
|
||||
.root(cx);
|
||||
|
||||
// Even though there is only one worktree, that worktree's filename
|
||||
// is included in the matching, because the worktree is a single file.
|
||||
@ -1015,61 +1030,6 @@ mod tests {
|
||||
finder.read_with(cx, |f, _| assert_eq!(f.delegate().matches.len(), 0));
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_multiple_matches_with_same_relative_path(cx: &mut TestAppContext) {
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
.insert_tree(
|
||||
"/root",
|
||||
json!({
|
||||
"dir1": { "a.txt": "" },
|
||||
"dir2": { "a.txt": "" }
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(
|
||||
app_state.fs.clone(),
|
||||
["/root/dir1".as_ref(), "/root/dir2".as_ref()],
|
||||
cx,
|
||||
)
|
||||
.await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
|
||||
let (_, finder) = cx.add_window(|cx| {
|
||||
Picker::new(
|
||||
FileFinderDelegate::new(
|
||||
workspace.downgrade(),
|
||||
workspace.read(cx).project().clone(),
|
||||
None,
|
||||
Vec::new(),
|
||||
cx,
|
||||
),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
// Run a search that matches two files with the same relative path.
|
||||
finder
|
||||
.update(cx, |f, cx| {
|
||||
f.delegate_mut().spawn_search(test_path_like("a.t"), cx)
|
||||
})
|
||||
.await;
|
||||
|
||||
// Can switch between different matches with the same relative path.
|
||||
finder.update(cx, |finder, cx| {
|
||||
let delegate = finder.delegate_mut();
|
||||
assert_eq!(delegate.matches.len(), 2);
|
||||
assert_eq!(delegate.selected_index(), 0);
|
||||
delegate.set_selected_index(1, cx);
|
||||
assert_eq!(delegate.selected_index(), 1);
|
||||
delegate.set_selected_index(0, cx);
|
||||
assert_eq!(delegate.selected_index(), 0);
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_path_distance_ordering(cx: &mut TestAppContext) {
|
||||
let app_state = init_test(cx);
|
||||
@ -1089,7 +1049,9 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let workspace = cx
|
||||
.add_window(|cx| Workspace::test_new(project, cx))
|
||||
.root(cx);
|
||||
let worktree_id = cx.read(|cx| {
|
||||
let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
|
||||
assert_eq!(worktrees.len(), 1);
|
||||
@ -1103,18 +1065,20 @@ mod tests {
|
||||
worktree_id,
|
||||
path: Arc::from(Path::new("/root/dir2/b.txt")),
|
||||
}));
|
||||
let (_, finder) = cx.add_window(|cx| {
|
||||
Picker::new(
|
||||
FileFinderDelegate::new(
|
||||
workspace.downgrade(),
|
||||
workspace.read(cx).project().clone(),
|
||||
b_path,
|
||||
Vec::new(),
|
||||
let finder = cx
|
||||
.add_window(|cx| {
|
||||
Picker::new(
|
||||
FileFinderDelegate::new(
|
||||
workspace.downgrade(),
|
||||
workspace.read(cx).project().clone(),
|
||||
b_path,
|
||||
Vec::new(),
|
||||
cx,
|
||||
),
|
||||
cx,
|
||||
),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
)
|
||||
})
|
||||
.root(cx);
|
||||
|
||||
finder
|
||||
.update(cx, |f, cx| {
|
||||
@ -1151,19 +1115,23 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let (_, finder) = cx.add_window(|cx| {
|
||||
Picker::new(
|
||||
FileFinderDelegate::new(
|
||||
workspace.downgrade(),
|
||||
workspace.read(cx).project().clone(),
|
||||
None,
|
||||
Vec::new(),
|
||||
let workspace = cx
|
||||
.add_window(|cx| Workspace::test_new(project, cx))
|
||||
.root(cx);
|
||||
let finder = cx
|
||||
.add_window(|cx| {
|
||||
Picker::new(
|
||||
FileFinderDelegate::new(
|
||||
workspace.downgrade(),
|
||||
workspace.read(cx).project().clone(),
|
||||
None,
|
||||
Vec::new(),
|
||||
cx,
|
||||
),
|
||||
cx,
|
||||
),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
)
|
||||
})
|
||||
.root(cx);
|
||||
finder
|
||||
.update(cx, |f, cx| {
|
||||
f.delegate_mut().spawn_search(test_path_like("dir"), cx)
|
||||
@ -1198,7 +1166,8 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let workspace = window.root(cx);
|
||||
let worktree_id = cx.read(|cx| {
|
||||
let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
|
||||
assert_eq!(worktrees.len(), 1);
|
||||
@ -1216,7 +1185,7 @@ mod tests {
|
||||
"fir",
|
||||
1,
|
||||
"first.rs",
|
||||
window_id,
|
||||
window.into(),
|
||||
&workspace,
|
||||
&deterministic,
|
||||
cx,
|
||||
@ -1231,7 +1200,7 @@ mod tests {
|
||||
"sec",
|
||||
1,
|
||||
"second.rs",
|
||||
window_id,
|
||||
window.into(),
|
||||
&workspace,
|
||||
&deterministic,
|
||||
cx,
|
||||
@ -1253,7 +1222,7 @@ mod tests {
|
||||
"thi",
|
||||
1,
|
||||
"third.rs",
|
||||
window_id,
|
||||
window.into(),
|
||||
&workspace,
|
||||
&deterministic,
|
||||
cx,
|
||||
@ -1285,7 +1254,7 @@ mod tests {
|
||||
"sec",
|
||||
1,
|
||||
"second.rs",
|
||||
window_id,
|
||||
window.into(),
|
||||
&workspace,
|
||||
&deterministic,
|
||||
cx,
|
||||
@ -1324,7 +1293,7 @@ mod tests {
|
||||
"thi",
|
||||
1,
|
||||
"third.rs",
|
||||
window_id,
|
||||
window.into(),
|
||||
&workspace,
|
||||
&deterministic,
|
||||
cx,
|
||||
@ -1404,7 +1373,8 @@ mod tests {
|
||||
.detach();
|
||||
deterministic.run_until_parked();
|
||||
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||
let workspace = window.root(cx);
|
||||
let worktree_id = cx.read(|cx| {
|
||||
let worktrees = workspace.read(cx).worktrees(cx).collect::<Vec<_>>();
|
||||
assert_eq!(worktrees.len(), 1,);
|
||||
@ -1439,7 +1409,7 @@ mod tests {
|
||||
"sec",
|
||||
1,
|
||||
"second.rs",
|
||||
window_id,
|
||||
window.into(),
|
||||
&workspace,
|
||||
&deterministic,
|
||||
cx,
|
||||
@ -1461,7 +1431,7 @@ mod tests {
|
||||
"fir",
|
||||
1,
|
||||
"first.rs",
|
||||
window_id,
|
||||
window.into(),
|
||||
&workspace,
|
||||
&deterministic,
|
||||
cx,
|
||||
@ -1493,12 +1463,12 @@ mod tests {
|
||||
input: &str,
|
||||
expected_matches: usize,
|
||||
expected_editor_title: &str,
|
||||
window_id: usize,
|
||||
window: gpui::AnyWindowHandle,
|
||||
workspace: &ViewHandle<Workspace>,
|
||||
deterministic: &gpui::executor::Deterministic,
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) -> Vec<FoundPath> {
|
||||
cx.dispatch_action(window_id, Toggle);
|
||||
cx.dispatch_action(window, Toggle);
|
||||
let finder = cx.read(|cx| workspace.read(cx).modal::<FileFinder>().unwrap());
|
||||
finder
|
||||
.update(cx, |finder, cx| {
|
||||
@ -1515,8 +1485,8 @@ mod tests {
|
||||
});
|
||||
|
||||
let active_pane = cx.read(|cx| workspace.read(cx).active_pane().clone());
|
||||
cx.dispatch_action(window_id, SelectNext);
|
||||
cx.dispatch_action(window_id, Confirm);
|
||||
cx.dispatch_action(window, SelectNext);
|
||||
cx.dispatch_action(window, Confirm);
|
||||
deterministic.run_until_parked();
|
||||
active_pane
|
||||
.condition(cx, |pane, _| pane.active_item().is_some())
|
||||
|
@ -135,7 +135,7 @@ impl Entity for GoToLine {
|
||||
|
||||
fn release(&mut self, cx: &mut AppContext) {
|
||||
let scroll_position = self.prev_scroll_position.take();
|
||||
cx.update_window(self.active_editor.window_id(), |cx| {
|
||||
self.active_editor.window().update(cx, |cx| {
|
||||
self.active_editor.update(cx, |editor, cx| {
|
||||
editor.highlight_rows(None);
|
||||
if let Some(scroll_position) = scroll_position {
|
||||
|
@ -22,6 +22,7 @@ sqlez = { path = "../sqlez" }
|
||||
async-task = "4.0.3"
|
||||
backtrace = { version = "0.3", optional = true }
|
||||
ctor.workspace = true
|
||||
derive_more.workspace = true
|
||||
dhat = { version = "0.3", optional = true }
|
||||
env_logger = { version = "0.9", optional = true }
|
||||
etagere = "0.2"
|
||||
|
155
crates/gpui/examples/corner_radii.rs
Normal file
155
crates/gpui/examples/corner_radii.rs
Normal file
@ -0,0 +1,155 @@
|
||||
use gpui::{
|
||||
color::Color, geometry::rect::RectF, scene::Shadow, AnyElement, App, Element, Entity, Quad,
|
||||
View,
|
||||
};
|
||||
use log::LevelFilter;
|
||||
use pathfinder_geometry::vector::vec2f;
|
||||
use simplelog::SimpleLogger;
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
|
||||
|
||||
App::new(()).unwrap().run(|cx| {
|
||||
cx.platform().activate(true);
|
||||
cx.add_window(Default::default(), |_| CornersView);
|
||||
});
|
||||
}
|
||||
|
||||
struct CornersView;
|
||||
|
||||
impl Entity for CornersView {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl View for CornersView {
|
||||
fn ui_name() -> &'static str {
|
||||
"CornersView"
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut gpui::ViewContext<Self>) -> AnyElement<CornersView> {
|
||||
CornersElement.into_any()
|
||||
}
|
||||
}
|
||||
|
||||
struct CornersElement;
|
||||
|
||||
impl<V: View> gpui::Element<V> for CornersElement {
|
||||
type LayoutState = ();
|
||||
|
||||
type PaintState = ();
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
constraint: gpui::SizeConstraint,
|
||||
_: &mut V,
|
||||
_: &mut gpui::LayoutContext<V>,
|
||||
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
|
||||
(constraint.max, ())
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut gpui::SceneBuilder,
|
||||
bounds: pathfinder_geometry::rect::RectF,
|
||||
_: pathfinder_geometry::rect::RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
_: &mut gpui::PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
scene.push_quad(Quad {
|
||||
bounds,
|
||||
background: Some(Color::white()),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
scene.push_layer(None);
|
||||
|
||||
scene.push_quad(Quad {
|
||||
bounds: RectF::new(vec2f(100., 100.), vec2f(100., 100.)),
|
||||
background: Some(Color::red()),
|
||||
border: Default::default(),
|
||||
corner_radii: gpui::scene::CornerRadii {
|
||||
top_left: 20.,
|
||||
..Default::default()
|
||||
},
|
||||
});
|
||||
|
||||
scene.push_quad(Quad {
|
||||
bounds: RectF::new(vec2f(200., 100.), vec2f(100., 100.)),
|
||||
background: Some(Color::green()),
|
||||
border: Default::default(),
|
||||
corner_radii: gpui::scene::CornerRadii {
|
||||
top_right: 20.,
|
||||
..Default::default()
|
||||
},
|
||||
});
|
||||
|
||||
scene.push_quad(Quad {
|
||||
bounds: RectF::new(vec2f(100., 200.), vec2f(100., 100.)),
|
||||
background: Some(Color::blue()),
|
||||
border: Default::default(),
|
||||
corner_radii: gpui::scene::CornerRadii {
|
||||
bottom_left: 20.,
|
||||
..Default::default()
|
||||
},
|
||||
});
|
||||
|
||||
scene.push_quad(Quad {
|
||||
bounds: RectF::new(vec2f(200., 200.), vec2f(100., 100.)),
|
||||
background: Some(Color::yellow()),
|
||||
border: Default::default(),
|
||||
corner_radii: gpui::scene::CornerRadii {
|
||||
bottom_right: 20.,
|
||||
..Default::default()
|
||||
},
|
||||
});
|
||||
|
||||
scene.push_shadow(Shadow {
|
||||
bounds: RectF::new(vec2f(400., 100.), vec2f(100., 100.)),
|
||||
corner_radii: gpui::scene::CornerRadii {
|
||||
bottom_right: 20.,
|
||||
..Default::default()
|
||||
},
|
||||
sigma: 20.0,
|
||||
color: Color::black(),
|
||||
});
|
||||
|
||||
scene.push_layer(None);
|
||||
scene.push_quad(Quad {
|
||||
bounds: RectF::new(vec2f(400., 100.), vec2f(100., 100.)),
|
||||
background: Some(Color::red()),
|
||||
border: Default::default(),
|
||||
corner_radii: gpui::scene::CornerRadii {
|
||||
bottom_right: 20.,
|
||||
..Default::default()
|
||||
},
|
||||
});
|
||||
|
||||
scene.pop_layer();
|
||||
scene.pop_layer();
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
&self,
|
||||
_: std::ops::Range<usize>,
|
||||
_: pathfinder_geometry::rect::RectF,
|
||||
_: pathfinder_geometry::rect::RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
_: &V,
|
||||
_: &gpui::ViewContext<V>,
|
||||
) -> Option<pathfinder_geometry::rect::RectF> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
_: pathfinder_geometry::rect::RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
_: &V,
|
||||
_: &gpui::ViewContext<V>,
|
||||
) -> serde_json::Value {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -77,9 +77,9 @@ pub(crate) fn setup_menu_handlers(foreground_platform: &dyn ForegroundPlatform,
|
||||
let cx = app.0.clone();
|
||||
move |action| {
|
||||
let mut cx = cx.borrow_mut();
|
||||
if let Some(main_window_id) = cx.platform.main_window_id() {
|
||||
let dispatched = cx
|
||||
.update_window(main_window_id, |cx| {
|
||||
if let Some(main_window) = cx.active_window() {
|
||||
let dispatched = main_window
|
||||
.update(&mut *cx, |cx| {
|
||||
if let Some(view_id) = cx.focused_view_id() {
|
||||
cx.dispatch_action(Some(view_id), action);
|
||||
true
|
||||
|
@ -9,7 +9,7 @@ use collections::{hash_map::Entry, HashMap, HashSet};
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
use crate::util::post_inc;
|
||||
use crate::ElementStateId;
|
||||
use crate::{AnyWindowHandle, ElementStateId};
|
||||
|
||||
lazy_static! {
|
||||
static ref LEAK_BACKTRACE: bool =
|
||||
@ -26,7 +26,7 @@ pub struct RefCounts {
|
||||
entity_counts: HashMap<usize, usize>,
|
||||
element_state_counts: HashMap<ElementStateId, ElementStateRefCount>,
|
||||
dropped_models: HashSet<usize>,
|
||||
dropped_views: HashSet<(usize, usize)>,
|
||||
dropped_views: HashSet<(AnyWindowHandle, usize)>,
|
||||
dropped_element_states: HashSet<ElementStateId>,
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
@ -55,12 +55,12 @@ impl RefCounts {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inc_view(&mut self, window_id: usize, view_id: usize) {
|
||||
pub fn inc_view(&mut self, window: AnyWindowHandle, view_id: usize) {
|
||||
match self.entity_counts.entry(view_id) {
|
||||
Entry::Occupied(mut entry) => *entry.get_mut() += 1,
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(1);
|
||||
self.dropped_views.remove(&(window_id, view_id));
|
||||
self.dropped_views.remove(&(window, view_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,12 +94,12 @@ impl RefCounts {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dec_view(&mut self, window_id: usize, view_id: usize) {
|
||||
pub fn dec_view(&mut self, window: AnyWindowHandle, view_id: usize) {
|
||||
let count = self.entity_counts.get_mut(&view_id).unwrap();
|
||||
*count -= 1;
|
||||
if *count == 0 {
|
||||
self.entity_counts.remove(&view_id);
|
||||
self.dropped_views.insert((window_id, view_id));
|
||||
self.dropped_views.insert((window, view_id));
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,7 +120,7 @@ impl RefCounts {
|
||||
&mut self,
|
||||
) -> (
|
||||
HashSet<usize>,
|
||||
HashSet<(usize, usize)>,
|
||||
HashSet<(AnyWindowHandle, usize)>,
|
||||
HashSet<ElementStateId>,
|
||||
) {
|
||||
(
|
||||
|
@ -4,9 +4,9 @@ use crate::{
|
||||
keymap_matcher::{Binding, Keystroke},
|
||||
platform,
|
||||
platform::{Event, InputHandler, KeyDownEvent, Platform},
|
||||
Action, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache, Handle,
|
||||
ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakHandle,
|
||||
WindowContext,
|
||||
Action, AnyWindowHandle, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache,
|
||||
Handle, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle,
|
||||
WeakHandle, WindowContext, WindowHandle,
|
||||
};
|
||||
use collections::BTreeMap;
|
||||
use futures::Future;
|
||||
@ -60,7 +60,7 @@ impl TestAppContext {
|
||||
RefCounts::new(leak_detector),
|
||||
(),
|
||||
);
|
||||
cx.next_entity_id = first_entity_id;
|
||||
cx.next_id = first_entity_id;
|
||||
let cx = TestAppContext {
|
||||
cx: Rc::new(RefCell::new(cx)),
|
||||
foreground_platform,
|
||||
@ -72,8 +72,8 @@ impl TestAppContext {
|
||||
cx
|
||||
}
|
||||
|
||||
pub fn dispatch_action<A: Action>(&mut self, window_id: usize, action: A) {
|
||||
self.update_window(window_id, |window| {
|
||||
pub fn dispatch_action<A: Action>(&mut self, window: AnyWindowHandle, action: A) {
|
||||
self.update_window(window, |window| {
|
||||
window.dispatch_action(window.focused_view_id(), &action);
|
||||
})
|
||||
.expect("window not found");
|
||||
@ -81,10 +81,10 @@ impl TestAppContext {
|
||||
|
||||
pub fn available_actions(
|
||||
&self,
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
view_id: usize,
|
||||
) -> Vec<(&'static str, Box<dyn Action>, SmallVec<[Binding; 1]>)> {
|
||||
self.read_window(window_id, |cx| cx.available_actions(view_id))
|
||||
self.read_window(window, |cx| cx.available_actions(view_id))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
@ -92,33 +92,34 @@ impl TestAppContext {
|
||||
self.update(|cx| cx.dispatch_global_action_any(&action));
|
||||
}
|
||||
|
||||
pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: Keystroke, is_held: bool) {
|
||||
let handled = self
|
||||
.cx
|
||||
.borrow_mut()
|
||||
.update_window(window_id, |cx| {
|
||||
if cx.dispatch_keystroke(&keystroke) {
|
||||
return true;
|
||||
}
|
||||
pub fn dispatch_keystroke(
|
||||
&mut self,
|
||||
window: AnyWindowHandle,
|
||||
keystroke: Keystroke,
|
||||
is_held: bool,
|
||||
) {
|
||||
let handled = window.update(self, |cx| {
|
||||
if cx.dispatch_keystroke(&keystroke) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if cx.dispatch_event(
|
||||
Event::KeyDown(KeyDownEvent {
|
||||
keystroke: keystroke.clone(),
|
||||
is_held,
|
||||
}),
|
||||
false,
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if cx.dispatch_event(
|
||||
Event::KeyDown(KeyDownEvent {
|
||||
keystroke: keystroke.clone(),
|
||||
is_held,
|
||||
}),
|
||||
false,
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
})
|
||||
.unwrap_or(false);
|
||||
false
|
||||
});
|
||||
|
||||
if !handled && !keystroke.cmd && !keystroke.ctrl {
|
||||
WindowInputHandler {
|
||||
app: self.cx.clone(),
|
||||
window_id,
|
||||
window,
|
||||
}
|
||||
.replace_text_in_range(None, &keystroke.key)
|
||||
}
|
||||
@ -126,18 +127,18 @@ impl TestAppContext {
|
||||
|
||||
pub fn read_window<T, F: FnOnce(&WindowContext) -> T>(
|
||||
&self,
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
callback: F,
|
||||
) -> Option<T> {
|
||||
self.cx.borrow().read_window(window_id, callback)
|
||||
self.cx.borrow().read_window(window, callback)
|
||||
}
|
||||
|
||||
pub fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
|
||||
&mut self,
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
callback: F,
|
||||
) -> Option<T> {
|
||||
self.cx.borrow_mut().update_window(window_id, callback)
|
||||
self.cx.borrow_mut().update_window(window, callback)
|
||||
}
|
||||
|
||||
pub fn add_model<T, F>(&mut self, build_model: F) -> ModelHandle<T>
|
||||
@ -148,26 +149,17 @@ impl TestAppContext {
|
||||
self.cx.borrow_mut().add_model(build_model)
|
||||
}
|
||||
|
||||
pub fn add_window<T, F>(&mut self, build_root_view: F) -> (usize, ViewHandle<T>)
|
||||
pub fn add_window<V, F>(&mut self, build_root_view: F) -> WindowHandle<V>
|
||||
where
|
||||
T: View,
|
||||
F: FnOnce(&mut ViewContext<T>) -> T,
|
||||
V: View,
|
||||
F: FnOnce(&mut ViewContext<V>) -> V,
|
||||
{
|
||||
let (window_id, view) = self
|
||||
let window = self
|
||||
.cx
|
||||
.borrow_mut()
|
||||
.add_window(Default::default(), build_root_view);
|
||||
self.simulate_window_activation(Some(window_id));
|
||||
(window_id, view)
|
||||
}
|
||||
|
||||
pub fn add_view<T, F>(&mut self, window_id: usize, build_view: F) -> ViewHandle<T>
|
||||
where
|
||||
T: View,
|
||||
F: FnOnce(&mut ViewContext<T>) -> T,
|
||||
{
|
||||
self.update_window(window_id, |cx| cx.add_view(build_view))
|
||||
.expect("window not found")
|
||||
window.simulate_activation(self);
|
||||
window
|
||||
}
|
||||
|
||||
pub fn observe_global<E, F>(&mut self, callback: F) -> Subscription
|
||||
@ -190,8 +182,8 @@ impl TestAppContext {
|
||||
self.cx.borrow_mut().subscribe_global(callback)
|
||||
}
|
||||
|
||||
pub fn window_ids(&self) -> Vec<usize> {
|
||||
self.cx.borrow().windows.keys().copied().collect()
|
||||
pub fn windows(&self) -> Vec<AnyWindowHandle> {
|
||||
self.cx.borrow().windows().collect()
|
||||
}
|
||||
|
||||
pub fn remove_all_windows(&mut self) {
|
||||
@ -261,76 +253,6 @@ impl TestAppContext {
|
||||
self.foreground_platform.as_ref().did_prompt_for_new_path()
|
||||
}
|
||||
|
||||
pub fn simulate_prompt_answer(&self, window_id: usize, answer: usize) {
|
||||
use postage::prelude::Sink as _;
|
||||
|
||||
let mut done_tx = self
|
||||
.platform_window_mut(window_id)
|
||||
.pending_prompts
|
||||
.borrow_mut()
|
||||
.pop_front()
|
||||
.expect("prompt was not called");
|
||||
done_tx.try_send(answer).ok();
|
||||
}
|
||||
|
||||
pub fn has_pending_prompt(&self, window_id: usize) -> bool {
|
||||
let window = self.platform_window_mut(window_id);
|
||||
let prompts = window.pending_prompts.borrow_mut();
|
||||
!prompts.is_empty()
|
||||
}
|
||||
|
||||
pub fn current_window_title(&self, window_id: usize) -> Option<String> {
|
||||
self.platform_window_mut(window_id).title.clone()
|
||||
}
|
||||
|
||||
pub fn simulate_window_close(&self, window_id: usize) -> bool {
|
||||
let handler = self
|
||||
.platform_window_mut(window_id)
|
||||
.should_close_handler
|
||||
.take();
|
||||
if let Some(mut handler) = handler {
|
||||
let should_close = handler();
|
||||
self.platform_window_mut(window_id).should_close_handler = Some(handler);
|
||||
should_close
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn simulate_window_resize(&self, window_id: usize, size: Vector2F) {
|
||||
let mut window = self.platform_window_mut(window_id);
|
||||
window.size = size;
|
||||
let mut handlers = mem::take(&mut window.resize_handlers);
|
||||
drop(window);
|
||||
for handler in &mut handlers {
|
||||
handler();
|
||||
}
|
||||
self.platform_window_mut(window_id).resize_handlers = handlers;
|
||||
}
|
||||
|
||||
pub fn simulate_window_activation(&self, to_activate: Option<usize>) {
|
||||
self.cx.borrow_mut().update(|cx| {
|
||||
let other_window_ids = cx
|
||||
.windows
|
||||
.keys()
|
||||
.filter(|window_id| Some(**window_id) != to_activate)
|
||||
.copied()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for window_id in other_window_ids {
|
||||
cx.window_changed_active_status(window_id, false)
|
||||
}
|
||||
|
||||
if let Some(to_activate) = to_activate {
|
||||
cx.window_changed_active_status(to_activate, true)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn is_window_edited(&self, window_id: usize) -> bool {
|
||||
self.platform_window_mut(window_id).edited
|
||||
}
|
||||
|
||||
pub fn leak_detector(&self) -> Arc<Mutex<LeakDetector>> {
|
||||
self.cx.borrow().leak_detector()
|
||||
}
|
||||
@ -351,18 +273,6 @@ impl TestAppContext {
|
||||
self.assert_dropped(weak);
|
||||
}
|
||||
|
||||
fn platform_window_mut(&self, window_id: usize) -> std::cell::RefMut<platform::test::Window> {
|
||||
std::cell::RefMut::map(self.cx.borrow_mut(), |state| {
|
||||
let window = state.windows.get_mut(&window_id).unwrap();
|
||||
let test_window = window
|
||||
.platform_window
|
||||
.as_any_mut()
|
||||
.downcast_mut::<platform::test::Window>()
|
||||
.unwrap();
|
||||
test_window
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_condition_duration(&mut self, duration: Option<Duration>) {
|
||||
self.condition_duration = duration;
|
||||
}
|
||||
@ -405,19 +315,39 @@ impl BorrowAppContext for TestAppContext {
|
||||
}
|
||||
|
||||
impl BorrowWindowContext for TestAppContext {
|
||||
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
|
||||
type Result<T> = T;
|
||||
|
||||
fn read_window<T, F: FnOnce(&WindowContext) -> T>(&self, window: AnyWindowHandle, f: F) -> T {
|
||||
self.cx
|
||||
.borrow()
|
||||
.read_window(window_id, f)
|
||||
.read_window(window, f)
|
||||
.expect("window was closed")
|
||||
}
|
||||
|
||||
fn update<T, F: FnOnce(&mut WindowContext) -> T>(&mut self, window_id: usize, f: F) -> T {
|
||||
fn read_window_optional<T, F>(&self, window: AnyWindowHandle, f: F) -> Option<T>
|
||||
where
|
||||
F: FnOnce(&WindowContext) -> Option<T>,
|
||||
{
|
||||
BorrowWindowContext::read_window(self, window, f)
|
||||
}
|
||||
|
||||
fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
|
||||
&mut self,
|
||||
window: AnyWindowHandle,
|
||||
f: F,
|
||||
) -> T {
|
||||
self.cx
|
||||
.borrow_mut()
|
||||
.update_window(window_id, f)
|
||||
.update_window(window, f)
|
||||
.expect("window was closed")
|
||||
}
|
||||
|
||||
fn update_window_optional<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Option<T>
|
||||
where
|
||||
F: FnOnce(&mut WindowContext) -> Option<T>,
|
||||
{
|
||||
BorrowWindowContext::update_window(self, window, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Entity> ModelHandle<T> {
|
||||
@ -532,6 +462,71 @@ impl<T: Entity> ModelHandle<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl AnyWindowHandle {
|
||||
pub fn has_pending_prompt(&self, cx: &mut TestAppContext) -> bool {
|
||||
let window = self.platform_window_mut(cx);
|
||||
let prompts = window.pending_prompts.borrow_mut();
|
||||
!prompts.is_empty()
|
||||
}
|
||||
|
||||
pub fn current_title(&self, cx: &mut TestAppContext) -> Option<String> {
|
||||
self.platform_window_mut(cx).title.clone()
|
||||
}
|
||||
|
||||
pub fn simulate_close(&self, cx: &mut TestAppContext) -> bool {
|
||||
let handler = self.platform_window_mut(cx).should_close_handler.take();
|
||||
if let Some(mut handler) = handler {
|
||||
let should_close = handler();
|
||||
self.platform_window_mut(cx).should_close_handler = Some(handler);
|
||||
should_close
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn simulate_resize(&self, size: Vector2F, cx: &mut TestAppContext) {
|
||||
let mut window = self.platform_window_mut(cx);
|
||||
window.size = size;
|
||||
let mut handlers = mem::take(&mut window.resize_handlers);
|
||||
drop(window);
|
||||
for handler in &mut handlers {
|
||||
handler();
|
||||
}
|
||||
self.platform_window_mut(cx).resize_handlers = handlers;
|
||||
}
|
||||
|
||||
pub fn is_edited(&self, cx: &mut TestAppContext) -> bool {
|
||||
self.platform_window_mut(cx).edited
|
||||
}
|
||||
|
||||
pub fn simulate_prompt_answer(&self, answer: usize, cx: &mut TestAppContext) {
|
||||
use postage::prelude::Sink as _;
|
||||
|
||||
let mut done_tx = self
|
||||
.platform_window_mut(cx)
|
||||
.pending_prompts
|
||||
.borrow_mut()
|
||||
.pop_front()
|
||||
.expect("prompt was not called");
|
||||
done_tx.try_send(answer).ok();
|
||||
}
|
||||
|
||||
fn platform_window_mut<'a>(
|
||||
&self,
|
||||
cx: &'a mut TestAppContext,
|
||||
) -> std::cell::RefMut<'a, platform::test::Window> {
|
||||
std::cell::RefMut::map(cx.cx.borrow_mut(), |state| {
|
||||
let window = state.windows.get_mut(&self).unwrap();
|
||||
let test_window = window
|
||||
.platform_window
|
||||
.as_any_mut()
|
||||
.downcast_mut::<platform::test::Window>()
|
||||
.unwrap();
|
||||
test_window
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: View> ViewHandle<T> {
|
||||
pub fn next_notification(&self, cx: &TestAppContext) -> impl Future<Output = ()> {
|
||||
use postage::prelude::{Sink as _, Stream as _};
|
||||
|
@ -13,9 +13,10 @@ use crate::{
|
||||
},
|
||||
text_layout::TextLayoutCache,
|
||||
util::post_inc,
|
||||
Action, AnyView, AnyViewHandle, AppContext, BorrowAppContext, BorrowWindowContext, Effect,
|
||||
Element, Entity, Handle, LayoutContext, MouseRegion, MouseRegionId, SceneBuilder, Subscription,
|
||||
View, ViewContext, ViewHandle, WindowInvalidation,
|
||||
Action, AnyView, AnyViewHandle, AnyWindowHandle, AppContext, BorrowAppContext,
|
||||
BorrowWindowContext, Effect, Element, Entity, Handle, LayoutContext, MouseRegion,
|
||||
MouseRegionId, PaintContext, SceneBuilder, Subscription, View, ViewContext, ViewHandle,
|
||||
WindowInvalidation,
|
||||
};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use collections::{HashMap, HashSet};
|
||||
@ -51,8 +52,8 @@ pub struct Window {
|
||||
cursor_regions: Vec<CursorRegion>,
|
||||
mouse_regions: Vec<(MouseRegion, usize)>,
|
||||
last_mouse_moved_event: Option<Event>,
|
||||
pub(crate) hovered_region_ids: HashSet<MouseRegionId>,
|
||||
pub(crate) clicked_region_ids: HashSet<MouseRegionId>,
|
||||
pub(crate) hovered_region_ids: Vec<MouseRegionId>,
|
||||
pub(crate) clicked_region_ids: Vec<MouseRegionId>,
|
||||
pub(crate) clicked_region: Option<(MouseRegionId, MouseButton)>,
|
||||
mouse_position: Vector2F,
|
||||
text_layout_cache: TextLayoutCache,
|
||||
@ -60,7 +61,7 @@ pub struct Window {
|
||||
|
||||
impl Window {
|
||||
pub fn new<V, F>(
|
||||
window_id: usize,
|
||||
handle: AnyWindowHandle,
|
||||
platform_window: Box<dyn platform::Window>,
|
||||
cx: &mut AppContext,
|
||||
build_view: F,
|
||||
@ -92,7 +93,7 @@ impl Window {
|
||||
appearance,
|
||||
};
|
||||
|
||||
let mut window_context = WindowContext::mutable(cx, &mut window, window_id);
|
||||
let mut window_context = WindowContext::mutable(cx, &mut window, handle);
|
||||
let root_view = window_context.add_view(|cx| build_view(cx));
|
||||
if let Some(invalidation) = window_context.window.invalidation.take() {
|
||||
window_context.invalidate(invalidation, appearance);
|
||||
@ -113,7 +114,7 @@ impl Window {
|
||||
pub struct WindowContext<'a> {
|
||||
pub(crate) app_context: Reference<'a, AppContext>,
|
||||
pub(crate) window: Reference<'a, Window>,
|
||||
pub(crate) window_id: usize,
|
||||
pub(crate) window_handle: AnyWindowHandle,
|
||||
pub(crate) removed: bool,
|
||||
}
|
||||
|
||||
@ -142,42 +143,66 @@ impl BorrowAppContext for WindowContext<'_> {
|
||||
}
|
||||
|
||||
impl BorrowWindowContext for WindowContext<'_> {
|
||||
fn read_with<T, F: FnOnce(&WindowContext) -> T>(&self, window_id: usize, f: F) -> T {
|
||||
if self.window_id == window_id {
|
||||
type Result<T> = T;
|
||||
|
||||
fn read_window<T, F: FnOnce(&WindowContext) -> T>(&self, handle: AnyWindowHandle, f: F) -> T {
|
||||
if self.window_handle == handle {
|
||||
f(self)
|
||||
} else {
|
||||
panic!("read_with called with id of window that does not belong to this context")
|
||||
}
|
||||
}
|
||||
|
||||
fn update<T, F: FnOnce(&mut WindowContext) -> T>(&mut self, window_id: usize, f: F) -> T {
|
||||
if self.window_id == window_id {
|
||||
fn read_window_optional<T, F>(&self, window: AnyWindowHandle, f: F) -> Option<T>
|
||||
where
|
||||
F: FnOnce(&WindowContext) -> Option<T>,
|
||||
{
|
||||
BorrowWindowContext::read_window(self, window, f)
|
||||
}
|
||||
|
||||
fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
|
||||
&mut self,
|
||||
handle: AnyWindowHandle,
|
||||
f: F,
|
||||
) -> T {
|
||||
if self.window_handle == handle {
|
||||
f(self)
|
||||
} else {
|
||||
panic!("update called with id of window that does not belong to this context")
|
||||
}
|
||||
}
|
||||
|
||||
fn update_window_optional<T, F>(&mut self, handle: AnyWindowHandle, f: F) -> Option<T>
|
||||
where
|
||||
F: FnOnce(&mut WindowContext) -> Option<T>,
|
||||
{
|
||||
BorrowWindowContext::update_window(self, handle, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> WindowContext<'a> {
|
||||
pub fn mutable(
|
||||
app_context: &'a mut AppContext,
|
||||
window: &'a mut Window,
|
||||
window_id: usize,
|
||||
handle: AnyWindowHandle,
|
||||
) -> Self {
|
||||
Self {
|
||||
app_context: Reference::Mutable(app_context),
|
||||
window: Reference::Mutable(window),
|
||||
window_id,
|
||||
window_handle: handle,
|
||||
removed: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn immutable(app_context: &'a AppContext, window: &'a Window, window_id: usize) -> Self {
|
||||
pub fn immutable(
|
||||
app_context: &'a AppContext,
|
||||
window: &'a Window,
|
||||
handle: AnyWindowHandle,
|
||||
) -> Self {
|
||||
Self {
|
||||
app_context: Reference::Immutable(app_context),
|
||||
window: Reference::Immutable(window),
|
||||
window_id,
|
||||
window_handle: handle,
|
||||
removed: false,
|
||||
}
|
||||
}
|
||||
@ -186,8 +211,8 @@ impl<'a> WindowContext<'a> {
|
||||
self.removed = true;
|
||||
}
|
||||
|
||||
pub fn window_id(&self) -> usize {
|
||||
self.window_id
|
||||
pub fn window(&self) -> AnyWindowHandle {
|
||||
self.window_handle
|
||||
}
|
||||
|
||||
pub fn app_context(&mut self) -> &mut AppContext {
|
||||
@ -210,10 +235,10 @@ impl<'a> WindowContext<'a> {
|
||||
where
|
||||
F: FnOnce(&mut dyn AnyView, &mut Self) -> T,
|
||||
{
|
||||
let window_id = self.window_id;
|
||||
let mut view = self.views.remove(&(window_id, view_id))?;
|
||||
let handle = self.window_handle;
|
||||
let mut view = self.views.remove(&(handle, view_id))?;
|
||||
let result = f(view.as_mut(), self);
|
||||
self.views.insert((window_id, view_id), view);
|
||||
self.views.insert((handle, view_id), view);
|
||||
Some(result)
|
||||
}
|
||||
|
||||
@ -238,9 +263,9 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut WindowContext)) {
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
self.app_context.defer(move |cx| {
|
||||
cx.update_window(window_id, |cx| callback(cx));
|
||||
cx.update_window(handle, |cx| callback(cx));
|
||||
})
|
||||
}
|
||||
|
||||
@ -280,10 +305,10 @@ impl<'a> WindowContext<'a> {
|
||||
H: Handle<E>,
|
||||
F: 'static + FnMut(H, &E::Event, &mut WindowContext) -> bool,
|
||||
{
|
||||
let window_id = self.window_id;
|
||||
let window_handle = self.window_handle;
|
||||
self.app_context
|
||||
.subscribe_internal(handle, move |emitter, event, cx| {
|
||||
cx.update_window(window_id, |cx| callback(emitter, event, cx))
|
||||
cx.update_window(window_handle, |cx| callback(emitter, event, cx))
|
||||
.unwrap_or(false)
|
||||
})
|
||||
}
|
||||
@ -292,17 +317,17 @@ impl<'a> WindowContext<'a> {
|
||||
where
|
||||
F: 'static + FnMut(bool, &mut WindowContext) -> bool,
|
||||
{
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
let subscription_id = post_inc(&mut self.next_subscription_id);
|
||||
self.pending_effects
|
||||
.push_back(Effect::WindowActivationObservation {
|
||||
window_id,
|
||||
window: handle,
|
||||
subscription_id,
|
||||
callback: Box::new(callback),
|
||||
});
|
||||
Subscription::WindowActivationObservation(
|
||||
self.window_activation_observations
|
||||
.subscribe(window_id, subscription_id),
|
||||
.subscribe(handle, subscription_id),
|
||||
)
|
||||
}
|
||||
|
||||
@ -310,17 +335,17 @@ impl<'a> WindowContext<'a> {
|
||||
where
|
||||
F: 'static + FnMut(bool, &mut WindowContext) -> bool,
|
||||
{
|
||||
let window_id = self.window_id;
|
||||
let window = self.window_handle;
|
||||
let subscription_id = post_inc(&mut self.next_subscription_id);
|
||||
self.pending_effects
|
||||
.push_back(Effect::WindowFullscreenObservation {
|
||||
window_id,
|
||||
window,
|
||||
subscription_id,
|
||||
callback: Box::new(callback),
|
||||
});
|
||||
Subscription::WindowActivationObservation(
|
||||
self.window_activation_observations
|
||||
.subscribe(window_id, subscription_id),
|
||||
.subscribe(window, subscription_id),
|
||||
)
|
||||
}
|
||||
|
||||
@ -328,17 +353,17 @@ impl<'a> WindowContext<'a> {
|
||||
where
|
||||
F: 'static + FnMut(WindowBounds, Uuid, &mut WindowContext) -> bool,
|
||||
{
|
||||
let window_id = self.window_id;
|
||||
let window = self.window_handle;
|
||||
let subscription_id = post_inc(&mut self.next_subscription_id);
|
||||
self.pending_effects
|
||||
.push_back(Effect::WindowBoundsObservation {
|
||||
window_id,
|
||||
window,
|
||||
subscription_id,
|
||||
callback: Box::new(callback),
|
||||
});
|
||||
Subscription::WindowBoundsObservation(
|
||||
self.window_bounds_observations
|
||||
.subscribe(window_id, subscription_id),
|
||||
.subscribe(window, subscription_id),
|
||||
)
|
||||
}
|
||||
|
||||
@ -347,13 +372,13 @@ impl<'a> WindowContext<'a> {
|
||||
F: 'static
|
||||
+ FnMut(&Keystroke, &MatchResult, Option<&Box<dyn Action>>, &mut WindowContext) -> bool,
|
||||
{
|
||||
let window_id = self.window_id;
|
||||
let window = self.window_handle;
|
||||
let subscription_id = post_inc(&mut self.next_subscription_id);
|
||||
self.keystroke_observations
|
||||
.add_callback(window_id, subscription_id, Box::new(callback));
|
||||
.add_callback(window, subscription_id, Box::new(callback));
|
||||
Subscription::KeystrokeObservation(
|
||||
self.keystroke_observations
|
||||
.subscribe(window_id, subscription_id),
|
||||
.subscribe(window, subscription_id),
|
||||
)
|
||||
}
|
||||
|
||||
@ -361,11 +386,11 @@ impl<'a> WindowContext<'a> {
|
||||
&self,
|
||||
view_id: usize,
|
||||
) -> Vec<(&'static str, Box<dyn Action>, SmallVec<[Binding; 1]>)> {
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
let mut contexts = Vec::new();
|
||||
let mut handler_depths_by_action_id = HashMap::<TypeId, usize>::default();
|
||||
for (depth, view_id) in self.ancestors(view_id).enumerate() {
|
||||
if let Some(view_metadata) = self.views_metadata.get(&(window_id, view_id)) {
|
||||
if let Some(view_metadata) = self.views_metadata.get(&(handle, view_id)) {
|
||||
contexts.push(view_metadata.keymap_context.clone());
|
||||
if let Some(actions) = self.actions.get(&view_metadata.type_id) {
|
||||
handler_depths_by_action_id
|
||||
@ -410,13 +435,13 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_keystroke(&mut self, keystroke: &Keystroke) -> bool {
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
if let Some(focused_view_id) = self.focused_view_id() {
|
||||
let dispatch_path = self
|
||||
.ancestors(focused_view_id)
|
||||
.filter_map(|view_id| {
|
||||
self.views_metadata
|
||||
.get(&(window_id, view_id))
|
||||
.get(&(handle, view_id))
|
||||
.map(|view| (view_id, view.keymap_context.clone()))
|
||||
})
|
||||
.collect();
|
||||
@ -441,15 +466,10 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
self.keystroke(
|
||||
window_id,
|
||||
keystroke.clone(),
|
||||
handled_by,
|
||||
match_result.clone(),
|
||||
);
|
||||
self.keystroke(handle, keystroke.clone(), handled_by, match_result.clone());
|
||||
keystroke_handled
|
||||
} else {
|
||||
self.keystroke(window_id, keystroke.clone(), None, MatchResult::None);
|
||||
self.keystroke(handle, keystroke.clone(), None, MatchResult::None);
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -457,7 +477,7 @@ impl<'a> WindowContext<'a> {
|
||||
pub(crate) fn dispatch_event(&mut self, event: Event, event_reused: bool) -> bool {
|
||||
let mut mouse_events = SmallVec::<[_; 2]>::new();
|
||||
let mut notified_views: HashSet<usize> = Default::default();
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
|
||||
// 1. Handle platform event. Keyboard events get dispatched immediately, while mouse events
|
||||
// get mapped into the mouse-specific MouseEvent type.
|
||||
@ -658,6 +678,7 @@ impl<'a> WindowContext<'a> {
|
||||
let mut highest_z_index = None;
|
||||
let mouse_position = self.window.mouse_position.clone();
|
||||
let window = &mut *self.window;
|
||||
let prev_hovered_regions = mem::take(&mut window.hovered_region_ids);
|
||||
for (region, z_index) in window.mouse_regions.iter().rev() {
|
||||
// Allow mouse regions to appear transparent to hovers
|
||||
if !region.hoverable {
|
||||
@ -676,7 +697,11 @@ impl<'a> WindowContext<'a> {
|
||||
// highest_z_index is set.
|
||||
if contains_mouse && z_index == highest_z_index.unwrap() {
|
||||
//Ensure that hover entrance events aren't sent twice
|
||||
if window.hovered_region_ids.insert(region.id()) {
|
||||
if let Err(ix) = window.hovered_region_ids.binary_search(®ion.id()) {
|
||||
window.hovered_region_ids.insert(ix, region.id());
|
||||
}
|
||||
// window.hovered_region_ids.insert(region.id());
|
||||
if !prev_hovered_regions.contains(®ion.id()) {
|
||||
valid_regions.push(region.clone());
|
||||
if region.notify_on_hover {
|
||||
notified_views.insert(region.id().view_id());
|
||||
@ -684,7 +709,7 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
} else {
|
||||
// Ensure that hover exit events aren't sent twice
|
||||
if window.hovered_region_ids.remove(®ion.id()) {
|
||||
if prev_hovered_regions.contains(®ion.id()) {
|
||||
valid_regions.push(region.clone());
|
||||
if region.notify_on_hover {
|
||||
notified_views.insert(region.id().view_id());
|
||||
@ -821,19 +846,19 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
for view_id in notified_views {
|
||||
self.notify_view(window_id, view_id);
|
||||
self.notify_view(handle, view_id);
|
||||
}
|
||||
|
||||
any_event_handled
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_key_down(&mut self, event: &KeyDownEvent) -> bool {
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
if let Some(focused_view_id) = self.window.focused_view_id {
|
||||
for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
|
||||
if let Some(mut view) = self.views.remove(&(window_id, view_id)) {
|
||||
if let Some(mut view) = self.views.remove(&(handle, view_id)) {
|
||||
let handled = view.key_down(event, self, view_id);
|
||||
self.views.insert((window_id, view_id), view);
|
||||
self.views.insert((handle, view_id), view);
|
||||
if handled {
|
||||
return true;
|
||||
}
|
||||
@ -847,12 +872,12 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_key_up(&mut self, event: &KeyUpEvent) -> bool {
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
if let Some(focused_view_id) = self.window.focused_view_id {
|
||||
for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
|
||||
if let Some(mut view) = self.views.remove(&(window_id, view_id)) {
|
||||
if let Some(mut view) = self.views.remove(&(handle, view_id)) {
|
||||
let handled = view.key_up(event, self, view_id);
|
||||
self.views.insert((window_id, view_id), view);
|
||||
self.views.insert((handle, view_id), view);
|
||||
if handled {
|
||||
return true;
|
||||
}
|
||||
@ -866,12 +891,12 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_modifiers_changed(&mut self, event: &ModifiersChangedEvent) -> bool {
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
if let Some(focused_view_id) = self.window.focused_view_id {
|
||||
for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
|
||||
if let Some(mut view) = self.views.remove(&(window_id, view_id)) {
|
||||
if let Some(mut view) = self.views.remove(&(handle, view_id)) {
|
||||
let handled = view.modifiers_changed(event, self, view_id);
|
||||
self.views.insert((window_id, view_id), view);
|
||||
self.views.insert((handle, view_id), view);
|
||||
if handled {
|
||||
return true;
|
||||
}
|
||||
@ -906,14 +931,14 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
pub fn render_view(&mut self, params: RenderParams) -> Result<Box<dyn AnyRootElement>> {
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
let view_id = params.view_id;
|
||||
let mut view = self
|
||||
.views
|
||||
.remove(&(window_id, view_id))
|
||||
.remove(&(handle, view_id))
|
||||
.ok_or_else(|| anyhow!("view not found"))?;
|
||||
let element = view.render(self, view_id);
|
||||
self.views.insert((window_id, view_id), view);
|
||||
self.views.insert((handle, view_id), view);
|
||||
Ok(element)
|
||||
}
|
||||
|
||||
@ -941,9 +966,9 @@ impl<'a> WindowContext<'a> {
|
||||
} else if old_parent_id == new_parent_id {
|
||||
current_view_id = *old_parent_id.unwrap();
|
||||
} else {
|
||||
let window_id = self.window_id;
|
||||
let handle = self.window_handle;
|
||||
for view_id_to_notify in view_ids_to_notify {
|
||||
self.notify_view(window_id, view_id_to_notify);
|
||||
self.notify_view(handle, view_id_to_notify);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1111,7 +1136,7 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
pub fn focus(&mut self, view_id: Option<usize>) {
|
||||
self.app_context.focus(self.window_id, view_id);
|
||||
self.app_context.focus(self.window_handle, view_id);
|
||||
}
|
||||
|
||||
pub fn window_bounds(&self) -> WindowBounds {
|
||||
@ -1151,17 +1176,6 @@ impl<'a> WindowContext<'a> {
|
||||
self.window.platform_window.prompt(level, msg, answers)
|
||||
}
|
||||
|
||||
pub fn replace_root_view<V, F>(&mut self, build_root_view: F) -> ViewHandle<V>
|
||||
where
|
||||
V: View,
|
||||
F: FnOnce(&mut ViewContext<V>) -> V,
|
||||
{
|
||||
let root_view = self.add_view(|cx| build_root_view(cx));
|
||||
self.window.root_view = Some(root_view.clone().into_any());
|
||||
self.window.focused_view_id = Some(root_view.id());
|
||||
root_view
|
||||
}
|
||||
|
||||
pub fn add_view<T, F>(&mut self, build_view: F) -> ViewHandle<T>
|
||||
where
|
||||
T: View,
|
||||
@ -1175,26 +1189,26 @@ impl<'a> WindowContext<'a> {
|
||||
T: View,
|
||||
F: FnOnce(&mut ViewContext<T>) -> Option<T>,
|
||||
{
|
||||
let window_id = self.window_id;
|
||||
let view_id = post_inc(&mut self.next_entity_id);
|
||||
let handle = self.window_handle;
|
||||
let view_id = post_inc(&mut self.next_id);
|
||||
let mut cx = ViewContext::mutable(self, view_id);
|
||||
let handle = if let Some(view) = build_view(&mut cx) {
|
||||
let mut keymap_context = KeymapContext::default();
|
||||
view.update_keymap_context(&mut keymap_context, cx.app_context());
|
||||
self.views_metadata.insert(
|
||||
(window_id, view_id),
|
||||
(handle, view_id),
|
||||
ViewMetadata {
|
||||
type_id: TypeId::of::<T>(),
|
||||
keymap_context,
|
||||
},
|
||||
);
|
||||
self.views.insert((window_id, view_id), Box::new(view));
|
||||
self.views.insert((handle, view_id), Box::new(view));
|
||||
self.window
|
||||
.invalidation
|
||||
.get_or_insert_with(Default::default)
|
||||
.updated
|
||||
.insert(view_id);
|
||||
Some(ViewHandle::new(window_id, view_id, &self.ref_counts))
|
||||
Some(ViewHandle::new(handle, view_id, &self.ref_counts))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -1371,7 +1385,7 @@ pub struct ChildView {
|
||||
|
||||
impl ChildView {
|
||||
pub fn new(view: &AnyViewHandle, cx: &AppContext) -> Self {
|
||||
let view_name = cx.view_ui_name(view.window_id(), view.id()).unwrap();
|
||||
let view_name = cx.view_ui_name(view.window, view.id()).unwrap();
|
||||
Self {
|
||||
view_id: view.id(),
|
||||
view_name,
|
||||
@ -1420,7 +1434,7 @@ impl<V: View> Element<V> for ChildView {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) {
|
||||
if let Some(mut rendered_view) = cx.window.rendered_views.remove(&self.view_id) {
|
||||
rendered_view
|
||||
|
@ -2,11 +2,11 @@ use std::{cell::RefCell, ops::Range, rc::Rc};
|
||||
|
||||
use pathfinder_geometry::rect::RectF;
|
||||
|
||||
use crate::{platform::InputHandler, window::WindowContext, AnyView, AppContext};
|
||||
use crate::{platform::InputHandler, window::WindowContext, AnyView, AnyWindowHandle, AppContext};
|
||||
|
||||
pub struct WindowInputHandler {
|
||||
pub app: Rc<RefCell<AppContext>>,
|
||||
pub window_id: usize,
|
||||
pub window: AnyWindowHandle,
|
||||
}
|
||||
|
||||
impl WindowInputHandler {
|
||||
@ -21,13 +21,12 @@ impl WindowInputHandler {
|
||||
//
|
||||
// See https://github.com/zed-industries/community/issues/444
|
||||
let mut app = self.app.try_borrow_mut().ok()?;
|
||||
app.update_window(self.window_id, |cx| {
|
||||
self.window.update_optional(&mut *app, |cx| {
|
||||
let view_id = cx.window.focused_view_id?;
|
||||
let view = cx.views.get(&(self.window_id, view_id))?;
|
||||
let view = cx.views.get(&(self.window, view_id))?;
|
||||
let result = f(view.as_ref(), &cx);
|
||||
Some(result)
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
|
||||
fn update_focused_view<T, F>(&mut self, f: F) -> Option<T>
|
||||
@ -35,11 +34,12 @@ impl WindowInputHandler {
|
||||
F: FnOnce(&mut dyn AnyView, &mut WindowContext, usize) -> T,
|
||||
{
|
||||
let mut app = self.app.try_borrow_mut().ok()?;
|
||||
app.update_window(self.window_id, |cx| {
|
||||
let view_id = cx.window.focused_view_id?;
|
||||
cx.update_any_view(view_id, |view, cx| f(view, cx, view_id))
|
||||
})
|
||||
.flatten()
|
||||
self.window
|
||||
.update(&mut *app, |cx| {
|
||||
let view_id = cx.window.focused_view_id?;
|
||||
cx.update_any_view(view_id, |view, cx| f(view, cx, view_id))
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,9 +83,8 @@ impl InputHandler for WindowInputHandler {
|
||||
}
|
||||
|
||||
fn rect_for_range(&self, range_utf16: Range<usize>) -> Option<RectF> {
|
||||
self.app
|
||||
.borrow()
|
||||
.read_window(self.window_id, |cx| cx.rect_for_text_range(range_utf16))
|
||||
.flatten()
|
||||
self.window.read_optional_with(&*self.app.borrow(), |cx| {
|
||||
cx.rect_for_text_range(range_utf16)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -33,8 +33,8 @@ use crate::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json, Action, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, WeakViewHandle,
|
||||
WindowContext,
|
||||
json, Action, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
WeakViewHandle, WindowContext,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use collections::HashMap;
|
||||
@ -61,7 +61,7 @@ pub trait Element<V: View>: 'static {
|
||||
visible_bounds: RectF,
|
||||
layout: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState;
|
||||
|
||||
fn rect_for_text_range(
|
||||
@ -170,7 +170,7 @@ pub trait Element<V: View>: 'static {
|
||||
fn with_tooltip<Tag: 'static>(
|
||||
self,
|
||||
id: usize,
|
||||
text: String,
|
||||
text: impl Into<Cow<'static, str>>,
|
||||
action: Option<Box<dyn Action>>,
|
||||
style: TooltipStyle,
|
||||
cx: &mut ViewContext<V>,
|
||||
@ -178,7 +178,7 @@ pub trait Element<V: View>: 'static {
|
||||
where
|
||||
Self: 'static + Sized,
|
||||
{
|
||||
Tooltip::new::<Tag, V>(id, text, action, style, self.into_any(), cx)
|
||||
Tooltip::new::<Tag>(id, text, action, style, self.into_any(), cx)
|
||||
}
|
||||
|
||||
fn resizable(
|
||||
@ -201,6 +201,10 @@ pub trait Element<V: View>: 'static {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RenderElement {
|
||||
fn render<V: View>(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
|
||||
}
|
||||
|
||||
trait AnyElementState<V: View> {
|
||||
fn layout(
|
||||
&mut self,
|
||||
@ -298,7 +302,14 @@ impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
|
||||
mut layout,
|
||||
} => {
|
||||
let bounds = RectF::new(origin, size);
|
||||
let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
|
||||
let paint = element.paint(
|
||||
scene,
|
||||
bounds,
|
||||
visible_bounds,
|
||||
&mut layout,
|
||||
view,
|
||||
&mut PaintContext::new(cx),
|
||||
);
|
||||
ElementState::PostPaint {
|
||||
element,
|
||||
constraint,
|
||||
@ -316,7 +327,14 @@ impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
|
||||
..
|
||||
} => {
|
||||
let bounds = RectF::new(origin, bounds.size());
|
||||
let paint = element.paint(scene, bounds, visible_bounds, &mut layout, view, cx);
|
||||
let paint = element.paint(
|
||||
scene,
|
||||
bounds,
|
||||
visible_bounds,
|
||||
&mut layout,
|
||||
view,
|
||||
&mut PaintContext::new(cx),
|
||||
);
|
||||
ElementState::PostPaint {
|
||||
element,
|
||||
constraint,
|
||||
@ -513,7 +531,7 @@ impl<V: View> Element<V> for AnyElement<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
self.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json, AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
};
|
||||
use json::ToJson;
|
||||
|
||||
@ -69,7 +70,7 @@ impl<V: View> Element<V> for Align<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let my_center = bounds.size() / 2.;
|
||||
let my_target = my_center + my_center * self.alignment;
|
||||
|
@ -3,7 +3,7 @@ use std::marker::PhantomData;
|
||||
use super::Element;
|
||||
use crate::{
|
||||
json::{self, json},
|
||||
SceneBuilder, View, ViewContext,
|
||||
PaintContext, SceneBuilder, View, ViewContext,
|
||||
};
|
||||
use json::ToJson;
|
||||
use pathfinder_geometry::{
|
||||
@ -56,7 +56,7 @@ where
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
self.0(scene, bounds, visible_bounds, view, cx)
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ use pathfinder_geometry::{rect::RectF, vector::Vector2F};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
json, AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
};
|
||||
|
||||
pub struct Clipped<V: View> {
|
||||
@ -37,7 +38,7 @@ impl<V: View> Element<V> for Clipped<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
scene.paint_layer(Some(bounds), |scene| {
|
||||
self.child
|
||||
|
@ -5,7 +5,8 @@ use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json, AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
};
|
||||
|
||||
pub struct ConstrainedBox<V: View> {
|
||||
@ -156,7 +157,7 @@ impl<V: View> Element<V> for ConstrainedBox<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
scene.paint_layer(Some(visible_bounds), |scene| {
|
||||
self.child
|
||||
|
@ -9,8 +9,9 @@ use crate::{
|
||||
},
|
||||
json::ToJson,
|
||||
platform::CursorStyle,
|
||||
scene::{self, Border, CursorRegion, Quad},
|
||||
AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
scene::{self, Border, CornerRadii, CursorRegion, Quad},
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
@ -29,7 +30,8 @@ pub struct ContainerStyle {
|
||||
#[serde(default)]
|
||||
pub border: Border,
|
||||
#[serde(default)]
|
||||
pub corner_radius: f32,
|
||||
#[serde(alias = "corner_radius")]
|
||||
pub corner_radii: CornerRadii,
|
||||
#[serde(default)]
|
||||
pub shadow: Option<Shadow>,
|
||||
#[serde(default)]
|
||||
@ -132,7 +134,10 @@ impl<V: View> Container<V> {
|
||||
}
|
||||
|
||||
pub fn with_corner_radius(mut self, radius: f32) -> Self {
|
||||
self.style.corner_radius = radius;
|
||||
self.style.corner_radii.top_left = radius;
|
||||
self.style.corner_radii.top_right = radius;
|
||||
self.style.corner_radii.bottom_right = radius;
|
||||
self.style.corner_radii.bottom_left = radius;
|
||||
self
|
||||
}
|
||||
|
||||
@ -214,7 +219,7 @@ impl<V: View> Element<V> for Container<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let quad_bounds = RectF::from_points(
|
||||
bounds.origin() + vec2f(self.style.margin.left, self.style.margin.top),
|
||||
@ -224,7 +229,7 @@ impl<V: View> Element<V> for Container<V> {
|
||||
if let Some(shadow) = self.style.shadow.as_ref() {
|
||||
scene.push_shadow(scene::Shadow {
|
||||
bounds: quad_bounds + shadow.offset,
|
||||
corner_radius: self.style.corner_radius,
|
||||
corner_radii: self.style.corner_radii,
|
||||
sigma: shadow.blur,
|
||||
color: shadow.color,
|
||||
});
|
||||
@ -247,7 +252,7 @@ impl<V: View> Element<V> for Container<V> {
|
||||
bounds: quad_bounds,
|
||||
background: self.style.background_color,
|
||||
border: Default::default(),
|
||||
corner_radius: self.style.corner_radius,
|
||||
corner_radii: self.style.corner_radii.into(),
|
||||
});
|
||||
|
||||
self.child
|
||||
@ -258,7 +263,7 @@ impl<V: View> Element<V> for Container<V> {
|
||||
bounds: quad_bounds,
|
||||
background: self.style.overlay_color,
|
||||
border: self.style.border,
|
||||
corner_radius: self.style.corner_radius,
|
||||
corner_radii: self.style.corner_radii.into(),
|
||||
});
|
||||
scene.pop_layer();
|
||||
} else {
|
||||
@ -266,7 +271,7 @@ impl<V: View> Element<V> for Container<V> {
|
||||
bounds: quad_bounds,
|
||||
background: self.style.background_color,
|
||||
border: self.style.border,
|
||||
corner_radius: self.style.corner_radius,
|
||||
corner_radii: self.style.corner_radii.into(),
|
||||
});
|
||||
|
||||
let child_origin = child_origin
|
||||
@ -283,7 +288,7 @@ impl<V: View> Element<V> for Container<V> {
|
||||
bounds: quad_bounds,
|
||||
background: self.style.overlay_color,
|
||||
border: Default::default(),
|
||||
corner_radius: 0.,
|
||||
corner_radii: self.style.corner_radii.into(),
|
||||
});
|
||||
scene.pop_layer();
|
||||
}
|
||||
@ -327,7 +332,7 @@ impl ToJson for ContainerStyle {
|
||||
"padding": self.padding.to_json(),
|
||||
"background_color": self.background_color.to_json(),
|
||||
"border": self.border.to_json(),
|
||||
"corner_radius": self.corner_radius,
|
||||
"corner_radius": self.corner_radii,
|
||||
"shadow": self.shadow.to_json(),
|
||||
})
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{json, ToJson},
|
||||
LayoutContext, SceneBuilder, View, ViewContext,
|
||||
LayoutContext, PaintContext, SceneBuilder, View, ViewContext,
|
||||
};
|
||||
use crate::{Element, SizeConstraint};
|
||||
|
||||
@ -57,7 +57,7 @@ impl<V: View> Element<V> for Empty {
|
||||
_: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
_: &mut ViewContext<V>,
|
||||
_: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,8 @@ use std::ops::Range;
|
||||
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json, AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
@ -61,7 +62,7 @@ impl<V: View> Element<V> for Expanded<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
|
@ -2,8 +2,8 @@ use std::{any::Any, cell::Cell, f32::INFINITY, ops::Range, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
json::{self, ToJson, Value},
|
||||
AnyElement, Axis, Element, ElementStateHandle, LayoutContext, SceneBuilder, SizeConstraint,
|
||||
Vector2FExt, View, ViewContext,
|
||||
AnyElement, Axis, Element, ElementStateHandle, LayoutContext, PaintContext, SceneBuilder,
|
||||
SizeConstraint, Vector2FExt, View, ViewContext,
|
||||
};
|
||||
use pathfinder_geometry::{
|
||||
rect::RectF,
|
||||
@ -258,7 +258,7 @@ impl<V: View> Element<V> for Flex<V> {
|
||||
visible_bounds: RectF,
|
||||
remaining_space: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||
|
||||
@ -449,7 +449,7 @@ impl<V: View> Element<V> for FlexItem<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx)
|
||||
|
@ -3,7 +3,8 @@ use std::ops::Range;
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::json,
|
||||
AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
};
|
||||
|
||||
pub struct Hook<V: View> {
|
||||
@ -52,7 +53,7 @@ impl<V: View> Element<V> for Hook<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) {
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
|
@ -5,8 +5,8 @@ use crate::{
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{json, ToJson},
|
||||
scene, Border, Element, ImageData, LayoutContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
scene, Border, Element, ImageData, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
View, ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
@ -97,13 +97,13 @@ impl<V: View> Element<V> for Image {
|
||||
_: RectF,
|
||||
layout: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
_: &mut ViewContext<V>,
|
||||
_: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
if let Some(data) = layout {
|
||||
scene.push_image(scene::Image {
|
||||
bounds,
|
||||
border: self.style.border,
|
||||
corner_radius: self.style.corner_radius,
|
||||
corner_radii: self.style.corner_radius.into(),
|
||||
grayscale: self.style.grayscale,
|
||||
data: data.clone(),
|
||||
});
|
||||
|
@ -66,7 +66,7 @@ impl<V: View> Element<V> for KeystrokeLabel {
|
||||
visible_bounds: RectF,
|
||||
element: &mut AnyElement<V>,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) {
|
||||
element.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
},
|
||||
json::{ToJson, Value},
|
||||
text_layout::{Line, RunStyle},
|
||||
Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
@ -163,7 +163,7 @@ impl<V: View> Element<V> for Label {
|
||||
visible_bounds: RectF,
|
||||
line: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||
line.paint(
|
||||
|
@ -4,8 +4,8 @@ use crate::{
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::json,
|
||||
AnyElement, Element, LayoutContext, MouseRegion, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
AnyElement, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder, SizeConstraint,
|
||||
View, ViewContext,
|
||||
};
|
||||
use std::{cell::RefCell, collections::VecDeque, fmt::Debug, ops::Range, rc::Rc};
|
||||
use sum_tree::{Bias, SumTree};
|
||||
@ -255,7 +255,7 @@ impl<V: View> Element<V> for List<V> {
|
||||
visible_bounds: RectF,
|
||||
scroll_top: &mut ListOffset,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) {
|
||||
let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
|
||||
scene.push_layer(Some(visible_bounds));
|
||||
@ -647,7 +647,7 @@ impl<'a> sum_tree::SeekTarget<'a, ListItemSummary, ListItemSummary> for Height {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{elements::Empty, geometry::vector::vec2f, Entity};
|
||||
use crate::{elements::Empty, geometry::vector::vec2f, Entity, PaintContext};
|
||||
use rand::prelude::*;
|
||||
use std::env;
|
||||
|
||||
@ -988,7 +988,7 @@ mod tests {
|
||||
_: RectF,
|
||||
_: &mut (),
|
||||
_: &mut V,
|
||||
_: &mut ViewContext<V>,
|
||||
_: &mut PaintContext<V>,
|
||||
) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
@ -10,8 +10,8 @@ use crate::{
|
||||
CursorRegion, HandlerSet, MouseClick, MouseClickOut, MouseDown, MouseDownOut, MouseDrag,
|
||||
MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
|
||||
},
|
||||
AnyElement, Element, EventContext, LayoutContext, MouseRegion, MouseState, SceneBuilder,
|
||||
SizeConstraint, View, ViewContext,
|
||||
AnyElement, Element, EventContext, LayoutContext, MouseRegion, MouseState, PaintContext,
|
||||
SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::{marker::PhantomData, ops::Range};
|
||||
@ -256,7 +256,7 @@ impl<Tag, V: View> Element<V> for MouseEventHandler<Tag, V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
if self.above {
|
||||
self.child
|
||||
|
@ -3,8 +3,8 @@ use std::ops::Range;
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::ToJson,
|
||||
AnyElement, Axis, Element, LayoutContext, MouseRegion, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
AnyElement, Axis, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder,
|
||||
SizeConstraint, View, ViewContext,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
@ -143,7 +143,7 @@ impl<V: View> Element<V> for Overlay<V> {
|
||||
_: RectF,
|
||||
size: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) {
|
||||
let (anchor_position, mut bounds) = match self.position_mode {
|
||||
OverlayPositionMode::Window => {
|
||||
|
@ -7,8 +7,8 @@ use crate::{
|
||||
geometry::rect::RectF,
|
||||
platform::{CursorStyle, MouseButton},
|
||||
scene::MouseDrag,
|
||||
AnyElement, Axis, Element, LayoutContext, MouseRegion, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
AnyElement, Axis, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder,
|
||||
SizeConstraint, View, ViewContext,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@ -125,7 +125,7 @@ impl<V: View> Element<V> for Resizable<V> {
|
||||
visible_bounds: pathfinder_geometry::rect::RectF,
|
||||
constraint: &mut SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
scene.push_stacking_context(None, None);
|
||||
|
||||
|
@ -3,7 +3,8 @@ use std::ops::Range;
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::{self, json, ToJson},
|
||||
AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
};
|
||||
|
||||
/// Element which renders it's children in a stack on top of each other.
|
||||
@ -57,7 +58,7 @@ impl<V: View> Element<V> for Stack<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
for child in &mut self.children {
|
||||
scene.paint_layer(None, |scene| {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::constrain_size_preserving_aspect_ratio;
|
||||
use crate::json::ToJson;
|
||||
use crate::PaintContext;
|
||||
use crate::{
|
||||
color::Color,
|
||||
geometry::{
|
||||
@ -73,7 +74,7 @@ impl<V: View> Element<V> for Svg {
|
||||
_visible_bounds: RectF,
|
||||
svg: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
_: &mut ViewContext<V>,
|
||||
_: &mut PaintContext<V>,
|
||||
) {
|
||||
if let Some(svg) = svg.clone() {
|
||||
scene.push_icon(scene::Icon {
|
||||
|
@ -7,8 +7,8 @@ use crate::{
|
||||
},
|
||||
json::{ToJson, Value},
|
||||
text_layout::{Line, RunStyle, ShapedBoundary},
|
||||
AppContext, Element, FontCache, LayoutContext, SceneBuilder, SizeConstraint, TextLayoutCache,
|
||||
View, ViewContext,
|
||||
AppContext, Element, FontCache, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
TextLayoutCache, View, ViewContext,
|
||||
};
|
||||
use log::warn;
|
||||
use serde_json::json;
|
||||
@ -171,7 +171,7 @@ impl<V: View> Element<V> for Text {
|
||||
visible_bounds: RectF,
|
||||
layout: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let mut origin = bounds.origin();
|
||||
let empty = Vec::new();
|
||||
|
@ -6,12 +6,13 @@ use crate::{
|
||||
fonts::TextStyle,
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::json,
|
||||
Action, Axis, ElementStateHandle, LayoutContext, SceneBuilder, SizeConstraint, Task, View,
|
||||
ViewContext,
|
||||
Action, Axis, ElementStateHandle, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
Task, View, ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
cell::{Cell, RefCell},
|
||||
ops::Range,
|
||||
rc::Rc,
|
||||
@ -52,9 +53,9 @@ pub struct KeystrokeStyle {
|
||||
}
|
||||
|
||||
impl<V: View> Tooltip<V> {
|
||||
pub fn new<Tag: 'static, T: View>(
|
||||
pub fn new<Tag: 'static>(
|
||||
id: usize,
|
||||
text: String,
|
||||
text: impl Into<Cow<'static, str>>,
|
||||
action: Option<Box<dyn Action>>,
|
||||
style: TooltipStyle,
|
||||
child: AnyElement<V>,
|
||||
@ -66,6 +67,8 @@ impl<V: View> Tooltip<V> {
|
||||
|
||||
let state_handle = cx.default_element_state::<ElementState<Tag>, Rc<TooltipState>>(id);
|
||||
let state = state_handle.read(cx).clone();
|
||||
let text = text.into();
|
||||
|
||||
let tooltip = if state.visible.get() {
|
||||
let mut collapsed_tooltip = Self::render_tooltip(
|
||||
focused_view_id,
|
||||
@ -127,7 +130,7 @@ impl<V: View> Tooltip<V> {
|
||||
|
||||
pub fn render_tooltip(
|
||||
focused_view_id: Option<usize>,
|
||||
text: String,
|
||||
text: impl Into<Cow<'static, str>>,
|
||||
style: TooltipStyle,
|
||||
action: Option<Box<dyn Action>>,
|
||||
measure: bool,
|
||||
@ -194,7 +197,7 @@ impl<V: View> Element<V> for Tooltip<V> {
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) {
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
},
|
||||
json::{self, json},
|
||||
platform::ScrollWheelEvent,
|
||||
AnyElement, LayoutContext, MouseRegion, SceneBuilder, View, ViewContext,
|
||||
AnyElement, LayoutContext, MouseRegion, PaintContext, SceneBuilder, View, ViewContext,
|
||||
};
|
||||
use json::ToJson;
|
||||
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
|
||||
@ -278,7 +278,7 @@ impl<V: View> Element<V> for UniformList<V> {
|
||||
visible_bounds: RectF,
|
||||
layout: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
|
||||
|
||||
|
@ -71,6 +71,32 @@ pub struct TextStyle {
|
||||
pub underline: Underline,
|
||||
}
|
||||
|
||||
impl TextStyle {
|
||||
pub fn refine(self, refinement: TextStyleRefinement) -> TextStyle {
|
||||
TextStyle {
|
||||
color: refinement.color.unwrap_or(self.color),
|
||||
font_family_name: refinement
|
||||
.font_family_name
|
||||
.unwrap_or_else(|| self.font_family_name.clone()),
|
||||
font_family_id: refinement.font_family_id.unwrap_or(self.font_family_id),
|
||||
font_id: refinement.font_id.unwrap_or(self.font_id),
|
||||
font_size: refinement.font_size.unwrap_or(self.font_size),
|
||||
font_properties: refinement.font_properties.unwrap_or(self.font_properties),
|
||||
underline: refinement.underline.unwrap_or(self.underline),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextStyleRefinement {
|
||||
pub color: Option<Color>,
|
||||
pub font_family_name: Option<Arc<str>>,
|
||||
pub font_family_id: Option<FamilyId>,
|
||||
pub font_id: Option<FontId>,
|
||||
pub font_size: Option<f32>,
|
||||
pub font_properties: Option<Properties>,
|
||||
pub underline: Option<Underline>,
|
||||
}
|
||||
|
||||
#[derive(JsonSchema)]
|
||||
#[serde(remote = "Properties")]
|
||||
pub struct PropertiesDef {
|
||||
|
@ -19,7 +19,7 @@ use crate::{
|
||||
},
|
||||
keymap_matcher::KeymapMatcher,
|
||||
text_layout::{LineLayout, RunStyle},
|
||||
Action, ClipboardItem, Menu, Scene,
|
||||
Action, AnyWindowHandle, ClipboardItem, Menu, Scene,
|
||||
};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use async_task::Runnable;
|
||||
@ -58,13 +58,13 @@ pub trait Platform: Send + Sync {
|
||||
|
||||
fn open_window(
|
||||
&self,
|
||||
id: usize,
|
||||
handle: AnyWindowHandle,
|
||||
options: WindowOptions,
|
||||
executor: Rc<executor::Foreground>,
|
||||
) -> Box<dyn Window>;
|
||||
fn main_window_id(&self) -> Option<usize>;
|
||||
fn main_window(&self) -> Option<AnyWindowHandle>;
|
||||
|
||||
fn add_status_item(&self, id: usize) -> Box<dyn Window>;
|
||||
fn add_status_item(&self, handle: AnyWindowHandle) -> Box<dyn Window>;
|
||||
|
||||
fn write_to_clipboard(&self, item: ClipboardItem);
|
||||
fn read_from_clipboard(&self) -> Option<ClipboardItem>;
|
||||
|
@ -21,7 +21,7 @@ pub use fonts::FontSystem;
|
||||
use platform::{MacForegroundPlatform, MacPlatform};
|
||||
pub use renderer::Surface;
|
||||
use std::{ops::Range, rc::Rc, sync::Arc};
|
||||
use window::Window;
|
||||
use window::MacWindow;
|
||||
|
||||
use crate::executor;
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
use super::{
|
||||
event::key_to_native, screen::Screen, status_item::StatusItem, BoolExt as _, Dispatcher,
|
||||
FontSystem, Window,
|
||||
FontSystem, MacWindow,
|
||||
};
|
||||
use crate::{
|
||||
executor,
|
||||
keymap_matcher::KeymapMatcher,
|
||||
platform::{self, AppVersion, CursorStyle, Event},
|
||||
Action, ClipboardItem, Menu, MenuItem,
|
||||
Action, AnyWindowHandle, ClipboardItem, Menu, MenuItem,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use block::ConcreteBlock;
|
||||
@ -590,18 +590,18 @@ impl platform::Platform for MacPlatform {
|
||||
|
||||
fn open_window(
|
||||
&self,
|
||||
id: usize,
|
||||
handle: AnyWindowHandle,
|
||||
options: platform::WindowOptions,
|
||||
executor: Rc<executor::Foreground>,
|
||||
) -> Box<dyn platform::Window> {
|
||||
Box::new(Window::open(id, options, executor, self.fonts()))
|
||||
Box::new(MacWindow::open(handle, options, executor, self.fonts()))
|
||||
}
|
||||
|
||||
fn main_window_id(&self) -> Option<usize> {
|
||||
Window::main_window_id()
|
||||
fn main_window(&self) -> Option<AnyWindowHandle> {
|
||||
MacWindow::main_window()
|
||||
}
|
||||
|
||||
fn add_status_item(&self, _id: usize) -> Box<dyn platform::Window> {
|
||||
fn add_status_item(&self, _handle: AnyWindowHandle) -> Box<dyn platform::Window> {
|
||||
Box::new(StatusItem::add(self.fonts()))
|
||||
}
|
||||
|
||||
|
@ -509,10 +509,14 @@ impl Renderer {
|
||||
};
|
||||
for (ix, shadow) in shadows.iter().enumerate() {
|
||||
let shape_bounds = shadow.bounds * scale_factor;
|
||||
let corner_radii = shadow.corner_radii * scale_factor;
|
||||
let shader_shadow = shaders::GPUIShadow {
|
||||
origin: shape_bounds.origin().to_float2(),
|
||||
size: shape_bounds.size().to_float2(),
|
||||
corner_radius: shadow.corner_radius * scale_factor,
|
||||
corner_radius_top_left: corner_radii.top_left,
|
||||
corner_radius_top_right: corner_radii.top_right,
|
||||
corner_radius_bottom_right: corner_radii.bottom_right,
|
||||
corner_radius_bottom_left: corner_radii.bottom_left,
|
||||
sigma: shadow.sigma,
|
||||
color: shadow.color.to_uchar4(),
|
||||
};
|
||||
@ -586,7 +590,10 @@ impl Renderer {
|
||||
border_bottom: border_width * (quad.border.bottom as usize as f32),
|
||||
border_left: border_width * (quad.border.left as usize as f32),
|
||||
border_color: quad.border.color.to_uchar4(),
|
||||
corner_radius: quad.corner_radius * scale_factor,
|
||||
corner_radius_top_left: quad.corner_radii.top_left * scale_factor,
|
||||
corner_radius_top_right: quad.corner_radii.top_right * scale_factor,
|
||||
corner_radius_bottom_right: quad.corner_radii.bottom_right * scale_factor,
|
||||
corner_radius_bottom_left: quad.corner_radii.bottom_left * scale_factor,
|
||||
};
|
||||
unsafe {
|
||||
*(buffer_contents.add(ix)) = shader_quad;
|
||||
@ -738,7 +745,7 @@ impl Renderer {
|
||||
for image in images {
|
||||
let origin = image.bounds.origin() * scale_factor;
|
||||
let target_size = image.bounds.size() * scale_factor;
|
||||
let corner_radius = image.corner_radius * scale_factor;
|
||||
let corner_radii = image.corner_radii * scale_factor;
|
||||
let border_width = image.border.width * scale_factor;
|
||||
let (alloc_id, atlas_bounds) = self.image_cache.render(&image.data);
|
||||
images_by_atlas
|
||||
@ -754,7 +761,10 @@ impl Renderer {
|
||||
border_bottom: border_width * (image.border.bottom as usize as f32),
|
||||
border_left: border_width * (image.border.left as usize as f32),
|
||||
border_color: image.border.color.to_uchar4(),
|
||||
corner_radius,
|
||||
corner_radius_top_left: corner_radii.top_left,
|
||||
corner_radius_top_right: corner_radii.top_right,
|
||||
corner_radius_bottom_right: corner_radii.bottom_right,
|
||||
corner_radius_bottom_left: corner_radii.bottom_left,
|
||||
grayscale: image.grayscale as u8,
|
||||
});
|
||||
}
|
||||
@ -777,7 +787,10 @@ impl Renderer {
|
||||
border_bottom: 0.,
|
||||
border_left: 0.,
|
||||
border_color: Default::default(),
|
||||
corner_radius: 0.,
|
||||
corner_radius_top_left: 0.,
|
||||
corner_radius_top_right: 0.,
|
||||
corner_radius_bottom_right: 0.,
|
||||
corner_radius_bottom_left: 0.,
|
||||
grayscale: false as u8,
|
||||
});
|
||||
} else {
|
||||
|
@ -19,7 +19,10 @@ typedef struct {
|
||||
float border_bottom;
|
||||
float border_left;
|
||||
vector_uchar4 border_color;
|
||||
float corner_radius;
|
||||
float corner_radius_top_left;
|
||||
float corner_radius_top_right;
|
||||
float corner_radius_bottom_right;
|
||||
float corner_radius_bottom_left;
|
||||
} GPUIQuad;
|
||||
|
||||
typedef enum {
|
||||
@ -31,7 +34,10 @@ typedef enum {
|
||||
typedef struct {
|
||||
vector_float2 origin;
|
||||
vector_float2 size;
|
||||
float corner_radius;
|
||||
float corner_radius_top_left;
|
||||
float corner_radius_top_right;
|
||||
float corner_radius_bottom_right;
|
||||
float corner_radius_bottom_left;
|
||||
float sigma;
|
||||
vector_uchar4 color;
|
||||
} GPUIShadow;
|
||||
@ -89,7 +95,10 @@ typedef struct {
|
||||
float border_bottom;
|
||||
float border_left;
|
||||
vector_uchar4 border_color;
|
||||
float corner_radius;
|
||||
float corner_radius_top_left;
|
||||
float corner_radius_top_right;
|
||||
float corner_radius_bottom_right;
|
||||
float corner_radius_bottom_left;
|
||||
uint8_t grayscale;
|
||||
} GPUIImage;
|
||||
|
||||
|
@ -43,7 +43,10 @@ struct QuadFragmentInput {
|
||||
float border_bottom;
|
||||
float border_left;
|
||||
float4 border_color;
|
||||
float corner_radius;
|
||||
float corner_radius_top_left;
|
||||
float corner_radius_top_right;
|
||||
float corner_radius_bottom_right;
|
||||
float corner_radius_bottom_left;
|
||||
uchar grayscale; // only used in image shader
|
||||
};
|
||||
|
||||
@ -51,12 +54,27 @@ float4 quad_sdf(QuadFragmentInput input) {
|
||||
float2 half_size = input.size / 2.;
|
||||
float2 center = input.origin + half_size;
|
||||
float2 center_to_point = input.position.xy - center;
|
||||
float2 rounded_edge_to_point = abs(center_to_point) - half_size + input.corner_radius;
|
||||
float distance = length(max(0., rounded_edge_to_point)) + min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - input.corner_radius;
|
||||
float corner_radius;
|
||||
if (center_to_point.x < 0.) {
|
||||
if (center_to_point.y < 0.) {
|
||||
corner_radius = input.corner_radius_top_left;
|
||||
} else {
|
||||
corner_radius = input.corner_radius_bottom_left;
|
||||
}
|
||||
} else {
|
||||
if (center_to_point.y < 0.) {
|
||||
corner_radius = input.corner_radius_top_right;
|
||||
} else {
|
||||
corner_radius = input.corner_radius_bottom_right;
|
||||
}
|
||||
}
|
||||
|
||||
float2 rounded_edge_to_point = abs(center_to_point) - half_size + corner_radius;
|
||||
float distance = length(max(0., rounded_edge_to_point)) + min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - corner_radius;
|
||||
|
||||
float vertical_border = center_to_point.x <= 0. ? input.border_left : input.border_right;
|
||||
float horizontal_border = center_to_point.y <= 0. ? input.border_top : input.border_bottom;
|
||||
float2 inset_size = half_size - input.corner_radius - float2(vertical_border, horizontal_border);
|
||||
float2 inset_size = half_size - corner_radius - float2(vertical_border, horizontal_border);
|
||||
float2 point_to_inset_corner = abs(center_to_point) - inset_size;
|
||||
float border_width;
|
||||
if (point_to_inset_corner.x < 0. && point_to_inset_corner.y < 0.) {
|
||||
@ -110,7 +128,10 @@ vertex QuadFragmentInput quad_vertex(
|
||||
quad.border_bottom,
|
||||
quad.border_left,
|
||||
coloru_to_colorf(quad.border_color),
|
||||
quad.corner_radius,
|
||||
quad.corner_radius_top_left,
|
||||
quad.corner_radius_top_right,
|
||||
quad.corner_radius_bottom_right,
|
||||
quad.corner_radius_bottom_left,
|
||||
0,
|
||||
};
|
||||
}
|
||||
@ -125,7 +146,10 @@ struct ShadowFragmentInput {
|
||||
float4 position [[position]];
|
||||
vector_float2 origin;
|
||||
vector_float2 size;
|
||||
float corner_radius;
|
||||
float corner_radius_top_left;
|
||||
float corner_radius_top_right;
|
||||
float corner_radius_bottom_right;
|
||||
float corner_radius_bottom_left;
|
||||
float sigma;
|
||||
vector_uchar4 color;
|
||||
};
|
||||
@ -148,7 +172,10 @@ vertex ShadowFragmentInput shadow_vertex(
|
||||
device_position,
|
||||
shadow.origin,
|
||||
shadow.size,
|
||||
shadow.corner_radius,
|
||||
shadow.corner_radius_top_left,
|
||||
shadow.corner_radius_top_right,
|
||||
shadow.corner_radius_bottom_right,
|
||||
shadow.corner_radius_bottom_left,
|
||||
shadow.sigma,
|
||||
shadow.color,
|
||||
};
|
||||
@ -158,10 +185,24 @@ fragment float4 shadow_fragment(
|
||||
ShadowFragmentInput input [[stage_in]]
|
||||
) {
|
||||
float sigma = input.sigma;
|
||||
float corner_radius = input.corner_radius;
|
||||
float2 half_size = input.size / 2.;
|
||||
float2 center = input.origin + half_size;
|
||||
float2 point = input.position.xy - center;
|
||||
float2 center_to_point = input.position.xy - center;
|
||||
float corner_radius;
|
||||
if (center_to_point.x < 0.) {
|
||||
if (center_to_point.y < 0.) {
|
||||
corner_radius = input.corner_radius_top_left;
|
||||
} else {
|
||||
corner_radius = input.corner_radius_bottom_left;
|
||||
}
|
||||
} else {
|
||||
if (center_to_point.y < 0.) {
|
||||
corner_radius = input.corner_radius_top_right;
|
||||
} else {
|
||||
corner_radius = input.corner_radius_bottom_right;
|
||||
}
|
||||
}
|
||||
|
||||
// The signal is only non-zero in a limited range, so don't waste samples
|
||||
float low = point.y - half_size.y;
|
||||
@ -252,7 +293,10 @@ vertex QuadFragmentInput image_vertex(
|
||||
image.border_bottom,
|
||||
image.border_left,
|
||||
coloru_to_colorf(image.border_color),
|
||||
image.corner_radius,
|
||||
image.corner_radius_top_left,
|
||||
image.corner_radius_top_right,
|
||||
image.corner_radius_bottom_right,
|
||||
image.corner_radius_bottom_left,
|
||||
image.grayscale,
|
||||
};
|
||||
}
|
||||
@ -266,7 +310,7 @@ fragment float4 image_fragment(
|
||||
if (input.grayscale) {
|
||||
float grayscale =
|
||||
0.2126 * input.background_color.r +
|
||||
0.7152 * input.background_color.g +
|
||||
0.7152 * input.background_color.g +
|
||||
0.0722 * input.background_color.b;
|
||||
input.background_color = float4(grayscale, grayscale, grayscale, input.background_color.a);
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ use crate::{
|
||||
Event, InputHandler, KeyDownEvent, Modifiers, ModifiersChangedEvent, MouseButton,
|
||||
MouseButtonEvent, MouseMovedEvent, Scene, WindowBounds, WindowKind,
|
||||
},
|
||||
AnyWindowHandle,
|
||||
};
|
||||
use block::ConcreteBlock;
|
||||
use cocoa::{
|
||||
@ -282,7 +283,7 @@ struct InsertText {
|
||||
}
|
||||
|
||||
struct WindowState {
|
||||
id: usize,
|
||||
handle: AnyWindowHandle,
|
||||
native_window: id,
|
||||
kind: WindowKind,
|
||||
event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
|
||||
@ -422,11 +423,11 @@ impl WindowState {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Window(Rc<RefCell<WindowState>>);
|
||||
pub struct MacWindow(Rc<RefCell<WindowState>>);
|
||||
|
||||
impl Window {
|
||||
impl MacWindow {
|
||||
pub fn open(
|
||||
id: usize,
|
||||
handle: AnyWindowHandle,
|
||||
options: platform::WindowOptions,
|
||||
executor: Rc<executor::Foreground>,
|
||||
fonts: Arc<dyn platform::FontSystem>,
|
||||
@ -504,7 +505,7 @@ impl Window {
|
||||
assert!(!native_view.is_null());
|
||||
|
||||
let window = Self(Rc::new(RefCell::new(WindowState {
|
||||
id,
|
||||
handle,
|
||||
native_window,
|
||||
kind: options.kind,
|
||||
event_callback: None,
|
||||
@ -621,13 +622,13 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main_window_id() -> Option<usize> {
|
||||
pub fn main_window() -> Option<AnyWindowHandle> {
|
||||
unsafe {
|
||||
let app = NSApplication::sharedApplication(nil);
|
||||
let main_window: id = msg_send![app, mainWindow];
|
||||
if msg_send![main_window, isKindOfClass: WINDOW_CLASS] {
|
||||
let id = get_window_state(&*main_window).borrow().id;
|
||||
Some(id)
|
||||
let handle = get_window_state(&*main_window).borrow().handle;
|
||||
Some(handle)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -635,7 +636,7 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
impl Drop for MacWindow {
|
||||
fn drop(&mut self) {
|
||||
let this = self.0.borrow();
|
||||
let window = this.native_window;
|
||||
@ -649,7 +650,7 @@ impl Drop for Window {
|
||||
}
|
||||
}
|
||||
|
||||
impl platform::Window for Window {
|
||||
impl platform::Window for MacWindow {
|
||||
fn bounds(&self) -> WindowBounds {
|
||||
self.0.as_ref().borrow().bounds()
|
||||
}
|
||||
@ -881,7 +882,7 @@ impl platform::Window for Window {
|
||||
|
||||
fn is_topmost_for_position(&self, position: Vector2F) -> bool {
|
||||
let self_borrow = self.0.borrow();
|
||||
let self_id = self_borrow.id;
|
||||
let self_handle = self_borrow.handle;
|
||||
|
||||
unsafe {
|
||||
let app = NSApplication::sharedApplication(nil);
|
||||
@ -898,8 +899,8 @@ impl platform::Window for Window {
|
||||
let is_panel: BOOL = msg_send![top_most_window, isKindOfClass: PANEL_CLASS];
|
||||
let is_window: BOOL = msg_send![top_most_window, isKindOfClass: WINDOW_CLASS];
|
||||
if is_panel == YES || is_window == YES {
|
||||
let topmost_window_id = get_window_state(&*top_most_window).borrow().id;
|
||||
topmost_window_id == self_id
|
||||
let topmost_window = get_window_state(&*top_most_window).borrow().handle;
|
||||
topmost_window == self_handle
|
||||
} else {
|
||||
// Someone else's window is on top
|
||||
false
|
||||
@ -1086,7 +1087,10 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
|
||||
button: MouseButton::Left,
|
||||
modifiers: Modifiers { ctrl: true, .. },
|
||||
..
|
||||
}) => return,
|
||||
}) => {
|
||||
window_state_borrow.synthetic_drag_counter += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
_ => None,
|
||||
};
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
keymap_matcher::KeymapMatcher,
|
||||
Action, ClipboardItem, Menu,
|
||||
Action, AnyWindowHandle, ClipboardItem, Menu,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use collections::VecDeque;
|
||||
@ -102,7 +102,7 @@ pub struct Platform {
|
||||
fonts: Arc<dyn super::FontSystem>,
|
||||
current_clipboard_item: Mutex<Option<ClipboardItem>>,
|
||||
cursor: Mutex<CursorStyle>,
|
||||
active_window_id: Arc<Mutex<Option<usize>>>,
|
||||
active_window: Arc<Mutex<Option<AnyWindowHandle>>>,
|
||||
}
|
||||
|
||||
impl Platform {
|
||||
@ -112,7 +112,7 @@ impl Platform {
|
||||
fonts: Arc::new(super::current::FontSystem::new()),
|
||||
current_clipboard_item: Default::default(),
|
||||
cursor: Mutex::new(CursorStyle::Arrow),
|
||||
active_window_id: Default::default(),
|
||||
active_window: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -146,30 +146,30 @@ impl super::Platform for Platform {
|
||||
|
||||
fn open_window(
|
||||
&self,
|
||||
id: usize,
|
||||
handle: AnyWindowHandle,
|
||||
options: super::WindowOptions,
|
||||
_executor: Rc<super::executor::Foreground>,
|
||||
) -> Box<dyn super::Window> {
|
||||
*self.active_window_id.lock() = Some(id);
|
||||
*self.active_window.lock() = Some(handle);
|
||||
Box::new(Window::new(
|
||||
id,
|
||||
handle,
|
||||
match options.bounds {
|
||||
WindowBounds::Maximized | WindowBounds::Fullscreen => vec2f(1024., 768.),
|
||||
WindowBounds::Fixed(rect) => rect.size(),
|
||||
},
|
||||
self.active_window_id.clone(),
|
||||
self.active_window.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn main_window_id(&self) -> Option<usize> {
|
||||
self.active_window_id.lock().clone()
|
||||
fn main_window(&self) -> Option<AnyWindowHandle> {
|
||||
self.active_window.lock().clone()
|
||||
}
|
||||
|
||||
fn add_status_item(&self, id: usize) -> Box<dyn crate::platform::Window> {
|
||||
fn add_status_item(&self, handle: AnyWindowHandle) -> Box<dyn crate::platform::Window> {
|
||||
Box::new(Window::new(
|
||||
id,
|
||||
handle,
|
||||
vec2f(24., 24.),
|
||||
self.active_window_id.clone(),
|
||||
self.active_window.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
@ -256,7 +256,7 @@ impl super::Screen for Screen {
|
||||
}
|
||||
|
||||
pub struct Window {
|
||||
id: usize,
|
||||
handle: AnyWindowHandle,
|
||||
pub(crate) size: Vector2F,
|
||||
scale_factor: f32,
|
||||
current_scene: Option<crate::Scene>,
|
||||
@ -270,13 +270,17 @@ pub struct Window {
|
||||
pub(crate) title: Option<String>,
|
||||
pub(crate) edited: bool,
|
||||
pub(crate) pending_prompts: RefCell<VecDeque<oneshot::Sender<usize>>>,
|
||||
active_window_id: Arc<Mutex<Option<usize>>>,
|
||||
active_window: Arc<Mutex<Option<AnyWindowHandle>>>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(id: usize, size: Vector2F, active_window_id: Arc<Mutex<Option<usize>>>) -> Self {
|
||||
pub fn new(
|
||||
handle: AnyWindowHandle,
|
||||
size: Vector2F,
|
||||
active_window: Arc<Mutex<Option<AnyWindowHandle>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
handle,
|
||||
size,
|
||||
event_handlers: Default::default(),
|
||||
resize_handlers: Default::default(),
|
||||
@ -290,7 +294,7 @@ impl Window {
|
||||
title: None,
|
||||
edited: false,
|
||||
pending_prompts: Default::default(),
|
||||
active_window_id,
|
||||
active_window,
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,7 +346,7 @@ impl super::Window for Window {
|
||||
}
|
||||
|
||||
fn activate(&self) {
|
||||
*self.active_window_id.lock() = Some(self.id);
|
||||
*self.active_window.lock() = Some(self.handle);
|
||||
}
|
||||
|
||||
fn set_title(&mut self, title: &str) {
|
||||
|
@ -3,8 +3,10 @@ mod mouse_region;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
use collections::HashSet;
|
||||
use derive_more::Mul;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde_derive::Serialize;
|
||||
use serde_json::json;
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
@ -65,13 +67,73 @@ pub struct Quad {
|
||||
pub bounds: RectF,
|
||||
pub background: Option<Color>,
|
||||
pub border: Border,
|
||||
pub corner_radius: f32,
|
||||
pub corner_radii: CornerRadii,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Mul, Clone, Copy, Serialize, JsonSchema)]
|
||||
pub struct CornerRadii {
|
||||
pub top_left: f32,
|
||||
pub top_right: f32,
|
||||
pub bottom_right: f32,
|
||||
pub bottom_left: f32,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for CornerRadii {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
pub struct CornerRadiiHelper {
|
||||
pub top_left: Option<f32>,
|
||||
pub top_right: Option<f32>,
|
||||
pub bottom_right: Option<f32>,
|
||||
pub bottom_left: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum RadiusOrRadii {
|
||||
Radius(f32),
|
||||
Radii(CornerRadiiHelper),
|
||||
}
|
||||
|
||||
let json = RadiusOrRadii::deserialize(deserializer)?;
|
||||
|
||||
let result = match json {
|
||||
RadiusOrRadii::Radius(radius) => CornerRadii::from(radius),
|
||||
RadiusOrRadii::Radii(CornerRadiiHelper {
|
||||
top_left,
|
||||
top_right,
|
||||
bottom_right,
|
||||
bottom_left,
|
||||
}) => CornerRadii {
|
||||
top_left: top_left.unwrap_or(0.0),
|
||||
top_right: top_right.unwrap_or(0.0),
|
||||
bottom_right: bottom_right.unwrap_or(0.0),
|
||||
bottom_left: bottom_left.unwrap_or(0.0),
|
||||
},
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for CornerRadii {
|
||||
fn from(radius: f32) -> Self {
|
||||
Self {
|
||||
top_left: radius,
|
||||
top_right: radius,
|
||||
bottom_right: radius,
|
||||
bottom_left: radius,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Shadow {
|
||||
pub bounds: RectF,
|
||||
pub corner_radius: f32,
|
||||
pub corner_radii: CornerRadii,
|
||||
pub sigma: f32,
|
||||
pub color: Color,
|
||||
}
|
||||
@ -177,7 +239,7 @@ pub struct PathVertex {
|
||||
pub struct Image {
|
||||
pub bounds: RectF,
|
||||
pub border: Border,
|
||||
pub corner_radius: f32,
|
||||
pub corner_radii: CornerRadii,
|
||||
pub grayscale: bool,
|
||||
pub data: Arc<ImageData>,
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ impl MouseRegion {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)]
|
||||
pub struct MouseRegionId {
|
||||
view_id: usize,
|
||||
tag: TypeId,
|
||||
|
@ -14,3 +14,5 @@ syn = "1.0"
|
||||
quote = "1.0"
|
||||
proc-macro2 = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
gpui = { path = "../gpui" }
|
||||
|
@ -283,8 +283,12 @@ pub fn element_derive(input: TokenStream) -> TokenStream {
|
||||
|
||||
// The name of the struct/enum
|
||||
let name = input.ident;
|
||||
let must_implement = format_ident!("{}MustImplementRenderElement", name);
|
||||
|
||||
let expanded = quote! {
|
||||
trait #must_implement : gpui::elements::RenderElement {}
|
||||
impl #must_implement for #name {}
|
||||
|
||||
impl<V: gpui::View> gpui::elements::Element<V> for #name {
|
||||
type LayoutState = gpui::elements::AnyElement<V>;
|
||||
type PaintState = ();
|
||||
@ -307,7 +311,7 @@ pub fn element_derive(input: TokenStream) -> TokenStream {
|
||||
visible_bounds: gpui::geometry::rect::RectF,
|
||||
element: &mut gpui::elements::AnyElement<V>,
|
||||
view: &mut V,
|
||||
cx: &mut gpui::ViewContext<V>,
|
||||
cx: &mut gpui::PaintContext<V>,
|
||||
) {
|
||||
element.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
}
|
||||
@ -332,7 +336,7 @@ pub fn element_derive(input: TokenStream) -> TokenStream {
|
||||
_: &(),
|
||||
view: &V,
|
||||
cx: &gpui::ViewContext<V>,
|
||||
) -> serde_json::Value {
|
||||
) -> gpui::serde_json::Value {
|
||||
element.debug(view, cx)
|
||||
}
|
||||
}
|
||||
|
14
crates/gpui_macros/tests/test.rs
Normal file
14
crates/gpui_macros/tests/test.rs
Normal file
@ -0,0 +1,14 @@
|
||||
use gpui::{elements::RenderElement, View, ViewContext};
|
||||
use gpui_macros::Element;
|
||||
|
||||
#[test]
|
||||
fn test_derive_render_element() {
|
||||
#[derive(Element)]
|
||||
struct TestElement {}
|
||||
|
||||
impl RenderElement for TestElement {
|
||||
fn render<V: View>(&mut self, _: &mut V, _: &mut ViewContext<V>) -> gpui::AnyElement<V> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
@ -45,7 +45,7 @@ use syntax_map::SyntaxSnapshot;
|
||||
use theme::{SyntaxTheme, Theme};
|
||||
use tree_sitter::{self, Query};
|
||||
use unicase::UniCase;
|
||||
use util::http::HttpClient;
|
||||
use util::{http::HttpClient, paths::PathExt};
|
||||
use util::{merge_json_value_into, post_inc, ResultExt, TryFutureExt as _, UnwrapFuture};
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
@ -182,8 +182,8 @@ impl CachedLspAdapter {
|
||||
self.adapter.workspace_configuration(cx)
|
||||
}
|
||||
|
||||
pub async fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) {
|
||||
self.adapter.process_diagnostics(params).await
|
||||
pub fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) {
|
||||
self.adapter.process_diagnostics(params)
|
||||
}
|
||||
|
||||
pub async fn process_completion(&self, completion_item: &mut lsp::CompletionItem) {
|
||||
@ -262,7 +262,7 @@ pub trait LspAdapter: 'static + Send + Sync {
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary>;
|
||||
|
||||
async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
|
||||
fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
|
||||
|
||||
async fn process_completion(&self, _: &mut lsp::CompletionItem) {}
|
||||
|
||||
@ -777,7 +777,7 @@ impl LanguageRegistry {
|
||||
) -> UnwrapFuture<oneshot::Receiver<Result<Arc<Language>>>> {
|
||||
let path = path.as_ref();
|
||||
let filename = path.file_name().and_then(|name| name.to_str());
|
||||
let extension = path.extension().and_then(|name| name.to_str());
|
||||
let extension = path.extension_or_hidden_file_name();
|
||||
let path_suffixes = [extension, filename];
|
||||
self.get_or_load_language(|config| {
|
||||
let path_matches = config
|
||||
@ -1487,12 +1487,6 @@ impl Language {
|
||||
None
|
||||
}
|
||||
|
||||
pub async fn process_diagnostics(&self, diagnostics: &mut lsp::PublishDiagnosticsParams) {
|
||||
for adapter in &self.adapters {
|
||||
adapter.process_diagnostics(diagnostics).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn process_completion(self: &Arc<Self>, completion: &mut lsp::CompletionItem) {
|
||||
for adapter in &self.adapters {
|
||||
adapter.process_completion(completion).await;
|
||||
@ -1756,7 +1750,7 @@ impl LspAdapter for Arc<FakeLspAdapter> {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
|
||||
fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
|
||||
|
||||
async fn disk_based_diagnostic_sources(&self) -> Vec<String> {
|
||||
self.disk_based_diagnostics_sources.clone()
|
||||
|
@ -61,7 +61,9 @@ async fn test_lsp_logs(cx: &mut TestAppContext) {
|
||||
.receive_notification::<lsp::notification::DidOpenTextDocument>()
|
||||
.await;
|
||||
|
||||
let (_, log_view) = cx.add_window(|cx| LspLogView::new(project.clone(), log_store.clone(), cx));
|
||||
let log_view = cx
|
||||
.add_window(|cx| LspLogView::new(project.clone(), log_store.clone(), cx))
|
||||
.root(cx);
|
||||
|
||||
language_server.notify::<lsp::notification::LogMessage>(lsp::LogMessageParams {
|
||||
message: "hello from the server".into(),
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -434,7 +434,9 @@ impl LanguageServer {
|
||||
..Default::default()
|
||||
}),
|
||||
inlay_hint: Some(InlayHintClientCapabilities {
|
||||
resolve_support: None,
|
||||
resolve_support: Some(InlayHintResolveClientCapabilities {
|
||||
properties: vec!["textEdits".to_string(), "tooltip".to_string()],
|
||||
}),
|
||||
dynamic_registration: Some(false),
|
||||
}),
|
||||
..Default::default()
|
||||
|
@ -1954,7 +1954,7 @@ impl LspCommand for InlayHints {
|
||||
_: &mut Project,
|
||||
_: PeerId,
|
||||
buffer_version: &clock::Global,
|
||||
cx: &mut AppContext,
|
||||
_: &mut AppContext,
|
||||
) -> proto::InlayHintsResponse {
|
||||
proto::InlayHintsResponse {
|
||||
hints: response
|
||||
@ -1963,51 +1963,17 @@ impl LspCommand for InlayHints {
|
||||
position: Some(language::proto::serialize_anchor(&response_hint.position)),
|
||||
padding_left: response_hint.padding_left,
|
||||
padding_right: response_hint.padding_right,
|
||||
label: Some(proto::InlayHintLabel {
|
||||
label: Some(match response_hint.label {
|
||||
InlayHintLabel::String(s) => proto::inlay_hint_label::Label::Value(s),
|
||||
InlayHintLabel::LabelParts(label_parts) => {
|
||||
proto::inlay_hint_label::Label::LabelParts(proto::InlayHintLabelParts {
|
||||
parts: label_parts.into_iter().map(|label_part| proto::InlayHintLabelPart {
|
||||
value: label_part.value,
|
||||
tooltip: label_part.tooltip.map(|tooltip| {
|
||||
let proto_tooltip = match tooltip {
|
||||
InlayHintLabelPartTooltip::String(s) => proto::inlay_hint_label_part_tooltip::Content::Value(s),
|
||||
InlayHintLabelPartTooltip::MarkupContent(markup_content) => proto::inlay_hint_label_part_tooltip::Content::MarkupContent(proto::MarkupContent {
|
||||
kind: markup_content.kind,
|
||||
value: markup_content.value,
|
||||
}),
|
||||
};
|
||||
proto::InlayHintLabelPartTooltip {content: Some(proto_tooltip)}
|
||||
}),
|
||||
location: label_part.location.map(|location| proto::Location {
|
||||
start: Some(serialize_anchor(&location.range.start)),
|
||||
end: Some(serialize_anchor(&location.range.end)),
|
||||
buffer_id: location.buffer.read(cx).remote_id(),
|
||||
}),
|
||||
}).collect()
|
||||
})
|
||||
}
|
||||
}),
|
||||
}),
|
||||
kind: response_hint.kind.map(|kind| kind.name().to_string()),
|
||||
tooltip: response_hint.tooltip.map(|response_tooltip| {
|
||||
let proto_tooltip = match response_tooltip {
|
||||
InlayHintTooltip::String(s) => {
|
||||
proto::inlay_hint_tooltip::Content::Value(s)
|
||||
}
|
||||
InlayHintTooltip::MarkupContent(markup_content) => {
|
||||
proto::inlay_hint_tooltip::Content::MarkupContent(
|
||||
proto::MarkupContent {
|
||||
kind: markup_content.kind,
|
||||
value: markup_content.value,
|
||||
},
|
||||
)
|
||||
}
|
||||
};
|
||||
proto::InlayHintTooltip {
|
||||
content: Some(proto_tooltip),
|
||||
}
|
||||
// Do not pass extra data such as tooltips to clients: host can put tooltip data from the cache during resolution.
|
||||
tooltip: None,
|
||||
// Similarly, do not pass label parts to clients: host can return a detailed list during resolution.
|
||||
label: Some(proto::InlayHintLabel {
|
||||
label: Some(proto::inlay_hint_label::Label::Value(
|
||||
match response_hint.label {
|
||||
InlayHintLabel::String(s) => s,
|
||||
InlayHintLabel::LabelParts(_) => response_hint.text(),
|
||||
},
|
||||
)),
|
||||
}),
|
||||
})
|
||||
.collect(),
|
||||
|
@ -2769,24 +2769,21 @@ impl Project {
|
||||
language_server
|
||||
.on_notification::<lsp::notification::PublishDiagnostics, _>({
|
||||
let adapter = adapter.clone();
|
||||
move |mut params, cx| {
|
||||
move |mut params, mut cx| {
|
||||
let this = this;
|
||||
let adapter = adapter.clone();
|
||||
cx.spawn(|mut cx| async move {
|
||||
adapter.process_diagnostics(&mut params).await;
|
||||
if let Some(this) = this.upgrade(&cx) {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.update_diagnostics(
|
||||
server_id,
|
||||
params,
|
||||
&adapter.disk_based_diagnostic_sources,
|
||||
cx,
|
||||
)
|
||||
.log_err();
|
||||
});
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
adapter.process_diagnostics(&mut params);
|
||||
if let Some(this) = this.upgrade(&cx) {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.update_diagnostics(
|
||||
server_id,
|
||||
params,
|
||||
&adapter.disk_based_diagnostic_sources,
|
||||
cx,
|
||||
)
|
||||
.log_err();
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::Project;
|
||||
use gpui::{ModelContext, ModelHandle, WeakModelHandle};
|
||||
use gpui::{AnyWindowHandle, ModelContext, ModelHandle, WeakModelHandle};
|
||||
use std::path::PathBuf;
|
||||
use terminal::{Terminal, TerminalBuilder, TerminalSettings};
|
||||
|
||||
@ -11,7 +11,7 @@ impl Project {
|
||||
pub fn create_terminal(
|
||||
&mut self,
|
||||
working_directory: Option<PathBuf>,
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> anyhow::Result<ModelHandle<Terminal>> {
|
||||
if self.is_remote() {
|
||||
@ -27,7 +27,7 @@ impl Project {
|
||||
settings.env.clone(),
|
||||
Some(settings.blinking.clone()),
|
||||
settings.alternate_scroll,
|
||||
window_id,
|
||||
window,
|
||||
)
|
||||
.map(|builder| {
|
||||
let terminal_handle = cx.add_model(|cx| builder.subscribe(cx));
|
||||
|
@ -2369,7 +2369,7 @@ impl BackgroundScannerState {
|
||||
}
|
||||
|
||||
// Remove any git repositories whose .git entry no longer exists.
|
||||
let mut snapshot = &mut self.snapshot;
|
||||
let snapshot = &mut self.snapshot;
|
||||
let mut repositories = mem::take(&mut snapshot.git_repositories);
|
||||
let mut repository_entries = mem::take(&mut snapshot.repository_entries);
|
||||
repositories.retain(|work_directory_id, _| {
|
||||
|
@ -4,7 +4,7 @@ use collections::HashMap;
|
||||
|
||||
use gpui::{AppContext, AssetSource};
|
||||
use serde_derive::Deserialize;
|
||||
use util::iife;
|
||||
use util::{iife, paths::PathExt};
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct TypeConfig {
|
||||
@ -48,14 +48,7 @@ impl FileAssociations {
|
||||
// FIXME: Associate a type with the languages and have the file's langauge
|
||||
// override these associations
|
||||
iife!({
|
||||
let suffix = path
|
||||
.file_name()
|
||||
.and_then(|os_str| os_str.to_str())
|
||||
.and_then(|file_name| {
|
||||
file_name
|
||||
.find('.')
|
||||
.and_then(|dot_index| file_name.get(dot_index + 1..))
|
||||
})?;
|
||||
let suffix = path.icon_suffix()?;
|
||||
|
||||
this.suffixes
|
||||
.get(suffix)
|
||||
|
@ -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) {
|
||||
@ -1407,7 +1415,7 @@ impl ProjectPanel {
|
||||
|
||||
if cx
|
||||
.global::<DragAndDrop<Workspace>>()
|
||||
.currently_dragged::<ProjectEntryId>(cx.window_id())
|
||||
.currently_dragged::<ProjectEntryId>(cx.window())
|
||||
.is_some()
|
||||
&& dragged_entry_destination
|
||||
.as_ref()
|
||||
@ -1451,7 +1459,7 @@ impl ProjectPanel {
|
||||
.on_up(MouseButton::Left, move |_, this, cx| {
|
||||
if let Some((_, dragged_entry)) = cx
|
||||
.global::<DragAndDrop<Workspace>>()
|
||||
.currently_dragged::<ProjectEntryId>(cx.window_id())
|
||||
.currently_dragged::<ProjectEntryId>(cx.window())
|
||||
{
|
||||
this.move_entry(
|
||||
*dragged_entry,
|
||||
@ -1464,7 +1472,7 @@ impl ProjectPanel {
|
||||
.on_move(move |_, this, cx| {
|
||||
if cx
|
||||
.global::<DragAndDrop<Workspace>>()
|
||||
.currently_dragged::<ProjectEntryId>(cx.window_id())
|
||||
.currently_dragged::<ProjectEntryId>(cx.window())
|
||||
.is_some()
|
||||
{
|
||||
this.dragged_entry_destination = if matches!(kind, EntryKind::File(_)) {
|
||||
@ -1718,7 +1726,7 @@ impl ClipboardEntry {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use gpui::{TestAppContext, ViewHandle};
|
||||
use gpui::{AnyWindowHandle, TestAppContext, ViewHandle, WindowHandle};
|
||||
use pretty_assertions::assert_eq;
|
||||
use project::FakeFs;
|
||||
use serde_json::json;
|
||||
@ -1772,7 +1780,9 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let workspace = cx
|
||||
.add_window(|cx| Workspace::test_new(project.clone(), cx))
|
||||
.root(cx);
|
||||
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
|
||||
assert_eq!(
|
||||
visible_entries_as_strings(&panel, 0..50, cx),
|
||||
@ -1860,7 +1870,8 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let workspace = window.root(cx);
|
||||
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
|
||||
|
||||
select_path(&panel, "root1", cx);
|
||||
@ -1882,7 +1893,7 @@ mod tests {
|
||||
// Add a file with the root folder selected. The filename editor is placed
|
||||
// before the first file in the root folder.
|
||||
panel.update(cx, |panel, cx| panel.new_file(&NewFile, cx));
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
let panel = panel.read(cx);
|
||||
assert!(panel.filename_editor.is_focused(cx));
|
||||
});
|
||||
@ -2211,7 +2222,8 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let workspace = window.root(cx);
|
||||
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
|
||||
|
||||
select_path(&panel, "root1", cx);
|
||||
@ -2233,7 +2245,7 @@ mod tests {
|
||||
// Add a file with the root folder selected. The filename editor is placed
|
||||
// before the first file in the root folder.
|
||||
panel.update(cx, |panel, cx| panel.new_file(&NewFile, cx));
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
let panel = panel.read(cx);
|
||||
assert!(panel.filename_editor.is_focused(cx));
|
||||
});
|
||||
@ -2311,7 +2323,9 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs.clone(), ["/root1".as_ref()], cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let workspace = cx
|
||||
.add_window(|cx| Workspace::test_new(project.clone(), cx))
|
||||
.root(cx);
|
||||
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
|
||||
|
||||
panel.update(cx, |panel, cx| {
|
||||
@ -2384,7 +2398,8 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let workspace = window.root(cx);
|
||||
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
|
||||
|
||||
toggle_expand_dir(&panel, "src/test", cx);
|
||||
@ -2401,9 +2416,9 @@ mod tests {
|
||||
" third.rs"
|
||||
]
|
||||
);
|
||||
ensure_single_file_is_opened(window_id, &workspace, "test/first.rs", cx);
|
||||
ensure_single_file_is_opened(window, "test/first.rs", cx);
|
||||
|
||||
submit_deletion(window_id, &panel, cx);
|
||||
submit_deletion(window.into(), &panel, cx);
|
||||
assert_eq!(
|
||||
visible_entries_as_strings(&panel, 0..10, cx),
|
||||
&[
|
||||
@ -2414,7 +2429,7 @@ mod tests {
|
||||
],
|
||||
"Project panel should have no deleted file, no other file is selected in it"
|
||||
);
|
||||
ensure_no_open_items_and_panes(window_id, &workspace, cx);
|
||||
ensure_no_open_items_and_panes(window.into(), &workspace, cx);
|
||||
|
||||
select_path(&panel, "src/test/second.rs", cx);
|
||||
panel.update(cx, |panel, cx| panel.open_file(&Open, cx));
|
||||
@ -2428,9 +2443,9 @@ mod tests {
|
||||
" third.rs"
|
||||
]
|
||||
);
|
||||
ensure_single_file_is_opened(window_id, &workspace, "test/second.rs", cx);
|
||||
ensure_single_file_is_opened(window, "test/second.rs", cx);
|
||||
|
||||
cx.update_window(window_id, |cx| {
|
||||
window.update(cx, |cx| {
|
||||
let active_items = workspace
|
||||
.read(cx)
|
||||
.panes()
|
||||
@ -2446,13 +2461,13 @@ mod tests {
|
||||
.expect("Open item should be an editor");
|
||||
open_editor.update(cx, |editor, cx| editor.set_text("Another text!", cx));
|
||||
});
|
||||
submit_deletion(window_id, &panel, cx);
|
||||
submit_deletion(window.into(), &panel, cx);
|
||||
assert_eq!(
|
||||
visible_entries_as_strings(&panel, 0..10, cx),
|
||||
&["v src", " v test", " third.rs"],
|
||||
"Project panel should have no deleted file, with one last file remaining"
|
||||
);
|
||||
ensure_no_open_items_and_panes(window_id, &workspace, cx);
|
||||
ensure_no_open_items_and_panes(window.into(), &workspace, cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
@ -2473,7 +2488,8 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let workspace = window.root(cx);
|
||||
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
|
||||
|
||||
select_path(&panel, "src/", cx);
|
||||
@ -2484,7 +2500,7 @@ mod tests {
|
||||
&["v src <== selected", " > test"]
|
||||
);
|
||||
panel.update(cx, |panel, cx| panel.new_directory(&NewDirectory, cx));
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
let panel = panel.read(cx);
|
||||
assert!(panel.filename_editor.is_focused(cx));
|
||||
});
|
||||
@ -2515,7 +2531,7 @@ mod tests {
|
||||
&["v src", " > test <== selected"]
|
||||
);
|
||||
panel.update(cx, |panel, cx| panel.new_file(&NewFile, cx));
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
let panel = panel.read(cx);
|
||||
assert!(panel.filename_editor.is_focused(cx));
|
||||
});
|
||||
@ -2565,7 +2581,7 @@ mod tests {
|
||||
],
|
||||
);
|
||||
panel.update(cx, |panel, cx| panel.rename(&Rename, cx));
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
let panel = panel.read(cx);
|
||||
assert!(panel.filename_editor.is_focused(cx));
|
||||
});
|
||||
@ -2619,7 +2635,9 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let workspace = cx
|
||||
.add_window(|cx| Workspace::test_new(project.clone(), cx))
|
||||
.root(cx);
|
||||
let panel = workspace.update(cx, |workspace, cx| ProjectPanel::new(workspace, cx));
|
||||
|
||||
let new_search_events_count = Arc::new(AtomicUsize::new(0));
|
||||
@ -2678,6 +2696,65 @@ 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))
|
||||
.root(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>,
|
||||
@ -2801,13 +2878,11 @@ mod tests {
|
||||
}
|
||||
|
||||
fn ensure_single_file_is_opened(
|
||||
window_id: usize,
|
||||
workspace: &ViewHandle<Workspace>,
|
||||
window: WindowHandle<Workspace>,
|
||||
expected_path: &str,
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
cx.read_window(window_id, |cx| {
|
||||
let workspace = workspace.read(cx);
|
||||
window.update_root(cx, |workspace, cx| {
|
||||
let worktrees = workspace.worktrees(cx).collect::<Vec<_>>();
|
||||
assert_eq!(worktrees.len(), 1);
|
||||
let worktree_id = WorktreeId::from_usize(worktrees[0].id());
|
||||
@ -2829,12 +2904,12 @@ mod tests {
|
||||
}
|
||||
|
||||
fn submit_deletion(
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
panel: &ViewHandle<ProjectPanel>,
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
assert!(
|
||||
!cx.has_pending_prompt(window_id),
|
||||
!window.has_pending_prompt(cx),
|
||||
"Should have no prompts before the deletion"
|
||||
);
|
||||
panel.update(cx, |panel, cx| {
|
||||
@ -2844,27 +2919,27 @@ mod tests {
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
assert!(
|
||||
cx.has_pending_prompt(window_id),
|
||||
window.has_pending_prompt(cx),
|
||||
"Should have a prompt after the deletion"
|
||||
);
|
||||
cx.simulate_prompt_answer(window_id, 0);
|
||||
window.simulate_prompt_answer(0, cx);
|
||||
assert!(
|
||||
!cx.has_pending_prompt(window_id),
|
||||
!window.has_pending_prompt(cx),
|
||||
"Should have no prompts after prompt was replied to"
|
||||
);
|
||||
cx.foreground().run_until_parked();
|
||||
}
|
||||
|
||||
fn ensure_no_open_items_and_panes(
|
||||
window_id: usize,
|
||||
window: AnyWindowHandle,
|
||||
workspace: &ViewHandle<Workspace>,
|
||||
cx: &mut TestAppContext,
|
||||
) {
|
||||
assert!(
|
||||
!cx.has_pending_prompt(window_id),
|
||||
!window.has_pending_prompt(cx),
|
||||
"Should have no prompts after deletion operation closes the file"
|
||||
);
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
let open_project_paths = workspace
|
||||
.read(cx)
|
||||
.panes()
|
||||
@ -2878,3 +2953,4 @@ mod tests {
|
||||
});
|
||||
}
|
||||
}
|
||||
// TODO - a workspace command?
|
||||
|
@ -326,10 +326,11 @@ mod tests {
|
||||
},
|
||||
);
|
||||
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
let workspace = window.root(cx);
|
||||
|
||||
// Create the project symbols view.
|
||||
let symbols = cx.add_view(window_id, |cx| {
|
||||
let symbols = window.add_view(cx, |cx| {
|
||||
ProjectSymbols::new(
|
||||
ProjectSymbolsDelegate::new(workspace.downgrade(), project.clone()),
|
||||
cx,
|
||||
|
@ -5,6 +5,7 @@ use gpui::{
|
||||
elements::{Label, LabelStyle},
|
||||
AnyElement, Element, View,
|
||||
};
|
||||
use util::paths::PathExt;
|
||||
use workspace::WorkspaceLocation;
|
||||
|
||||
pub struct HighlightedText {
|
||||
@ -61,7 +62,7 @@ impl HighlightedWorkspaceLocation {
|
||||
.paths()
|
||||
.iter()
|
||||
.map(|path| {
|
||||
let path = util::paths::compact(&path);
|
||||
let path = path.compact();
|
||||
let highlighted_text = Self::highlights_for_path(
|
||||
path.as_ref(),
|
||||
&string_match.positions,
|
||||
|
@ -11,6 +11,7 @@ use highlighted_workspace_location::HighlightedWorkspaceLocation;
|
||||
use ordered_float::OrderedFloat;
|
||||
use picker::{Picker, PickerDelegate, PickerEvent};
|
||||
use std::sync::Arc;
|
||||
use util::paths::PathExt;
|
||||
use workspace::{
|
||||
notifications::simple_message_notification::MessageNotification, Workspace, WorkspaceLocation,
|
||||
WORKSPACE_DB,
|
||||
@ -134,7 +135,7 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||
let combined_string = location
|
||||
.paths()
|
||||
.iter()
|
||||
.map(|path| util::paths::compact(&path).to_string_lossy().into_owned())
|
||||
.map(|path| path.compact().to_string_lossy().into_owned())
|
||||
.collect::<Vec<_>>()
|
||||
.join("");
|
||||
StringMatchCandidate::new(id, combined_string)
|
||||
|
@ -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)]
|
||||
@ -779,11 +849,10 @@ mod tests {
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let (window_id, _root_view) = cx.add_window(|_| EmptyView);
|
||||
let window = cx.add_window(|_| EmptyView);
|
||||
let editor = window.add_view(cx, |cx| Editor::for_buffer(buffer.clone(), None, cx));
|
||||
|
||||
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 search_bar = window.add_view(cx, |cx| {
|
||||
let mut search_bar = BufferSearchBar::new(cx);
|
||||
search_bar.set_active_pane_item(Some(&editor), cx);
|
||||
search_bar.show(cx);
|
||||
@ -1159,11 +1228,10 @@ mod tests {
|
||||
"Should pick a query with multiple results"
|
||||
);
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, buffer_text, cx));
|
||||
let (window_id, _root_view) = cx.add_window(|_| EmptyView);
|
||||
let window = cx.add_window(|_| EmptyView);
|
||||
let editor = window.add_view(cx, |cx| Editor::for_buffer(buffer.clone(), None, cx));
|
||||
|
||||
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 search_bar = window.add_view(cx, |cx| {
|
||||
let mut search_bar = BufferSearchBar::new(cx);
|
||||
search_bar.set_active_pane_item(Some(&editor), cx);
|
||||
search_bar.show(cx);
|
||||
@ -1179,12 +1247,13 @@ mod tests {
|
||||
search_bar.activate_current_match(cx);
|
||||
});
|
||||
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
assert!(
|
||||
!editor.is_focused(cx),
|
||||
"Initially, the editor should not be focused"
|
||||
);
|
||||
});
|
||||
|
||||
let initial_selections = editor.update(cx, |editor, cx| {
|
||||
let initial_selections = editor.selections.display_ranges(cx);
|
||||
assert_eq!(
|
||||
@ -1201,7 +1270,7 @@ mod tests {
|
||||
cx.focus(search_bar.query_editor.as_any());
|
||||
search_bar.select_all_matches(&SelectAllMatches, cx);
|
||||
});
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
assert!(
|
||||
editor.is_focused(cx),
|
||||
"Should focus editor after successful SelectAllMatches"
|
||||
@ -1225,7 +1294,7 @@ mod tests {
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.select_next_match(&SelectNextMatch, cx);
|
||||
});
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
assert!(
|
||||
editor.is_focused(cx),
|
||||
"Should still have editor focused after SelectNextMatch"
|
||||
@ -1254,7 +1323,7 @@ mod tests {
|
||||
cx.focus(search_bar.query_editor.as_any());
|
||||
search_bar.select_all_matches(&SelectAllMatches, cx);
|
||||
});
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
assert!(
|
||||
editor.is_focused(cx),
|
||||
"Should focus editor after successful SelectAllMatches"
|
||||
@ -1278,7 +1347,7 @@ mod tests {
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.select_prev_match(&SelectPrevMatch, cx);
|
||||
});
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
assert!(
|
||||
editor.is_focused(cx),
|
||||
"Should still have editor focused after SelectPrevMatch"
|
||||
@ -1314,7 +1383,7 @@ mod tests {
|
||||
search_bar.update(cx, |search_bar, cx| {
|
||||
search_bar.select_all_matches(&SelectAllMatches, cx);
|
||||
});
|
||||
cx.read_window(window_id, |cx| {
|
||||
window.read_with(cx, |cx| {
|
||||
assert!(
|
||||
!editor.is_focused(cx),
|
||||
"Should not switch focus to editor if SelectAllMatches does not find any matches"
|
||||
@ -1333,4 +1402,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 = cx.add_window(|_| EmptyView);
|
||||
|
||||
let editor = window.add_view(cx, |cx| Editor::for_buffer(buffer.clone(), None, cx));
|
||||
|
||||
let search_bar = window.add_view(cx, |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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user