on master help sign in
Merge branch 'master' into debug
commit0a6b60085a56876068bf652c9491c435e9cca229 authorBlaž Hrastnik <blaz@mxxn.io> dateSun 17 Oct 2021, 13:51 (5 years ago) parentbc0084de216e96
31 files changed+846−117
.gitmodules+8 Cargo.lock+35−34 README.md+2 book/src/configuration.md+2 book/theme/css/chrome.css+1−1 book/theme/css/general.css+4−1 book/theme/css/variables.css+4−10 helix-core/Cargo.toml+1 helix-core/src/selection.rs+4−4 helix-core/src/syntax.rs+12−1 helix-lsp/src/lib.rs+1−9 helix-syntax/languages/tree-sitter-tsq+1 helix-syntax/languages/tree-sitter-vue+1 helix-term/src/application.rs+62−9 helix-term/src/commands.rs+31−11 helix-term/src/compositor.rs+6 helix-term/src/ui/completion.rs+24−6 helix-term/src/ui/editor.rs+14−2 helix-term/src/ui/menu.rs+8 helix-term/src/ui/picker.rs+6 helix-view/src/editor.rs+35−2 helix-view/src/macros.rs+16 languages.toml+43−12 runtime/queries/c-sharp/highlights.scm+238 runtime/queries/java/highlights.scm+1−1 runtime/queries/php/highlights.scm+1−1 runtime/queries/tsq/highlights.scm+46 runtime/queries/vue/highlights.scm+21 runtime/queries/vue/injections.scm+17 runtime/themes/nord.toml+3−3 runtime/tutor.txt+198−10
.gitmodules+8
@@ -122,3 +122,11 @@
122122 path = helix-syntax/languages/tree-sitter-svelte
123123 url = https://github.com/Himujjal/tree-sitter-svelte
124124 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"
3737
3838[[package]]
3939name = "bstr"
40−version = "0.2.16"
40+version = "0.2.17"
4141source = "registry+https://github.com/rust-lang/crates.io-index"
42−checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279"
42+checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
4343dependencies = [
4444 "lazy_static",
4545 "memchr",
@@ -54,9 +54,9 @@ checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e"
5454
5555[[package]]
5656name = "bytes"
57−version = "1.0.1"
57+version = "1.1.0"
5858source = "registry+https://github.com/rust-lang/crates.io-index"
59−checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
59+checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
6060
6161[[package]]
6262name = "cassowary"
@@ -66,9 +66,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
6666
6767[[package]]
6868name = "cc"
69−version = "1.0.70"
69+version = "1.0.71"
7070source = "registry+https://github.com/rust-lang/crates.io-index"
71−checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
71+checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
7272
7373[[package]]
7474name = "cfg-if"
@@ -369,6 +369,7 @@ dependencies = [
369369 "regex",
370370 "ropey",
371371 "serde",
372+ "serde_json",
372373 "similar",
373374 "smallvec",
374375 "tendril",
@@ -531,18 +532,18 @@ dependencies = [
531532
532533[[package]]
533534name = "instant"
534−version = "0.1.10"
535+version = "0.1.11"
535536source = "registry+https://github.com/rust-lang/crates.io-index"
536−checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d"
537+checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
537538dependencies = [
538539 "cfg-if",
539540]
540541
541542[[package]]
542543name = "itoa"
543−version = "0.4.7"
544+version = "0.4.8"
544545source = "registry+https://github.com/rust-lang/crates.io-index"
545−checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
546+checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
546547
547548[[package]]
548549name = "jsonrpc-core"
@@ -565,15 +566,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
565566
566567[[package]]
567568name = "libc"
568−version = "0.2.99"
569+version = "0.2.103"
569570source = "registry+https://github.com/rust-lang/crates.io-index"
570−checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
571+checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
571572
572573[[package]]
573574name = "libloading"
574−version = "0.7.0"
575+version = "0.7.1"
575576source = "registry+https://github.com/rust-lang/crates.io-index"
576−checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a"
577+checksum = "c0cf036d15402bea3c5d4de17b3fce76b3e4a56ebc1f577be0e7a72f7c607cf0"
577578dependencies = [
578579 "cfg-if",
579580 "winapi",
@@ -581,9 +582,9 @@ dependencies = [
581582
582583[[package]]
583584name = "lock_api"
584−version = "0.4.4"
585+version = "0.4.5"
585586source = "registry+https://github.com/rust-lang/crates.io-index"
586−checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
587+checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
587588dependencies = [
588589 "scopeguard",
589590]
@@ -599,9 +600,9 @@ dependencies = [
599600
600601[[package]]
601602name = "lsp-types"
602−version = "0.90.0"
603+version = "0.90.1"
603604source = "registry+https://github.com/rust-lang/crates.io-index"
604−checksum = "a7404037aab080771c90b0a499836d9d8a10336ecd07badf969567b65c6d51a1"
605+checksum = "6f3734ab1d7d157fc0c45110e06b587c31cd82bea2ccfd6b563cbff0aaeeb1d3"
605606dependencies = [
606607 "bitflags",
607608 "serde",
@@ -711,9 +712,9 @@ checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
711712
712713[[package]]
713714name = "parking_lot"
714−version = "0.11.1"
715+version = "0.11.2"
715716source = "registry+https://github.com/rust-lang/crates.io-index"
716−checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
717+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
717718dependencies = [
718719 "instant",
719720 "lock_api",
@@ -722,9 +723,9 @@ dependencies = [
722723
723724[[package]]
724725name = "parking_lot_core"
725−version = "0.8.3"
726+version = "0.8.5"
726727source = "registry+https://github.com/rust-lang/crates.io-index"
727−checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
728+checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
728729dependencies = [
729730 "cfg-if",
730731 "instant",
@@ -754,9 +755,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
754755
755756[[package]]
756757name = "proc-macro2"
757−version = "1.0.28"
758+version = "1.0.29"
758759source = "registry+https://github.com/rust-lang/crates.io-index"
759−checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
760+checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
760761dependencies = [
761762 "unicode-xid",
762763]
@@ -999,9 +1000,9 @@ checksum = "d44a3643b4ff9caf57abcee9c2c621d6c03d9135e0d8b589bd9afb5992cb176a"
9991000
10001001[[package]]
10011002name = "syn"
1002−version = "1.0.74"
1003+version = "1.0.78"
10031004source = "registry+https://github.com/rust-lang/crates.io-index"
1004−checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
1005+checksum = "a4eac2e6c19f5c3abc0c229bea31ff0b9b091c7b14990e8924b92902a303a0c0"
10051006dependencies = [
10061007 "proc-macro2",
10071008 "quote",
@@ -1021,18 +1022,18 @@ dependencies = [
10211022
10221023[[package]]
10231024name = "thiserror"
1024−version = "1.0.29"
1025+version = "1.0.30"
10251026source = "registry+https://github.com/rust-lang/crates.io-index"
1026−checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88"
1027+checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
10271028dependencies = [
10281029 "thiserror-impl",
10291030]
10301031
10311032[[package]]
10321033name = "thiserror-impl"
1033−version = "1.0.29"
1034+version = "1.0.30"
10341035source = "registry+https://github.com/rust-lang/crates.io-index"
1035−checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c"
1036+checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
10361037dependencies = [
10371038 "proc-macro2",
10381039 "quote",
@@ -1059,9 +1060,9 @@ dependencies = [
10591060
10601061[[package]]
10611062name = "tinyvec"
1062−version = "1.3.1"
1063+version = "1.5.0"
10631064source = "registry+https://github.com/rust-lang/crates.io-index"
1064−checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338"
1065+checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7"
10651066dependencies = [
10661067 "tinyvec_macros",
10671068]
@@ -1094,9 +1095,9 @@ dependencies = [
10941095
10951096[[package]]
10961097name = "tokio-macros"
1097−version = "1.3.0"
1098+version = "1.4.1"
10981099source = "registry+https://github.com/rust-lang/crates.io-index"
1099−checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110"
1100+checksum = "154794c8f499c2619acd19e839294703e9e32e7630ef5f46ea80d4ef0fbee5eb"
11001101dependencies = [
11011102 "proc-macro2",
11021103 "quote",
README.md+2
@@ -83,4 +83,6 @@ a good overview of the internals.
8383
8484# Getting help
8585
86+Your question might already be answered on the [FAQ](https://github.com/helix-editor/helix/wiki/FAQ).
87+
8688Discuss 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
1919| `line-number` | Line number display (`absolute`, `relative`) | `absolute` |
2020| `smart-case` | Enable smart case regex searching (case insensitive unless pattern contains upper case characters) | `true` |
2121| `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` |
2224
2325## LSP
2426
book/theme/css/chrome.css+1−1
@@ -196,7 +196,7 @@ a > .hljs {
196196 border-radius: 3px;
197197}
198198
199−:not(pre):not(a) > .hljs {
199+:not(pre):not(a):not(td):not(p) > .hljs {
200200 color: var(--inline-code-color);
201201 overflow-x: initial;
202202}
book/theme/css/general.css+4−1
@@ -162,7 +162,6 @@ table thead td {
162162table thead th {
163163 padding: .75rem;
164164 text-align: left;
165− color: var(--table-border-color);
166165 font-weight: 500;
167166 line-height: 1.5;
168167 width: auto;
@@ -228,3 +227,7 @@ blockquote *:last-child {
228227 margin: 5px 0px;
229228 font-weight: bold;
230229}
230+
231+.result-no-output {
232+ font-style: italic;
233+}
book/theme/css/variables.css+4−10
@@ -13,7 +13,6 @@
1313.ayu {
1414 --bg: hsl(210, 25%, 8%);
1515 --fg: #c5c5c5;
16− --heading-fg: #c5c5c5;
1716
1817 --sidebar-bg: #14191f;
1918 --sidebar-fg: #c8c9db;
@@ -54,7 +53,6 @@
5453.coal {
5554 --bg: hsl(200, 7%, 8%);
5655 --fg: #98a3ad;
57− --heading-fg: #98a3ad;
5856
5957 --sidebar-bg: #292c2f;
6058 --sidebar-fg: #a1adb8;
@@ -95,7 +93,6 @@
9593.light {
9694 --bg: hsl(0, 0%, 100%);
9795 --fg: hsl(0, 0%, 0%);
98− --heading-fg: hsl(0, 0%, 0%);
9996
10097 --sidebar-bg: #fafafa;
10198 --sidebar-fg: hsl(0, 0%, 0%);
@@ -110,7 +107,7 @@
110107
111108 --links: #20609f;
112109
113− --inline-code-color: #a39e9b;
110+ --inline-code-color: #301900;
114111
115112 --theme-popup-bg: #fafafa;
116113 --theme-popup-border: #cccccc;
@@ -136,7 +133,6 @@
136133.navy {
137134 --bg: hsl(226, 23%, 11%);
138135 --fg: #bcbdd0;
139− --heading-fg: #bcbdd0;
140136
141137 --sidebar-bg: #282d3f;
142138 --sidebar-fg: #c8c9db;
@@ -177,7 +173,6 @@
177173.rust {
178174 --bg: hsl(60, 9%, 87%);
179175 --fg: #262625;
180− --heading-fg: #262625;
181176
182177 --sidebar-bg: #3b2e2a;
183178 --sidebar-fg: #c8c9db;
@@ -192,7 +187,7 @@
192187
193188 --links: #2b79a2;
194189
195− --inline-code-color: #c5c8c6;
190+ --inline-code-color: #6e6b5e;
196191
197192 --theme-popup-bg: #e1e1db;
198193 --theme-popup-border: #b38f6b;
@@ -218,8 +213,7 @@
218213@media (prefers-color-scheme: dark) {
219214 .light.no-js {
220215 --bg: hsl(200, 7%, 8%);
221− --fg: #ebeafa;
222− --heading-fg: #ebeafa;
216+ --fg: #98a3ad;
223217
224218 --sidebar-bg: #292c2f;
225219 --sidebar-fg: #a1adb8;
@@ -234,7 +228,7 @@
234228
235229 --links: #2b79a2;
236230
237− --inline-code-color: #6e6b5e;
231+ --inline-code-color: #c5c8c6;
238232
239233 --theme-popup-bg: #141617;
240234 --theme-popup-border: #43484d;
helix-core/Cargo.toml+1
@@ -29,6 +29,7 @@ arc-swap = "1"
2929regex = "1"
3030
3131serde = { version = "1.0", features = ["derive"] }
32+serde_json = "1.0"
3233toml = "0.5"
3334
3435similar = "2.1"
helix-core/src/selection.rs+4−4
@@ -29,10 +29,10 @@ use std::borrow::Cow;
2929/// "(anchor, head)", followed by example text with "[" and "]"
3030/// inserted to represent the anchor and head positions:
3131///
32−/// - (0, 3): [Som]e text.
33−/// - (3, 0): ]Som[e text.
34−/// - (2, 7): So[me te]xt.
35−/// - (1, 1): S[]ome text.
32+/// - (0, 3): `[Som]e text`.
33+/// - (3, 0): `]Som[e text`.
34+/// - (2, 7): `So[me te]xt`.
35+/// - (1, 1): `S[]ome text`.
3636///
3737/// Ranges are considered to be inclusive on the left and
3838/// exclusive on the right, regardless of anchor-head ordering.
helix-core/src/syntax.rs+12−1
@@ -31,6 +31,15 @@ where
3131 .transpose()
3232}
3333
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+
3443#[derive(Debug, Serialize, Deserialize)]
3544pub struct Configuration {
3645 pub language: Vec<LanguageConfiguration>,
@@ -46,7 +55,9 @@ pub struct LanguageConfiguration {
4655 pub file_types: Vec<String>, // filename ends_with? <Gemfile, rb, etc>
4756 pub roots: Vec<String>, // these indicate project roots <.git, Cargo.toml>
4857 pub comment_token: Option<String>,
49− pub config: Option<String>,
58+
59+ #[serde(default, skip_serializing, deserialize_with = "deserialize_lsp_config")]
60+ pub config: Option<serde_json::Value>,
5061
5162 #[serde(default)]
5263 pub auto_format: bool,
helix-lsp/src/lib.rs+1−9
@@ -318,15 +318,7 @@ impl Registry {
318318 let (client, incoming, initialize_notify) = Client::start(
319319 &config.command,
320320 &config.args,
321− serde_json::from_str(language_config.config.as_deref().unwrap_or(""))
322− .map_err(|e| {
323− log::error!(
324− "LSP Config, {}, in `languages.toml` for `{}`",
325− e,
326− language_config.scope()
327− )
328− })
329− .ok(),
321+ language_config.config.clone(),
330322 id,
331323 )?;
332324 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 {
102102 if !args.files.is_empty() {
103103 let first = &args.files[0]; // we know it's not empty
104104 if first.is_dir() {
105+ std::env::set_current_dir(&first)?;
105106 editor.new_file(Action::VerticalSplit);
106107 compositor.push(Box::new(ui::file_picker(first.clone())));
107108 } else {
@@ -204,6 +205,11 @@ impl Application {
204205 self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback);
205206 self.render();
206207 }
208+ _ = &mut self.editor.idle_timer => {
209+ // idle timeout
210+ self.editor.clear_idle_timer();
211+ self.handle_idle_timeout();
212+ }
207213 }
208214 }
209215 }
@@ -233,6 +239,38 @@ impl Application {
233239 }
234240 }
235241
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+
236274 pub fn handle_terminal_events(&mut self, event: Option<Result<Event, crossterm::ErrorKind>>) {
237275 let mut cx = crate::compositor::Context {
238276 editor: &mut self.editor,
@@ -417,14 +455,6 @@ impl Application {
417455 server_id: usize,
418456 ) {
419457 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();
428458
429459 match call {
430460 Call::Notification(helix_lsp::jsonrpc::Notification { method, params, .. }) => {
@@ -534,7 +564,19 @@ impl Application {
534564 Notification::LogMessage(params) => {
535565 log::info!("window/logMessage: {:?}", params);
536566 }
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();
538580 let lsp::ProgressParams { token, value } = params;
539581
540582 let lsp::ProgressParamsValue::WorkDone(work) = value;
@@ -609,6 +651,9 @@ impl Application {
609651 self.editor.set_status(status);
610652 }
611653 }
654+ Notification::ProgressMessage(_params) => {
655+ // do nothing
656+ }
612657 }
613658 }
614659 Call::MethodCall(helix_lsp::jsonrpc::MethodCall {
@@ -643,6 +688,14 @@ impl Application {
643688 MethodCall::WorkDoneProgressCreate(params) => {
644689 self.lsp_progress.create(server_id, params.token);
645690
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();
646699 let spinner = editor_view.spinners_mut().get_or_create(server_id);
647700 if spinner.is_stopped() {
648701 spinner.start();
helix-term/src/commands.rs+31−11
@@ -1310,7 +1310,8 @@ fn global_search(cx: &mut Context) {
13101310
13111311 cx.push_layer(Box::new(prompt));
13121312
1313− let root = find_root(None).unwrap_or_else(|| PathBuf::from("./"));
1313+ let current_path = doc_mut!(cx.editor).path().cloned();
1314+
13141315 let show_picker = async move {
13151316 let all_matches: Vec<(usize, PathBuf)> =
13161317 UnboundedReceiverStream::new(all_matches_rx).collect().await;
@@ -1320,14 +1321,19 @@ fn global_search(cx: &mut Context) {
13201321 editor.set_status("No matches found".to_string());
13211322 return;
13221323 }
1324+
13231325 let picker = FilePicker::new(
13241326 all_matches,
13251327 move |(_line_num, path)| {
1326− path.strip_prefix(&root)
1327− .unwrap_or(path)
1328+ let relative_path = helix_core::path::get_relative_path(path)
13281329 .to_str()
13291330 .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+ }
13311337 },
13321338 move |editor: &mut Editor, (line_num, path), action| {
13331339 match editor.open(path.into(), action) {
@@ -4160,7 +4166,7 @@ fn remove_primary_selection(cx: &mut Context) {
41604166 doc.set_selection(view.id, selection);
41614167}
41624168
4163−fn completion(cx: &mut Context) {
4169+pub fn completion(cx: &mut Context) {
41644170 // trigger on trigger char, or if user calls it
41654171 // (or on word char typing??)
41664172 // after it's triggered, if response marked is_incomplete, update on every subsequent keypress
@@ -4205,10 +4211,8 @@ fn completion(cx: &mut Context) {
42054211 };
42064212
42074213 let offset_encoding = language_server.offset_encoding();
4208− let cursor = doc
4209− .selection(view.id)
4210− .primary()
4211− .cursor(doc.text().slice(..));
4214+ let text = doc.text().slice(..);
4215+ let cursor = doc.selection(view.id).primary().cursor(text);
42124216
42134217 let pos = pos_to_lsp_pos(doc.text(), cursor, offset_encoding);
42144218
@@ -4216,6 +4220,15 @@ fn completion(cx: &mut Context) {
42164220
42174221 let trigger_offset = cursor;
42184222
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+
42194232 cx.callback(
42204233 future,
42214234 move |editor: &mut Editor,
@@ -4238,7 +4251,7 @@ fn completion(cx: &mut Context) {
42384251 };
42394252
42404253 if items.is_empty() {
4241− editor.set_error("No completion available".to_string());
4254+ // editor.set_error("No completion available".to_string());
42424255 return;
42434256 }
42444257 let size = compositor.size();
@@ -4246,7 +4259,14 @@ fn completion(cx: &mut Context) {
42464259 .find(std::any::type_name::<ui::EditorView>())
42474260 .unwrap();
42484261 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+ );
42504270 };
42514271 },
42524272 );
helix-term/src/compositor.rs+6
@@ -171,6 +171,12 @@ impl Compositor {
171171 (None, CursorKind::Hidden)
172172 }
173173
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+
174180 pub fn find(&mut self, type_name: &str) -> Option<&mut dyn Component> {
175181 self.layers
176182 .iter_mut()
helix-term/src/ui/completion.rs+24−6
@@ -69,14 +69,18 @@ impl menu::Item for CompletionItem {
6969/// Wraps a Menu.
7070pub struct Completion {
7171 popup: Popup<Menu<CompletionItem>>,
72+ start_offset: usize,
73+ #[allow(dead_code)]
7274 trigger_offset: usize,
7375 // TODO: maintain a completioncontext with trigger kind & trigger char
7476}
7577
7678impl Completion {
7779 pub fn new(
80+ editor: &Editor,
7881 items: Vec<CompletionItem>,
7982 offset_encoding: helix_lsp::OffsetEncoding,
83+ start_offset: usize,
8084 trigger_offset: usize,
8185 ) -> Self {
8286 // let items: Vec<CompletionItem> = Vec::new();
@@ -175,16 +179,22 @@ impl Completion {
175179 };
176180 });
177181 let popup = Popup::new(menu);
178− Self {
182+ let mut completion = Self {
179183 popup,
184+ start_offset,
180185 trigger_offset,
181− }
186+ };
187+
188+ // need to recompute immediately in case start_offset != trigger_offset
189+ completion.recompute_filter(editor);
190+
191+ completion
182192 }
183193
184− pub fn update(&mut self, cx: &mut commands::Context) {
194+ pub fn recompute_filter(&mut self, editor: &Editor) {
185195 // recompute menu based on matches
186196 let menu = self.popup.contents_mut();
187− let (view, doc) = current!(cx.editor);
197+ let (view, doc) = current_ref!(editor);
188198
189199 // cx.hooks()
190200 // cx.add_hook(enum type, ||)
@@ -200,14 +210,22 @@ impl Completion {
200210 .selection(view.id)
201211 .primary()
202212 .cursor(doc.text().slice(..));
203− if self.trigger_offset <= cursor {
204− let fragment = doc.text().slice(self.trigger_offset..cursor);
213+ if self.start_offset <= cursor {
214+ let fragment = doc.text().slice(self.start_offset..cursor);
205215 let text = Cow::from(fragment);
206216 // TODO: logic is same as ui/picker
207217 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();
208222 }
209223 }
210224
225+ pub fn update(&mut self, cx: &mut commands::Context) {
226+ self.recompute_filter(cx.editor)
227+ }
228+
211229 pub fn is_empty(&self) -> bool {
212230 self.popup.contents().is_empty()
213231 }
helix-term/src/ui/editor.rs+14−2
@@ -37,7 +37,7 @@ pub struct EditorView {
3737 keymaps: Keymaps,
3838 on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
3939 last_insert: (commands::Command, Vec<KeyEvent>),
40− completion: Option<Completion>,
40+ pub(crate) completion: Option<Completion>,
4141 spinners: ProgressSpinners,
4242 autoinfo: Option<Info>,
4343}
@@ -984,12 +984,21 @@ impl EditorView {
984984
985985 pub fn set_completion(
986986 &mut self,
987+ editor: &Editor,
987988 items: Vec<helix_lsp::lsp::CompletionItem>,
988989 offset_encoding: helix_lsp::OffsetEncoding,
990+ start_offset: usize,
989991 trigger_offset: usize,
990992 size: Rect,
991993 ) {
992− let mut completion = Completion::new(items, offset_encoding, trigger_offset);
994+ let mut completion =
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+
9931002 // TODO : propagate required size on resize to completion too
9941003 completion.required_size((size.width, size.height));
9951004 self.completion = Some(completion);
@@ -1211,6 +1220,7 @@ impl Component for EditorView {
12111220 EventResult::Consumed(None)
12121221 }
12131222 Event::Key(key) => {
1223+ cxt.editor.reset_idle_timer();
12141224 let mut key = KeyEvent::from(key);
12151225 canonicalize_key(&mut key);
12161226 // clear status
@@ -1245,6 +1255,7 @@ impl Component for EditorView {
12451255 if callback.is_some() {
12461256 // assume close_fn
12471257 self.completion = None;
1258+ cxt.editor.clear_idle_timer(); // don't retrigger
12481259 }
12491260 }
12501261 }
@@ -1258,6 +1269,7 @@ impl Component for EditorView {
12581269 completion.update(&mut cxt);
12591270 if completion.is_empty() {
12601271 self.completion = None;
1272+ cxt.editor.clear_idle_timer(); // don't retrigger
12611273 }
12621274 }
12631275 }
helix-term/src/ui/menu.rs+8
@@ -90,6 +90,14 @@ impl<T: Item> Menu<T> {
9090 self.recalculate = true;
9191 }
9292
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+
93101 pub fn move_up(&mut self) {
94102 let len = self.matches.len();
95103 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> {
271271 }
272272
273273 pub fn move_up(&mut self) {
274+ if self.matches.is_empty() {
275+ return;
276+ }
274277 let len = self.matches.len();
275278 let pos = ((self.cursor + len.saturating_sub(1)) % len) % len;
276279 self.cursor = pos;
277280 }
278281
279282 pub fn move_down(&mut self) {
283+ if self.matches.is_empty() {
284+ return;
285+ }
280286 let len = self.matches.len();
281287 let pos = (self.cursor + 1) % len;
282288 self.cursor = pos;
helix-view/src/editor.rs+35−2
@@ -13,10 +13,12 @@ use tokio_stream::wrappers::UnboundedReceiverStream;
1313use std::{
1414 collections::HashMap,
1515 path::{Path, PathBuf},
16+ pin::Pin,
1617 sync::Arc,
17− time::Duration,
1818};
1919
20+use tokio::time::{sleep, Duration, Instant, Sleep};
21+
2022use slotmap::SlotMap;
2123
2224use anyhow::Error;
@@ -29,6 +31,14 @@ use helix_dap as dap;
2931
3032use serde::Deserialize;
3133
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+
3242#[derive(Debug, Clone, PartialEq, Deserialize)]
3343#[serde(rename_all = "kebab-case", default)]
3444pub struct Config {
@@ -42,12 +52,17 @@ pub struct Config {
4252 pub shell: Vec<String>,
4353 /// Line number mode.
4454 pub line_number: LineNumber,
45− /// Middle click paste support. Defaults to true
55+ /// Middle click paste support. Defaults to true.
4656 pub middle_click_paste: bool,
4757 /// Smart case: Case insensitive searching unless pattern contains upper case characters. Defaults to true.
4858 pub smart_case: bool,
4959 /// Automatic insertion of pairs to parentheses, brackets, etc. Defaults to true.
5060 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,
5166}
5267
5368#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
@@ -75,6 +90,8 @@ impl Default for Config {
7590 middle_click_paste: true,
7691 smart_case: true,
7792 auto_pairs: true,
93+ auto_completion: true,
94+ idle_timeout: Duration::from_millis(400),
7895 }
7996 }
8097}
@@ -105,6 +122,8 @@ pub struct Editor {
105122 pub status_msg: Option<(String, Severity)>,
106123
107124 pub config: Config,
125+
126+ pub idle_timer: Pin<Box<Sleep>>,
108127}
109128
110129#[derive(Debug, Copy, Clone)]
@@ -146,10 +165,24 @@ impl Editor {
146165 registers: Registers::default(),
147166 clipboard_provider: get_clipboard_provider(),
148167 status_msg: None,
168+ idle_timer: Box::pin(sleep(config.idle_timeout)),
149169 config,
150170 }
151171 }
152172
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+
153186 pub fn clear_status(&mut self) {
154187 self.status_msg = None;
155188 }
helix-view/src/macros.rs+16
@@ -44,3 +44,19 @@ macro_rules! view {
4444 $( $editor ).+ .tree.get($( $editor ).+ .tree.focus)
4545 }};
4646}
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"]
66roots = []
77auto-format = true
88comment-token = "//"
9−config = """
10−{
11− "cargo": {
12− "loadOutDirsFromCheck": true
13− },
14− "procMacro": {
15− "enable": false
16− }
17−}
18−"""
19−
209language-server = { command = "rust-analyzer" }
2110indent = { tab-width = 4, unit = " " }
11+[language.config]
12+cargo = { loadOutDirsFromCheck = true }
13+procMacro = { enable = false }
2214
2315[language.debugger]
2416name = "lldb-vscode"
@@ -133,6 +125,16 @@ request = "attach"
133125completion = [ "pid" ]
134126args = { console = "internalConsole", pid = "{0}" }
135127
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+
136138[[language]]
137139name = "go"
138140scope = "source.go"
@@ -248,7 +250,7 @@ file-types = ["py"]
248250roots = []
249251comment-token = "#"
250252
251−language-server = { command = "pyls" }
253+language-server = { command = "pylsp" }
252254# TODO: pyls needs utf-8 offsets
253255indent = { tab-width = 4, unit = " " }
254256
@@ -380,6 +382,15 @@ roots = []
380382indent = { tab-width = 2, unit = " " }
381383language-server = { command = "svelteserver", args = ["--stdio"] }
382384
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+
383394[[language]]
384395name = "yaml"
385396scope = "source.yaml"
@@ -409,3 +420,23 @@ comment-token = "//"
409420
410421language-server = { command = "zls" }
411422indent = { 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 @@
4747; Variables
4848
4949((identifier) @constant
50− (#match? @constant "^_*[A-Z][A-Z\d_]+"))
50+ (#match? @constant "^_*[A-Z][A-Z\\d_]+$"))
5151
5252(identifier) @variable
5353
runtime/queries/php/highlights.scm+1−1
@@ -42,7 +42,7 @@
4242(relative_scope) @variable.builtin
4343
4444((name) @constant
45− (#match? @constant "^_?[A-Z][A-Z\d_]+$"))
45+ (#match? @constant "^_?[A-Z][A-Z\\d_]+$"))
4646
4747((name) @constructor
4848 (#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 @@
11# Author : RayGervais<raygervais@hotmail.ca>
22
33# "ui.linenr.selected" = { fg = "#d8dee9" }
4−# "ui.text.focus" = { fg = "#e5ded6", modifiers= ["bold"] }
4+"ui.text.focus" = { fg = "#88c0d0", modifiers= ["bold"] }
55# "ui.menu.selected" = { fg = "#e5ded6", bg = "#313f4e" }
66
77# "info" = "#b48ead"
@@ -24,8 +24,8 @@
2424"ui.cursor.match" = { bg = "434c5e" }
2525
2626# nord3 - comments
27−"comment" = "#4c566a"
28−"ui.linenr" = { fg = "#4c566a" }
27+"comment" = "#616E88"
28+"ui.linenr" = { fg = "#616E88" }
2929
3030# Snow Storm
3131# nord4 - cursor, variables, constants, attributes, fields
runtime/tutor.txt+198−10
@@ -20,7 +20,6 @@ _________________________________________________________________
2020 the first lesson.
2121
2222
23−
2423=================================================================
2524= BASIC CURSOR MOVEMENT =
2625=================================================================
@@ -43,7 +42,6 @@ _________________________________________________________________
4342
4443
4544
46−
4745=================================================================
4846= EXITING HELIX =
4947=================================================================
@@ -66,7 +64,6 @@ _________________________________________________________________
6664
6765
6866
69−
7067=================================================================
7168= DELETION =
7269=================================================================
@@ -89,7 +86,6 @@ _________________________________________________________________
8986
9087
9188
92−
9389=================================================================
9490= INSERT MODE =
9591=================================================================
@@ -112,7 +108,6 @@ _________________________________________________________________
112108 Note: The status bar will display your current mode.
113109 Notice that when you press i, 'NOR' changes to 'INS'.
114110
115−
116111=================================================================
117112= MORE ON INSERT MODE =
118113=================================================================
@@ -123,7 +118,7 @@ _________________________________________________________________
123118
124119 Common examples of insertion commands include:
125120 i - Insert before the selection.
126− a - Insert after the selection. (a means "append")
121+ a - Insert after the selection. (a means 'append')
127122 I - Insert at the start of the line.
128123 A - Insert at the end of the line.
129124
@@ -135,7 +130,6 @@ _________________________________________________________________
135130 --> This sentence is miss
136131 This sentence is missing some text.
137132
138−
139133=================================================================
140134= SAVING A FILE =
141135=================================================================
@@ -158,7 +152,6 @@ _________________________________________________________________
158152
159153
160154
161−
162155=================================================================
163156= RECAP =
164157=================================================================
@@ -181,7 +174,6 @@ _________________________________________________________________
181174
182175
183176
184−
185177=================================================================
186178= MOTIONS AND SELECTIONS =
187179=================================================================
@@ -204,7 +196,6 @@ _________________________________________________________________
204196
205197
206198
207−
208199=================================================================
209200= MORE ON MOTIONS =
210201=================================================================
@@ -227,6 +218,203 @@ _________________________________________________________________
227218
228219
229220
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+
230418
231419=================================================================
232420 This tutorial is still a work-in-progress.