on master help sign in
Merge branch 'master' into debug
0a6b600Blaž Hrastnik
.gitmodules+8
@@ -122,3 +122,11 @@
122 path = helix-syntax/languages/tree-sitter-svelte
122 path = helix-syntax/languages/tree-sitter-svelte
123 url = https://github.com/Himujjal/tree-sitter-svelte
123 url = https://github.com/Himujjal/tree-sitter-svelte
124 shallow = true
124 shallow = true
125+[submodule "helix-syntax/languages/tree-sitter-vue"]
126+ path = helix-syntax/languages/tree-sitter-vue
127+ url = https://github.com/ikatyang/tree-sitter-vue
128+ shallow = true
129+[submodule "helix-syntax/languages/tree-sitter-tsq"]
130+ path = helix-syntax/languages/tree-sitter-tsq
131+ url = https://github.com/tree-sitter/tree-sitter-tsq
132+ shallow = true
Cargo.lock+35−34
@@ -37,9 +37,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
37
37
38[[package]]
38[[package]]
39name = "bstr"
39name = "bstr"
40−version = "0.2.16"
40+version = "0.2.17"
41source = "registry+https://github.com/rust-lang/crates.io-index"
41source = "registry+https://github.com/rust-lang/crates.io-index"
42−checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279"
42+checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
43dependencies = [
43dependencies = [
44 "lazy_static",
44 "lazy_static",
45 "memchr",
45 "memchr",
@@ -54,9 +54,9 @@ checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e"
54
54
55[[package]]
55[[package]]
56name = "bytes"
56name = "bytes"
57−version = "1.0.1"
57+version = "1.1.0"
58source = "registry+https://github.com/rust-lang/crates.io-index"
58source = "registry+https://github.com/rust-lang/crates.io-index"
59−checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
59+checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
60
60
61[[package]]
61[[package]]
62name = "cassowary"
62name = "cassowary"
@@ -66,9 +66,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
66
66
67[[package]]
67[[package]]
68name = "cc"
68name = "cc"
69−version = "1.0.70"
69+version = "1.0.71"
70source = "registry+https://github.com/rust-lang/crates.io-index"
70source = "registry+https://github.com/rust-lang/crates.io-index"
71−checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
71+checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
72
72
73[[package]]
73[[package]]
74name = "cfg-if"
74name = "cfg-if"
@@ -369,6 +369,7 @@ dependencies = [
369 "regex",
369 "regex",
370 "ropey",
370 "ropey",
371 "serde",
371 "serde",
372+ "serde_json",
372 "similar",
373 "similar",
373 "smallvec",
374 "smallvec",
374 "tendril",
375 "tendril",
@@ -531,18 +532,18 @@ dependencies = [
531
532
532[[package]]
533[[package]]
533name = "instant"
534name = "instant"
534−version = "0.1.10"
535+version = "0.1.11"
535source = "registry+https://github.com/rust-lang/crates.io-index"
536source = "registry+https://github.com/rust-lang/crates.io-index"
536−checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d"
537+checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
537dependencies = [
538dependencies = [
538 "cfg-if",
539 "cfg-if",
539]
540]
540
541
541[[package]]
542[[package]]
542name = "itoa"
543name = "itoa"
543−version = "0.4.7"
544+version = "0.4.8"
544source = "registry+https://github.com/rust-lang/crates.io-index"
545source = "registry+https://github.com/rust-lang/crates.io-index"
545−checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
546+checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
546
547
547[[package]]
548[[package]]
548name = "jsonrpc-core"
549name = "jsonrpc-core"
@@ -565,15 +566,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
565
566
566[[package]]
567[[package]]
567name = "libc"
568name = "libc"
568−version = "0.2.99"
569+version = "0.2.103"
569source = "registry+https://github.com/rust-lang/crates.io-index"
570source = "registry+https://github.com/rust-lang/crates.io-index"
570−checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
571+checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
571
572
572[[package]]
573[[package]]
573name = "libloading"
574name = "libloading"
574−version = "0.7.0"
575+version = "0.7.1"
575source = "registry+https://github.com/rust-lang/crates.io-index"
576source = "registry+https://github.com/rust-lang/crates.io-index"
576−checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a"
577+checksum = "c0cf036d15402bea3c5d4de17b3fce76b3e4a56ebc1f577be0e7a72f7c607cf0"
577dependencies = [
578dependencies = [
578 "cfg-if",
579 "cfg-if",
579 "winapi",
580 "winapi",
@@ -581,9 +582,9 @@ dependencies = [
581
582
582[[package]]
583[[package]]
583name = "lock_api"
584name = "lock_api"
584−version = "0.4.4"
585+version = "0.4.5"
585source = "registry+https://github.com/rust-lang/crates.io-index"
586source = "registry+https://github.com/rust-lang/crates.io-index"
586−checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
587+checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
587dependencies = [
588dependencies = [
588 "scopeguard",
589 "scopeguard",
589]
590]
@@ -599,9 +600,9 @@ dependencies = [
599
600
600[[package]]
601[[package]]
601name = "lsp-types"
602name = "lsp-types"
602−version = "0.90.0"
603+version = "0.90.1"
603source = "registry+https://github.com/rust-lang/crates.io-index"
604source = "registry+https://github.com/rust-lang/crates.io-index"
604−checksum = "a7404037aab080771c90b0a499836d9d8a10336ecd07badf969567b65c6d51a1"
605+checksum = "6f3734ab1d7d157fc0c45110e06b587c31cd82bea2ccfd6b563cbff0aaeeb1d3"
605dependencies = [
606dependencies = [
606 "bitflags",
607 "bitflags",
607 "serde",
608 "serde",
@@ -711,9 +712,9 @@ checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
711
712
712[[package]]
713[[package]]
713name = "parking_lot"
714name = "parking_lot"
714−version = "0.11.1"
715+version = "0.11.2"
715source = "registry+https://github.com/rust-lang/crates.io-index"
716source = "registry+https://github.com/rust-lang/crates.io-index"
716−checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
717+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
717dependencies = [
718dependencies = [
718 "instant",
719 "instant",
719 "lock_api",
720 "lock_api",
@@ -722,9 +723,9 @@ dependencies = [
722
723
723[[package]]
724[[package]]
724name = "parking_lot_core"
725name = "parking_lot_core"
725−version = "0.8.3"
726+version = "0.8.5"
726source = "registry+https://github.com/rust-lang/crates.io-index"
727source = "registry+https://github.com/rust-lang/crates.io-index"
727−checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
728+checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
728dependencies = [
729dependencies = [
729 "cfg-if",
730 "cfg-if",
730 "instant",
731 "instant",
@@ -754,9 +755,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
754
755
755[[package]]
756[[package]]
756name = "proc-macro2"
757name = "proc-macro2"
757−version = "1.0.28"
758+version = "1.0.29"
758source = "registry+https://github.com/rust-lang/crates.io-index"
759source = "registry+https://github.com/rust-lang/crates.io-index"
759−checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
760+checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
760dependencies = [
761dependencies = [
761 "unicode-xid",
762 "unicode-xid",
762]
763]
@@ -999,9 +1000,9 @@ checksum = "d44a3643b4ff9caf57abcee9c2c621d6c03d9135e0d8b589bd9afb5992cb176a"
999
1000
1000[[package]]
1001[[package]]
1001name = "syn"
1002name = "syn"
1002−version = "1.0.74"
1003+version = "1.0.78"
1003source = "registry+https://github.com/rust-lang/crates.io-index"
1004source = "registry+https://github.com/rust-lang/crates.io-index"
1004−checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
1005+checksum = "a4eac2e6c19f5c3abc0c229bea31ff0b9b091c7b14990e8924b92902a303a0c0"
1005dependencies = [
1006dependencies = [
1006 "proc-macro2",
1007 "proc-macro2",
1007 "quote",
1008 "quote",
@@ -1021,18 +1022,18 @@ dependencies = [
1021
1022
1022[[package]]
1023[[package]]
1023name = "thiserror"
1024name = "thiserror"
1024−version = "1.0.29"
1025+version = "1.0.30"
1025source = "registry+https://github.com/rust-lang/crates.io-index"
1026source = "registry+https://github.com/rust-lang/crates.io-index"
1026−checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88"
1027+checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
1027dependencies = [
1028dependencies = [
1028 "thiserror-impl",
1029 "thiserror-impl",
1029]
1030]
1030
1031
1031[[package]]
1032[[package]]
1032name = "thiserror-impl"
1033name = "thiserror-impl"
1033−version = "1.0.29"
1034+version = "1.0.30"
1034source = "registry+https://github.com/rust-lang/crates.io-index"
1035source = "registry+https://github.com/rust-lang/crates.io-index"
1035−checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c"
1036+checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
1036dependencies = [
1037dependencies = [
1037 "proc-macro2",
1038 "proc-macro2",
1038 "quote",
1039 "quote",
@@ -1059,9 +1060,9 @@ dependencies = [
1059
1060
1060[[package]]
1061[[package]]
1061name = "tinyvec"
1062name = "tinyvec"
1062−version = "1.3.1"
1063+version = "1.5.0"
1063source = "registry+https://github.com/rust-lang/crates.io-index"
1064source = "registry+https://github.com/rust-lang/crates.io-index"
1064−checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338"
1065+checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7"
1065dependencies = [
1066dependencies = [
1066 "tinyvec_macros",
1067 "tinyvec_macros",
1067]
1068]
@@ -1094,9 +1095,9 @@ dependencies = [
1094
1095
1095[[package]]
1096[[package]]
1096name = "tokio-macros"
1097name = "tokio-macros"
1097−version = "1.3.0"
1098+version = "1.4.1"
1098source = "registry+https://github.com/rust-lang/crates.io-index"
1099source = "registry+https://github.com/rust-lang/crates.io-index"
1099−checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110"
1100+checksum = "154794c8f499c2619acd19e839294703e9e32e7630ef5f46ea80d4ef0fbee5eb"
1100dependencies = [
1101dependencies = [
1101 "proc-macro2",
1102 "proc-macro2",
1102 "quote",
1103 "quote",
README.md+2
@@ -83,4 +83,6 @@ a good overview of the internals.
83
83
84# Getting help
84# Getting help
85
85
86+Your question might already be answered on the [FAQ](https://github.com/helix-editor/helix/wiki/FAQ).
87+
86Discuss the project on the community [Matrix Space](https://matrix.to/#/#helix-community:matrix.org) (make sure to join `#helix-editor:matrix.org` if you're on a client that doesn't support Matrix Spaces yet).
88Discuss the project on the community [Matrix Space](https://matrix.to/#/#helix-community:matrix.org) (make sure to join `#helix-editor:matrix.org` if you're on a client that doesn't support Matrix Spaces yet).
book/src/configuration.md+2
@@ -19,6 +19,8 @@ To override global configuration parameters, create a `config.toml` file located
19| `line-number` | Line number display (`absolute`, `relative`) | `absolute` |
19| `line-number` | Line number display (`absolute`, `relative`) | `absolute` |
20| `smart-case` | Enable smart case regex searching (case insensitive unless pattern contains upper case characters) | `true` |
20| `smart-case` | Enable smart case regex searching (case insensitive unless pattern contains upper case characters) | `true` |
21| `auto-pairs` | Enable automatic insertion of pairs to parenthese, brackets, etc. | `true` |
21| `auto-pairs` | Enable automatic insertion of pairs to parenthese, brackets, etc. | `true` |
22+| `auto-completion` | Enable automatic pop up of auto-completion. | `true` |
23+| `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant. | `400` |
22
24
23## LSP
25## LSP
24
26
book/theme/css/chrome.css+1−1
@@ -196,7 +196,7 @@ a > .hljs {
196 border-radius: 3px;
196 border-radius: 3px;
197}
197}
198
198
199−:not(pre):not(a) > .hljs {
199+:not(pre):not(a):not(td):not(p) > .hljs {
200 color: var(--inline-code-color);
200 color: var(--inline-code-color);
201 overflow-x: initial;
201 overflow-x: initial;
202}
202}
book/theme/css/general.css+4−1
@@ -162,7 +162,6 @@ table thead td {
162table thead th {
162table thead th {
163 padding: .75rem;
163 padding: .75rem;
164 text-align: left;
164 text-align: left;
165− color: var(--table-border-color);
166 font-weight: 500;
165 font-weight: 500;
167 line-height: 1.5;
166 line-height: 1.5;
168 width: auto;
167 width: auto;
@@ -228,3 +227,7 @@ blockquote *:last-child {
228 margin: 5px 0px;
227 margin: 5px 0px;
229 font-weight: bold;
228 font-weight: bold;
230}
229}
230+
231+.result-no-output {
232+ font-style: italic;
233+}
book/theme/css/variables.css+4−10
@@ -13,7 +13,6 @@
13.ayu {
13.ayu {
14 --bg: hsl(210, 25%, 8%);
14 --bg: hsl(210, 25%, 8%);
15 --fg: #c5c5c5;
15 --fg: #c5c5c5;
16− --heading-fg: #c5c5c5;
17
16
18 --sidebar-bg: #14191f;
17 --sidebar-bg: #14191f;
19 --sidebar-fg: #c8c9db;
18 --sidebar-fg: #c8c9db;
@@ -54,7 +53,6 @@
54.coal {
53.coal {
55 --bg: hsl(200, 7%, 8%);
54 --bg: hsl(200, 7%, 8%);
56 --fg: #98a3ad;
55 --fg: #98a3ad;
57− --heading-fg: #98a3ad;
58
56
59 --sidebar-bg: #292c2f;
57 --sidebar-bg: #292c2f;
60 --sidebar-fg: #a1adb8;
58 --sidebar-fg: #a1adb8;
@@ -95,7 +93,6 @@
95.light {
93.light {
96 --bg: hsl(0, 0%, 100%);
94 --bg: hsl(0, 0%, 100%);
97 --fg: hsl(0, 0%, 0%);
95 --fg: hsl(0, 0%, 0%);
98− --heading-fg: hsl(0, 0%, 0%);
99
96
100 --sidebar-bg: #fafafa;
97 --sidebar-bg: #fafafa;
101 --sidebar-fg: hsl(0, 0%, 0%);
98 --sidebar-fg: hsl(0, 0%, 0%);
@@ -110,7 +107,7 @@
110
107
111 --links: #20609f;
108 --links: #20609f;
112
109
113− --inline-code-color: #a39e9b;
110+ --inline-code-color: #301900;
114
111
115 --theme-popup-bg: #fafafa;
112 --theme-popup-bg: #fafafa;
116 --theme-popup-border: #cccccc;
113 --theme-popup-border: #cccccc;
@@ -136,7 +133,6 @@
136.navy {
133.navy {
137 --bg: hsl(226, 23%, 11%);
134 --bg: hsl(226, 23%, 11%);
138 --fg: #bcbdd0;
135 --fg: #bcbdd0;
139− --heading-fg: #bcbdd0;
140
136
141 --sidebar-bg: #282d3f;
137 --sidebar-bg: #282d3f;
142 --sidebar-fg: #c8c9db;
138 --sidebar-fg: #c8c9db;
@@ -177,7 +173,6 @@
177.rust {
173.rust {
178 --bg: hsl(60, 9%, 87%);
174 --bg: hsl(60, 9%, 87%);
179 --fg: #262625;
175 --fg: #262625;
180− --heading-fg: #262625;
181
176
182 --sidebar-bg: #3b2e2a;
177 --sidebar-bg: #3b2e2a;
183 --sidebar-fg: #c8c9db;
178 --sidebar-fg: #c8c9db;
@@ -192,7 +187,7 @@
192
187
193 --links: #2b79a2;
188 --links: #2b79a2;
194
189
195− --inline-code-color: #c5c8c6;
190+ --inline-code-color: #6e6b5e;
196
191
197 --theme-popup-bg: #e1e1db;
192 --theme-popup-bg: #e1e1db;
198 --theme-popup-border: #b38f6b;
193 --theme-popup-border: #b38f6b;
@@ -218,8 +213,7 @@
218@media (prefers-color-scheme: dark) {
213@media (prefers-color-scheme: dark) {
219 .light.no-js {
214 .light.no-js {
220 --bg: hsl(200, 7%, 8%);
215 --bg: hsl(200, 7%, 8%);
221− --fg: #ebeafa;
216+ --fg: #98a3ad;
222− --heading-fg: #ebeafa;
223
217
224 --sidebar-bg: #292c2f;
218 --sidebar-bg: #292c2f;
225 --sidebar-fg: #a1adb8;
219 --sidebar-fg: #a1adb8;
@@ -234,7 +228,7 @@
234
228
235 --links: #2b79a2;
229 --links: #2b79a2;
236
230
237− --inline-code-color: #6e6b5e;
231+ --inline-code-color: #c5c8c6;
238
232
239 --theme-popup-bg: #141617;
233 --theme-popup-bg: #141617;
240 --theme-popup-border: #43484d;
234 --theme-popup-border: #43484d;
helix-core/Cargo.toml+1
@@ -29,6 +29,7 @@ arc-swap = "1"
29regex = "1"
29regex = "1"
30
30
31serde = { version = "1.0", features = ["derive"] }
31serde = { version = "1.0", features = ["derive"] }
32+serde_json = "1.0"
32toml = "0.5"
33toml = "0.5"
33
34
34similar = "2.1"
35similar = "2.1"
helix-core/src/selection.rs+4−4
@@ -29,10 +29,10 @@ use std::borrow::Cow;
29/// "(anchor, head)", followed by example text with "[" and "]"
29/// "(anchor, head)", followed by example text with "[" and "]"
30/// inserted to represent the anchor and head positions:
30/// inserted to represent the anchor and head positions:
31///
31///
32−/// - (0, 3): [Som]e text.
32+/// - (0, 3): `[Som]e text`.
33−/// - (3, 0): ]Som[e text.
33+/// - (3, 0): `]Som[e text`.
34−/// - (2, 7): So[me te]xt.
34+/// - (2, 7): `So[me te]xt`.
35−/// - (1, 1): S[]ome text.
35+/// - (1, 1): `S[]ome text`.
36///
36///
37/// Ranges are considered to be inclusive on the left and
37/// Ranges are considered to be inclusive on the left and
38/// exclusive on the right, regardless of anchor-head ordering.
38/// exclusive on the right, regardless of anchor-head ordering.
helix-core/src/syntax.rs+12−1
@@ -31,6 +31,15 @@ where
31 .transpose()
31 .transpose()
32}
32}
33
33
34+fn deserialize_lsp_config<'de, D>(deserializer: D) -> Result<Option<serde_json::Value>, D::Error>
35+where
36+ D: serde::Deserializer<'de>,
37+{
38+ Option::<toml::Value>::deserialize(deserializer)?
39+ .map(|toml| toml.try_into().map_err(serde::de::Error::custom))
40+ .transpose()
41+}
42+
34#[derive(Debug, Serialize, Deserialize)]
43#[derive(Debug, Serialize, Deserialize)]
35pub struct Configuration {
44pub struct Configuration {
36 pub language: Vec<LanguageConfiguration>,
45 pub language: Vec<LanguageConfiguration>,
@@ -46,7 +55,9 @@ pub struct LanguageConfiguration {
46 pub file_types: Vec<String>, // filename ends_with? <Gemfile, rb, etc>
55 pub file_types: Vec<String>, // filename ends_with? <Gemfile, rb, etc>
47 pub roots: Vec<String>, // these indicate project roots <.git, Cargo.toml>
56 pub roots: Vec<String>, // these indicate project roots <.git, Cargo.toml>
48 pub comment_token: Option<String>,
57 pub comment_token: Option<String>,
58+
59+ #[serde(default, skip_serializing, deserialize_with = "deserialize_lsp_config")]
49− pub config: Option<String>,
60+ pub config: Option<serde_json::Value>,
50
61
51 #[serde(default)]
62 #[serde(default)]
52 pub auto_format: bool,
63 pub auto_format: bool,
helix-lsp/src/lib.rs+1−9
@@ -318,15 +318,7 @@ impl Registry {
318 let (client, incoming, initialize_notify) = Client::start(
318 let (client, incoming, initialize_notify) = Client::start(
319 &config.command,
319 &config.command,
320 &config.args,
320 &config.args,
321− serde_json::from_str(language_config.config.as_deref().unwrap_or(""))
321+ language_config.config.clone(),
322− .map_err(|e| {
323− log::error!(
324− "LSP Config, {}, in `languages.toml` for `{}`",
325− e,
326− language_config.scope()
327− )
328− })
329− .ok(),
330 id,
322 id,
331 )?;
323 )?;
332 self.incoming.push(UnboundedReceiverStream::new(incoming));
324 self.incoming.push(UnboundedReceiverStream::new(incoming));
helix-syntax/languages/tree-sitter-tsqnew file+1

Submodule changed.

helix-syntax/languages/tree-sitter-vuenew file+1

Submodule changed.

helix-term/src/application.rs+62−9
@@ -102,6 +102,7 @@ impl Application {
102 if !args.files.is_empty() {
102 if !args.files.is_empty() {
103 let first = &args.files[0]; // we know it's not empty
103 let first = &args.files[0]; // we know it's not empty
104 if first.is_dir() {
104 if first.is_dir() {
105+ std::env::set_current_dir(&first)?;
105 editor.new_file(Action::VerticalSplit);
106 editor.new_file(Action::VerticalSplit);
106 compositor.push(Box::new(ui::file_picker(first.clone())));
107 compositor.push(Box::new(ui::file_picker(first.clone())));
107 } else {
108 } else {
@@ -204,6 +205,11 @@ impl Application {
204 self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback);
205 self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback);
205 self.render();
206 self.render();
206 }
207 }
208+ _ = &mut self.editor.idle_timer => {
209+ // idle timeout
210+ self.editor.clear_idle_timer();
211+ self.handle_idle_timeout();
212+ }
207 }
213 }
208 }
214 }
209 }
215 }
@@ -233,6 +239,38 @@ impl Application {
233 }
239 }
234 }
240 }
235
241
242+ pub fn handle_idle_timeout(&mut self) {
243+ use crate::commands::{completion, Context};
244+ use helix_view::document::Mode;
245+
246+ if doc_mut!(self.editor).mode != Mode::Insert || !self.config.editor.auto_completion {
247+ return;
248+ }
249+ let editor_view = self
250+ .compositor
251+ .find(std::any::type_name::<ui::EditorView>())
252+ .expect("expected at least one EditorView");
253+ let editor_view = editor_view
254+ .as_any_mut()
255+ .downcast_mut::<ui::EditorView>()
256+ .unwrap();
257+
258+ if editor_view.completion.is_some() {
259+ return;
260+ }
261+
262+ let mut cx = Context {
263+ register: None,
264+ editor: &mut self.editor,
265+ jobs: &mut self.jobs,
266+ count: None,
267+ callback: None,
268+ on_next_key_callback: None,
269+ };
270+ completion(&mut cx);
271+ self.render();
272+ }
273+
236 pub fn handle_terminal_events(&mut self, event: Option<Result<Event, crossterm::ErrorKind>>) {
274 pub fn handle_terminal_events(&mut self, event: Option<Result<Event, crossterm::ErrorKind>>) {
237 let mut cx = crate::compositor::Context {
275 let mut cx = crate::compositor::Context {
238 editor: &mut self.editor,
276 editor: &mut self.editor,
@@ -417,14 +455,6 @@ impl Application {
417 server_id: usize,
455 server_id: usize,
418 ) {
456 ) {
419 use helix_lsp::{Call, MethodCall, Notification};
457 use helix_lsp::{Call, MethodCall, Notification};
420− let editor_view = self
421− .compositor
422− .find(std::any::type_name::<ui::EditorView>())
423− .expect("expected at least one EditorView");
424− let editor_view = editor_view
425− .as_any_mut()
426− .downcast_mut::<ui::EditorView>()
427− .unwrap();
428
458
429 match call {
459 match call {
430 Call::Notification(helix_lsp::jsonrpc::Notification { method, params, .. }) => {
460 Call::Notification(helix_lsp::jsonrpc::Notification { method, params, .. }) => {
@@ -534,7 +564,19 @@ impl Application {
534 Notification::LogMessage(params) => {
564 Notification::LogMessage(params) => {
535 log::info!("window/logMessage: {:?}", params);
565 log::info!("window/logMessage: {:?}", params);
536 }
566 }
537− Notification::ProgressMessage(params) => {
567+ Notification::ProgressMessage(params)
568+ if !self
569+ .compositor
570+ .has_component(std::any::type_name::<ui::Prompt>()) =>
571+ {
572+ let editor_view = self
573+ .compositor
574+ .find(std::any::type_name::<ui::EditorView>())
575+ .expect("expected at least one EditorView");
576+ let editor_view = editor_view
577+ .as_any_mut()
578+ .downcast_mut::<ui::EditorView>()
579+ .unwrap();
538 let lsp::ProgressParams { token, value } = params;
580 let lsp::ProgressParams { token, value } = params;
539
581
540 let lsp::ProgressParamsValue::WorkDone(work) = value;
582 let lsp::ProgressParamsValue::WorkDone(work) = value;
@@ -609,6 +651,9 @@ impl Application {
609 self.editor.set_status(status);
651 self.editor.set_status(status);
610 }
652 }
611 }
653 }
654+ Notification::ProgressMessage(_params) => {
655+ // do nothing
656+ }
612 }
657 }
613 }
658 }
614 Call::MethodCall(helix_lsp::jsonrpc::MethodCall {
659 Call::MethodCall(helix_lsp::jsonrpc::MethodCall {
@@ -643,6 +688,14 @@ impl Application {
643 MethodCall::WorkDoneProgressCreate(params) => {
688 MethodCall::WorkDoneProgressCreate(params) => {
644 self.lsp_progress.create(server_id, params.token);
689 self.lsp_progress.create(server_id, params.token);
645
690
691+ let editor_view = self
692+ .compositor
693+ .find(std::any::type_name::<ui::EditorView>())
694+ .expect("expected at least one EditorView");
695+ let editor_view = editor_view
696+ .as_any_mut()
697+ .downcast_mut::<ui::EditorView>()
698+ .unwrap();
646 let spinner = editor_view.spinners_mut().get_or_create(server_id);
699 let spinner = editor_view.spinners_mut().get_or_create(server_id);
647 if spinner.is_stopped() {
700 if spinner.is_stopped() {
648 spinner.start();
701 spinner.start();
helix-term/src/commands.rs+31−11
@@ -1310,7 +1310,8 @@ fn global_search(cx: &mut Context) {
1310
1310
1311 cx.push_layer(Box::new(prompt));
1311 cx.push_layer(Box::new(prompt));
1312
1312
1313− let root = find_root(None).unwrap_or_else(|| PathBuf::from("./"));
1313+ let current_path = doc_mut!(cx.editor).path().cloned();
1314+
1314 let show_picker = async move {
1315 let show_picker = async move {
1315 let all_matches: Vec<(usize, PathBuf)> =
1316 let all_matches: Vec<(usize, PathBuf)> =
1316 UnboundedReceiverStream::new(all_matches_rx).collect().await;
1317 UnboundedReceiverStream::new(all_matches_rx).collect().await;
@@ -1320,14 +1321,19 @@ fn global_search(cx: &mut Context) {
1320 editor.set_status("No matches found".to_string());
1321 editor.set_status("No matches found".to_string());
1321 return;
1322 return;
1322 }
1323 }
1324+
1323 let picker = FilePicker::new(
1325 let picker = FilePicker::new(
1324 all_matches,
1326 all_matches,
1325 move |(_line_num, path)| {
1327 move |(_line_num, path)| {
1326− path.strip_prefix(&root)
1328+ let relative_path = helix_core::path::get_relative_path(path)
1327− .unwrap_or(path)
1328 .to_str()
1329 .to_str()
1329 .unwrap()
1330 .unwrap()
1330− .into()
1331+ .to_owned();
1332+ if current_path.as_ref().map(|p| p == path).unwrap_or(false) {
1333+ format!("{} (*)", relative_path).into()
1334+ } else {
1335+ relative_path.into()
1336+ }
1331 },
1337 },
1332 move |editor: &mut Editor, (line_num, path), action| {
1338 move |editor: &mut Editor, (line_num, path), action| {
1333 match editor.open(path.into(), action) {
1339 match editor.open(path.into(), action) {
@@ -4160,7 +4166,7 @@ fn remove_primary_selection(cx: &mut Context) {
4160 doc.set_selection(view.id, selection);
4166 doc.set_selection(view.id, selection);
4161}
4167}
4162
4168
4163−fn completion(cx: &mut Context) {
4169+pub fn completion(cx: &mut Context) {
4164 // trigger on trigger char, or if user calls it
4170 // trigger on trigger char, or if user calls it
4165 // (or on word char typing??)
4171 // (or on word char typing??)
4166 // after it's triggered, if response marked is_incomplete, update on every subsequent keypress
4172 // after it's triggered, if response marked is_incomplete, update on every subsequent keypress
@@ -4205,10 +4211,8 @@ fn completion(cx: &mut Context) {
4205 };
4211 };
4206
4212
4207 let offset_encoding = language_server.offset_encoding();
4213 let offset_encoding = language_server.offset_encoding();
4208− let cursor = doc
4214+ let text = doc.text().slice(..);
4209− .selection(view.id)
4215+ let cursor = doc.selection(view.id).primary().cursor(text);
4210− .primary()
4211− .cursor(doc.text().slice(..));
4212
4216
4213 let pos = pos_to_lsp_pos(doc.text(), cursor, offset_encoding);
4217 let pos = pos_to_lsp_pos(doc.text(), cursor, offset_encoding);
4214
4218
@@ -4216,6 +4220,15 @@ fn completion(cx: &mut Context) {
4216
4220
4217 let trigger_offset = cursor;
4221 let trigger_offset = cursor;
4218
4222
4223+ // TODO: trigger_offset should be the cursor offset but we also need a starting offset from where we want to apply
4224+ // completion filtering. For example logger.te| should filter the initial suggestion list with "te".
4225+
4226+ use helix_core::chars;
4227+ let mut iter = text.chars_at(cursor);
4228+ iter.reverse();
4229+ let offset = iter.take_while(|ch| chars::char_is_word(*ch)).count();
4230+ let start_offset = cursor.saturating_sub(offset);
4231+
4219 cx.callback(
4232 cx.callback(
4220 future,
4233 future,
4221 move |editor: &mut Editor,
4234 move |editor: &mut Editor,
@@ -4238,7 +4251,7 @@ fn completion(cx: &mut Context) {
4238 };
4251 };
4239
4252
4240 if items.is_empty() {
4253 if items.is_empty() {
4241− editor.set_error("No completion available".to_string());
4254+ // editor.set_error("No completion available".to_string());
4242 return;
4255 return;
4243 }
4256 }
4244 let size = compositor.size();
4257 let size = compositor.size();
@@ -4246,7 +4259,14 @@ fn completion(cx: &mut Context) {
4246 .find(std::any::type_name::<ui::EditorView>())
4259 .find(std::any::type_name::<ui::EditorView>())
4247 .unwrap();
4260 .unwrap();
4248 if let Some(ui) = ui.as_any_mut().downcast_mut::<ui::EditorView>() {
4261 if let Some(ui) = ui.as_any_mut().downcast_mut::<ui::EditorView>() {
4249− ui.set_completion(items, offset_encoding, trigger_offset, size);
4262+ ui.set_completion(
4263+ editor,
4264+ items,
4265+ offset_encoding,
4266+ start_offset,
4267+ trigger_offset,
4268+ size,
4269+ );
4250 };
4270 };
4251 },
4271 },
4252 );
4272 );
helix-term/src/compositor.rs+6
@@ -171,6 +171,12 @@ impl Compositor {
171 (None, CursorKind::Hidden)
171 (None, CursorKind::Hidden)
172 }
172 }
173
173
174+ pub fn has_component(&self, type_name: &str) -> bool {
175+ self.layers
176+ .iter()
177+ .any(|component| component.type_name() == type_name)
178+ }
179+
174 pub fn find(&mut self, type_name: &str) -> Option<&mut dyn Component> {
180 pub fn find(&mut self, type_name: &str) -> Option<&mut dyn Component> {
175 self.layers
181 self.layers
176 .iter_mut()
182 .iter_mut()
helix-term/src/ui/completion.rs+24−6
@@ -69,14 +69,18 @@ impl menu::Item for CompletionItem {
69/// Wraps a Menu.
69/// Wraps a Menu.
70pub struct Completion {
70pub struct Completion {
71 popup: Popup<Menu<CompletionItem>>,
71 popup: Popup<Menu<CompletionItem>>,
72+ start_offset: usize,
73+ #[allow(dead_code)]
72 trigger_offset: usize,
74 trigger_offset: usize,
73 // TODO: maintain a completioncontext with trigger kind & trigger char
75 // TODO: maintain a completioncontext with trigger kind & trigger char
74}
76}
75
77
76impl Completion {
78impl Completion {
77 pub fn new(
79 pub fn new(
80+ editor: &Editor,
78 items: Vec<CompletionItem>,
81 items: Vec<CompletionItem>,
79 offset_encoding: helix_lsp::OffsetEncoding,
82 offset_encoding: helix_lsp::OffsetEncoding,
83+ start_offset: usize,
80 trigger_offset: usize,
84 trigger_offset: usize,
81 ) -> Self {
85 ) -> Self {
82 // let items: Vec<CompletionItem> = Vec::new();
86 // let items: Vec<CompletionItem> = Vec::new();
@@ -175,16 +179,22 @@ impl Completion {
175 };
179 };
176 });
180 });
177 let popup = Popup::new(menu);
181 let popup = Popup::new(menu);
178− Self {
182+ let mut completion = Self {
179 popup,
183 popup,
184+ start_offset,
180 trigger_offset,
185 trigger_offset,
181− }
186+ };
187+
188+ // need to recompute immediately in case start_offset != trigger_offset
189+ completion.recompute_filter(editor);
190+
191+ completion
182 }
192 }
183
193
184− pub fn update(&mut self, cx: &mut commands::Context) {
194+ pub fn recompute_filter(&mut self, editor: &Editor) {
185 // recompute menu based on matches
195 // recompute menu based on matches
186 let menu = self.popup.contents_mut();
196 let menu = self.popup.contents_mut();
187− let (view, doc) = current!(cx.editor);
197+ let (view, doc) = current_ref!(editor);
188
198
189 // cx.hooks()
199 // cx.hooks()
190 // cx.add_hook(enum type, ||)
200 // cx.add_hook(enum type, ||)
@@ -200,14 +210,22 @@ impl Completion {
200 .selection(view.id)
210 .selection(view.id)
201 .primary()
211 .primary()
202 .cursor(doc.text().slice(..));
212 .cursor(doc.text().slice(..));
203− if self.trigger_offset <= cursor {
213+ if self.start_offset <= cursor {
204− let fragment = doc.text().slice(self.trigger_offset..cursor);
214+ let fragment = doc.text().slice(self.start_offset..cursor);
205 let text = Cow::from(fragment);
215 let text = Cow::from(fragment);
206 // TODO: logic is same as ui/picker
216 // TODO: logic is same as ui/picker
207 menu.score(&text);
217 menu.score(&text);
218+ } else {
219+ // we backspaced before the start offset, clear the menu
220+ // this will cause the editor to remove the completion popup
221+ menu.clear();
208 }
222 }
209 }
223 }
210
224
225+ pub fn update(&mut self, cx: &mut commands::Context) {
226+ self.recompute_filter(cx.editor)
227+ }
228+
211 pub fn is_empty(&self) -> bool {
229 pub fn is_empty(&self) -> bool {
212 self.popup.contents().is_empty()
230 self.popup.contents().is_empty()
213 }
231 }
helix-term/src/ui/editor.rs+14−2
@@ -37,7 +37,7 @@ pub struct EditorView {
37 keymaps: Keymaps,
37 keymaps: Keymaps,
38 on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
38 on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
39 last_insert: (commands::Command, Vec<KeyEvent>),
39 last_insert: (commands::Command, Vec<KeyEvent>),
40− completion: Option<Completion>,
40+ pub(crate) completion: Option<Completion>,
41 spinners: ProgressSpinners,
41 spinners: ProgressSpinners,
42 autoinfo: Option<Info>,
42 autoinfo: Option<Info>,
43}
43}
@@ -984,12 +984,21 @@ impl EditorView {
984
984
985 pub fn set_completion(
985 pub fn set_completion(
986 &mut self,
986 &mut self,
987+ editor: &Editor,
987 items: Vec<helix_lsp::lsp::CompletionItem>,
988 items: Vec<helix_lsp::lsp::CompletionItem>,
988 offset_encoding: helix_lsp::OffsetEncoding,
989 offset_encoding: helix_lsp::OffsetEncoding,
990+ start_offset: usize,
989 trigger_offset: usize,
991 trigger_offset: usize,
990 size: Rect,
992 size: Rect,
991 ) {
993 ) {
994+ let mut completion =
992− let mut completion = Completion::new(items, offset_encoding, trigger_offset);
995+ Completion::new(editor, items, offset_encoding, start_offset, trigger_offset);
996+
997+ if completion.is_empty() {
998+ // skip if we got no completion results
999+ return;
1000+ }
1001+
993 // TODO : propagate required size on resize to completion too
1002 // TODO : propagate required size on resize to completion too
994 completion.required_size((size.width, size.height));
1003 completion.required_size((size.width, size.height));
995 self.completion = Some(completion);
1004 self.completion = Some(completion);
@@ -1211,6 +1220,7 @@ impl Component for EditorView {
1211 EventResult::Consumed(None)
1220 EventResult::Consumed(None)
1212 }
1221 }
1213 Event::Key(key) => {
1222 Event::Key(key) => {
1223+ cxt.editor.reset_idle_timer();
1214 let mut key = KeyEvent::from(key);
1224 let mut key = KeyEvent::from(key);
1215 canonicalize_key(&mut key);
1225 canonicalize_key(&mut key);
1216 // clear status
1226 // clear status
@@ -1245,6 +1255,7 @@ impl Component for EditorView {
1245 if callback.is_some() {
1255 if callback.is_some() {
1246 // assume close_fn
1256 // assume close_fn
1247 self.completion = None;
1257 self.completion = None;
1258+ cxt.editor.clear_idle_timer(); // don't retrigger
1248 }
1259 }
1249 }
1260 }
1250 }
1261 }
@@ -1258,6 +1269,7 @@ impl Component for EditorView {
1258 completion.update(&mut cxt);
1269 completion.update(&mut cxt);
1259 if completion.is_empty() {
1270 if completion.is_empty() {
1260 self.completion = None;
1271 self.completion = None;
1272+ cxt.editor.clear_idle_timer(); // don't retrigger
1261 }
1273 }
1262 }
1274 }
1263 }
1275 }
helix-term/src/ui/menu.rs+8
@@ -90,6 +90,14 @@ impl<T: Item> Menu<T> {
90 self.recalculate = true;
90 self.recalculate = true;
91 }
91 }
92
92
93+ pub fn clear(&mut self) {
94+ self.matches.clear();
95+
96+ // reset cursor position
97+ self.cursor = None;
98+ self.scroll = 0;
99+ }
100+
93 pub fn move_up(&mut self) {
101 pub fn move_up(&mut self) {
94 let len = self.matches.len();
102 let len = self.matches.len();
95 let pos = self.cursor.map_or(0, |i| (i + len.saturating_sub(1)) % len) % len;
103 let pos = self.cursor.map_or(0, |i| (i + len.saturating_sub(1)) % len) % len;
helix-term/src/ui/picker.rs+6
@@ -271,12 +271,18 @@ impl<T> Picker<T> {
271 }
271 }
272
272
273 pub fn move_up(&mut self) {
273 pub fn move_up(&mut self) {
274+ if self.matches.is_empty() {
275+ return;
276+ }
274 let len = self.matches.len();
277 let len = self.matches.len();
275 let pos = ((self.cursor + len.saturating_sub(1)) % len) % len;
278 let pos = ((self.cursor + len.saturating_sub(1)) % len) % len;
276 self.cursor = pos;
279 self.cursor = pos;
277 }
280 }
278
281
279 pub fn move_down(&mut self) {
282 pub fn move_down(&mut self) {
283+ if self.matches.is_empty() {
284+ return;
285+ }
280 let len = self.matches.len();
286 let len = self.matches.len();
281 let pos = (self.cursor + 1) % len;
287 let pos = (self.cursor + 1) % len;
282 self.cursor = pos;
288 self.cursor = pos;
helix-view/src/editor.rs+35−2
@@ -13,10 +13,12 @@ use tokio_stream::wrappers::UnboundedReceiverStream;
13use std::{
13use std::{
14 collections::HashMap,
14 collections::HashMap,
15 path::{Path, PathBuf},
15 path::{Path, PathBuf},
16+ pin::Pin,
16 sync::Arc,
17 sync::Arc,
17− time::Duration,
18};
18};
19
19
20+use tokio::time::{sleep, Duration, Instant, Sleep};
21+
20use slotmap::SlotMap;
22use slotmap::SlotMap;
21
23
22use anyhow::Error;
24use anyhow::Error;
@@ -29,6 +31,14 @@ use helix_dap as dap;
29
31
30use serde::Deserialize;
32use serde::Deserialize;
31
33
34+fn deserialize_duration_millis<'de, D>(deserializer: D) -> Result<Duration, D::Error>
35+where
36+ D: serde::Deserializer<'de>,
37+{
38+ let millis = u64::deserialize(deserializer)?;
39+ Ok(Duration::from_millis(millis))
40+}
41+
32#[derive(Debug, Clone, PartialEq, Deserialize)]
42#[derive(Debug, Clone, PartialEq, Deserialize)]
33#[serde(rename_all = "kebab-case", default)]
43#[serde(rename_all = "kebab-case", default)]
34pub struct Config {
44pub struct Config {
@@ -42,12 +52,17 @@ pub struct Config {
42 pub shell: Vec<String>,
52 pub shell: Vec<String>,
43 /// Line number mode.
53 /// Line number mode.
44 pub line_number: LineNumber,
54 pub line_number: LineNumber,
45− /// Middle click paste support. Defaults to true
55+ /// Middle click paste support. Defaults to true.
46 pub middle_click_paste: bool,
56 pub middle_click_paste: bool,
47 /// Smart case: Case insensitive searching unless pattern contains upper case characters. Defaults to true.
57 /// Smart case: Case insensitive searching unless pattern contains upper case characters. Defaults to true.
48 pub smart_case: bool,
58 pub smart_case: bool,
49 /// Automatic insertion of pairs to parentheses, brackets, etc. Defaults to true.
59 /// Automatic insertion of pairs to parentheses, brackets, etc. Defaults to true.
50 pub auto_pairs: bool,
60 pub auto_pairs: bool,
61+ /// Automatic auto-completion, automatically pop up without user trigger. Defaults to true.
62+ pub auto_completion: bool,
63+ /// Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant. Defaults to 400ms.
64+ #[serde(skip_serializing, deserialize_with = "deserialize_duration_millis")]
65+ pub idle_timeout: Duration,
51}
66}
52
67
53#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
68#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
@@ -75,6 +90,8 @@ impl Default for Config {
75 middle_click_paste: true,
90 middle_click_paste: true,
76 smart_case: true,
91 smart_case: true,
77 auto_pairs: true,
92 auto_pairs: true,
93+ auto_completion: true,
94+ idle_timeout: Duration::from_millis(400),
78 }
95 }
79 }
96 }
80}
97}
@@ -105,6 +122,8 @@ pub struct Editor {
105 pub status_msg: Option<(String, Severity)>,
122 pub status_msg: Option<(String, Severity)>,
106
123
107 pub config: Config,
124 pub config: Config,
125+
126+ pub idle_timer: Pin<Box<Sleep>>,
108}
127}
109
128
110#[derive(Debug, Copy, Clone)]
129#[derive(Debug, Copy, Clone)]
@@ -146,10 +165,24 @@ impl Editor {
146 registers: Registers::default(),
165 registers: Registers::default(),
147 clipboard_provider: get_clipboard_provider(),
166 clipboard_provider: get_clipboard_provider(),
148 status_msg: None,
167 status_msg: None,
168+ idle_timer: Box::pin(sleep(config.idle_timeout)),
149 config,
169 config,
150 }
170 }
151 }
171 }
152
172
173+ pub fn clear_idle_timer(&mut self) {
174+ // equivalent to internal Instant::far_future() (30 years)
175+ self.idle_timer
176+ .as_mut()
177+ .reset(Instant::now() + Duration::from_secs(86400 * 365 * 30));
178+ }
179+
180+ pub fn reset_idle_timer(&mut self) {
181+ self.idle_timer
182+ .as_mut()
183+ .reset(Instant::now() + self.config.idle_timeout);
184+ }
185+
153 pub fn clear_status(&mut self) {
186 pub fn clear_status(&mut self) {
154 self.status_msg = None;
187 self.status_msg = None;
155 }
188 }
helix-view/src/macros.rs+16
@@ -44,3 +44,19 @@ macro_rules! view {
44 $( $editor ).+ .tree.get($( $editor ).+ .tree.focus)
44 $( $editor ).+ .tree.get($( $editor ).+ .tree.focus)
45 }};
45 }};
46}
46}
47+
48+#[macro_export]
49+macro_rules! doc {
50+ ( $( $editor:ident ).+ ) => {{
51+ $crate::current_ref!( $( $editor ).+ ).1
52+ }};
53+}
54+
55+#[macro_export]
56+macro_rules! current_ref {
57+ ( $( $editor:ident ).+ ) => {{
58+ let view = $( $editor ).+ .tree.get($( $editor ).+ .tree.focus);
59+ let doc = &$( $editor ).+ .documents[view.doc];
60+ (view, doc)
61+ }};
62+}
languages.toml+43−12
@@ -6,19 +6,11 @@ file-types = ["rs"]
6roots = []
6roots = []
7auto-format = true
7auto-format = true
8comment-token = "//"
8comment-token = "//"
9−config = """
10−{
11− "cargo": {
12− "loadOutDirsFromCheck": true
13− },
14− "procMacro": {
15− "enable": false
16− }
17−}
18−"""
19−
20language-server = { command = "rust-analyzer" }
9language-server = { command = "rust-analyzer" }
21indent = { tab-width = 4, unit = " " }
10indent = { tab-width = 4, unit = " " }
11+[language.config]
12+cargo = { loadOutDirsFromCheck = true }
13+procMacro = { enable = false }
22
14
23[language.debugger]
15[language.debugger]
24name = "lldb-vscode"
16name = "lldb-vscode"
@@ -133,6 +125,16 @@ request = "attach"
133completion = [ "pid" ]
125completion = [ "pid" ]
134args = { console = "internalConsole", pid = "{0}" }
126args = { console = "internalConsole", pid = "{0}" }
135
127
128+[[language]]
129+name = "c-sharp"
130+scope = "source.csharp"
131+injection-regex = "c-?sharp"
132+file-types = ["cs"]
133+roots = []
134+comment-token = "//"
135+
136+indent = { tab-width = 4, unit = "\t" }
137+
136[[language]]
138[[language]]
137name = "go"
139name = "go"
138scope = "source.go"
140scope = "source.go"
@@ -248,7 +250,7 @@ file-types = ["py"]
248roots = []
250roots = []
249comment-token = "#"
251comment-token = "#"
250
252
251−language-server = { command = "pyls" }
253+language-server = { command = "pylsp" }
252# TODO: pyls needs utf-8 offsets
254# TODO: pyls needs utf-8 offsets
253indent = { tab-width = 4, unit = " " }
255indent = { tab-width = 4, unit = " " }
254
256
@@ -380,6 +382,15 @@ roots = []
380indent = { tab-width = 2, unit = " " }
382indent = { tab-width = 2, unit = " " }
381language-server = { command = "svelteserver", args = ["--stdio"] }
383language-server = { command = "svelteserver", args = ["--stdio"] }
382
384
385+
386+[[language]]
387+name = "vue"
388+scope = "source.vue"
389+injection-regex = "vue"
390+file-types = ["vue"]
391+roots = []
392+indent = { tab-width = 2, unit = " " }
393+
383[[language]]
394[[language]]
384name = "yaml"
395name = "yaml"
385scope = "source.yaml"
396scope = "source.yaml"
@@ -409,3 +420,23 @@ comment-token = "//"
409
420
410language-server = { command = "zls" }
421language-server = { command = "zls" }
411indent = { tab-width = 4, unit = " " }
422indent = { tab-width = 4, unit = " " }
423+
424+[[language]]
425+name = "prolog"
426+scope = "source.prolog"
427+roots = []
428+file-types = ["pl", "prolog"]
429+comment-token = "%"
430+
431+language-server = { command = "swipl", args = [
432+ "-g", "use_module(library(lsp_server))",
433+ "-g", "lsp_server:main",
434+ "-t", "halt", "--", "stdio"] }
435+
436+[[language]]
437+name = "tsq"
438+scope = "source.tsq"
439+file-types = ["scm"]
440+roots = []
441+comment-token = ";"
442+indent = { tab-width = 2, unit = " " }
runtime/queries/c-sharp/highlights.scmnew file+238
@@ -0,0 +1,238 @@
1+;; Methods
2+(method_declaration (identifier) @type (identifier) @function)
3+
4+;; Types
5+(interface_declaration name: (identifier) @type)
6+(class_declaration name: (identifier) @type)
7+(enum_declaration name: (identifier) @type)
8+(struct_declaration (identifier) @type)
9+(record_declaration (identifier) @type)
10+(namespace_declaration name: (identifier) @type)
11+
12+(constructor_declaration name: (identifier) @type)
13+
14+[
15+ (implicit_type)
16+ (nullable_type)
17+ (pointer_type)
18+ (function_pointer_type)
19+ (predefined_type)
20+] @type.builtin
21+
22+;; Enum
23+(enum_member_declaration (identifier) @variable.property)
24+
25+;; Literals
26+[
27+ (real_literal)
28+ (integer_literal)
29+] @number
30+
31+[
32+ (character_literal)
33+ (string_literal)
34+ (verbatim_string_literal)
35+ (interpolated_string_text)
36+ (interpolated_verbatim_string_text)
37+ "\""
38+ "$\""
39+ "@$\""
40+ "$@\""
41+ ] @string
42+
43+[
44+ (boolean_literal)
45+ (null_literal)
46+ (void_keyword)
47+] @constant.builtin
48+
49+;; Comments
50+(comment) @comment
51+
52+;; Tokens
53+[
54+ ";"
55+ "."
56+ ","
57+] @punctuation.delimiter
58+
59+[
60+ "--"
61+ "-"
62+ "-="
63+ "&"
64+ "&&"
65+ "+"
66+ "++"
67+ "+="
68+ "<"
69+ "<<"
70+ "="
71+ "=="
72+ "!"
73+ "!="
74+ "=>"
75+ ">"
76+ ">>"
77+ "|"
78+ "||"
79+ "?"
80+ "??"
81+ "^"
82+ "~"
83+ "*"
84+ "/"
85+ "%"
86+ ":"
87+] @operator
88+
89+[
90+ "("
91+ ")"
92+ "["
93+ "]"
94+ "{"
95+ "}"
96+] @punctuation.bracket
97+
98+;; Keywords
99+(modifier) @keyword
100+(this_expression) @keyword
101+(escape_sequence) @keyword
102+
103+[
104+ "as"
105+ "base"
106+ "break"
107+ "case"
108+ "catch"
109+ "checked"
110+ "class"
111+ "continue"
112+ "default"
113+ "delegate"
114+ "do"
115+ "else"
116+ "enum"
117+ "event"
118+ "explicit"
119+ "finally"
120+ "for"
121+ "foreach"
122+ "goto"
123+ "if"
124+ "implicit"
125+ "interface"
126+ "is"
127+ "lock"
128+ "namespace"
129+ "operator"
130+ "params"
131+ "return"
132+ "sizeof"
133+ "stackalloc"
134+ "struct"
135+ "switch"
136+ "throw"
137+ "try"
138+ "typeof"
139+ "unchecked"
140+ "using"
141+ "while"
142+ "new"
143+ "await"
144+ "in"
145+ "yield"
146+ "get"
147+ "set"
148+ "when"
149+ "out"
150+ "ref"
151+ "from"
152+ "where"
153+ "select"
154+ "record"
155+ "init"
156+ "with"
157+ "let"
158+] @keyword
159+
160+
161+;; Linq
162+(from_clause (identifier) @variable)
163+(group_clause)
164+(order_by_clause)
165+(select_clause (identifier) @variable)
166+(query_continuation (identifier) @variable) @keyword
167+
168+;; Record
169+(with_expression
170+ (with_initializer_expression
171+ (simple_assignment_expression
172+ (identifier) @variable)))
173+
174+;; Exprs
175+(binary_expression (identifier) @variable (identifier) @variable)
176+(binary_expression (identifier)* @variable)
177+(conditional_expression (identifier) @variable)
178+(prefix_unary_expression (identifier) @variable)
179+(postfix_unary_expression (identifier)* @variable)
180+(assignment_expression (identifier) @variable)
181+(cast_expression (identifier) @type (identifier) @variable)
182+
183+;; Class
184+(base_list (identifier) @type)
185+(property_declaration (generic_name))
186+(property_declaration
187+ type: (nullable_type) @type
188+ name: (identifier) @variable)
189+(property_declaration
190+ type: (predefined_type) @type
191+ name: (identifier) @variable)
192+(property_declaration
193+ type: (identifier) @type
194+ name: (identifier) @variable)
195+
196+;; Lambda
197+(lambda_expression) @variable
198+
199+;; Attribute
200+(attribute) @type
201+
202+;; Parameter
203+(parameter
204+ type: (identifier) @type
205+ name: (identifier) @variable.parameter)
206+(parameter (identifier) @variable.parameter)
207+(parameter_modifier) @keyword
208+
209+;; Typeof
210+(type_of_expression (identifier) @type)
211+
212+;; Variable
213+(variable_declaration (identifier) @type)
214+(variable_declarator (identifier) @variable)
215+
216+;; Return
217+(return_statement (identifier) @variable)
218+(yield_statement (identifier) @variable)
219+
220+;; Type
221+(generic_name (identifier) @type)
222+(type_parameter (identifier) @variable.parameter)
223+(type_argument_list (identifier) @type)
224+
225+;; Type constraints
226+(type_parameter_constraints_clause (identifier) @variable.parameter)
227+(type_constraint (identifier) @type)
228+
229+;; Exception
230+(catch_declaration (identifier) @type (identifier) @variable)
231+(catch_declaration (identifier) @type)
232+
233+;; Switch
234+(switch_statement (identifier) @variable)
235+(switch_expression (identifier) @variable)
236+
237+;; Lock statement
238+(lock_statement (identifier) @variable)
runtime/queries/java/highlights.scm+1−1
@@ -47,7 +47,7 @@
47; Variables
47; Variables
48
48
49((identifier) @constant
49((identifier) @constant
50− (#match? @constant "^_*[A-Z][A-Z\d_]+"))
50+ (#match? @constant "^_*[A-Z][A-Z\\d_]+$"))
51
51
52(identifier) @variable
52(identifier) @variable
53
53
runtime/queries/php/highlights.scm+1−1
@@ -42,7 +42,7 @@
42(relative_scope) @variable.builtin
42(relative_scope) @variable.builtin
43
43
44((name) @constant
44((name) @constant
45− (#match? @constant "^_?[A-Z][A-Z\d_]+$"))
45+ (#match? @constant "^_?[A-Z][A-Z\\d_]+$"))
46
46
47((name) @constructor
47((name) @constructor
48 (#match? @constructor "^[A-Z]"))
48 (#match? @constructor "^[A-Z]"))
runtime/queries/tsq/highlights.scmnew file+46
@@ -0,0 +1,46 @@
1+; mark the string passed #match? as a regex
2+(((predicate_name) @function
3+ (capture)
4+ (string) @string.regexp)
5+ (#eq? @function "#match?"))
6+
7+; highlight inheritance comments
8+((query . (comment) @keyword.directive)
9+ (#match? @keyword.directive "^;\ +inherits *:"))
10+
11+[
12+ "("
13+ ")"
14+ "["
15+ "]"
16+] @punctuation.bracket
17+
18+":" @punctuation.delimiter
19+
20+[
21+ (one_or_more)
22+ (zero_or_one)
23+ (zero_or_more)
24+] @operator
25+
26+[
27+ (wildcard_node)
28+ (anchor)
29+] @constant.builtin
30+
31+[
32+ (anonymous_leaf)
33+ (string)
34+] @string
35+
36+(comment) @comment
37+
38+(field_name) @property
39+
40+(capture) @label
41+
42+(predicate_name) @function
43+
44+(escape_sequence) @escape
45+
46+(node_name) @variable
runtime/queries/vue/highlights.scmnew file+21
@@ -0,0 +1,21 @@
1+(tag_name) @tag
2+(end_tag) @tag
3+
4+(directive_name) @keyword
5+(directive_argument) @constant
6+
7+(attribute
8+ (attribute_name) @attribute
9+ (quoted_attribute_value
10+ (attribute_value) @string)
11+)
12+
13+(comment) @comment
14+
15+[
16+ "<"
17+ ">"
18+ "</"
19+ "{{"
20+ "}}"
21+] @punctuation.bracket
runtime/queries/vue/injections.scmnew file+17
@@ -0,0 +1,17 @@
1+(directive_attribute
2+ (directive_name) @keyword
3+ (quoted_attribute_value
4+ (attribute_value) @injection.content)
5+ (#set! injection.language "javascript"))
6+
7+((interpolation
8+ (raw_text) @injection.content)
9+ (#set! injection.language "javascript"))
10+
11+((script_element
12+ (raw_text) @injection.content)
13+ (#set! injection.language "javascript"))
14+
15+((style_element
16+ (raw_text) @injection.content)
17+ (#set! injection.language "css"))
runtime/themes/nord.toml+3−3
@@ -1,7 +1,7 @@
1# Author : RayGervais<raygervais@hotmail.ca>
1# Author : RayGervais<raygervais@hotmail.ca>
2
2
3# "ui.linenr.selected" = { fg = "#d8dee9" }
3# "ui.linenr.selected" = { fg = "#d8dee9" }
4−# "ui.text.focus" = { fg = "#e5ded6", modifiers= ["bold"] }
4+"ui.text.focus" = { fg = "#88c0d0", modifiers= ["bold"] }
5# "ui.menu.selected" = { fg = "#e5ded6", bg = "#313f4e" }
5# "ui.menu.selected" = { fg = "#e5ded6", bg = "#313f4e" }
6
6
7# "info" = "#b48ead"
7# "info" = "#b48ead"
@@ -24,8 +24,8 @@
24"ui.cursor.match" = { bg = "434c5e" }
24"ui.cursor.match" = { bg = "434c5e" }
25
25
26# nord3 - comments
26# nord3 - comments
27−"comment" = "#4c566a"
27+"comment" = "#616E88"
28−"ui.linenr" = { fg = "#4c566a" }
28+"ui.linenr" = { fg = "#616E88" }
29
29
30# Snow Storm
30# Snow Storm
31# nord4 - cursor, variables, constants, attributes, fields
31# nord4 - cursor, variables, constants, attributes, fields
runtime/tutor.txt+198−10
@@ -20,7 +20,6 @@ _________________________________________________________________
20 the first lesson.
20 the first lesson.
21
21
22
22
23−
24=================================================================
23=================================================================
25= BASIC CURSOR MOVEMENT =
24= BASIC CURSOR MOVEMENT =
26=================================================================
25=================================================================
@@ -43,7 +42,6 @@ _________________________________________________________________
43
42
44
43
45
44
46−
47=================================================================
45=================================================================
48= EXITING HELIX =
46= EXITING HELIX =
49=================================================================
47=================================================================
@@ -66,7 +64,6 @@ _________________________________________________________________
66
64
67
65
68
66
69−
70=================================================================
67=================================================================
71= DELETION =
68= DELETION =
72=================================================================
69=================================================================
@@ -89,7 +86,6 @@ _________________________________________________________________
89
86
90
87
91
88
92−
93=================================================================
89=================================================================
94= INSERT MODE =
90= INSERT MODE =
95=================================================================
91=================================================================
@@ -112,7 +108,6 @@ _________________________________________________________________
112 Note: The status bar will display your current mode.
108 Note: The status bar will display your current mode.
113 Notice that when you press i, 'NOR' changes to 'INS'.
109 Notice that when you press i, 'NOR' changes to 'INS'.
114
110
115−
116=================================================================
111=================================================================
117= MORE ON INSERT MODE =
112= MORE ON INSERT MODE =
118=================================================================
113=================================================================
@@ -123,7 +118,7 @@ _________________________________________________________________
123
118
124 Common examples of insertion commands include:
119 Common examples of insertion commands include:
125 i - Insert before the selection.
120 i - Insert before the selection.
126− a - Insert after the selection. (a means "append")
121+ a - Insert after the selection. (a means 'append')
127 I - Insert at the start of the line.
122 I - Insert at the start of the line.
128 A - Insert at the end of the line.
123 A - Insert at the end of the line.
129
124
@@ -135,7 +130,6 @@ _________________________________________________________________
135 --> This sentence is miss
130 --> This sentence is miss
136 This sentence is missing some text.
131 This sentence is missing some text.
137
132
138−
139=================================================================
133=================================================================
140= SAVING A FILE =
134= SAVING A FILE =
141=================================================================
135=================================================================
@@ -158,7 +152,6 @@ _________________________________________________________________
158
152
159
153
160
154
161−
162=================================================================
155=================================================================
163= RECAP =
156= RECAP =
164=================================================================
157=================================================================
@@ -181,7 +174,6 @@ _________________________________________________________________
181
174
182
175
183
176
184−
185=================================================================
177=================================================================
186= MOTIONS AND SELECTIONS =
178= MOTIONS AND SELECTIONS =
187=================================================================
179=================================================================
@@ -204,7 +196,6 @@ _________________________________________________________________
204
196
205
197
206
198
207−
208=================================================================
199=================================================================
209= MORE ON MOTIONS =
200= MORE ON MOTIONS =
210=================================================================
201=================================================================
@@ -227,6 +218,203 @@ _________________________________________________________________
227
218
228
219
229
220
221+=================================================================
222+= THE CHANGE COMMAND =
223+=================================================================
224+
225+ Press c to change the current selection.
226+
227+ The change command deletes the current selection and enters
228+ Insert mode, so it is a very common shorthand for di.
229+
230+ 1. Move the cursor to the line below marked -->.
231+ 2. Move to the start of an incorrect word and press w to
232+ select it.
233+ 3. Press c to delete the word and enter Insert mode.
234+ 4. Type the correct word.
235+ 5. Repeat until the line matches the line below it.
236+
237+ --> This paper has heavy words behind it.
238+ This sentence has incorrect words in it.
239+
240+
241+
242+
243+=================================================================
244+= COUNTS WITH MOTIONS =
245+=================================================================
246+
247+ Type a number before a motion to repeat it that many times.
248+
249+ 1. Move the cursor to the line below marked -->.
250+
251+ 2. Type 2w to move 2 words forward.
252+
253+ 3. Type 3e to move to the end of the third word forward.
254+
255+ 4. Type 2b to move 2 words backwards
256+
257+ 5. Try the above with different numbers.
258+
259+ --> This is just a line with words you can move around in.
260+
261+
262+
263+
264+
265+=================================================================
266+= SELECTING LINES =
267+=================================================================
268+
269+ Press x to select a whole line. Press again to select the next.
270+
271+ 1. Move the cursor to the second line below marked -->.
272+ 2. Press x to select the line, and d to delete it.
273+ 3. Move to the fourth line.
274+ 4. Press x twice or type 2x to select 2 lines, and d to delete.
275+
276+ --> 1) Roses are red,
277+ --> 2) Mud is fun,
278+ --> 3) Violets are blue,
279+ --> 4) I have a car,
280+ --> 5) Clocks tell time,
281+ --> 6) Sugar is sweet,
282+ --> 7) And so are you.
283+
284+
285+
286+
287+=================================================================
288+= UNDOING =
289+=================================================================
290+
291+ Type u to undo. Type U to redo.
292+
293+ 1. Move the cursor to the line below marked -->.
294+ 2. Move to the first error, and press d to delete it.
295+ 3. Type u to undo your deletion.
296+ 4. Fix all the errors on the line.
297+ 5. Type u several times to undo your fixes.
298+ 6. Type U (<SHIFT> + u) several times to redo your fixes.
299+
300+ --> Fiix the errors on thhis line and reeplace them witth undo.
301+
302+
303+
304+
305+
306+
307+
308+
309+=================================================================
310+= RECAP =
311+=================================================================
312+
313+ * Type w to select forward until the next word.
314+ * Type e to select to the end of the current word.
315+ * Type b to select backward to the start of the current word.
316+ * Use uppercase counterparts, W,E,B, to traverse WORDS.
317+
318+ * Typing d deletes the entire selection, so you can delete a
319+ word forward by typing wd.
320+
321+ * Type c to delete the selection and enter Insert mode.
322+
323+ * Type a number before a motion to repeat it that many times.
324+
325+ * Type x to select the entire current line. Type x again to
326+ select the next line.
327+
328+ * Type u to undo. Type U to redo.
329+
330+
331+=================================================================
332+= MULTIPLE CURSORS =
333+=================================================================
334+
335+ Type C to duplicate the cursor to the next line.
336+
337+ 1. Move the cursor to the first line below marked -->.
338+ 2. Type C to duplicate the cursor to the next line. Keys you
339+ press will now affect both cursors.
340+ 3. Use Insert mode to correct the lines. The two cursors will
341+ fix both lines simultaneously.
342+ 4. Type , to remove the second cursor.
343+
344+ --> Fix th two nes at same ime.
345+ --> Fix th two nes at same ime.
346+
347+ Fix these two lines at the same time.
348+
349+
350+
351+
352+
353+=================================================================
354+= THE SELECT COMMAND =
355+=================================================================
356+
357+ Type s to select matches in the selection.
358+
359+ 1. Move the cursor to the line below marked -->.
360+ 2. Press x to select the line.
361+ 3. Press s. A prompt will appear.
362+ 4. Type 'apples' and press <ENTER>. Both occurrences of
363+ 'apples' in the line will be selected.
364+ 5. You can now press c and change 'apples' to something else,
365+ like 'oranges'.
366+ 6. Type , to remove the second cursor.
367+
368+ --> I like to eat apples since my favorite fruit is apples.
369+
370+
371+
372+
373+
374+
375+=================================================================
376+= SELECTING VIA REGEX =
377+=================================================================
378+
379+ The select command selects regular expressions, not just exact
380+ matches, allowing you to target more complex patterns.
381+
382+ 1. Move the cursor to the line below marked -->.
383+ 2. Select the line with x and then press s.
384+ 3. Enter ' +' to select any amount of consecutive spaces >1.
385+ 4. Press c and change the matches to single spaces.
386+
387+ --> This sentence has some extra spaces.
388+
389+ Note: If you want to perform find-and-replace, the select
390+ command is the way to do it. Select the text you want
391+ to replace in — type % to select the whole file — and
392+ then perform the steps explained above.
393+
394+
395+
396+
397+=================================================================
398+= COLLAPSING SELECTIONS =
399+=================================================================
400+
401+ Type ; to collapse selections to single cursors.
402+
403+ Sometimes, you want to deselect without having to move the
404+ cursor(s). This can be done using the ; key.
405+
406+ 1. Move the cursor to the line below marked -->.
407+
408+ 2. Use the motions you have learned to move around the line,
409+ and try using ; to deselect the text after it is selected
410+ by the motions.
411+
412+ --> This is an error-free line with words to move around in.
413+
414+
415+
416+
417+
230
418
231=================================================================
419=================================================================
232 This tutorial is still a work-in-progress.
420 This tutorial is still a work-in-progress.