From 1fc20cd02b007dc75387dddc72b6e4742b5459b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Dec 2023 13:33:08 +0900 Subject: [PATCH 001/796] build(deps): bump thiserror from 1.0.51 to 1.0.52 (#9172) Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.51 to 1.0.52. - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.51...1.0.52) --- updated-dependencies: - dependency-name: thiserror dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5ab0c3a95..da51eb396 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2028,18 +2028,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3" dependencies = [ "proc-macro2", "quote", From 2ec4d5004d6efa4fa4df85dcd11d020dbeac7346 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Dec 2023 13:33:23 +0900 Subject: [PATCH 002/796] build(deps): bump futures-util from 0.3.29 to 0.3.30 (#9171) Bumps [futures-util](https://github.com/rust-lang/futures-rs) from 0.3.29 to 0.3.30. - [Release notes](https://github.com/rust-lang/futures-rs/releases) - [Changelog](https://github.com/rust-lang/futures-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/futures-rs/compare/0.3.29...0.3.30) --- updated-dependencies: - dependency-name: futures-util dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da51eb396..b62890c74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -469,9 +469,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" @@ -486,15 +486,15 @@ dependencies = [ [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-task", From 00367820598725ee5206bd7b3a3d48957ea92767 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Dec 2023 13:33:46 +0900 Subject: [PATCH 003/796] build(deps): bump anyhow from 1.0.75 to 1.0.76 (#9170) Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.75 to 1.0.76. - [Release notes](https://github.com/dtolnay/anyhow/releases) - [Commits](https://github.com/dtolnay/anyhow/compare/1.0.75...1.0.76) --- updated-dependencies: - dependency-name: anyhow dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b62890c74..beab4cd22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,9 +62,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355" [[package]] name = "arc-swap" From c874a896a5151043a5cd7336e12ebc3a928fc677 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Dec 2023 13:34:58 +0900 Subject: [PATCH 004/796] build(deps): bump tokio from 1.35.0 to 1.35.1 (#9169) Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.35.0 to 1.35.1. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.35.0...tokio-1.35.1) --- updated-dependencies: - dependency-name: tokio dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index beab4cd22..1488020a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2101,9 +2101,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", From 8653e1b02f6193330f8b0f2afff0a2ff103bd6a4 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Sun, 26 Mar 2023 18:10:09 +0200 Subject: [PATCH 005/796] Add config to mark diagnostic sources as persistent --- book/src/languages.md | 1 + helix-core/src/syntax.rs | 2 + helix-term/src/application.rs | 105 ++++++++++++++++++++++------------ helix-view/src/document.rs | 21 ++++++- languages.toml | 1 + 5 files changed, 90 insertions(+), 40 deletions(-) diff --git a/book/src/languages.md b/book/src/languages.md index 632a9146c..944ebf097 100644 --- a/book/src/languages.md +++ b/book/src/languages.md @@ -69,6 +69,7 @@ These configuration keys are available: | `soft-wrap` | [editor.softwrap](./configuration.md#editorsoft-wrap-section) | `text-width` | Maximum line length. Used for the `:reflow` command and soft-wrapping if `soft-wrap.wrap-at-text-width` is set, defaults to `editor.text-width` | | `workspace-lsp-roots` | Directories relative to the workspace root that are treated as LSP roots. Should only be set in `.helix/config.toml`. Overwrites the setting of the same name in `config.toml` if set. | +| `persistent-diagnostic-sources` | An array of LSP diagnostic sources assumed unchanged when the language server resends the same set of diagnostics. Helix can track the position for these diagnostics internally instead. Useful for diagnostics that are recomputed on save. ### File-type detection and the `file-types` key diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index dd9223160..8d433260e 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -155,6 +155,8 @@ pub struct LanguageConfiguration { /// Hardcoded LSP root directories relative to the workspace root, like `examples` or `tools/fuzz`. /// Falling back to the current working directory if none are configured. pub workspace_lsp_roots: Option>, + #[serde(default)] + pub persistent_diagnostic_sources: Vec, } #[derive(Debug, PartialEq, Eq, Hash)] diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index ed085749b..35111ca59 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -717,7 +717,7 @@ impl Application { )); } } - Notification::PublishDiagnostics(params) => { + Notification::PublishDiagnostics(mut params) => { let path = match params.uri.to_file_path() { Ok(path) => path, Err(_) => { @@ -731,31 +731,69 @@ impl Application { return; } let offset_encoding = language_server.offset_encoding(); - let doc = self.editor.document_by_path_mut(&path).filter(|doc| { - if let Some(version) = params.version { - if version != doc.version() { - log::info!("Version ({version}) is out of date for {path:?} (expected ({}), dropping PublishDiagnostic notification", doc.version()); - return false; + // have to inline the function because of borrow checking... + let doc = self.editor.documents.values_mut() + .find(|doc| doc.path().map(|p| p == &path).unwrap_or(false)) + .filter(|doc| { + if let Some(version) = params.version { + if version != doc.version() { + log::info!("Version ({version}) is out of date for {path:?} (expected ({}), dropping PublishDiagnostic notification", doc.version()); + return false; + } } - } - - true - }); + true + }); if let Some(doc) = doc { - let lang_conf = doc.language_config(); - let text = doc.text(); + let lang_conf = doc.language.clone(); + let text = doc.text().clone(); + + let mut unchaged_diag_sources_ = Vec::new(); + if let Some(lang_conf) = &lang_conf { + if let Some(old_diagnostics) = + self.editor.diagnostics.get(¶ms.uri) + { + if !lang_conf.persistent_diagnostic_sources.is_empty() { + // Sort diagnostics first by severity and then by line numbers. + // Note: The `lsp::DiagnosticSeverity` enum is already defined in decreasing order + params + .diagnostics + .sort_unstable_by_key(|d| (d.severity, d.range.start)); + } + for source in &lang_conf.persistent_diagnostic_sources { + let new_diagnostics = params + .diagnostics + .iter() + .filter(|d| d.source.as_ref() == Some(source)); + let old_diagnostics = old_diagnostics + .iter() + .filter(|(d, d_server)| { + *d_server == server_id + && d.source.as_ref() == Some(source) + }) + .map(|(d, _)| d); + if new_diagnostics.eq(old_diagnostics) { + unchaged_diag_sources_.push(source.clone()) + } + } + } + } - let diagnostics = params - .diagnostics - .iter() - .filter_map(|diagnostic| { + let unchaged_diag_sources = &unchaged_diag_sources_; + let diagnostics = + params.diagnostics.iter().filter_map(move |diagnostic| { use helix_core::diagnostic::{Diagnostic, Range, Severity::*}; use lsp::DiagnosticSeverity; + if diagnostic.source.as_ref().map_or(false, |source| { + unchaged_diag_sources.contains(source) + }) { + return None; + } + // TODO: convert inside server let start = if let Some(start) = lsp_pos_to_pos( - text, + &text, diagnostic.range.start, offset_encoding, ) { @@ -766,7 +804,7 @@ impl Application { }; let end = if let Some(end) = - lsp_pos_to_pos(text, diagnostic.range.end, offset_encoding) + lsp_pos_to_pos(&text, diagnostic.range.end, offset_encoding) { end } else { @@ -786,7 +824,7 @@ impl Application { ), }); - if let Some(lang_conf) = lang_conf { + if let Some(lang_conf) = &lang_conf { if let Some(severity) = severity { if severity < lang_conf.diagnostic_severity { return None; @@ -836,38 +874,31 @@ impl Application { data: diagnostic.data.clone(), language_server_id: server_id, }) - }) - .collect(); + }); - doc.replace_diagnostics(diagnostics, server_id); + doc.replace_diagnostics(diagnostics, unchaged_diag_sources, server_id); } - let mut diagnostics = params - .diagnostics - .into_iter() - .map(|d| (d, server_id)) - .collect(); + let diagnostics = params.diagnostics.into_iter().map(|d| (d, server_id)); // Insert the original lsp::Diagnostics here because we may have no open document // for diagnosic message and so we can't calculate the exact position. // When using them later in the diagnostics picker, we calculate them on-demand. - match self.editor.diagnostics.entry(params.uri) { + let diagnostics = match self.editor.diagnostics.entry(params.uri) { Entry::Occupied(o) => { let current_diagnostics = o.into_mut(); // there may entries of other language servers, which is why we can't overwrite the whole entry current_diagnostics.retain(|(_, lsp_id)| *lsp_id != server_id); - current_diagnostics.append(&mut diagnostics); - // Sort diagnostics first by severity and then by line numbers. - // Note: The `lsp::DiagnosticSeverity` enum is already defined in decreasing order + current_diagnostics.extend(diagnostics); current_diagnostics - .sort_unstable_by_key(|(d, _)| (d.severity, d.range.start)); - } - Entry::Vacant(v) => { - diagnostics - .sort_unstable_by_key(|(d, _)| (d.severity, d.range.start)); - v.insert(diagnostics); + // Sort diagnostics first by severity and then by line numbers. } + Entry::Vacant(v) => v.insert(diagnostics.collect()), }; + + // Sort diagnostics first by severity and then by line numbers. + // Note: The `lsp::DiagnosticSeverity` enum is already defined in decreasing order + diagnostics.sort_unstable_by_key(|(d, _)| (d.severity, d.range.start)); } Notification::ShowMessage(params) => { log::warn!("unhandled window/showMessage: {:?}", params); diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index bb61eaa6a..37b8a0bf7 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -1706,11 +1706,26 @@ impl Document { pub fn replace_diagnostics( &mut self, - mut diagnostics: Vec, + diagnostics: impl IntoIterator, + unchanged_sources: &[String], language_server_id: usize, ) { - self.clear_diagnostics(language_server_id); - self.diagnostics.append(&mut diagnostics); + if unchanged_sources.is_empty() { + self.clear_diagnostics(language_server_id); + } else { + self.diagnostics.retain(|d| { + if d.language_server_id != language_server_id { + return true; + } + + if let Some(source) = &d.source { + unchanged_sources.contains(source) + } else { + false + } + }); + } + self.diagnostics.extend(diagnostics); self.diagnostics .sort_unstable_by_key(|diagnostic| diagnostic.range); } diff --git a/languages.toml b/languages.toml index 4c6c9447d..b47fb4e2b 100644 --- a/languages.toml +++ b/languages.toml @@ -191,6 +191,7 @@ auto-format = true comment-token = "//" language-servers = [ "rust-analyzer" ] indent = { tab-width = 4, unit = " " } +persistent-diagnostic-sources = ["rustc", "clippy"] [language.auto-pairs] '(' = ')' From 515ef17207878b78e4f5eda8bf710fa0aa1eae87 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Sun, 26 Mar 2023 18:14:42 +0200 Subject: [PATCH 006/796] make diagnostics stick to word boundaries Diagnostics are currently extended if text is inserted at their end. This is desirable when inserting text after an identifier. For example consider: let foo = 2; --- unused variable Renaming the identifier should extend the diagnostic: let foobar = 2; ------ unused variable This is currently implemented in helix but as a consequence adding whitespaces or a type hint also extends the diagnostic: let foo = 2; -------- unused variable let foo: Bar = 2; -------- unused variable In these cases the diagnostic should remain unchanged: let foo = 2; --- unused variable let foo: Bar = 2; --- unused variable As a heuristic helix will now only extend diagnostics that end on a word char if new chars are appended to the word (so not for punctuation/ whitespace). The idea for this mapping was inspired for the word level tracking vscode uses for many positions. While VSCode doesn't currently update diagnostics after receiving publishDiagnostic it does use this system for inlay hints for example. Similarly, the new association mechanism implemented here can be used for word level tracking of inlay hints. A similar mapping function is implemented for word starts. Together these can be used to make a diagnostic stick to a word. If that word is removed that diagnostic is automatically removed too. This is the exact same behavior VSCode inlay hints eixibit. --- helix-core/src/diagnostic.rs | 4 ++ helix-core/src/transaction.rs | 87 +++++++++++++++++++++++++++++------ helix-term/src/application.rs | 11 ++++- helix-view/src/document.rs | 38 +++++++++------ 4 files changed, 112 insertions(+), 28 deletions(-) diff --git a/helix-core/src/diagnostic.rs b/helix-core/src/diagnostic.rs index 0b75d2a58..52d77a9aa 100644 --- a/helix-core/src/diagnostic.rs +++ b/helix-core/src/diagnostic.rs @@ -39,6 +39,10 @@ pub enum DiagnosticTag { #[derive(Debug, Clone)] pub struct Diagnostic { pub range: Range, + // whether this diagnostic ends at the end of(or inside) a word + pub ends_at_word: bool, + pub starts_at_word: bool, + pub zero_width: bool, pub line: usize, pub message: String, pub severity: Option, diff --git a/helix-core/src/transaction.rs b/helix-core/src/transaction.rs index 1cea69116..f5a49cc11 100644 --- a/helix-core/src/transaction.rs +++ b/helix-core/src/transaction.rs @@ -1,7 +1,7 @@ use ropey::RopeSlice; use smallvec::SmallVec; -use crate::{Range, Rope, Selection, Tendril}; +use crate::{chars::char_is_word, Range, Rope, Selection, Tendril}; use std::{borrow::Cow, iter::once}; /// (from, to, replacement) @@ -23,6 +23,30 @@ pub enum Operation { pub enum Assoc { Before, After, + /// Acts like `After` if a word character is inserted + /// after the position, otherwise acts like `Before` + AfterWord, + /// Acts like `Before` if a word character is inserted + /// before the position, otherwise acts like `After` + BeforeWord, +} + +impl Assoc { + /// Whether to stick to gaps + fn stay_at_gaps(self) -> bool { + !matches!(self, Self::BeforeWord | Self::AfterWord) + } + + fn insert_offset(self, s: &str) -> usize { + let chars = s.chars().count(); + match self { + Assoc::After => chars, + Assoc::AfterWord => s.chars().take_while(|&c| char_is_word(c)).count(), + // return position before inserted text + Assoc::Before => 0, + Assoc::BeforeWord => chars - s.chars().rev().take_while(|&c| char_is_word(c)).count(), + } + } } #[derive(Debug, Default, Clone, PartialEq, Eq)] @@ -415,8 +439,6 @@ impl ChangeSet { map!(|pos, _| (old_end > pos).then_some(new_pos), i); } Insert(s) => { - let ins = s.chars().count(); - // a subsequent delete means a replace, consume it if let Some((_, Delete(len))) = iter.peek() { iter.next(); @@ -424,13 +446,13 @@ impl ChangeSet { old_end = old_pos + len; // in range of replaced text map!( - |pos, assoc| (old_end > pos).then(|| { + |pos, assoc: Assoc| (old_end > pos).then(|| { // at point or tracking before - if pos == old_pos || assoc == Assoc::Before { + if pos == old_pos && assoc.stay_at_gaps() { new_pos } else { // place to end of insert - new_pos + ins + new_pos + assoc.insert_offset(s) } }), i @@ -438,20 +460,15 @@ impl ChangeSet { } else { // at insert point map!( - |pos, assoc| (old_pos == pos).then(|| { + |pos, assoc: Assoc| (old_pos == pos).then(|| { // return position before inserted text - if assoc == Assoc::Before { - new_pos - } else { - // after text - new_pos + ins - } + new_pos + assoc.insert_offset(s) }), i ); } - new_pos += ins; + new_pos += s.chars().count(); } } old_pos = old_end; @@ -884,6 +901,48 @@ mod test { let mut positions = [4, 2]; cs.update_positions(positions.iter_mut().map(|pos| (pos, Assoc::After))); assert_eq!(positions, [4, 2]); + // stays at word boundary + let cs = ChangeSet { + changes: vec![ + Retain(2), // + Insert(" ab".into()), + Retain(2), // cd + Insert("de ".into()), + ], + len: 4, + len_after: 10, + }; + assert_eq!(cs.map_pos(2, Assoc::BeforeWord), 3); + assert_eq!(cs.map_pos(4, Assoc::AfterWord), 9); + let cs = ChangeSet { + changes: vec![ + Retain(1), // + Insert(" b".into()), + Delete(1), // c + Retain(1), // d + Insert("e ".into()), + Delete(1), // + ], + len: 5, + len_after: 7, + }; + assert_eq!(cs.map_pos(1, Assoc::BeforeWord), 2); + assert_eq!(cs.map_pos(3, Assoc::AfterWord), 5); + let cs = ChangeSet { + changes: vec![ + Retain(1), // + Insert("a".into()), + Delete(2), // b + Retain(1), // d + Insert("e".into()), + Delete(1), // f + Retain(1), // + ], + len: 5, + len_after: 7, + }; + assert_eq!(cs.map_pos(2, Assoc::BeforeWord), 1); + assert_eq!(cs.map_pos(4, Assoc::AfterWord), 4); } #[test] diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 35111ca59..2dd97b428 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -1,6 +1,7 @@ use arc_swap::{access::Map, ArcSwap}; use futures_util::Stream; use helix_core::{ + chars::char_is_word, diagnostic::{DiagnosticTag, NumberOrString}, path::get_relative_path, pos_at_coords, syntax, Selection, @@ -811,7 +812,6 @@ impl Application { log::warn!("lsp position out of bounds - {:?}", diagnostic); return None; }; - let severity = diagnostic.severity.map(|severity| match severity { DiagnosticSeverity::ERROR => Error, @@ -863,8 +863,17 @@ impl Application { Vec::new() }; + let ends_at_word = start != end + && end != 0 + && text.get_char(end - 1).map_or(false, char_is_word); + let starts_at_word = start != end + && text.get_char(start).map_or(false, char_is_word); + Some(Diagnostic { range: Range { start, end }, + ends_at_word, + starts_at_word, + zero_width: start == end, line: diagnostic.range.start.line as usize, message: diagnostic.message.clone(), severity, diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 37b8a0bf7..b4971c8a8 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -1213,20 +1213,32 @@ impl Document { let changes = transaction.changes(); - changes.update_positions( - self.diagnostics - .iter_mut() - .map(|diagnostic| (&mut diagnostic.range.start, Assoc::After)), - ); - changes.update_positions( - self.diagnostics - .iter_mut() - .map(|diagnostic| (&mut diagnostic.range.end, Assoc::After)), - ); - // map state.diagnostics over changes::map_pos too - for diagnostic in &mut self.diagnostics { + // map diagnostics over changes too + changes.update_positions(self.diagnostics.iter_mut().map(|diagnostic| { + let assoc = if diagnostic.starts_at_word { + Assoc::BeforeWord + } else { + Assoc::After + }; + (&mut diagnostic.range.start, assoc) + })); + changes.update_positions(self.diagnostics.iter_mut().map(|diagnostic| { + let assoc = if diagnostic.ends_at_word { + Assoc::AfterWord + } else { + Assoc::Before + }; + (&mut diagnostic.range.end, assoc) + })); + self.diagnostics.retain_mut(|diagnostic| { + if diagnostic.range.start > diagnostic.range.end + || (!diagnostic.zero_width && diagnostic.range.start == diagnostic.range.end) + { + return false; + } diagnostic.line = self.text.char_to_line(diagnostic.range.start); - } + true + }); self.diagnostics .sort_unstable_by_key(|diagnostic| diagnostic.range); From 783ff27b1ba901e30dbf9897f4faaeb123bebb12 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Sat, 20 May 2023 21:29:23 +0200 Subject: [PATCH 007/796] consistent diagnostic sorting --- helix-term/src/application.rs | 4 +++- helix-view/src/document.rs | 18 ++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 2dd97b428..3abe9cae5 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -907,7 +907,9 @@ impl Application { // Sort diagnostics first by severity and then by line numbers. // Note: The `lsp::DiagnosticSeverity` enum is already defined in decreasing order - diagnostics.sort_unstable_by_key(|(d, _)| (d.severity, d.range.start)); + diagnostics.sort_unstable_by_key(|(d, server_id)| { + (d.severity, d.range.start, *server_id) + }); } Notification::ShowMessage(params) => { log::warn!("unhandled window/showMessage: {:?}", params); diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index b4971c8a8..af950a3fc 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -1240,8 +1240,13 @@ impl Document { true }); - self.diagnostics - .sort_unstable_by_key(|diagnostic| diagnostic.range); + self.diagnostics.sort_unstable_by_key(|diagnostic| { + ( + diagnostic.range, + diagnostic.severity, + diagnostic.language_server_id, + ) + }); // Update the inlay hint annotations' positions, helping ensure they are displayed in the proper place let apply_inlay_hint_changes = |annotations: &mut Rc<[InlineAnnotation]>| { @@ -1738,8 +1743,13 @@ impl Document { }); } self.diagnostics.extend(diagnostics); - self.diagnostics - .sort_unstable_by_key(|diagnostic| diagnostic.range); + self.diagnostics.sort_unstable_by_key(|diagnostic| { + ( + diagnostic.range, + diagnostic.severity, + diagnostic.language_server_id, + ) + }); } pub fn clear_diagnostics(&mut self, language_server_id: usize) { From 85fce2f5b6c9f35ab9d3361f3933288a28db83d4 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Fri, 29 Dec 2023 20:46:01 +0100 Subject: [PATCH 008/796] build(deps): bump gix from 0.56.0 to 0.57.0 (#9188) --- Cargo.lock | 155 ++++++++++++++++++++++++------------------- helix-vcs/Cargo.toml | 2 +- 2 files changed, 88 insertions(+), 69 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1488020a4..3bfe61320 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -522,9 +522,9 @@ checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "gix" -version = "0.56.0" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0dcdc9c60d66535897fa40a7ea2a635e72f99456b1d9ae86b7e170e80618cb" +checksum = "721c7497ab24b665ed5a1eb2b3526936aa60068e61ba260651f7e77c52feec69" dependencies = [ "gix-actor", "gix-commitgraph", @@ -563,9 +563,9 @@ dependencies = [ [[package]] name = "gix-actor" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eadca029ef716b4378f7afb19f7ee101fde9e58ba1f1445971315ac866db417" +checksum = "886014c4865b93ce268f1d3eddd4fd3261242c3f3ee61eb36009f913016a9059" dependencies = [ "bstr", "btoi", @@ -577,18 +577,18 @@ dependencies = [ [[package]] name = "gix-chunk" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d411ecd9b558b0c20b3252b7e409eec48eabc41d18324954fe526bac6e2db55f" +checksum = "2ade37ef69870de0ed966b97c57a3a947a22ff3482a52c3b99b205f77bcb08fb" dependencies = [ "thiserror", ] [[package]] name = "gix-commitgraph" -version = "0.22.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a7007ba021f059803afaf6f8a48872422abc20550ac12ede6ddea2936cec36" +checksum = "e7559ea9cefee188cd88b05afcc8e3ef7a3cb4a5c647bccf06b981e591b02b77" dependencies = [ "bstr", "gix-chunk", @@ -600,9 +600,9 @@ dependencies = [ [[package]] name = "gix-config" -version = "0.32.1" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0341471d55d8676e98b88e121d7065dfa4c9c5acea4b6d6ecdd2846e85cce0c3" +checksum = "9c35dc7f9c00a42bbc9cfa1ca2ec0a78ad1b76ff0736d3d35dfd612962244467" dependencies = [ "bstr", "gix-config-value", @@ -621,9 +621,9 @@ dependencies = [ [[package]] name = "gix-config-value" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6419db582ea84dfb58c7e7b0af7fd62c808aa14954af2936a33f89b0f4ed018e" +checksum = "39f388cb2396aee82d6f460a2e7770659bdf8854e9e5478f7d2b1324a9698284" dependencies = [ "bitflags 2.4.1", "bstr", @@ -634,9 +634,9 @@ dependencies = [ [[package]] name = "gix-date" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "468dfbe411f335f01525a1352271727f8e7772075a93fa747260f502086b30be" +checksum = "9d85f4a01e0d05c3de585e28bae514d4baf01655e3fc3f14ce6f30bf62405345" dependencies = [ "bstr", "itoa", @@ -646,9 +646,9 @@ dependencies = [ [[package]] name = "gix-diff" -version = "0.38.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8119a985887cfe68f4bdf92e51bd64bc758a73882d82fcfc03ebcb164441c85d" +checksum = "ac0e75f5afd2f6c47c800b6b0a000a08045739d0450d20482e8faa42543f62d1" dependencies = [ "bstr", "gix-hash", @@ -658,9 +658,9 @@ dependencies = [ [[package]] name = "gix-discover" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fad89416ebe0b3b7df78464124e2a02417b6cd3743d48ad93df86f4d2929c07" +checksum = "f0b63ce2ad81632ac1a84ac370f85a5e580c3261580bd85ea17ff35d3a0bba18" dependencies = [ "bstr", "dunce", @@ -673,9 +673,9 @@ dependencies = [ [[package]] name = "gix-features" -version = "0.36.1" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d46a4a5c6bb5bebec9c0d18b65ada20e6517dbd7cf855b87dd4bbdce3a771b2" +checksum = "befe7edea299a824504b5acc96d7a3a538125b38c42f3a8379f6912a29c90c81" dependencies = [ "crc32fast", "flate2", @@ -691,18 +691,18 @@ dependencies = [ [[package]] name = "gix-fs" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20e86eb040f5776a5ade092282e51cdcad398adb77d948b88d17583c2ae4e107" +checksum = "007017ce93b819ea52c0ec68306f7d72212a2d307c3d70f1548c7141c015d0a1" dependencies = [ "gix-features", ] [[package]] name = "gix-glob" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db19298c5eeea2961e5b3bf190767a2d1f09b8802aeb5f258e42276350aff19" +checksum = "61fb116c2516d3a1170e010118f639944a6389872c875a008960cdab2a44ac72" dependencies = [ "bitflags 2.4.1", "bstr", @@ -712,9 +712,9 @@ dependencies = [ [[package]] name = "gix-hash" -version = "0.13.3" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f8cf8c2266f63e582b7eb206799b63aa5fa68ee510ad349f637dfe2d0653de0" +checksum = "c52c3170a17b6031833cd2eb4ad7bc23f2755d06e6e70f78dec21d42e8fe1b30" dependencies = [ "faster-hex", "thiserror", @@ -722,9 +722,9 @@ dependencies = [ [[package]] name = "gix-hashtable" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb61880816d7ec4f0b20606b498147d480860ddd9133ba542628df2f548d3ca" +checksum = "40a838e6366a5e5b84668b6997ce0981b833136468e8ba949f424c0ef2927eba" dependencies = [ "gix-hash", "hashbrown 0.14.3", @@ -733,9 +733,9 @@ dependencies = [ [[package]] name = "gix-lock" -version = "11.0.0" +version = "12.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4feb1dcd304fe384ddc22edba9dd56a42b0800032de6537728cea2f033a4f37" +checksum = "6cf112ddee94223c119a8534dad027740dc3aba3365ac5edeef8a7f6660c74db" dependencies = [ "gix-tempfile", "gix-utils", @@ -744,9 +744,9 @@ dependencies = [ [[package]] name = "gix-macros" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02a5bcaf6704d9354a3071cede7e77d366a5980c7352e102e2c2f9b645b1d3ae" +checksum = "fc207b64189cf71bcb17fc841ab99a5d63d0845546b611e2703ce467f659323a" dependencies = [ "proc-macro2", "quote", @@ -755,9 +755,9 @@ dependencies = [ [[package]] name = "gix-object" -version = "0.39.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "febf79c5825720c1c63fe974c7bbe695d0cb54aabad73f45671c60ce0e501e33" +checksum = "e4c77e47ffba92127faf632a841fce23d19547e269bd8b88e68961a70eab4e93" dependencies = [ "bstr", "btoi", @@ -774,9 +774,9 @@ dependencies = [ [[package]] name = "gix-odb" -version = "0.55.0" +version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fae5f971540c99c6ecc8d4368ecc9d18a9dc8b9391025c68c4399747dc93bac" +checksum = "a3254f2005cc7553ea78e85e816a09150c6f7a64e6b7627b8d1fdc56721bea73" dependencies = [ "arc-swap", "gix-date", @@ -793,9 +793,9 @@ dependencies = [ [[package]] name = "gix-pack" -version = "0.45.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569491c92446fddf373456ff360aff9a9effd627b40a70f2d7914dcd75a3205" +checksum = "02ebc8cd657eec207d82d8f876ca402361308ecd3c87a47935b0299506257b4f" dependencies = [ "clru", "gix-chunk", @@ -813,9 +813,9 @@ dependencies = [ [[package]] name = "gix-path" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86d6fac2fabe07b67b7835f46d07571f68b11aa1aaecae94fe722ea4ef305e1" +checksum = "10931652a3da126990ac93bce1b1c600cc99d7c268d712b6360ed52174ce1b68" dependencies = [ "bstr", "gix-trace", @@ -826,9 +826,9 @@ dependencies = [ [[package]] name = "gix-quote" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f84845efa535468bc79c5a87b9d29219f1da0313c8ecf0365a5daa7e72786f2" +checksum = "6c43530cb94a7759807e6f8d180e17ac9c65673b891645c6c433831dc0cf4342" dependencies = [ "bstr", "btoi", @@ -837,9 +837,9 @@ dependencies = [ [[package]] name = "gix-ref" -version = "0.39.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac23ed741583c792f573c028785db683496a6dfcd672ec701ee54ba6a77e1ff" +checksum = "baa951b3b850d6d1be4f900768e49af20b76bf9505beac22af723d57249a2f1d" dependencies = [ "gix-actor", "gix-date", @@ -858,9 +858,9 @@ dependencies = [ [[package]] name = "gix-refspec" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d9d3b82e1ee78fc0dc1c37ea5ea76c2dbc73f407db155f0dfcea285e583bee" +checksum = "88c61f849d58c06e3068a0b601cf10127d2a07cdad00a725ed66cf303f76f6b3" dependencies = [ "bstr", "gix-hash", @@ -872,9 +872,9 @@ dependencies = [ [[package]] name = "gix-revision" -version = "0.24.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe5dd51710ce5434bc315ea30394fab483c5377276494edd79222b321a5a9544" +checksum = "bcbbf91f4200c5c76802ef5f057b96f5a336827508881fe55a780be71a794d22" dependencies = [ "bstr", "gix-date", @@ -888,9 +888,9 @@ dependencies = [ [[package]] name = "gix-revwalk" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d4ed2493ca94a475fdf147138e1ef8bab3b6ebb56abf3d9bda1c05372ec1dd" +checksum = "33ab66354d83f70f2730391747d0c75f94ef3c3dd40ebde2e206db9faaf7a0a7" dependencies = [ "gix-commitgraph", "gix-date", @@ -903,21 +903,21 @@ dependencies = [ [[package]] name = "gix-sec" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a36ea2c5907d64a9b4b5d3cc9f430e6c30f0509646b5e38eb275ca57c5bf29e2" +checksum = "a9963f38a42144253ed4d571882d7db5ef644c12e6726e4b75135597cc9d0e1a" dependencies = [ "bitflags 2.4.1", "gix-path", "libc", - "windows", + "windows 0.52.0", ] [[package]] name = "gix-tempfile" -version = "11.0.0" +version = "12.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05cc2205cf10d99f70b96e04e16c55d4c7cf33efc151df1f793e29fd12a931f8" +checksum = "e76a494bd530e1a1309188ff971825a24f159c76c2db0bf71fa5dfb469a2c915" dependencies = [ "gix-fs", "libc", @@ -928,15 +928,15 @@ dependencies = [ [[package]] name = "gix-trace" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b686a35799b53a9825575ca3f06481d0a053a409c4d97ffcf5ddd67a8760b497" +checksum = "bda62acb44dd86a40c7c3762a5403cfc1ac789ea559df54085cedf79864f809e" [[package]] name = "gix-traverse" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2112088122a0206592c84fbd42020db63b2ccaed66a0293779f2e5fbf80474" +checksum = "8661fab39985c9214e56d81a63ceb5886ac948cec2fba76c39494d1e0e307ea8" dependencies = [ "gix-commitgraph", "gix-date", @@ -950,9 +950,9 @@ dependencies = [ [[package]] name = "gix-url" -version = "0.25.2" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c427a1a11ccfa53a4a2da47d9442c2241deee63a154bc15cc14b8312fbc4005" +checksum = "10a0129c1e8b52736d7c5128300a4485dbc85863001371e2771ac1754bd89fd7" dependencies = [ "bstr", "gix-features", @@ -964,18 +964,18 @@ dependencies = [ [[package]] name = "gix-utils" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f82c41937f00e15a1f6cb0b55307f0ca1f77f4407ff2bf440be35aa688c6a3e" +checksum = "ab9277a5e32e85d53f738096d872a4a9b76067ad471894ad31bd99c8fa2da1dc" dependencies = [ "fastrand", ] [[package]] name = "gix-validate" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b7d8e4274be69f284bbc7e6bb2ccf7065dbcdeba22d8c549f2451ae426883f" +checksum = "f805ebbdbaa4bfd98e2ee43e6d14099b8a4d9141f5d7b8202fea4d48e44263e7" dependencies = [ "bstr", "thiserror", @@ -1286,7 +1286,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows 0.48.0", ] [[package]] @@ -1624,9 +1624,9 @@ dependencies = [ [[package]] name = "prodash" -version = "26.2.2" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794b5bf8e2d19b53dcdcec3e4bba628e20f5b6062503ba89281fa7037dd7bbcf" +checksum = "744a264d26b88a6a7e37cbad97953fa233b94d585236310bcbc88474b4092d79" [[package]] name = "pulldown-cmark" @@ -2384,6 +2384,25 @@ dependencies = [ "windows-targets 0.48.0", ] +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-sys" version = "0.45.0" diff --git a/helix-vcs/Cargo.toml b/helix-vcs/Cargo.toml index bdf4b54d9..dae4c0237 100644 --- a/helix-vcs/Cargo.toml +++ b/helix-vcs/Cargo.toml @@ -19,7 +19,7 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "p parking_lot = "0.12" arc-swap = { version = "1.6.0" } -gix = { version = "0.56.0", default-features = false , optional = true } +gix = { version = "0.57.0", default-features = false , optional = true } imara-diff = "0.1.5" anyhow = "1" From 7fd266efa9b90861f585d0cd7366c94bbeeaa81a Mon Sep 17 00:00:00 2001 From: Gabriel Lopes Rodrigues Date: Tue, 2 Jan 2024 12:29:22 -0300 Subject: [PATCH 009/796] Avoid crashing with 2 instances of the same LSP (#9134) --- helix-lsp/src/lib.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index b6a990659..34278cd54 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -915,10 +915,17 @@ fn start_client( } // next up, notify - _client + let notification_result = _client .notify::(lsp::InitializedParams {}) - .await - .unwrap(); + .await; + + if let Err(e) = notification_result { + log::error!( + "failed to notify language server of its initialization: {}", + e + ); + return; + } initialize_notify.notify_one(); }); From a680b2e409769bf387e5bb7469215b90775e01ba Mon Sep 17 00:00:00 2001 From: Rose Hudson Date: Tue, 2 Jan 2024 15:38:13 +0000 Subject: [PATCH 010/796] rust highlights: clean up constructor logic (#8957) Enum variants and (tuple) structs are indistinguishable in general, so we mark any PascalCase pattern or expression as a "constructor", which covers all three. --- runtime/queries/rust/highlights.scm | 64 +++++++++++++++++++---------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/runtime/queries/rust/highlights.scm b/runtime/queries/rust/highlights.scm index 47e57e800..3cda9d520 100644 --- a/runtime/queries/rust/highlights.scm +++ b/runtime/queries/rust/highlights.scm @@ -189,6 +189,33 @@ ; TODO: variable.mut to highlight mutable identifiers via locals.scm +; ------- +; Constructors +; ------- +; TODO: this is largely guesswork, remove it once we get actual info from locals.scm or r-a + +(struct_expression + name: (type_identifier) @constructor) + +(tuple_struct_pattern + type: [ + (identifier) @constructor + (scoped_identifier + name: (identifier) @constructor) + ]) +(struct_pattern + type: [ + ((type_identifier) @constructor) + (scoped_type_identifier + name: (type_identifier) @constructor) + ]) +(match_pattern + ((identifier) @constructor) (#match? @constructor "^[A-Z]")) +(or_pattern + ((identifier) @constructor) + ((identifier) @constructor) + (#match? @constructor "^[A-Z]")) + ; ------- ; Guess Other Types ; ------- @@ -203,33 +230,28 @@ (call_expression function: [ - ((identifier) @type.enum.variant - (#match? @type.enum.variant "^[A-Z]")) + ((identifier) @constructor + (#match? @constructor "^[A-Z]")) (scoped_identifier - name: ((identifier) @type.enum.variant - (#match? @type.enum.variant "^[A-Z]"))) + name: ((identifier) @constructor + (#match? @constructor "^[A-Z]"))) ]) ; --- -; Assume that types in match arms are enums and not -; tuple structs. Same for `if let` expressions. +; PascalCase identifiers under a path which is also PascalCase +; are assumed to be constructors if they have methods or fields. ; --- -(match_pattern - (scoped_identifier - name: (identifier) @constructor)) -(tuple_struct_pattern - type: [ - ((identifier) @constructor) - (scoped_identifier - name: (identifier) @constructor) - ]) -(struct_pattern - type: [ - ((type_identifier) @constructor) - (scoped_type_identifier - name: (type_identifier) @constructor) - ]) +(field_expression + value: (scoped_identifier + path: [ + (identifier) @type + (scoped_identifier + name: (identifier) @type) + ] + name: (identifier) @constructor + (#match? @type "^[A-Z]") + (#match? @constructor "^[A-Z]"))) ; --- ; Other PascalCase identifiers are assumed to be structs. From efc4865c783e86812e34fd18ac333d5215dce708 Mon Sep 17 00:00:00 2001 From: Manuel Mendez <708570+mmlb@users.noreply.github.com> Date: Tue, 2 Jan 2024 17:16:37 -0500 Subject: [PATCH 011/796] Reduce logo sizes even more (#9211) * Reduce logo.svg even more While reading through commits to helix I saw https://github.com/helix-editor/helix/pull/9106 and wondered if the relatively-new-to-me svgo would do better than -95B diff, indeed it does. Seeing as this file is "a minified file we're basically treating as binary" anyway I figured might as well minify it further. * Minimize all the svg logos --- logo.svg | 2 +- logo_dark.svg | 116 +------------------------------------------------ logo_light.svg | 116 +------------------------------------------------ 3 files changed, 3 insertions(+), 231 deletions(-) diff --git a/logo.svg b/logo.svg index a2d1c1109..1408f897f 100644 --- a/logo.svg +++ b/logo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/logo_dark.svg b/logo_dark.svg index f6e94f1b4..30ff77963 100644 --- a/logo_dark.svg +++ b/logo_dark.svg @@ -1,115 +1 @@ - - + \ No newline at end of file diff --git a/logo_light.svg b/logo_light.svg index cdd5ddb8b..014443bce 100644 --- a/logo_light.svg +++ b/logo_light.svg @@ -1,115 +1 @@ - - + \ No newline at end of file From a6ed104ea2787b523379895b7eb23998aae121f4 Mon Sep 17 00:00:00 2001 From: Jaakko Paju Date: Wed, 3 Jan 2024 00:47:59 +0200 Subject: [PATCH 012/796] Add .prettierrc to json file types (#9214) --- languages.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index b47fb4e2b..7b4b5b8b5 100644 --- a/languages.toml +++ b/languages.toml @@ -348,7 +348,8 @@ file-types = [ ".vuerc", "composer.lock", ".watchmanconfig", - "avsc" + "avsc", + ".prettierrc" ] language-servers = [ "vscode-json-language-server" ] auto-format = true From 5876b763e1d0787a773749a6b26a237c223216b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:37:22 +0900 Subject: [PATCH 013/796] build(deps): bump gix from 0.57.0 to 0.57.1 (#9202) Bumps [gix](https://github.com/Byron/gitoxide) from 0.57.0 to 0.57.1. - [Release notes](https://github.com/Byron/gitoxide/releases) - [Changelog](https://github.com/Byron/gitoxide/blob/main/CHANGELOG.md) - [Commits](https://github.com/Byron/gitoxide/compare/gix-v0.57.0...gix-v0.57.1) --- updated-dependencies: - dependency-name: gix dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 120 +++++++++++++++++++++---------------------- helix-vcs/Cargo.toml | 2 +- 2 files changed, 61 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3bfe61320..4fdb4c46e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -522,9 +522,9 @@ checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "gix" -version = "0.57.0" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721c7497ab24b665ed5a1eb2b3526936aa60068e61ba260651f7e77c52feec69" +checksum = "6dd025382892c7b500a9ce1582cd803f9c2ebfe44aff52e9c7f86feee7ced75e" dependencies = [ "gix-actor", "gix-commitgraph", @@ -563,9 +563,9 @@ dependencies = [ [[package]] name = "gix-actor" -version = "0.29.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "886014c4865b93ce268f1d3eddd4fd3261242c3f3ee61eb36009f913016a9059" +checksum = "da27b5ab4ab5c75ff891dccd48409f8cc53c28a79480f1efdd33184b2dc1d958" dependencies = [ "bstr", "btoi", @@ -577,18 +577,18 @@ dependencies = [ [[package]] name = "gix-chunk" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ade37ef69870de0ed966b97c57a3a947a22ff3482a52c3b99b205f77bcb08fb" +checksum = "003ec6deacf68076a0c157271a127e0bb2c031c1a41f7168cbe5d248d9b85c78" dependencies = [ "thiserror", ] [[package]] name = "gix-commitgraph" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7559ea9cefee188cd88b05afcc8e3ef7a3cb4a5c647bccf06b981e591b02b77" +checksum = "8a39c675fd737cb43a2120eddf1aa652c19d76b28d79783a198ac1b398ed9ce6" dependencies = [ "bstr", "gix-chunk", @@ -600,9 +600,9 @@ dependencies = [ [[package]] name = "gix-config" -version = "0.33.0" +version = "0.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c35dc7f9c00a42bbc9cfa1ca2ec0a78ad1b76ff0736d3d35dfd612962244467" +checksum = "367304855b369cadcac4ee5fb5a3a20da9378dd7905106141070b79f85241079" dependencies = [ "bstr", "gix-config-value", @@ -621,9 +621,9 @@ dependencies = [ [[package]] name = "gix-config-value" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39f388cb2396aee82d6f460a2e7770659bdf8854e9e5478f7d2b1324a9698284" +checksum = "52e0be46f4cf1f8f9e88d0e3eb7b29718aff23889563249f379119bd1ab6910e" dependencies = [ "bitflags 2.4.1", "bstr", @@ -634,9 +634,9 @@ dependencies = [ [[package]] name = "gix-date" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d85f4a01e0d05c3de585e28bae514d4baf01655e3fc3f14ce6f30bf62405345" +checksum = "fb7f3dfb72bebe3449b5e642be64e3c6ccbe9821c8b8f19f487cf5bfbbf4067e" dependencies = [ "bstr", "itoa", @@ -646,9 +646,9 @@ dependencies = [ [[package]] name = "gix-diff" -version = "0.39.0" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac0e75f5afd2f6c47c800b6b0a000a08045739d0450d20482e8faa42543f62d1" +checksum = "fd6a0454f8c42d686f17e7f084057c717c082b7dbb8209729e4e8f26749eb93a" dependencies = [ "bstr", "gix-hash", @@ -658,9 +658,9 @@ dependencies = [ [[package]] name = "gix-discover" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0b63ce2ad81632ac1a84ac370f85a5e580c3261580bd85ea17ff35d3a0bba18" +checksum = "b8d7b2896edc3d899d28a646ccc6df729827a6600e546570b2783466404a42d6" dependencies = [ "bstr", "dunce", @@ -673,9 +673,9 @@ dependencies = [ [[package]] name = "gix-features" -version = "0.37.0" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "befe7edea299a824504b5acc96d7a3a538125b38c42f3a8379f6912a29c90c81" +checksum = "77a80f0fe688d654c2a741751578b11131071026d1934d03c1820d6d767525ce" dependencies = [ "crc32fast", "flate2", @@ -691,18 +691,18 @@ dependencies = [ [[package]] name = "gix-fs" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "007017ce93b819ea52c0ec68306f7d72212a2d307c3d70f1548c7141c015d0a1" +checksum = "7555c23a005537434bbfcb8939694e18cad42602961d0de617f8477cc2adecdd" dependencies = [ "gix-features", ] [[package]] name = "gix-glob" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61fb116c2516d3a1170e010118f639944a6389872c875a008960cdab2a44ac72" +checksum = "ae6232f18b262770e343dcdd461c0011c9b9ae27f0c805e115012aa2b902c1b8" dependencies = [ "bitflags 2.4.1", "bstr", @@ -712,9 +712,9 @@ dependencies = [ [[package]] name = "gix-hash" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c52c3170a17b6031833cd2eb4ad7bc23f2755d06e6e70f78dec21d42e8fe1b30" +checksum = "b0ed89cdc1dce26685c80271c4287077901de3c3dd90234d5fa47c22b2268653" dependencies = [ "faster-hex", "thiserror", @@ -722,9 +722,9 @@ dependencies = [ [[package]] name = "gix-hashtable" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a838e6366a5e5b84668b6997ce0981b833136468e8ba949f424c0ef2927eba" +checksum = "ebe47d8c0887f82355e2e9e16b6cecaa4d5e5346a7a474ca78ff94de1db35a5b" dependencies = [ "gix-hash", "hashbrown 0.14.3", @@ -744,9 +744,9 @@ dependencies = [ [[package]] name = "gix-macros" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc207b64189cf71bcb17fc841ab99a5d63d0845546b611e2703ce467f659323a" +checksum = "d75e7ab728059f595f6ddc1ad8771b8d6a231971ae493d9d5948ecad366ee8bb" dependencies = [ "proc-macro2", "quote", @@ -755,9 +755,9 @@ dependencies = [ [[package]] name = "gix-object" -version = "0.40.0" +version = "0.40.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4c77e47ffba92127faf632a841fce23d19547e269bd8b88e68961a70eab4e93" +checksum = "0c89402e8faa41b49fde348665a8f38589e461036475af43b6b70615a6a313a2" dependencies = [ "bstr", "btoi", @@ -774,9 +774,9 @@ dependencies = [ [[package]] name = "gix-odb" -version = "0.56.0" +version = "0.56.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3254f2005cc7553ea78e85e816a09150c6f7a64e6b7627b8d1fdc56721bea73" +checksum = "46ae6da873de41c6c2b73570e82c571b69df5154dcd8f46dfafc6687767c33b1" dependencies = [ "arc-swap", "gix-date", @@ -793,9 +793,9 @@ dependencies = [ [[package]] name = "gix-pack" -version = "0.46.0" +version = "0.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02ebc8cd657eec207d82d8f876ca402361308ecd3c87a47935b0299506257b4f" +checksum = "782b4d42790a14072d5c400deda9851f5765f50fe72bca6dece0da1cd6f05a9a" dependencies = [ "clru", "gix-chunk", @@ -813,9 +813,9 @@ dependencies = [ [[package]] name = "gix-path" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10931652a3da126990ac93bce1b1c600cc99d7c268d712b6360ed52174ce1b68" +checksum = "b8dd0998ab245f33d40ca2267e58d542fe54185ebd1dc41923346cf28d179fb6" dependencies = [ "bstr", "gix-trace", @@ -826,9 +826,9 @@ dependencies = [ [[package]] name = "gix-quote" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c43530cb94a7759807e6f8d180e17ac9c65673b891645c6c433831dc0cf4342" +checksum = "9f7dc10303d73a960d10fb82f81188b036ac3e6b11b5795b20b1a60b51d1321f" dependencies = [ "bstr", "btoi", @@ -837,9 +837,9 @@ dependencies = [ [[package]] name = "gix-ref" -version = "0.40.0" +version = "0.40.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baa951b3b850d6d1be4f900768e49af20b76bf9505beac22af723d57249a2f1d" +checksum = "64d9bd1984638d8f3511a2fcbe84fcedb8a5b5d64df677353620572383f42649" dependencies = [ "gix-actor", "gix-date", @@ -858,9 +858,9 @@ dependencies = [ [[package]] name = "gix-refspec" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c61f849d58c06e3068a0b601cf10127d2a07cdad00a725ed66cf303f76f6b3" +checksum = "be219df5092c1735abb2a53eccdf775e945eea6986ee1b6e7a5896dccc0be704" dependencies = [ "bstr", "gix-hash", @@ -872,9 +872,9 @@ dependencies = [ [[package]] name = "gix-revision" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcbbf91f4200c5c76802ef5f057b96f5a336827508881fe55a780be71a794d22" +checksum = "aa78e1df3633bc937d4db15f8dca2abdb1300ca971c0fabcf9fa97e38cf4cd9f" dependencies = [ "bstr", "gix-date", @@ -888,9 +888,9 @@ dependencies = [ [[package]] name = "gix-revwalk" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab66354d83f70f2730391747d0c75f94ef3c3dd40ebde2e206db9faaf7a0a7" +checksum = "702de5fe5c2bbdde80219f3a8b9723eb927466e7ecd187cfd1b45d986408e45f" dependencies = [ "gix-commitgraph", "gix-date", @@ -903,9 +903,9 @@ dependencies = [ [[package]] name = "gix-sec" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9963f38a42144253ed4d571882d7db5ef644c12e6726e4b75135597cc9d0e1a" +checksum = "78f6dce0c6683e2219e8169aac4b1c29e89540a8262fef7056b31d80d969408c" dependencies = [ "bitflags 2.4.1", "gix-path", @@ -928,15 +928,15 @@ dependencies = [ [[package]] name = "gix-trace" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda62acb44dd86a40c7c3762a5403cfc1ac789ea559df54085cedf79864f809e" +checksum = "e8e1127ede0475b58f4fe9c0aaa0d9bb0bad2af90bbd93ccd307c8632b863d89" [[package]] name = "gix-traverse" -version = "0.36.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8661fab39985c9214e56d81a63ceb5886ac948cec2fba76c39494d1e0e307ea8" +checksum = "cb64213e52e1b726cb04581690c1e98b5910f983b977d5e9f2eb09f1a7fea6d2" dependencies = [ "gix-commitgraph", "gix-date", @@ -950,9 +950,9 @@ dependencies = [ [[package]] name = "gix-url" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a0129c1e8b52736d7c5128300a4485dbc85863001371e2771ac1754bd89fd7" +checksum = "8f0f17cceb7552a231d1fec690bc2740c346554e3be6f5d2c41dfa809594dc44" dependencies = [ "bstr", "gix-features", @@ -964,18 +964,18 @@ dependencies = [ [[package]] name = "gix-utils" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9277a5e32e85d53f738096d872a4a9b76067ad471894ad31bd99c8fa2da1dc" +checksum = "de6225e2de30b6e9bca2d9f1cc4731640fcef0fb3cabddceee366e7e85d3e94f" dependencies = [ "fastrand", ] [[package]] name = "gix-validate" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f805ebbdbaa4bfd98e2ee43e6d14099b8a4d9141f5d7b8202fea4d48e44263e7" +checksum = "ac7cc36f496bd5d96cdca0f9289bb684480725d40db60f48194aa7723b883854" dependencies = [ "bstr", "thiserror", diff --git a/helix-vcs/Cargo.toml b/helix-vcs/Cargo.toml index dae4c0237..aa601dcd1 100644 --- a/helix-vcs/Cargo.toml +++ b/helix-vcs/Cargo.toml @@ -19,7 +19,7 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "p parking_lot = "0.12" arc-swap = { version = "1.6.0" } -gix = { version = "0.57.0", default-features = false , optional = true } +gix = { version = "0.57.1", default-features = false , optional = true } imara-diff = "0.1.5" anyhow = "1" From 78d85eb13f4a6cd2201d4ce0fc020cc747829823 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:37:41 +0900 Subject: [PATCH 014/796] build(deps): bump futures-executor from 0.3.29 to 0.3.30 (#9168) Bumps [futures-executor](https://github.com/rust-lang/futures-rs) from 0.3.29 to 0.3.30. - [Release notes](https://github.com/rust-lang/futures-rs/releases) - [Changelog](https://github.com/rust-lang/futures-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/futures-rs/compare/0.3.29...0.3.30) --- updated-dependencies: - dependency-name: futures-executor dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4fdb4c46e..90134a059 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -475,9 +475,9 @@ checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", From b908abae2d3f5fff18c4836dc86fb4a1b5deb85c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:37:49 +0900 Subject: [PATCH 015/796] build(deps): bump anyhow from 1.0.76 to 1.0.78 (#9200) Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.76 to 1.0.78. - [Release notes](https://github.com/dtolnay/anyhow/releases) - [Commits](https://github.com/dtolnay/anyhow/compare/1.0.76...1.0.78) --- updated-dependencies: - dependency-name: anyhow dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 90134a059..164b6d792 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,9 +62,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.76" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355" +checksum = "ca87830a3e3fb156dc96cfbd31cb620265dd053be734723f22b760d6cc3c3051" [[package]] name = "arc-swap" From 2e3f330b127af7ce37539aa95e472dbccb7c492c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:38:05 +0900 Subject: [PATCH 016/796] build(deps): bump tempfile from 3.8.1 to 3.9.0 (#9199) Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.8.1 to 3.9.0. - [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md) - [Commits](https://github.com/Stebalien/tempfile/compare/v3.8.1...v3.9.0) --- updated-dependencies: - dependency-name: tempfile dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 6 +++--- helix-loader/Cargo.toml | 2 +- helix-term/Cargo.toml | 2 +- helix-vcs/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 164b6d792..85a4a4bde 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1986,15 +1986,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" dependencies = [ "cfg-if", "fastrand", "redox_syscall 0.4.1", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] diff --git a/helix-loader/Cargo.toml b/helix-loader/Cargo.toml index c40bf4dbc..187475385 100644 --- a/helix-loader/Cargo.toml +++ b/helix-loader/Cargo.toml @@ -29,7 +29,7 @@ which = "5.0.0" # cloning/compiling tree-sitter grammars cc = { version = "1" } threadpool = { version = "1.0" } -tempfile = "3.8.1" +tempfile = "3.9.0" dunce = "1.0.4" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 4ff7fc0b1..32049a438 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -84,4 +84,4 @@ helix-loader = { path = "../helix-loader" } [dev-dependencies] smallvec = "1.11" indoc = "2.0.4" -tempfile = "3.8.1" +tempfile = "3.9.0" diff --git a/helix-vcs/Cargo.toml b/helix-vcs/Cargo.toml index aa601dcd1..f6dbe076d 100644 --- a/helix-vcs/Cargo.toml +++ b/helix-vcs/Cargo.toml @@ -29,4 +29,4 @@ log = "0.4" git = ["gix"] [dev-dependencies] -tempfile = "3.8" +tempfile = "3.9" From 8f2e611b7e30d74546afe5ec6a16eed6d34f4c39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 15:38:12 +0900 Subject: [PATCH 017/796] build(deps): bump serde_json from 1.0.108 to 1.0.109 (#9201) Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.108 to 1.0.109. - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.108...v1.0.109) --- updated-dependencies: - dependency-name: serde_json dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 85a4a4bde..9c612a7e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1822,9 +1822,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9" dependencies = [ "itoa", "ryu", From da4afaf3daf6bc9355953c404d1766321ed9fd99 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 4 Jan 2024 08:51:00 +0200 Subject: [PATCH 018/796] remove build warnings (#9180) --- helix-dap/src/client.rs | 1 - helix-term/src/lib.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/helix-dap/src/client.rs b/helix-dap/src/client.rs index acdfc5b7e..55ebab577 100644 --- a/helix-dap/src/client.rs +++ b/helix-dap/src/client.rs @@ -9,7 +9,6 @@ use helix_core::syntax::DebuggerQuirks; use serde_json::Value; use anyhow::anyhow; -pub use log::{error, info}; use std::{ collections::HashMap, future::Future, diff --git a/helix-term/src/lib.rs b/helix-term/src/lib.rs index 2f6ec12b1..a94c5e494 100644 --- a/helix-term/src/lib.rs +++ b/helix-term/src/lib.rs @@ -13,7 +13,6 @@ pub mod ui; use std::path::Path; use ignore::DirEntry; -pub use keymap::macros::*; #[cfg(not(windows))] fn true_color() -> bool { From 7e389b67c24dfe4466112c988b240c807e7e2414 Mon Sep 17 00:00:00 2001 From: "petrak@" Date: Thu, 4 Jan 2024 15:49:50 -0500 Subject: [PATCH 019/796] Add auto-pairs to scheme language support (#9232) Currently, typing a single quote in a `.scm` file "helpfully" auto- completes a closing quote. This is because there is no auto-pairs section in the languages.toml. This commit adds that. --- languages.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/languages.toml b/languages.toml index 7b4b5b8b5..a013dab8c 100644 --- a/languages.toml +++ b/languages.toml @@ -1914,6 +1914,12 @@ shebangs = ["scheme", "guile", "chicken"] comment-token = ";" indent = { tab-width = 2, unit = " " } +[language.auto-pairs] +'(' = ')' +'{' = '}' +'[' = ']' +'"' = '"' + [[grammar]] name = "scheme" source = { git = "https://github.com/6cdh/tree-sitter-scheme", rev = "af3af6c9356b936f8a515a1e449c32e804c2b1a8" } From f8ae2bc61b0fee8b2820c07f10f25ee23c13af7f Mon Sep 17 00:00:00 2001 From: DuckDuckWhale <28813824+DuckDuckWhale@users.noreply.github.com> Date: Sun, 7 Jan 2024 18:03:56 -0800 Subject: [PATCH 020/796] Fix: misleading active tab color in monokai_pro* (#9148) --- runtime/themes/monokai_pro.toml | 5 +++++ runtime/themes/monokai_pro_machine.toml | 5 +++++ runtime/themes/monokai_pro_octagon.toml | 5 +++++ runtime/themes/monokai_pro_ristretto.toml | 5 +++++ runtime/themes/monokai_pro_spectrum.toml | 5 +++++ 5 files changed, 25 insertions(+) diff --git a/runtime/themes/monokai_pro.toml b/runtime/themes/monokai_pro.toml index 57bede94a..a898671f6 100644 --- a/runtime/themes/monokai_pro.toml +++ b/runtime/themes/monokai_pro.toml @@ -30,6 +30,11 @@ "ui.cursor.match" = { bg = "base4" } "ui.cursorline" = { bg = "base1" } +# bufferline, inlay hints +"ui.bufferline" = { fg = "base6", bg = "base8x0c" } +"ui.bufferline.active" = { fg = "base8", bg = "base4" } +"ui.virtual.inlay-hint" = { fg = "base6" } + # comments, nord3 based lighter color "comment" = { fg = "base5", modifiers = ["italic"] } "ui.linenr" = { fg = "base5" } diff --git a/runtime/themes/monokai_pro_machine.toml b/runtime/themes/monokai_pro_machine.toml index b292e6b02..519c9ed39 100644 --- a/runtime/themes/monokai_pro_machine.toml +++ b/runtime/themes/monokai_pro_machine.toml @@ -27,6 +27,11 @@ "ui.cursor.match" = { bg = "base4" } "ui.cursorline" = { bg = "base1" } +# bufferline, inlay hints +"ui.bufferline" = { fg = "base6", bg = "base8x0c" } +"ui.bufferline.active" = { fg = "base8", bg = "base4" } +"ui.virtual.inlay-hint" = { fg = "base6" } + # comments, nord3 based lighter color "comment" = { fg = "base5", modifiers = ["italic"] } "ui.linenr" = { fg = "base5" } diff --git a/runtime/themes/monokai_pro_octagon.toml b/runtime/themes/monokai_pro_octagon.toml index 3236fc167..9be6bdc00 100644 --- a/runtime/themes/monokai_pro_octagon.toml +++ b/runtime/themes/monokai_pro_octagon.toml @@ -30,6 +30,11 @@ "ui.cursor.match" = { bg = "base4" } "ui.cursorline" = { bg = "base1" } +# bufferline, inlay hints +"ui.bufferline" = { fg = "base6", bg = "base8x0c" } +"ui.bufferline.active" = { fg = "base8", bg = "base4" } +"ui.virtual.inlay-hint" = { fg = "base6" } + # comments, nord3 based lighter color "comment" = { fg = "base5", modifiers = ["italic"] } "ui.linenr" = { fg = "base5" } diff --git a/runtime/themes/monokai_pro_ristretto.toml b/runtime/themes/monokai_pro_ristretto.toml index f897bddbd..d002d13fa 100644 --- a/runtime/themes/monokai_pro_ristretto.toml +++ b/runtime/themes/monokai_pro_ristretto.toml @@ -27,6 +27,11 @@ "ui.cursor.match" = { bg = "base4" } "ui.cursorline" = { bg = "base1" } +# bufferline, inlay hints +"ui.bufferline" = { fg = "base6", bg = "base8x0c" } +"ui.bufferline.active" = { fg = "base8", bg = "base4" } +"ui.virtual.inlay-hint" = { fg = "base6" } + # comments, nord3 based lighter color "comment" = { fg = "base5", modifiers = ["italic"] } "ui.linenr" = { fg = "base5" } diff --git a/runtime/themes/monokai_pro_spectrum.toml b/runtime/themes/monokai_pro_spectrum.toml index 74533404e..3cec2f1a3 100644 --- a/runtime/themes/monokai_pro_spectrum.toml +++ b/runtime/themes/monokai_pro_spectrum.toml @@ -27,6 +27,11 @@ "ui.cursor.match" = { bg = "base4" } "ui.cursorline" = { bg = "base1" } +# bufferline, inlay hints +"ui.bufferline" = { fg = "base6", bg = "base8x0c" } +"ui.bufferline.active" = { fg = "base8", bg = "base4" } +"ui.virtual.inlay-hint" = { fg = "base6" } + # comments, nord3 based lighter color "comment" = { fg = "base5", modifiers = ["italic"] } "ui.linenr" = { fg = "base5" } From a32d537d0ab61c29dca556d6ffe35431d9f4f1d1 Mon Sep 17 00:00:00 2001 From: Jaakko Paju Date: Mon, 8 Jan 2024 04:04:43 +0200 Subject: [PATCH 021/796] Add HOCON language support (#9203) * Add HOCON language support * Remove error query Co-authored-by: Michael Davis * Change include query * Fix query error --------- Co-authored-by: Michael Davis --- book/src/generated/lang-support.md | 1 + languages.toml | 12 +++++++++++ runtime/queries/hocon/highlights.scm | 31 ++++++++++++++++++++++++++++ runtime/queries/hocon/indents.scm | 10 +++++++++ 4 files changed, 54 insertions(+) create mode 100644 runtime/queries/hocon/highlights.scm create mode 100644 runtime/queries/hocon/indents.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 0c5b35cc6..ba68e206a 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -68,6 +68,7 @@ | haskell-persistent | ✓ | | | | | hcl | ✓ | | ✓ | `terraform-ls` | | heex | ✓ | ✓ | | `elixir-ls` | +| hocon | ✓ | | ✓ | | | hosts | ✓ | | | | | html | ✓ | | | `vscode-html-language-server` | | hurl | ✓ | | ✓ | | diff --git a/languages.toml b/languages.toml index a013dab8c..77ed0eb61 100644 --- a/languages.toml +++ b/languages.toml @@ -3013,3 +3013,15 @@ file-types = ["janet"] comment-token = "#" indent = { tab-width = 2, unit = " " } grammar = "clojure" + +[[language]] +name = "hocon" +scope = "source.conf" +file-types = ["conf"] +comment-token = "#" +auto-format = true +indent = { tab-width = 2, unit = " " } + +[[grammar]] +name = "hocon" +source = { git = "https://github.com/antosha417/tree-sitter-hocon", rev = "c390f10519ae69fdb03b3e5764f5592fb6924bcc" } diff --git a/runtime/queries/hocon/highlights.scm b/runtime/queries/hocon/highlights.scm new file mode 100644 index 000000000..d1aa38a27 --- /dev/null +++ b/runtime/queries/hocon/highlights.scm @@ -0,0 +1,31 @@ +(comment) @comment + +(null) @constant.builtin +[(true) (false)] @constant.builtin.boolean +(number) @constant.numeric +(string) @string +(multiline_string) @string +(string (escape_sequence) @constant.character.escape) +(unquoted_string) @string + +(value [":" "=" "+=" ] @operator) + +(substitution (_) @string) +(substitution ["${" "${?" "}"] @punctuation.special) + +[ + "url" + "file" + "classpath" + "required" +] @function.builtin + +(include) @keyword.directive + +[ "(" ")" "[" "]" "{" "}" ] @punctuation.bracket + +(unit) @keyword +(path (_) @keyword) +(unquoted_path "." @punctuation.delimiter) +[ "," ] @punctuation.delimiter + diff --git a/runtime/queries/hocon/indents.scm b/runtime/queries/hocon/indents.scm new file mode 100644 index 000000000..27c2c988f --- /dev/null +++ b/runtime/queries/hocon/indents.scm @@ -0,0 +1,10 @@ +[ + (object) + (array) +] @indent + +[ + "]" + "}" +] @outdent + From 73deba70443073c0171b292dba0e9f3ef2648754 Mon Sep 17 00:00:00 2001 From: Jaakko Paju Date: Mon, 8 Jan 2024 04:05:10 +0200 Subject: [PATCH 022/796] Add textobject queries for Scala (#9191) --- book/src/generated/lang-support.md | 2 +- runtime/queries/scala/textobjects.scm | 59 +++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 runtime/queries/scala/textobjects.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index ba68e206a..d068f91c0 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -145,7 +145,7 @@ | ruby | ✓ | ✓ | ✓ | `solargraph` | | rust | ✓ | ✓ | ✓ | `rust-analyzer` | | sage | ✓ | ✓ | | | -| scala | ✓ | | ✓ | `metals` | +| scala | ✓ | ✓ | ✓ | `metals` | | scheme | ✓ | | ✓ | | | scss | ✓ | | | `vscode-css-language-server` | | slint | ✓ | | ✓ | `slint-lsp` | diff --git a/runtime/queries/scala/textobjects.scm b/runtime/queries/scala/textobjects.scm new file mode 100644 index 000000000..fe0b8c255 --- /dev/null +++ b/runtime/queries/scala/textobjects.scm @@ -0,0 +1,59 @@ +; Function queries + +(function_definition + body: (_) @function.inside) @function.around + +; Does not match block lambdas or Scala 3 braceless lambdas +(lambda_expression + (_) @function.inside) @function.around + + +; Class queries + +(object_definition + body: (_)? @class.inside) @class.around + +(class_definition + body: (_)? @class.inside) @class.around + +(trait_definition + body: (_)? @class.inside) @class.around + +(type_definition) @class.around + +(enum_case_definitions) @class.around + +(enum_definition + body: (_)? @class.inside) @class.around + + +; Parameter queries + +(parameters + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + +(parameter_types + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + +(bindings + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + +; Does not match context bounds or higher-kinded types +(type_parameters + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + +(arguments + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + +(type_arguments + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + + +; Comment queries + +(comment) @comment.inside +(comment) @comment.around ; Does not match consecutive block comments + + +; Test queries +; Not supported From 154d9b6ed1e24aa846784a55c9d8bc1ba9506c7e Mon Sep 17 00:00:00 2001 From: Paul Graydon <148399603+UntimelyCreation@users.noreply.github.com> Date: Sun, 7 Jan 2024 18:08:20 -0800 Subject: [PATCH 023/796] Update tokyonight themes (#9099) --- runtime/themes/tokyonight.toml | 175 ++++++++++++++++----------- runtime/themes/tokyonight_day.toml | 41 +++++++ runtime/themes/tokyonight_moon.toml | 41 +++++++ runtime/themes/tokyonight_storm.toml | 12 +- 4 files changed, 195 insertions(+), 74 deletions(-) create mode 100644 runtime/themes/tokyonight_day.toml create mode 100644 runtime/themes/tokyonight_moon.toml diff --git a/runtime/themes/tokyonight.toml b/runtime/themes/tokyonight.toml index cc99689fc..95ebd4087 100644 --- a/runtime/themes/tokyonight.toml +++ b/runtime/themes/tokyonight.toml @@ -1,75 +1,96 @@ -# Author: Paul Graydon +# Author: Paul Graydon -"comment" = { fg = "comment", modifiers = ["italic"] } -"constant" = { fg = "orange" } +attribute = { fg = "cyan" } +comment = { fg = "comment", modifiers = ["italic"] } +"comment.block.documentation" = { fg = "yellow" } +constant = { fg = "orange" } +"constant.builtin" = { fg = "aqua" } +"constant.character" = { fg = "light-green" } "constant.character.escape" = { fg = "magenta" } -"function" = { fg = "blue", modifiers = ["italic"] } +constructor = { fg = "aqua" } +function = { fg = "blue", modifiers = ["italic"] } +"function.builtin" = { fg = "aqua" } "function.macro" = { fg = "cyan" } -"keyword" = { fg = "cyan", modifiers = ["italic"] } +"function.special" = { fg = "cyan" } +keyword = { fg = "purple", modifiers = ["italic"] } "keyword.control" = { fg = "magenta" } "keyword.control.import" = { fg = "cyan" } -"keyword.operator" = { fg = "turquoise" } -"keyword.function" = { fg = "magenta", modifiers = ["italic"] } -"operator" = { fg = "turquoise" } -"punctuation" = { fg = "turquoise" } -"string" = { fg = "light-green" } -"string.regexp" = { fg = "light-blue" } -"tag" = { fg = "red" } -"type" = { fg = "teal" } -"namespace" = { fg = "blue" } -"variable" = { fg = "white" } +"keyword.control.return" = { fg = "purple", modifiers = ["italic"] } +"keyword.directive" = { fg = "cyan" } +"keyword.function" = { fg = "magenta" } +"keyword.operator" = { fg = "magenta" } +label = { fg = "blue" } +namespace = { fg = "cyan" } +operator = { fg = "turquoise" } +punctuation = { fg = "turquoise" } +special = { fg = "aqua" } +string = { fg = "light-green" } +"string.regexp" = { fg = "light-cyan" } +"string.special" = { fg = "aqua" } +tag = { fg = "magenta" } +type = { fg = "aqua" } +"type.builtin" = { fg = "aqua" } +"type.enum.variant" = { fg = "orange" } +variable = { fg = "fg" } "variable.builtin" = { fg = "red" } "variable.other.member" = { fg = "green" } "variable.parameter" = { fg = "yellow", modifiers = ["italic"] } -"diff.plus" = { fg = "green" } -"diff.delta" = { fg = "orange" } -"diff.minus" = { fg = "red" } +"markup.bold" = { modifiers = ["bold"] } +"markup.heading" = { fg = "blue", modifiers = ["bold"] } +"markup.heading.completion" = { bg = "bg-menu", fg = "fg" } +"markup.heading.hover" = { bg = "fg-selected" } +"markup.italic" = { modifiers = ["italic"] } +"markup.link" = { fg = "blue", underline = { style = "line" } } +"markup.link.label" = { fg = "teal" } +"markup.link.text" = { fg = "teal" } +"markup.link.url" = { underline = { style = "line" } } +"markup.list" = { fg = "orange", modifiers = ["bold"] } +"markup.normal.completion" = { fg = "comment" } +"markup.normal.hover" = { fg = "fg-dark" } +"markup.raw" = { fg = "teal" } +"markup.raw.inline" = { bg = "black", fg = "blue" } +"markup.strikethrough" = { modifiers = ["crossed_out"] } -"ui.background" = { fg = "foreground", bg = "background" } -"ui.cursor" = { modifiers = ["reversed"] } -"ui.cursor.match" = { fg = "orange", modifiers = ["bold"] } -"ui.cursor.primary" = { modifiers = ["reversed"] } -"ui.cursorline.primary" = { bg = "background_menu" } -"ui.help" = { fg = "foreground", bg = "background_menu" } -"ui.linenr" = { fg = "foreground_gutter" } -"ui.linenr.selected" = { fg = "foreground" } -"ui.menu" = { fg = "foreground", bg = "background_menu" } -"ui.menu.selected" = { bg = "background_highlight" } -"ui.popup" = { fg = "foreground", bg = "background_menu" } -"ui.selection" = { bg = "background_highlight" } -"ui.selection.primary" = { bg = "background_highlight" } -"ui.statusline" = { fg = "foreground", bg = "background_menu" } -"ui.statusline.inactive" = { fg = "foreground_gutter", bg = "background_menu" } -"ui.statusline.normal" = { fg = "black", bg = "blue" } -"ui.statusline.insert" = { fg = "black", bg = "green" } -"ui.statusline.select" = { fg = "black", bg = "magenta" } -"ui.text" = { fg = "foreground" } -"ui.text.focus" = { fg = "cyan" } -"ui.virtual.ruler" = { bg = "foreground_gutter" } -"ui.virtual.whitespace" = { fg = "foreground_gutter" } -"ui.virtual.inlay-hint" = { fg = "comment" } -"ui.window" = { fg = "black" } +"diff.delta" = { fg = "change" } +"diff.delta.moved" = { fg = "blue" } +"diff.minus" = { fg = "delete" } +"diff.plus" = { fg = "add" } -"error" = { fg = "red" } -"warning" = { fg = "yellow" } -"info" = { fg = "blue" } -"hint" = { fg = "teal" } -"diagnostic.error" = { underline = { style = "curl", color = "red" } } -"diagnostic.warning" = { underline = { style = "curl", color = "yellow" } } -"diagnostic.info" = { underline = { style = "curl", color = "blue" } } -"diagnostic.hint" = { underline = { style = "curl", color = "teal" } } -"special" = { fg = "orange" } +error = { fg = "error" } +hint = { fg = "hint" } +info = { fg = "info" } +warning = { fg = "yellow" } +"diagnostic.error" = { underline = { style = "curl" } } +"diagnostic.warning" = { underline = { style = "curl" } } +"diagnostic.info" = { underline = { style = "curl" } } +"diagnostic.hint" = { underline = { style = "curl" } } -"markup.heading" = { fg = "cyan", modifiers = ["bold"] } -"markup.list" = { fg = "cyan" } -"markup.bold" = { fg = "orange", modifiers = ["bold"] } -"markup.italic" = { fg = "yellow", modifiers = ["italic"] } -"markup.strikethrough" = { modifiers = ["crossed_out"] } -"markup.link.url" = { fg = "green" } -"markup.link.text" = { fg = "light-gray" } -"markup.quote" = { fg = "yellow", modifiers = ["italic"] } -"markup.raw" = { fg = "cyan" } +"ui.background" = { bg = "bg", fg = "fg" } +"ui.cursor" = { modifiers = ["reversed"] } +"ui.cursor.match" = { fg = "orange", modifiers = ["bold"] } +"ui.cursorline.primary" = { bg = "bg-menu" } +"ui.help" = { bg = "bg-menu", fg = "fg" } +"ui.linenr" = { fg = "fg-gutter" } +"ui.linenr.selected" = { fg = "fg-linenr" } +"ui.menu" = { bg = "bg-menu", fg = "fg" } +"ui.menu.selected" = { bg = "fg-selected" } +"ui.popup" = { bg = "bg-menu", fg = "border-highlight" } +"ui.selection" = { bg = "bg-highlight" } +"ui.selection.primary" = { bg = "bg-highlight" } +"ui.statusline" = { bg = "bg-menu", fg = "fg-dark" } +"ui.statusline.inactive" = { bg = "bg-menu", fg = "fg-gutter" } +"ui.statusline.normal" = { bg = "blue", fg = "bg", modifiers = ["bold"] } +"ui.statusline.insert" = { bg = "light-green", fg = "bg", modifiers = ["bold"] } +"ui.statusline.select" = { bg = "magenta", fg = "bg", modifiers = ["bold"] } +"ui.text" = { bg = "bg", fg = "fg" } +"ui.text.focus" = { bg = "bg-visual" } +"ui.text.inactive" = { fg = "comment", modifiers = ["italic"] } +"ui.text.info" = { bg = "bg-menu", fg = "fg" } +"ui.virtual.ruler" = { bg = "fg-gutter" } +"ui.virtual.whitespace" = { fg = "fg-gutter" } +"ui.virtual.inlay-hint" = { bg = "bg-inlay", fg = "teal" } +"ui.window" = { fg = "border", modifiers = ["bold"] } [palette] red = "#f7768e" @@ -77,20 +98,34 @@ orange = "#ff9e64" yellow = "#e0af68" light-green = "#9ece6a" green = "#73daca" +aqua = "#2ac3de" +teal = "#1abc9c" turquoise = "#89ddff" light-cyan = "#b4f9f8" -teal = "#2ac3de" cyan = "#7dcfff" blue = "#7aa2f7" +purple = "#9d7cd8" magenta = "#bb9af7" -white = "#c0caf5" -light-gray = "#9aa5ce" -parameters = "#cfc9c2" comment = "#565f89" black = "#414868" -foreground = "#a9b1d6" -foreground_highlight = "#c0caf5" -foreground_gutter = "#363b54" -background = "#1a1b26" -background_highlight = "#30374b" -background_menu = "#16161e" + +add = "#449dab" +change = "#6183bb" +delete = "#914c54" + +error = "#db4b4b" +hint = "#1abc9c" +info = "#0db9d7" + +fg = "#c0caf5" +fg-dark = "#a9b1d6" +fg-gutter = "#3b4261" +fg-linenr = "#737aa2" +fg-selected = "#343a55" +border = "#15161e" +border-highlight = "#27a1b9" +bg = "#1a1b26" +bg-inlay = "#1a2b32" +bg-highlight = "#292e42" +bg-menu = "#16161e" +bg-visual = "#283457" diff --git a/runtime/themes/tokyonight_day.toml b/runtime/themes/tokyonight_day.toml new file mode 100644 index 000000000..54caf8d6f --- /dev/null +++ b/runtime/themes/tokyonight_day.toml @@ -0,0 +1,41 @@ +# Author: Paul Graydon + +inherits = "tokyonight" + +[palette] +red = "#f52a65" +orange = "#b15c00" +yellow = "#8c6c3e" +light-green = "#587539" +green = "#387068" +aqua = "#188092" +teal = "#118c74" +turquoise = "#006a83" +light-cyan = "#2e5857" +cyan = "#007197" +blue = "#2e7de9" +purple = "#7847bd" +magenta = "#9854f1" +comment = "#848cb5" +black = "#a1a6c5" + +add = "#aecde6" +change = "#d6d8e3" +delete = "#dfccd4" + +error = "#c64343" +hint = "#118c74" +info = "#07879d" + +fg = "#3760bf" +fg-dark = "#6172b0" +fg-gutter = "#a8aecb" +fg-linenr = "#68709a" +fg-selected = "#b3b8d1" +border = "#e9e9ed" +border-highlight = "#2496ac" +bg = "#e1e2e7" +bg-inlay = "#acd7eb" +bg-highlight = "#c4c8da" +bg-menu = "#e9e9ec" +bg-visual = "#b6bfe2" diff --git a/runtime/themes/tokyonight_moon.toml b/runtime/themes/tokyonight_moon.toml new file mode 100644 index 000000000..8468051e6 --- /dev/null +++ b/runtime/themes/tokyonight_moon.toml @@ -0,0 +1,41 @@ +# Author: Paul Graydon + +inherits = "tokyonight" + +[palette] +red = "#ff757f" +orange = "#ff966c" +yellow = "#ffc777" +light-green = "#c3e88d" +green = "#4fd6be" +aqua = "#65bcff" +teal = "#4fd6be" +turquoise = "#89ddff" +light-cyan = "#b4f9f8" +cyan = "#86e1fc" +blue = "#82aaff" +purple = "#fca7ea" +magenta = "#c099ff" +comment = "#636da6" +black = "#444a73" + +add = "#b8db87" +change = "#7ca1f2" +delete = "#e26a75" + +error = "#c53b53" +hint = "#4fd6be" +info = "#0db9d7" + +fg = "#c8d3f5" +fg-dark = "#828bb8" +fg-gutter = "#3b4261" +fg-linenr = "#737aa2" +fg-selected = "#363c58" +border = "#1b1d2b" +border-highlight = "#589ed7" +bg = "#222436" +bg-inlay = "#273644" +bg-highlight = "#2f334d" +bg-menu = "#1e2030" +bg-visual = "#2d3f76" diff --git a/runtime/themes/tokyonight_storm.toml b/runtime/themes/tokyonight_storm.toml index e82c43409..5ec4a16a0 100644 --- a/runtime/themes/tokyonight_storm.toml +++ b/runtime/themes/tokyonight_storm.toml @@ -1,8 +1,12 @@ -# Author: Paul Graydon +# Author: Paul Graydon inherits = "tokyonight" [palette] -background = "#24283b" -background_highlight = "#373d5a" -background_menu = "#1f2335" +border = "#1d202f" +bg = "#24283b" +bg-inlay = "#233745" +bg-highlight = "#373d5a" +bg-menu = "#1f2335" +bg-visual = "#2e3c64" +border-highlight = "#29a4bd" From c8e58304bf4b1b327067bd92ca6e3c9db897c572 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Sun, 7 Jan 2024 21:08:41 -0500 Subject: [PATCH 024/796] Add textobject queries for protobuf grammar. (#9184) Given `message Foo {string s = 1;}` - `mat` selects `message Foo {string s = 1}` - `mit` selects `{string s = 1;}` Given `service SearchService { rpc Search(Req) returns (Resp); } - `mit` or `mat` selects `Req` or `Resp` - `mif` or `maf` selects `rpc Search(Req) returns (Resp);` - `mit` selects { rpc Search(Req) returns (Resp); }` - `mat` selects `service SearchService { rpc Search(Req) returns (Resp); }` --- book/src/generated/lang-support.md | 2 +- runtime/queries/protobuf/textobjects.scm | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 runtime/queries/protobuf/textobjects.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index d068f91c0..ee01c4030 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -128,7 +128,7 @@ | ponylang | ✓ | ✓ | ✓ | | | prisma | ✓ | | | `prisma-language-server` | | prolog | | | | `swipl` | -| protobuf | ✓ | | ✓ | `bufls`, `pb` | +| protobuf | ✓ | ✓ | ✓ | `bufls`, `pb` | | prql | ✓ | | | | | purescript | ✓ | ✓ | | `purescript-language-server` | | python | ✓ | ✓ | ✓ | `pylsp` | diff --git a/runtime/queries/protobuf/textobjects.scm b/runtime/queries/protobuf/textobjects.scm new file mode 100644 index 000000000..6f06b1349 --- /dev/null +++ b/runtime/queries/protobuf/textobjects.scm @@ -0,0 +1,9 @@ +(message (messageBody) @class.inside) @class.around +(enum (enumBody) @class.inside) @class.around +(service (serviceBody) @class.inside) @class.around + +(rpc (enumMessageType) @parameter.inside) @function.inside +(rpc (enumMessageType) @parameter.around) @function.around + +(comment) @comment.inside +(comment)+ @comment.around From 00d681cc69fcc0d58f3603709400d26ea2647114 Mon Sep 17 00:00:00 2001 From: jw013 Date: Sun, 7 Jan 2024 21:11:18 -0500 Subject: [PATCH 025/796] Update goto_file docs (#8563) (#9001) Make the pluralization of files and selections consistent to emphasize the 1-to-1 relation between files and selections. The prior wording with plural "files" and singular "selection" can mislead users into thinking the command can open multiple files from a single selection. --- book/src/keymap.md | 6 +++--- helix-term/src/commands.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/book/src/keymap.md b/book/src/keymap.md index c6981b286..a3e41666f 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -205,7 +205,7 @@ Jumps to various locations. | ----- | ----------- | ------- | | `g` | Go to line number `` else start of file | `goto_file_start` | | `e` | Go to the end of the file | `goto_last_line` | -| `f` | Go to files in the selection | `goto_file` | +| `f` | Go to files in the selections | `goto_file` | | `h` | Go to the start of the line | `goto_line_start` | | `l` | Go to the end of the line | `goto_line_end` | | `s` | Go to first non-whitespace character of the line | `goto_first_nonwhitespace` | @@ -253,8 +253,8 @@ This layer is similar to Vim keybindings as Kakoune does not support windows. | `w`, `Ctrl-w` | Switch to next window | `rotate_view` | | `v`, `Ctrl-v` | Vertical right split | `vsplit` | | `s`, `Ctrl-s` | Horizontal bottom split | `hsplit` | -| `f` | Go to files in the selection in horizontal splits | `goto_file` | -| `F` | Go to files in the selection in vertical splits | `goto_file` | +| `f` | Go to files in the selections in horizontal splits | `goto_file` | +| `F` | Go to files in the selections in vertical splits | `goto_file` | | `h`, `Ctrl-h`, `Left` | Move to left split | `jump_view_left` | | `j`, `Ctrl-j`, `Down` | Move to split below | `jump_view_down` | | `k`, `Ctrl-k`, `Up` | Move to split above | `jump_view_up` | diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 1b8f9e1f5..ff0849f20 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -337,9 +337,9 @@ impl MappableCommand { goto_implementation, "Goto implementation", goto_file_start, "Goto line number else file start", goto_file_end, "Goto file end", - goto_file, "Goto files/URLs in selection", - goto_file_hsplit, "Goto files in selection (hsplit)", - goto_file_vsplit, "Goto files in selection (vsplit)", + goto_file, "Goto files/URLs in selections", + goto_file_hsplit, "Goto files in selections (hsplit)", + goto_file_vsplit, "Goto files in selections (vsplit)", goto_reference, "Goto references", goto_window_top, "Goto window top", goto_window_center, "Goto window center", From e46fb585954a131a8f96cfd71cf2705fe0ff1e02 Mon Sep 17 00:00:00 2001 From: NitinKM <70827815+NewtonChutney@users.noreply.github.com> Date: Mon, 8 Jan 2024 08:16:53 +0530 Subject: [PATCH 026/796] Info on how to skip grammar build when building from source (#8698) * info: no grammar compile Added instructions on how to compile without compiling grammars * Update book/src/install.md Co-authored-by: Michael Davis --------- Co-authored-by: Michael Davis --- book/src/install.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/book/src/install.md b/book/src/install.md index 897985013..2a4273b86 100644 --- a/book/src/install.md +++ b/book/src/install.md @@ -204,6 +204,8 @@ RUSTFLAGS="-C target-feature=-crt-static" This command will create the `hx` executable and construct the tree-sitter grammars in the local `runtime` folder. +> 💡 If you do not want to fetch or build grammars, set an environment variable `HELIX_DISABLE_AUTO_GRAMMAR_BUILD` + > 💡 Tree-sitter grammars can be fetched and compiled if not pre-packaged. Fetch > grammars with `hx --grammar fetch` and compile them with > `hx --grammar build`. This will install them in From 918bd9c2b06934b5b0b1666f2228e219809130ac Mon Sep 17 00:00:00 2001 From: Greedwolf DSS Date: Mon, 8 Jan 2024 10:54:16 +0800 Subject: [PATCH 027/796] feat: update wren tree-sitter grammar (#8544) Co-authored-by: masai.dss --- languages.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index 77ed0eb61..81f7974d9 100644 --- a/languages.toml +++ b/languages.toml @@ -2886,7 +2886,7 @@ source = { git = "https://github.com/varpeti/tree-sitter-jinja2", rev = "a533cd3 [[grammar]] name = "wren" -source = { git = "https://git.sr.ht/~jummit/tree-sitter-wren", rev = "793d58266924e6efcc40e411663393e9d72bec87"} +source = { git = "https://git.sr.ht/~jummit/tree-sitter-wren", rev = "6748694be32f11e7ec6b5faeb1b48ca6156d4e06" } [[language]] name = "wren" From 77ab792ac72698189120b2c62e26d2a26807a9ca Mon Sep 17 00:00:00 2001 From: Tomas Date: Mon, 8 Jan 2024 03:57:04 +0100 Subject: [PATCH 028/796] runtime/themes: adding "ttox" theme (#8524) * runtime/themes: adding 'ttox' theme * Improving primary selections --- runtime/themes/ttox.toml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 runtime/themes/ttox.toml diff --git a/runtime/themes/ttox.toml b/runtime/themes/ttox.toml new file mode 100644 index 000000000..16909cc70 --- /dev/null +++ b/runtime/themes/ttox.toml @@ -0,0 +1,31 @@ +# Author : Tomas Ruud + +"ui.selection" = { fg = "white", bg = "gray" } +"ui.cursor" = { fg = "black", bg = "light-gray" } +"ui.cursor.primary" = { fg = "black", bg = "light-gray" } +"ui.cursor.match" = { modifiers = ["underlined"] } +"ui.background.separator" = "gray" +"ui.linenr" = "gray" +"ui.linenr.selected" = { fg = "white", bg = "gray" } +"ui.statusline" = { bg = "black", fg = "white" } +"ui.menu" = { fg = "white", bg = "black" } +"ui.menu.selected" = { bg = "light-gray", fg = "black" } +"ui.popup" = { fg = "white", bg = "black" } +"ui.help" = { fg = "white", bg = "black" } +"ui.virtual.ruler" = { underline = { style = "line"} } + +"string" = { bg = "light-green", fg = "black" } +"constant" = { bg = "light-cyan", fg = "black" } +"comment" = { bg = "light-magenta", fg = "black" } + +"diff.plus" = "green" +"diff.minus" = "red" +"diff.delta" = "gray" + +"warning" = { fg = "black", bg = "light-yellow" } +"error" = { fg = "black", bg = "light-red" } +"hint" = { fg = "black", bg = "light-blue" } + +"diagnostic.warning" = { fg = "black", bg = "light-yellow" } +"diagnostic.error" = { fg = "black", bg = "light-red" } +"diagnostic.hint" = { fg = "black", bg = "light-blue" } From e2e8d2739af5abdcc1f4af8745511e4c5ddbdc27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 09:51:25 +0900 Subject: [PATCH 029/796] build(deps): bump serde from 1.0.193 to 1.0.195 (#9285) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9c612a7e8..fdbd7e4c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -330,7 +330,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.38", + "syn 2.0.48", ] [[package]] @@ -347,7 +347,7 @@ checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.48", ] [[package]] @@ -750,7 +750,7 @@ checksum = "d75e7ab728059f595f6ddc1ad8771b8d6a231971ae493d9d5948ecad366ee8bb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.48", ] [[package]] @@ -1615,9 +1615,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] @@ -1650,9 +1650,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.29" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1802,22 +1802,22 @@ checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.48", ] [[package]] @@ -1839,7 +1839,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.48", ] [[package]] @@ -1975,9 +1975,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.38" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -2043,7 +2043,7 @@ checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.48", ] [[package]] @@ -2126,7 +2126,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.48", ] [[package]] @@ -2647,5 +2647,5 @@ checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.48", ] From f4212421da66f30137fe2652fb1cfb3daf05f7ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 09:51:38 +0900 Subject: [PATCH 030/796] build(deps): bump ahash from 0.8.6 to 0.8.7 (#9281) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- helix-core/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fdbd7e4c9..91407ca32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ "cfg-if", "getrandom", diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index d7fff6c6f..ff0bbcc3e 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -31,7 +31,7 @@ once_cell = "1.19" arc-swap = "1" regex = "1" bitflags = "2.4" -ahash = "0.8.6" +ahash = "0.8.7" hashbrown = { version = "0.14.3", features = ["raw"] } dunce = "1.0" From bad10a5ddd5d34bf8acaf5d49a81adadd1c9ac21 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 09:51:58 +0900 Subject: [PATCH 031/796] build(deps): bump libc from 0.2.151 to 0.2.152 (#9282) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- helix-term/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 91407ca32..b1fafb618 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1387,9 +1387,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.151" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "libloading" diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 32049a438..0859dbd74 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -73,7 +73,7 @@ grep-searcher = "0.1.13" [target.'cfg(not(windows))'.dependencies] # https://github.com/vorner/signal-hook/issues/100 signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] } -libc = "0.2.151" +libc = "0.2.152" [target.'cfg(target_os = "macos")'.dependencies] crossterm = { version = "0.27", features = ["event-stream", "use-dev-tty"] } From 97145eaae6ad0347c8d13aeb19c1b6d3c7fe4fc6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 09:52:25 +0900 Subject: [PATCH 032/796] build(deps): bump ignore from 0.4.21 to 0.4.22 (#9283) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1fafb618..cb3cbd2d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1311,9 +1311,9 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747ad1b4ae841a78e8aba0d63adbfbeaea26b517b63705d47856b73015d27060" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" dependencies = [ "crossbeam-deque", "globset", From 20b91fd99a91621d6153cd00f5527b7571290df4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 09:52:36 +0900 Subject: [PATCH 033/796] build(deps): bump serde_json from 1.0.109 to 1.0.111 (#9284) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cb3cbd2d1..9f0e8dcdb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1822,9 +1822,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.109" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" dependencies = [ "itoa", "ryu", From 4da6191a1c821eaff580392b41a2eb7245a475b7 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Tue, 9 Jan 2024 01:54:55 +0100 Subject: [PATCH 034/796] don't automatically dismiss zero width diagnostics (#9280) --- helix-view/src/document.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index af950a3fc..51668ab16 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -1222,18 +1222,23 @@ impl Document { }; (&mut diagnostic.range.start, assoc) })); - changes.update_positions(self.diagnostics.iter_mut().map(|diagnostic| { + changes.update_positions(self.diagnostics.iter_mut().filter_map(|diagnostic| { + if diagnostic.zero_width { + // for zero width diagnostics treat the diagnostic as a point + // rather than a range + return None; + } let assoc = if diagnostic.ends_at_word { Assoc::AfterWord } else { Assoc::Before }; - (&mut diagnostic.range.end, assoc) + Some((&mut diagnostic.range.end, assoc)) })); self.diagnostics.retain_mut(|diagnostic| { - if diagnostic.range.start > diagnostic.range.end - || (!diagnostic.zero_width && diagnostic.range.start == diagnostic.range.end) - { + if diagnostic.zero_width { + diagnostic.range.end = diagnostic.range.start + } else if diagnostic.range.start >= diagnostic.range.end { return false; } diagnostic.line = self.text.char_to_line(diagnostic.range.start); From 0cbd8d3df1ec6faa088f4439e52865f8daf78dc3 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Mon, 8 Jan 2024 19:55:11 -0500 Subject: [PATCH 035/796] Check for rename support before showing LSP rename prompt (#9277) --- helix-term/src/commands/lsp.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index ac6a1a213..34ffa529c 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -1421,6 +1421,16 @@ pub fn rename_symbol(cx: &mut Context) { let (view, doc) = current_ref!(cx.editor); + if doc + .language_servers_with_feature(LanguageServerFeature::RenameSymbol) + .next() + .is_none() + { + cx.editor + .set_error("No configured language server supports symbol renaming"); + return; + } + let language_server_with_prepare_rename_support = doc .language_servers_with_feature(LanguageServerFeature::RenameSymbol) .find(|ls| { From 48c49f02278712f20235f3ba3912fba9e55b8ebb Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Tue, 9 Jan 2024 01:56:09 +0100 Subject: [PATCH 036/796] update history of a newly focused view (#9271) --- helix-term/src/ui/editor.rs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index c808be175..ff267e42d 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -1302,8 +1302,6 @@ impl Component for EditorView { cx.editor.status_msg = None; let mode = cx.editor.mode(); - let (view, _) = current!(cx.editor); - let focus = view.id; if let Some(on_next_key) = self.on_next_key.take() { // if there's a command waiting input, do that first @@ -1385,20 +1383,16 @@ impl Component for EditorView { return EventResult::Ignored(None); } - // if the focused view still exists and wasn't closed - if cx.editor.tree.contains(focus) { - let config = cx.editor.config(); - let mode = cx.editor.mode(); - let view = view_mut!(cx.editor, focus); - let doc = doc_mut!(cx.editor, &view.doc); + let config = cx.editor.config(); + let mode = cx.editor.mode(); + let (view, doc) = current!(cx.editor); - view.ensure_cursor_in_view(doc, config.scrolloff); + view.ensure_cursor_in_view(doc, config.scrolloff); - // Store a history state if not in insert mode. This also takes care of - // committing changes when leaving insert mode. - if mode != Mode::Insert { - doc.append_changes_to_history(view); - } + // Store a history state if not in insert mode. This also takes care of + // committing changes when leaving insert mode. + if mode != Mode::Insert { + doc.append_changes_to_history(view); } EventResult::Consumed(callback) From 7af78c7788eb0ab711df812fcdba44fc8742d7ca Mon Sep 17 00:00:00 2001 From: Kirawi <67773714+kirawi@users.noreply.github.com> Date: Mon, 8 Jan 2024 19:56:51 -0500 Subject: [PATCH 037/796] update comment grammar (#9253) --- languages.toml | 2 +- runtime/queries/comment/highlights.scm | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index 81f7974d9..2a0efb494 100644 --- a/languages.toml +++ b/languages.toml @@ -1282,7 +1282,7 @@ injection-regex = "comment" [[grammar]] name = "comment" -source = { git = "https://github.com/stsewd/tree-sitter-comment", rev = "a37ca370310ac6f89b6e0ebf2b86b2219780494e" } +source = { git = "https://github.com/stsewd/tree-sitter-comment", rev = "aefcc2813392eb6ffe509aa0fc8b4e9b57413ee1" } [[language]] name = "wgsl" diff --git a/runtime/queries/comment/highlights.scm b/runtime/queries/comment/highlights.scm index 9583f9c53..4cefcdf74 100644 --- a/runtime/queries/comment/highlights.scm +++ b/runtime/queries/comment/highlights.scm @@ -44,3 +44,5 @@ ; User mention (@user) ("text" @tag (#match? @tag "^[@][a-zA-Z0-9_-]+$")) + +(uri) @markup.link.url From 46ecc102ba6d6c2e6e707b551e24faa240752c87 Mon Sep 17 00:00:00 2001 From: rojebd <132220348+rojebd@users.noreply.github.com> Date: Mon, 8 Jan 2024 17:57:14 -0700 Subject: [PATCH 038/796] added voxed theme (#9164) --- runtime/themes/voxed.toml | 102 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 runtime/themes/voxed.toml diff --git a/runtime/themes/voxed.toml b/runtime/themes/voxed.toml new file mode 100644 index 000000000..e55b46e5d --- /dev/null +++ b/runtime/themes/voxed.toml @@ -0,0 +1,102 @@ +attribute = "buff" +keyword = "sglow" +"keyword.directive" = "defineish" +namespace = "blue" +punctuation = "white" +"punctuation.delimiter" = "functionish" +operator = "greenish" +special = "maize" +"variable.other.member" = "bsienna" +variable = "tan" +"variable.parameter" = { fg = "parameters" } +"variable.builtin" = "white" +type = "light-blue" +"type.builtin" = "functionish" +constructor = "typeish" +function = "functionish" +"function.macro" = "blue" +"function.builtin" = "typeish" +tag = "functionish" +comment = "bgrey" +constant = "tan" +"constant.builtin" = "#D38588" +string = "redish" +"constant.numeric" = "functionish" +"constant.character.escape" = "cyan" +label = "yellow" + +"markup.heading" = "functionish" +"markup.list" = "status-two" +"markup.quote" = "tan" +"markup.bold" = { fg = "sglow", modifiers = ["bold"] } +"markup.italic" = { fg = "sglow", modifiers = ["italic"] } +"markup.strikethrough" = { modifiers = ["crossed_out"] } +"markup.link.url" = { fg = "sglow", modifiers = ["underlined"] } +"markup.link.text" = "greenish" +"markup.raw" = "light-grey" + +"diff.plus" = "#7DDF64" +"diff.minus" = "#F22B29" +"diff.delta" = "#6f44f0" + +"ui.background" = { fg = "#25262B", bg="#1f1f21" } +"ui.background.separator" = { fg = "sglow" } +"ui.linenr" = { fg = "light-grey", modifiers = ["italic"] } +"ui.linenr.selected" = { fg = "bpink", modifiers = ["bold"] } +"ui.statusline" = { fg = "black", bg = "light-grey", modifiers = ["bold"] } +"ui.statusline.inactive" = { fg = "black", bg = "bgrey-two" } +"ui.popup" = { fg = "bgrey", bg = "#25262B" } +"ui.window" = { fg = "white" } +"ui.help" = { bg = "#3f4047", fg = "light-grey" } + +"ui.text" = { fg = "white" } +"ui.text.focus" = { fg = "maize", bg = "bgrey" } +"ui.text.inactive" = "bgrey" +"ui.virtual" = { fg = "blue" } +"ui.virtual.ruler" = { bg = "bgrey-two" } +"ui.virtual.indent-guide" = { fg = "bpink" } + +"ui.selection" = { bg = "maize" } +"ui.selection.primary" = { fg = "white", bg = "bgrey" } +"ui.cursor.select" = { bg = "white" } +"ui.cursor.insert" = { bg = "white" } +"ui.cursor.match" = { fg = "#212121", bg = "#6C6999" } +"ui.cursor" = { bg = "bgrey-two", modifiers = ["reversed"] } +"ui.cursorline.primary" = { bg = "white" } +"ui.highlight" = { bg = "white" } +"ui.highlight.frameline" = { bg = "#634450" } +"ui.debug" = { fg = "#634450" } +"ui.debug.breakpoint" = { fg = "bpink" } +"ui.menu" = { fg = "white", bg = "#23232d" } +"ui.menu.selected" = { fg = "white", bg = "bgrey" } +"ui.menu.scroll" = { fg = "white", bg = "white" } + +"diagnostic.hint" = { underline = { color = "maize", style = "curl" } } +"diagnostic.info" = { underline = { color = "sglow", style = "curl" } } +"diagnostic.warning" = { underline = { color = "redish", style = "curl" } } +"diagnostic.error" = { underline = { color = "bpink", style = "curl" } } + +warning = "bpink" +error = "bsienna" +info = "maize" +hint = "tan" + +[palette] +parameters = "#d89182" +defineish = "#71c45c" +buff = "#f0dc82" +tan = "#DAB785" +typeish = "#AAAAA5" +greenish = "#458588" +functionish = "#b784a3" +bsienna = "#D5896F" +bpink = "#FF5964" +maize = "#FFE74C" +bgrey = "#8c8681" +sglow = "#FFCF56" +status = "#15616D" +status-two = "#3879A1" +redish = "#E76B74" +light-grey = "#b7afa8" +bgrey-two = "#706b68" +gruvgreen = "#B8BB26" From 41ca46cf8ca65dbd98df7e038fc12a272f0c9e2a Mon Sep 17 00:00:00 2001 From: Philipp Mildenberger Date: Tue, 9 Jan 2024 02:01:04 +0100 Subject: [PATCH 039/796] Initialize diagnostics when opening a document (#8873) --- helix-core/src/syntax.rs | 1 - helix-term/src/application.rs | 160 ++++++++----------------------- helix-term/src/commands.rs | 26 +++-- helix-term/src/commands/typed.rs | 6 +- helix-term/src/ui/editor.rs | 4 +- helix-term/src/ui/picker.rs | 13 ++- helix-term/src/ui/statusline.rs | 3 +- helix-view/src/document.rs | 116 ++++++++++++++++++---- helix-view/src/editor.rs | 60 +++++++++++- 9 files changed, 231 insertions(+), 158 deletions(-) diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 8d433260e..102ecb15d 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -1795,7 +1795,6 @@ impl HighlightConfiguration { let mut best_index = None; let mut best_match_len = 0; for (i, recognized_name) in recognized_names.iter().enumerate() { - let recognized_name = recognized_name; let mut len = 0; let mut matches = true; for (i, part) in recognized_name.split('.').enumerate() { diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 3abe9cae5..4eda8097c 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -1,14 +1,8 @@ use arc_swap::{access::Map, ArcSwap}; use futures_util::Stream; -use helix_core::{ - chars::char_is_word, - diagnostic::{DiagnosticTag, NumberOrString}, - path::get_relative_path, - pos_at_coords, syntax, Selection, -}; +use helix_core::{path::get_relative_path, pos_at_coords, syntax, Selection}; use helix_lsp::{ lsp::{self, notification::Notification}, - util::lsp_pos_to_pos, LspProgressMap, }; use helix_view::{ @@ -392,6 +386,12 @@ impl Application { self.editor.syn_loader = self.syn_loader.clone(); for document in self.editor.documents.values_mut() { document.detect_language(self.syn_loader.clone()); + let diagnostics = Editor::doc_diagnostics( + &self.editor.language_servers, + &self.editor.diagnostics, + document, + ); + document.replace_diagnostics(diagnostics, &[], None); } Ok(()) @@ -567,6 +567,14 @@ impl Application { let id = doc.id(); doc.detect_language(loader); self.editor.refresh_language_servers(id); + // and again a borrow checker workaround... + let doc = doc_mut!(self.editor, &doc_save_event.doc_id); + let diagnostics = Editor::doc_diagnostics( + &self.editor.language_servers, + &self.editor.diagnostics, + doc, + ); + doc.replace_diagnostics(diagnostics, &[], None); } // TODO: fix being overwritten by lsp @@ -731,7 +739,6 @@ impl Application { log::error!("Discarding publishDiagnostic notification sent by an uninitialized server: {}", language_server.name()); return; } - let offset_encoding = language_server.offset_encoding(); // have to inline the function because of borrow checking... let doc = self.editor.documents.values_mut() .find(|doc| doc.path().map(|p| p == &path).unwrap_or(false)) @@ -745,11 +752,10 @@ impl Application { true }); - if let Some(doc) = doc { + let mut unchanged_diag_sources = Vec::new(); + if let Some(doc) = &doc { let lang_conf = doc.language.clone(); - let text = doc.text().clone(); - let mut unchaged_diag_sources_ = Vec::new(); if let Some(lang_conf) = &lang_conf { if let Some(old_diagnostics) = self.editor.diagnostics.get(¶ms.uri) @@ -774,118 +780,11 @@ impl Application { }) .map(|(d, _)| d); if new_diagnostics.eq(old_diagnostics) { - unchaged_diag_sources_.push(source.clone()) + unchanged_diag_sources.push(source.clone()) } } } } - - let unchaged_diag_sources = &unchaged_diag_sources_; - let diagnostics = - params.diagnostics.iter().filter_map(move |diagnostic| { - use helix_core::diagnostic::{Diagnostic, Range, Severity::*}; - use lsp::DiagnosticSeverity; - - if diagnostic.source.as_ref().map_or(false, |source| { - unchaged_diag_sources.contains(source) - }) { - return None; - } - - // TODO: convert inside server - let start = if let Some(start) = lsp_pos_to_pos( - &text, - diagnostic.range.start, - offset_encoding, - ) { - start - } else { - log::warn!("lsp position out of bounds - {:?}", diagnostic); - return None; - }; - - let end = if let Some(end) = - lsp_pos_to_pos(&text, diagnostic.range.end, offset_encoding) - { - end - } else { - log::warn!("lsp position out of bounds - {:?}", diagnostic); - return None; - }; - let severity = - diagnostic.severity.map(|severity| match severity { - DiagnosticSeverity::ERROR => Error, - DiagnosticSeverity::WARNING => Warning, - DiagnosticSeverity::INFORMATION => Info, - DiagnosticSeverity::HINT => Hint, - severity => unreachable!( - "unrecognized diagnostic severity: {:?}", - severity - ), - }); - - if let Some(lang_conf) = &lang_conf { - if let Some(severity) = severity { - if severity < lang_conf.diagnostic_severity { - return None; - } - } - }; - - let code = match diagnostic.code.clone() { - Some(x) => match x { - lsp::NumberOrString::Number(x) => { - Some(NumberOrString::Number(x)) - } - lsp::NumberOrString::String(x) => { - Some(NumberOrString::String(x)) - } - }, - None => None, - }; - - let tags = if let Some(tags) = &diagnostic.tags { - let new_tags = tags - .iter() - .filter_map(|tag| match *tag { - lsp::DiagnosticTag::DEPRECATED => { - Some(DiagnosticTag::Deprecated) - } - lsp::DiagnosticTag::UNNECESSARY => { - Some(DiagnosticTag::Unnecessary) - } - _ => None, - }) - .collect(); - - new_tags - } else { - Vec::new() - }; - - let ends_at_word = start != end - && end != 0 - && text.get_char(end - 1).map_or(false, char_is_word); - let starts_at_word = start != end - && text.get_char(start).map_or(false, char_is_word); - - Some(Diagnostic { - range: Range { start, end }, - ends_at_word, - starts_at_word, - zero_width: start == end, - line: diagnostic.range.start.line as usize, - message: diagnostic.message.clone(), - severity, - code, - tags, - source: diagnostic.source.clone(), - data: diagnostic.data.clone(), - language_server_id: server_id, - }) - }); - - doc.replace_diagnostics(diagnostics, unchaged_diag_sources, server_id); } let diagnostics = params.diagnostics.into_iter().map(|d| (d, server_id)); @@ -910,6 +809,27 @@ impl Application { diagnostics.sort_unstable_by_key(|(d, server_id)| { (d.severity, d.range.start, *server_id) }); + + if let Some(doc) = doc { + let diagnostic_of_language_server_and_not_in_unchanged_sources = + |diagnostic: &lsp::Diagnostic, ls_id| { + ls_id == server_id + && diagnostic.source.as_ref().map_or(true, |source| { + !unchanged_diag_sources.contains(source) + }) + }; + let diagnostics = Editor::doc_diagnostics_with_filter( + &self.editor.language_servers, + &self.editor.diagnostics, + doc, + diagnostic_of_language_server_and_not_in_unchanged_sources, + ); + doc.replace_diagnostics( + diagnostics, + &unchanged_diag_sources, + Some(server_id), + ); + } } Notification::ShowMessage(params) => { log::warn!("unhandled window/showMessage: {:?}", params); @@ -1017,7 +937,7 @@ impl Application { // Clear any diagnostics for documents with this server open. for doc in self.editor.documents_mut() { - doc.clear_diagnostics(server_id); + doc.clear_diagnostics(Some(server_id)); } // Remove the language server from the registry. diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index ff0849f20..c95933804 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -3350,7 +3350,7 @@ fn exit_select_mode(cx: &mut Context) { fn goto_first_diag(cx: &mut Context) { let (view, doc) = current!(cx.editor); - let selection = match doc.shown_diagnostics().next() { + let selection = match doc.diagnostics().first() { Some(diag) => Selection::single(diag.range.start, diag.range.end), None => return, }; @@ -3359,7 +3359,7 @@ fn goto_first_diag(cx: &mut Context) { fn goto_last_diag(cx: &mut Context) { let (view, doc) = current!(cx.editor); - let selection = match doc.shown_diagnostics().last() { + let selection = match doc.diagnostics().last() { Some(diag) => Selection::single(diag.range.start, diag.range.end), None => return, }; @@ -3375,9 +3375,10 @@ fn goto_next_diag(cx: &mut Context) { .cursor(doc.text().slice(..)); let diag = doc - .shown_diagnostics() + .diagnostics() + .iter() .find(|diag| diag.range.start > cursor_pos) - .or_else(|| doc.shown_diagnostics().next()); + .or_else(|| doc.diagnostics().first()); let selection = match diag { Some(diag) => Selection::single(diag.range.start, diag.range.end), @@ -3395,10 +3396,11 @@ fn goto_prev_diag(cx: &mut Context) { .cursor(doc.text().slice(..)); let diag = doc - .shown_diagnostics() + .diagnostics() + .iter() .rev() .find(|diag| diag.range.start < cursor_pos) - .or_else(|| doc.shown_diagnostics().last()); + .or_else(|| doc.diagnostics().last()); let selection = match diag { // NOTE: the selection is reversed because we're jumping to the @@ -4185,9 +4187,13 @@ fn replace_with_yanked(cx: &mut Context) { } fn replace_with_yanked_impl(editor: &mut Editor, register: char, count: usize) { - let Some(values) = editor.registers + let Some(values) = editor + .registers .read(register, editor) - .filter(|values| values.len() > 0) else { return }; + .filter(|values| values.len() > 0) + else { + return; + }; let values: Vec<_> = values.map(|value| value.to_string()).collect(); let (view, doc) = current!(editor); @@ -4224,7 +4230,9 @@ fn replace_selections_with_primary_clipboard(cx: &mut Context) { } fn paste(editor: &mut Editor, register: char, pos: Paste, count: usize) { - let Some(values) = editor.registers.read(register, editor) else { return }; + let Some(values) = editor.registers.read(register, editor) else { + return; + }; let values: Vec<_> = values.map(|value| value.to_string()).collect(); let (view, doc) = current!(editor); diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index f530ce10d..208854e8a 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -1502,7 +1502,7 @@ fn lsp_stop( for doc in cx.editor.documents_mut() { if let Some(client) = doc.remove_language_server_by_name(ls_name) { - doc.clear_diagnostics(client.id()); + doc.clear_diagnostics(Some(client.id())); } } } @@ -2008,6 +2008,10 @@ fn language( let id = doc.id(); cx.editor.refresh_language_servers(id); + let doc = doc_mut!(cx.editor); + let diagnostics = + Editor::doc_diagnostics(&cx.editor.language_servers, &cx.editor.diagnostics, doc); + doc.replace_diagnostics(diagnostics, &[], None); Ok(()) } diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index ff267e42d..24fcdb014 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -386,7 +386,7 @@ impl EditorView { let mut warning_vec = Vec::new(); let mut error_vec = Vec::new(); - for diagnostic in doc.shown_diagnostics() { + for diagnostic in doc.diagnostics() { // Separate diagnostics into different Vecs by severity. let (vec, scope) = match diagnostic.severity { Some(Severity::Info) => (&mut info_vec, info), @@ -684,7 +684,7 @@ impl EditorView { .primary() .cursor(doc.text().slice(..)); - let diagnostics = doc.shown_diagnostics().filter(|diagnostic| { + let diagnostics = doc.diagnostics().iter().filter(|diagnostic| { diagnostic.range.start <= cursor && diagnostic.range.end >= cursor }); diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 9ba453357..08a367ba9 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -480,8 +480,7 @@ impl Picker { .find::>>() .map(|overlay| &mut overlay.content.file_picker), }; - let Some(picker) = picker - else { + let Some(picker) = picker else { log::info!("picker closed before syntax highlighting finished"); return; }; @@ -489,7 +488,15 @@ impl Picker { let doc = match current_file { PathOrId::Id(doc_id) => doc_mut!(editor, &doc_id), PathOrId::Path(path) => match picker.preview_cache.get_mut(&path) { - Some(CachedPreview::Document(ref mut doc)) => doc, + Some(CachedPreview::Document(ref mut doc)) => { + let diagnostics = Editor::doc_diagnostics( + &editor.language_servers, + &editor.diagnostics, + doc, + ); + doc.replace_diagnostics(diagnostics, &[], None); + doc + } _ => return, }, }; diff --git a/helix-term/src/ui/statusline.rs b/helix-term/src/ui/statusline.rs index 52dd49f9e..9871828ee 100644 --- a/helix-term/src/ui/statusline.rs +++ b/helix-term/src/ui/statusline.rs @@ -227,7 +227,8 @@ where { let (warnings, errors) = context .doc - .shown_diagnostics() + .diagnostics() + .iter() .fold((0, 0), |mut counts, diag| { use helix_core::diagnostic::Severity; match diag.severity { diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 51668ab16..0de0cd172 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -4,10 +4,12 @@ use arc_swap::ArcSwap; use futures_util::future::BoxFuture; use futures_util::FutureExt; use helix_core::auto_pairs::AutoPairs; +use helix_core::chars::char_is_word; use helix_core::doc_formatter::TextFormat; use helix_core::encoding::Encoding; use helix_core::syntax::{Highlight, LanguageServerFeature}; use helix_core::text_annotations::{InlineAnnotation, TextAnnotations}; +use helix_lsp::util::lsp_pos_to_pos; use helix_vcs::{DiffHandle, DiffProviderRegistry}; use ::parking_lot::Mutex; @@ -1075,14 +1077,6 @@ impl Document { }; } - /// Set the programming language for the file if you know the name (scope) but don't have the - /// [`syntax::LanguageConfiguration`] for it. - pub fn set_language2(&mut self, scope: &str, config_loader: Arc) { - let language_config = config_loader.language_config_for_scope(scope); - - self.set_language(language_config, Some(config_loader)); - } - /// Set the programming language for the file if you know the language but don't have the /// [`syntax::LanguageConfiguration`] for it. pub fn set_language_by_language_id( @@ -1714,29 +1708,107 @@ impl Document { ) } + pub fn lsp_diagnostic_to_diagnostic( + text: &Rope, + language_config: Option<&LanguageConfiguration>, + diagnostic: &helix_lsp::lsp::Diagnostic, + language_server_id: usize, + offset_encoding: helix_lsp::OffsetEncoding, + ) -> Option { + use helix_core::diagnostic::{Range, Severity::*}; + + // TODO: convert inside server + let start = + if let Some(start) = lsp_pos_to_pos(text, diagnostic.range.start, offset_encoding) { + start + } else { + log::warn!("lsp position out of bounds - {:?}", diagnostic); + return None; + }; + + let end = if let Some(end) = lsp_pos_to_pos(text, diagnostic.range.end, offset_encoding) { + end + } else { + log::warn!("lsp position out of bounds - {:?}", diagnostic); + return None; + }; + + let severity = diagnostic.severity.map(|severity| match severity { + lsp::DiagnosticSeverity::ERROR => Error, + lsp::DiagnosticSeverity::WARNING => Warning, + lsp::DiagnosticSeverity::INFORMATION => Info, + lsp::DiagnosticSeverity::HINT => Hint, + severity => unreachable!("unrecognized diagnostic severity: {:?}", severity), + }); + + if let Some(lang_conf) = language_config { + if let Some(severity) = severity { + if severity < lang_conf.diagnostic_severity { + return None; + } + } + }; + use helix_core::diagnostic::{DiagnosticTag, NumberOrString}; + + let code = match diagnostic.code.clone() { + Some(x) => match x { + lsp::NumberOrString::Number(x) => Some(NumberOrString::Number(x)), + lsp::NumberOrString::String(x) => Some(NumberOrString::String(x)), + }, + None => None, + }; + + let tags = if let Some(tags) = &diagnostic.tags { + let new_tags = tags + .iter() + .filter_map(|tag| match *tag { + lsp::DiagnosticTag::DEPRECATED => Some(DiagnosticTag::Deprecated), + lsp::DiagnosticTag::UNNECESSARY => Some(DiagnosticTag::Unnecessary), + _ => None, + }) + .collect(); + + new_tags + } else { + Vec::new() + }; + + let ends_at_word = + start != end && end != 0 && text.get_char(end - 1).map_or(false, char_is_word); + let starts_at_word = start != end && text.get_char(start).map_or(false, char_is_word); + + Some(Diagnostic { + range: Range { start, end }, + ends_at_word, + starts_at_word, + zero_width: start == end, + line: diagnostic.range.start.line as usize, + message: diagnostic.message.clone(), + severity, + code, + tags, + source: diagnostic.source.clone(), + data: diagnostic.data.clone(), + language_server_id, + }) + } + #[inline] pub fn diagnostics(&self) -> &[Diagnostic] { &self.diagnostics } - pub fn shown_diagnostics(&self) -> impl Iterator + DoubleEndedIterator { - self.diagnostics.iter().filter(|d| { - self.language_servers_with_feature(LanguageServerFeature::Diagnostics) - .any(|ls| ls.id() == d.language_server_id) - }) - } - pub fn replace_diagnostics( &mut self, diagnostics: impl IntoIterator, unchanged_sources: &[String], - language_server_id: usize, + language_server_id: Option, ) { if unchanged_sources.is_empty() { self.clear_diagnostics(language_server_id); } else { self.diagnostics.retain(|d| { - if d.language_server_id != language_server_id { + if language_server_id.map_or(false, |id| id != d.language_server_id) { return true; } @@ -1757,9 +1829,13 @@ impl Document { }); } - pub fn clear_diagnostics(&mut self, language_server_id: usize) { - self.diagnostics - .retain(|d| d.language_server_id != language_server_id); + /// clears diagnostics for a given language server id if set, otherwise all diagnostics are cleared + pub fn clear_diagnostics(&mut self, language_server_id: Option) { + if let Some(id) = language_server_id { + self.diagnostics.retain(|d| d.language_server_id != id); + } else { + self.diagnostics.clear(); + } } /// Get the document's auto pairs. If the document has a recognized diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 76429a876..c018668cb 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -42,7 +42,7 @@ use anyhow::{anyhow, bail, Error}; pub use helix_core::diagnostic::Severity; use helix_core::{ auto_pairs::AutoPairs, - syntax::{self, AutoPairConfig, IndentationHeuristic, SoftWrap}, + syntax::{self, AutoPairConfig, IndentationHeuristic, LanguageServerFeature, SoftWrap}, Change, LineEnding, Position, Selection, NATIVE_LINE_ENDING, }; use helix_dap as dap; @@ -1477,6 +1477,10 @@ impl Editor { self.config.clone(), )?; + let diagnostics = + Editor::doc_diagnostics(&self.language_servers, &self.diagnostics, &doc); + doc.replace_diagnostics(diagnostics, &[], None); + if let Some(diff_base) = self.diff_providers.get_diff_base(&path) { doc.set_diff_base(diff_base); } @@ -1706,6 +1710,60 @@ impl Editor { .find(|doc| doc.path().map(|p| p == path.as_ref()).unwrap_or(false)) } + /// Returns all supported diagnostics for the document + pub fn doc_diagnostics<'a>( + language_servers: &'a helix_lsp::Registry, + diagnostics: &'a BTreeMap>, + document: &Document, + ) -> impl Iterator + 'a { + Editor::doc_diagnostics_with_filter(language_servers, diagnostics, document, |_, _| true) + } + + /// Returns all supported diagnostics for the document + /// filtered by `filter` which is invocated with the raw `lsp::Diagnostic` and the language server id it came from + pub fn doc_diagnostics_with_filter<'a>( + language_servers: &'a helix_lsp::Registry, + diagnostics: &'a BTreeMap>, + + document: &Document, + filter: impl Fn(&lsp::Diagnostic, usize) -> bool + 'a, + ) -> impl Iterator + 'a { + let text = document.text().clone(); + let language_config = document.language.clone(); + document + .path() + .and_then(|path| url::Url::from_file_path(path).ok()) // TODO log error? + .and_then(|uri| diagnostics.get(&uri)) + .map(|diags| { + diags.iter().filter_map(move |(diagnostic, lsp_id)| { + let ls = language_servers.get_by_id(*lsp_id)?; + language_config + .as_ref() + .and_then(|c| { + c.language_servers.iter().find(|features| { + features.name == ls.name() + && features.has_feature(LanguageServerFeature::Diagnostics) + }) + }) + .and_then(|_| { + if filter(diagnostic, *lsp_id) { + Document::lsp_diagnostic_to_diagnostic( + &text, + language_config.as_deref(), + diagnostic, + *lsp_id, + ls.offset_encoding(), + ) + } else { + None + } + }) + }) + }) + .into_iter() + .flatten() + } + /// Gets the primary cursor position in screen coordinates, /// or `None` if the primary cursor is not visible on screen. pub fn cursor(&self) -> (Option, CursorKind) { From 305d6e9c89c89037ae4ef0835c4b1dcc9a134584 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Mon, 8 Jan 2024 20:04:34 -0500 Subject: [PATCH 040/796] Normalize `S-` keymaps to uppercase ascii (#9213) --- helix-view/src/input.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/helix-view/src/input.rs b/helix-view/src/input.rs index 0f4ffaacf..5f5067eac 100644 --- a/helix-view/src/input.rs +++ b/helix-view/src/input.rs @@ -325,7 +325,7 @@ impl std::str::FromStr for KeyEvent { fn from_str(s: &str) -> Result { let mut tokens: Vec<_> = s.split('-').collect(); - let code = match tokens.pop().ok_or_else(|| anyhow!("Missing key code"))? { + let mut code = match tokens.pop().ok_or_else(|| anyhow!("Missing key code"))? { keys::BACKSPACE => KeyCode::Backspace, keys::ENTER => KeyCode::Enter, keys::LEFT => KeyCode::Left, @@ -405,6 +405,18 @@ impl std::str::FromStr for KeyEvent { modifiers.insert(flag); } + // Normalize character keys so that characters like C-S-r and C-R + // are represented by equal KeyEvents. + match code { + KeyCode::Char(ch) + if ch.is_ascii_lowercase() && modifiers.contains(KeyModifiers::SHIFT) => + { + code = KeyCode::Char(ch.to_ascii_uppercase()); + modifiers.remove(KeyModifiers::SHIFT); + } + _ => (), + } + Ok(KeyEvent { code, modifiers }) } } @@ -684,6 +696,19 @@ mod test { modifiers: KeyModifiers::ALT | KeyModifiers::CONTROL } ); + + assert_eq!( + str::parse::("C-S-r").unwrap(), + str::parse::("C-R").unwrap(), + ); + + assert_eq!( + str::parse::("S-w").unwrap(), + KeyEvent { + code: KeyCode::Char('W'), + modifiers: KeyModifiers::NONE + } + ); } #[test] From 65d041288067f1d1bbcf659c8b73ae04d77afb02 Mon Sep 17 00:00:00 2001 From: Sammo98 <75253485+Sammo98@users.noreply.github.com> Date: Tue, 9 Jan 2024 01:15:50 +0000 Subject: [PATCH 041/796] health - add formatter to display (#7986) --- helix-term/src/health.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/helix-term/src/health.rs b/helix-term/src/health.rs index dff903192..44ae2a2f7 100644 --- a/helix-term/src/health.rs +++ b/helix-term/src/health.rs @@ -145,7 +145,7 @@ pub fn languages_all() -> std::io::Result<()> { } }; - let mut headings = vec!["Language", "LSP", "DAP"]; + let mut headings = vec!["Language", "LSP", "DAP", "Formatter"]; for feat in TsFeature::all() { headings.push(feat.short_title()) @@ -203,6 +203,12 @@ pub fn languages_all() -> std::io::Result<()> { let dap = lang.debugger.as_ref().map(|dap| dap.command.as_str()); check_binary(dap); + let formatter = lang + .formatter + .as_ref() + .map(|formatter| formatter.command.as_str()); + check_binary(formatter); + for ts_feat in TsFeature::all() { match load_runtime_file(&lang.language_id, ts_feat.runtime_filename()).is_ok() { true => column("✓", Color::Green), @@ -285,6 +291,13 @@ pub fn language(lang_str: String) -> std::io::Result<()> { lang.debugger.as_ref().map(|dap| dap.command.to_string()), )?; + probe_protocol( + "formatter", + lang.formatter + .as_ref() + .map(|formatter| formatter.command.to_string()), + )?; + for ts_feat in TsFeature::all() { probe_treesitter_feature(&lang_str, *ts_feat)? } From 84e24b33dcda16d1d64805f34dcc02d82d0de8f1 Mon Sep 17 00:00:00 2001 From: Gabriel Dinner-David <82682503+gabydd@users.noreply.github.com> Date: Mon, 8 Jan 2024 20:21:16 -0500 Subject: [PATCH 042/796] make sure to sync views when applying edits to unfocused views (#9173) --- helix-term/src/commands/lsp.rs | 17 +++-------------- helix-term/src/commands/typed.rs | 29 +++++++++-------------------- helix-view/src/editor.rs | 24 ++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index 34ffa529c..0096e6aa9 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -896,7 +896,6 @@ pub fn apply_workspace_edit( } }; - let current_view_id = view!(editor).id; let doc_id = match editor.open(&path, Action::Load) { Ok(doc_id) => doc_id, Err(err) => { @@ -907,7 +906,7 @@ pub fn apply_workspace_edit( } }; - let doc = doc_mut!(editor, &doc_id); + let doc = doc!(editor, &doc_id); if let Some(version) = version { if version != doc.version() { let err = format!("outdated workspace edit for {path:?}"); @@ -918,18 +917,8 @@ pub fn apply_workspace_edit( } // Need to determine a view for apply/append_changes_to_history - let selections = doc.selections(); - let view_id = if selections.contains_key(¤t_view_id) { - // use current if possible - current_view_id - } else { - // Hack: we take the first available view_id - selections - .keys() - .next() - .copied() - .expect("No view_id available") - }; + let view_id = editor.get_synced_view_id(doc_id); + let doc = doc_mut!(editor, &doc_id); let transaction = helix_lsp::util::generate_transaction_from_edits( doc.text(), diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 208854e8a..f1c3cb71c 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -674,13 +674,15 @@ pub fn write_all_impl( let mut errors: Vec<&'static str> = Vec::new(); let config = cx.editor.config(); let jobs = &mut cx.jobs; - let current_view = view!(cx.editor); - let saves: Vec<_> = cx .editor .documents - .values_mut() - .filter_map(|doc| { + .keys() + .cloned() + .collect::>() + .into_iter() + .filter_map(|id| { + let doc = doc!(cx.editor, &id); if !doc.is_modified() { return None; } @@ -691,22 +693,9 @@ pub fn write_all_impl( return None; } - // Look for a view to apply the formatting change to. If the document - // is in the current view, just use that. Otherwise, since we don't - // have any other metric available for better selection, just pick - // the first view arbitrarily so that we still commit the document - // state for undos. If somehow we have a document that has not been - // initialized with any view, initialize it with the current view. - let target_view = if doc.selections().contains_key(¤t_view.id) { - current_view.id - } else if let Some(view) = doc.selections().keys().next() { - *view - } else { - doc.ensure_view_init(current_view.id); - current_view.id - }; - - Some((doc.id(), target_view)) + // Look for a view to apply the formatting change to. + let target_view = cx.editor.get_synced_view_id(doc.id()); + Some((id, target_view)) }) .collect(); diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index c018668cb..f13df2135 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -1910,6 +1910,30 @@ impl Editor { .as_ref() .and_then(|debugger| debugger.current_stack_frame()) } + + /// Returns the id of a view that this doc contains a selection for, + /// making sure it is synced with the current changes + /// if possible or there are no selections returns current_view + /// otherwise uses an arbitrary view + pub fn get_synced_view_id(&mut self, id: DocumentId) -> ViewId { + let current_view = view_mut!(self); + let doc = self.documents.get_mut(&id).unwrap(); + if doc.selections().contains_key(¤t_view.id) { + // only need to sync current view if this is not the current doc + if current_view.doc != id { + current_view.sync_changes(doc); + } + current_view.id + } else if let Some(view_id) = doc.selections().keys().next() { + let view_id = *view_id; + let view = self.tree.get_mut(view_id); + view.sync_changes(doc); + view_id + } else { + doc.ensure_view_init(current_view.id); + current_view.id + } + } } fn try_restore_indent(doc: &mut Document, view: &mut View) { From 7739d3ece1828506bbf2ab87233d10e36c3f6098 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Thu, 11 Jan 2024 14:13:39 +0100 Subject: [PATCH 043/796] Revert "build(deps): bump ahash from 0.8.6 to 0.8.7" (#9294) --- Cargo.lock | 4 ++-- helix-core/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9f0e8dcdb..a7e06b6ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if", "getrandom", diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index ff0bbcc3e..d7fff6c6f 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -31,7 +31,7 @@ once_cell = "1.19" arc-swap = "1" regex = "1" bitflags = "2.4" -ahash = "0.8.7" +ahash = "0.8.6" hashbrown = { version = "0.14.3", features = ["raw"] } dunce = "1.0" From 17dd102e5cccbb2a9a0f0224af63e52f3dab846b Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Thu, 11 Jan 2024 10:26:25 -0500 Subject: [PATCH 044/796] Remove sourcehut tree-sitter grammars from default build (#9316) Sourcehut has outages occasionally that cause the CI and from-source builds to fail. It also doesn't setup redirects when a user renames themselves, so if a user that publishes a tree-sitter grammar we use changes their sourcehut name then it breaks the build and any prior builds using that grammar. For now let's remove them from the default build. It's a bandaid over a larger reliability and trust problem with the grammar repositories but it should fix the build for now. --- languages.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/languages.toml b/languages.toml index 2a0efb494..7983e75ac 100644 --- a/languages.toml +++ b/languages.toml @@ -1,6 +1,8 @@ # Language support configuration. # See the languages documentation: https://docs.helix-editor.com/master/languages.html +use-grammars = { except = [ "hare", "wren", "gemini" ] } + [language-server] als = { command = "als" } From 054ce3961af4006d66529fe5fbbc44b47e2ee079 Mon Sep 17 00:00:00 2001 From: jw013 Date: Sun, 14 Jan 2024 09:11:18 -0500 Subject: [PATCH 045/796] Fallback to filename for +arg (#9333) --- helix-term/src/args.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/helix-term/src/args.rs b/helix-term/src/args.rs index 6a49889b6..0b1c9cde0 100644 --- a/helix-term/src/args.rs +++ b/helix-term/src/args.rs @@ -90,10 +90,9 @@ impl Args { } } arg if arg.starts_with('+') => { - let arg = &arg[1..]; - line_number = match arg.parse::() { - Ok(n) => n.saturating_sub(1), - _ => anyhow::bail!("bad line number after +"), + match arg[1..].parse::() { + Ok(n) => line_number = n.saturating_sub(1), + _ => args.files.push(parse_file(arg)), }; } arg => args.files.push(parse_file(arg)), From a0b02106c35ede95438bd23069d2b7f999ed8684 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Sun, 14 Jan 2024 15:11:40 +0100 Subject: [PATCH 046/796] Make nix flake respect unused grammars (#9326) * Make nix flake respect unused grammars * Use default value * Refactor * Take use-grammars.only into account --------- Co-authored-by: Sebastian Zivota --- grammars.nix | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/grammars.nix b/grammars.nix index 843fa02ad..5152b5204 100644 --- a/grammars.nix +++ b/grammars.nix @@ -28,7 +28,17 @@ owner = builtins.elemAt match 0; repo = builtins.elemAt match 1; }; - gitGrammars = builtins.filter isGitGrammar languagesConfig.grammar; + # If `use-grammars.only` is set, use only those grammars. + # If `use-grammars.except` is set, use all other grammars. + # Otherwise use all grammars. + useGrammar = grammar: + if languagesConfig?use-grammars.only then + builtins.elem grammar.name languagesConfig.use-grammars.only + else if languagesConfig?use-grammars.except then + !(builtins.elem grammar.name languagesConfig.use-grammars.except) + else true; + grammarsToUse = builtins.filter useGrammar languagesConfig.grammar; + gitGrammars = builtins.filter isGitGrammar grammarsToUse; buildGrammar = grammar: let gh = toGitHubFetcher grammar.source.git; sourceGit = builtins.fetchTree { From 3f88a3f4e6f75bf04246a8015652931e640e0821 Mon Sep 17 00:00:00 2001 From: woojiq <122799969+woojiq@users.noreply.github.com> Date: Sun, 14 Jan 2024 16:46:32 +0200 Subject: [PATCH 047/796] Change path normalization strategy to not resolve symlinks (#9330) --- Cargo.lock | 1 + helix-core/Cargo.toml | 1 + helix-core/src/path.rs | 71 ++++++++++++++-------- helix-core/tests/path.rs | 124 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 171 insertions(+), 26 deletions(-) create mode 100644 helix-core/tests/path.rs diff --git a/Cargo.lock b/Cargo.lock index a7e06b6ef..3643b9532 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1074,6 +1074,7 @@ dependencies = [ "slotmap", "smallvec", "smartstring", + "tempfile", "textwrap", "toml", "tree-sitter", diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index d7fff6c6f..be41fa010 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -55,3 +55,4 @@ parking_lot = "0.12" [dev-dependencies] quickcheck = { version = "1", default-features = false } indoc = "2.0.4" +tempfile = "3.7.0" diff --git a/helix-core/src/path.rs b/helix-core/src/path.rs index ede37e044..0cf6f812f 100644 --- a/helix-core/src/path.rs +++ b/helix-core/src/path.rs @@ -30,31 +30,10 @@ pub fn expand_tilde(path: &Path) -> PathBuf { path.to_path_buf() } -/// Normalize a path, removing things like `.` and `..`. -/// -/// CAUTION: This does not resolve symlinks (unlike -/// [`std::fs::canonicalize`]). This may cause incorrect or surprising -/// behavior at times. This should be used carefully. Unfortunately, -/// [`std::fs::canonicalize`] can be hard to use correctly, since it can often -/// fail, or on Windows returns annoying device paths. This is a problem Cargo -/// needs to improve on. -/// Copied from cargo: +/// Normalize a path without resolving symlinks. +// Strategy: start from the first component and move up. Cannonicalize previous path, +// join component, cannonicalize new path, strip prefix and join to the final result. pub fn get_normalized_path(path: &Path) -> PathBuf { - // normalization strategy is to canonicalize first ancestor path that exists (i.e., canonicalize as much as possible), - // then run handrolled normalization on the non-existent remainder - let (base, path) = path - .ancestors() - .find_map(|base| { - let canonicalized_base = dunce::canonicalize(base).ok()?; - let remainder = path.strip_prefix(base).ok()?.into(); - Some((canonicalized_base, remainder)) - }) - .unwrap_or_else(|| (PathBuf::new(), PathBuf::from(path))); - - if path.as_os_str().is_empty() { - return base; - } - let mut components = path.components().peekable(); let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { components.next(); @@ -70,20 +49,60 @@ pub fn get_normalized_path(path: &Path) -> PathBuf { ret.push(component.as_os_str()); } Component::CurDir => {} + #[cfg(not(windows))] Component::ParentDir => { ret.pop(); } + #[cfg(windows)] + Component::ParentDir => { + if let Some(head) = ret.components().next_back() { + match head { + Component::Prefix(_) | Component::RootDir => {} + Component::CurDir => unreachable!(), + // If we left previous component as ".." it means we met a symlink before and we can't pop path. + Component::ParentDir => { + ret.push(".."); + } + Component::Normal(_) => { + if ret.is_symlink() { + ret.push(".."); + } else { + ret.pop(); + } + } + } + } + } + #[cfg(not(windows))] Component::Normal(c) => { ret.push(c); } + #[cfg(windows)] + Component::Normal(c) => 'normal: { + use std::fs::canonicalize; + + let new_path = ret.join(c); + if new_path.is_symlink() { + ret = new_path; + break 'normal; + } + let (can_new, can_old) = (canonicalize(&new_path), canonicalize(&ret)); + match (can_new, can_old) { + (Ok(can_new), Ok(can_old)) => { + let striped = can_new.strip_prefix(can_old); + ret.push(striped.unwrap_or_else(|_| c.as_ref())); + } + _ => ret.push(c), + } + } } } - base.join(ret) + dunce::simplified(&ret).to_path_buf() } /// Returns the canonical, absolute form of a path with all intermediate components normalized. /// -/// This function is used instead of `std::fs::canonicalize` because we don't want to verify +/// This function is used instead of [`std::fs::canonicalize`] because we don't want to verify /// here if the path exists, just normalize it's components. pub fn get_canonicalized_path(path: &Path) -> PathBuf { let path = expand_tilde(path); diff --git a/helix-core/tests/path.rs b/helix-core/tests/path.rs new file mode 100644 index 000000000..cbda5e1ab --- /dev/null +++ b/helix-core/tests/path.rs @@ -0,0 +1,124 @@ +#![cfg(windows)] + +use std::{ + env::set_current_dir, + error::Error, + path::{Component, Path, PathBuf}, +}; + +use helix_core::path::get_normalized_path; +use tempfile::Builder; + +// Paths on Windows are almost always case-insensitive. +// Normalization should return the original path. +// E.g. mkdir `CaSe`, normalize(`case`) = `CaSe`. +#[test] +fn test_case_folding_windows() -> Result<(), Box> { + // tmp/root/case + let tmp_prefix = std::env::temp_dir(); + set_current_dir(&tmp_prefix)?; + + let root = Builder::new().prefix("root-").tempdir()?; + let case = Builder::new().prefix("CaSe-").tempdir_in(&root)?; + + let root_without_prefix = root.path().strip_prefix(&tmp_prefix)?; + + let lowercase_case = format!( + "case-{}", + case.path() + .file_name() + .unwrap() + .to_string_lossy() + .split_at(5) + .1 + ); + let test_path = root_without_prefix.join(lowercase_case); + assert_eq!( + get_normalized_path(&test_path), + case.path().strip_prefix(&tmp_prefix)? + ); + + Ok(()) +} + +#[test] +fn test_normalize_path() -> Result<(), Box> { + /* + tmp/root/ + ├── link -> dir1/orig_file + ├── dir1/ + │ └── orig_file + └── dir2/ + └── dir_link -> ../dir1/ + */ + + let tmp_prefix = std::env::temp_dir(); + set_current_dir(&tmp_prefix)?; + + // Create a tree structure as shown above + let root = Builder::new().prefix("root-").tempdir()?; + let dir1 = Builder::new().prefix("dir1-").tempdir_in(&root)?; + let orig_file = Builder::new().prefix("orig_file-").tempfile_in(&dir1)?; + let dir2 = Builder::new().prefix("dir2-").tempdir_in(&root)?; + + // Create path and delete existing file + let dir_link = Builder::new() + .prefix("dir_link-") + .tempfile_in(&dir2)? + .path() + .to_owned(); + let link = Builder::new() + .prefix("link-") + .tempfile_in(&root)? + .path() + .to_owned(); + + use std::os::windows; + windows::fs::symlink_dir(&dir1, &dir_link)?; + windows::fs::symlink_file(&orig_file, &link)?; + + // root/link + let path = link.strip_prefix(&tmp_prefix)?; + assert_eq!( + get_normalized_path(path), + path, + "input {:?} and symlink last component shouldn't be resolved", + path + ); + + // root/dir2/dir_link/orig_file/../.. + let path = dir_link + .strip_prefix(&tmp_prefix) + .unwrap() + .join(orig_file.path().file_name().unwrap()) + .join(Component::ParentDir) + .join(Component::ParentDir); + let expected = dir_link + .strip_prefix(&tmp_prefix) + .unwrap() + .join(Component::ParentDir); + assert_eq!( + get_normalized_path(&path), + expected, + "input {:?} and \"..\" should not erase the simlink that goes ahead", + &path + ); + + // root/link/.././../dir2/../ + let path = link + .strip_prefix(&tmp_prefix) + .unwrap() + .join(Component::ParentDir) + .join(Component::CurDir) + .join(Component::ParentDir) + .join(dir2.path().file_name().unwrap()) + .join(Component::ParentDir); + let expected = link + .strip_prefix(&tmp_prefix) + .unwrap() + .join(Component::ParentDir) + .join(Component::ParentDir); + assert_eq!(get_normalized_path(&path), expected, "input {:?}", &path); + + Ok(()) +} From 2fb7e50b549842999639434dc433f22928678082 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Sun, 14 Jan 2024 00:00:31 +0100 Subject: [PATCH 048/796] don't crash in plaintext bracket match (mm) on empty file --- helix-core/src/match_brackets.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/helix-core/src/match_brackets.rs b/helix-core/src/match_brackets.rs index f6d9885e4..e49b408a8 100644 --- a/helix-core/src/match_brackets.rs +++ b/helix-core/src/match_brackets.rs @@ -141,7 +141,7 @@ fn find_pair( #[must_use] pub fn find_matching_bracket_plaintext(doc: RopeSlice, cursor_pos: usize) -> Option { // Don't do anything when the cursor is not on top of a bracket. - let bracket = doc.char(cursor_pos); + let bracket = doc.get_char(cursor_pos)?; if !is_valid_bracket(bracket) { return None; } @@ -265,6 +265,12 @@ fn as_char(doc: RopeSlice, node: &Node) -> Option<(usize, char)> { mod tests { use super::*; + #[test] + fn find_matching_bracket_empty_file() { + let actual = find_matching_bracket_plaintext("".into(), 0); + assert_eq!(actual, None); + } + #[test] fn test_find_matching_bracket_current_line_plaintext() { let assert = |input: &str, pos, expected| { From 445f7a273a27d74d8168eab7941dcb3479d31ebe Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Sun, 14 Jan 2024 00:21:49 +0100 Subject: [PATCH 049/796] ignore empty TS nodes in match bracket --- helix-core/src/match_brackets.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/helix-core/src/match_brackets.rs b/helix-core/src/match_brackets.rs index e49b408a8..150679b5c 100644 --- a/helix-core/src/match_brackets.rs +++ b/helix-core/src/match_brackets.rs @@ -60,7 +60,7 @@ fn find_pair( let tree = syntax.tree(); let pos = doc.char_to_byte(pos_); - let mut node = tree.root_node().descendant_for_byte_range(pos, pos)?; + let mut node = tree.root_node().descendant_for_byte_range(pos, pos + 1)?; loop { if node.is_named() { @@ -118,7 +118,9 @@ fn find_pair( }; node = parent; } - let node = tree.root_node().named_descendant_for_byte_range(pos, pos)?; + let node = tree + .root_node() + .named_descendant_for_byte_range(pos, pos + 1)?; if node.child_count() != 0 { return None; } From 3011df4f35e43f9f7690b236c85ab54f210c8b3a Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Mon, 15 Jan 2024 01:33:26 -0500 Subject: [PATCH 050/796] Bump tree-sitter to latest master (#9317) * query capture names now return `&str`s rather than `String`s * the `#any-of?` predicate is now supported --- Cargo.lock | 2 +- Cargo.toml | 2 +- book/src/guides/indent.md | 6 +++++- book/src/guides/injection.md | 3 +++ helix-core/src/indent.rs | 2 +- helix-core/src/syntax.rs | 6 +++--- runtime/queries/tsq/highlights.scm | 2 +- 7 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3643b9532..d07d5fa91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2178,7 +2178,7 @@ dependencies = [ [[package]] name = "tree-sitter" version = "0.20.10" -source = "git+https://github.com/tree-sitter/tree-sitter?rev=ab09ae20d640711174b8da8a654f6b3dec93da1a#ab09ae20d640711174b8da8a654f6b3dec93da1a" +source = "git+https://github.com/helix-editor/tree-sitter?rev=660481dbf71413eba5a928b0b0ab8da50c1109e0#660481dbf71413eba5a928b0b0ab8da50c1109e0" dependencies = [ "cc", "regex", diff --git a/Cargo.toml b/Cargo.toml index 6c006fbb4..f59896ecb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ package.helix-tui.opt-level = 2 package.helix-term.opt-level = 2 [workspace.dependencies] -tree-sitter = { version = "0.20", git = "https://github.com/tree-sitter/tree-sitter", rev = "ab09ae20d640711174b8da8a654f6b3dec93da1a" } +tree-sitter = { version = "0.20", git = "https://github.com/helix-editor/tree-sitter", rev = "660481dbf71413eba5a928b0b0ab8da50c1109e0" } nucleo = "0.2.0" [workspace.package] diff --git a/book/src/guides/indent.md b/book/src/guides/indent.md index a65ac5ac1..be140384a 100644 --- a/book/src/guides/indent.md +++ b/book/src/guides/indent.md @@ -315,6 +315,10 @@ The first argument (a capture) must/must not be equal to the second argument The first argument (a capture) must/must not match the regex given in the second argument (a string). +- `#any-of?`/`#not-any-of?`: +The first argument (a capture) must/must not be one of the other arguments +(strings). + Additionally, we support some custom predicates for indent queries: - `#not-kind-eq?`: @@ -366,4 +370,4 @@ Everything up to and including the closing brace gets an indent level of 1. Then, on the closing brace, we encounter an outdent with a scope of "all", which means the first line is included, and the indent level is cancelled out on this line. (Note these scopes are the defaults for `@indent` and `@outdent`—they are -written explicitly for demonstration.) \ No newline at end of file +written explicitly for demonstration.) diff --git a/book/src/guides/injection.md b/book/src/guides/injection.md index e842ae303..0a1d2c9a2 100644 --- a/book/src/guides/injection.md +++ b/book/src/guides/injection.md @@ -54,4 +54,7 @@ The first argument (a capture) must be equal to the second argument The first argument (a capture) must match the regex given in the second argument (a string). +- `#any-of?` (standard): +The first argument (a capture) must be one of the other arguments (strings). + [upstream-docs]: http://tree-sitter.github.io/tree-sitter/syntax-highlighting#language-injection diff --git a/helix-core/src/indent.rs b/helix-core/src/indent.rs index 1e90db472..c29bb3a0b 100644 --- a/helix-core/src/indent.rs +++ b/helix-core/src/indent.rs @@ -551,7 +551,7 @@ fn query_indents<'a>( // The row/column position of the optional anchor in this query let mut anchor: Option = None; for capture in m.captures { - let capture_name = query.capture_names()[capture.index as usize].as_str(); + let capture_name = query.capture_names()[capture.index as usize]; let capture_type = match capture_name { "indent" => IndentCaptureType::Indent, "indent.always" => IndentCaptureType::IndentAlways, diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 102ecb15d..4e44c4866 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -1727,7 +1727,7 @@ impl HighlightConfiguration { let mut local_scope_capture_index = None; for (i, name) in query.capture_names().iter().enumerate() { let i = Some(i as u32); - match name.as_str() { + match *name { "local.definition" => local_def_capture_index = i, "local.definition-value" => local_def_value_capture_index = i, "local.reference" => local_ref_capture_index = i, @@ -1738,7 +1738,7 @@ impl HighlightConfiguration { for (i, name) in injections_query.capture_names().iter().enumerate() { let i = Some(i as u32); - match name.as_str() { + match *name { "injection.content" => injection_content_capture_index = i, "injection.language" => injection_language_capture_index = i, "injection.filename" => injection_filename_capture_index = i, @@ -1768,7 +1768,7 @@ impl HighlightConfiguration { } /// Get a slice containing all of the highlight names used in the configuration. - pub fn names(&self) -> &[String] { + pub fn names(&self) -> &[&str] { self.query.capture_names() } diff --git a/runtime/queries/tsq/highlights.scm b/runtime/queries/tsq/highlights.scm index b59514bc2..5ef6bf4c8 100644 --- a/runtime/queries/tsq/highlights.scm +++ b/runtime/queries/tsq/highlights.scm @@ -41,7 +41,7 @@ (capture) @label ((predicate_name) @function - (#match? @function "^#(eq\\?|match\\?|is\\?|is-not\\?|not-same-line\\?|not-kind-eq\\?|set!|select-adjacent!|strip!)$")) + (#any-of? @function "#eq?" "#match?" "#any-of?" "#not-any-of?" "#is?" "#is-not?" "#not-same-line?" "#not-kind-eq?" "#set!" "#select-adjacent!" "#strip!")) (predicate_name) @error (escape_sequence) @constant.character.escape From eca3ccff76b9f45dd2b266215764fe7630dee884 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Mon, 15 Jan 2024 01:34:38 -0500 Subject: [PATCH 051/796] Select subtree within injections in :tree-sitter-subtree (#9309) `:tree-sitter-subtree` could previously only print subtrees of nodes in the root injection layer. We can improve on that by finding the layer that contains the given byte range and printing the subtree within that layer. That gives more useful results when a selection is within an injection layer. --- helix-core/src/syntax.rs | 43 ++++++++++++++++++++++++++++++++ helix-term/src/commands/typed.rs | 6 +---- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 4e44c4866..83bd09b4d 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -1338,6 +1338,23 @@ impl Syntax { result } + pub fn descendant_for_byte_range(&self, start: usize, end: usize) -> Option> { + let mut container_id = self.root; + + for (layer_id, layer) in self.layers.iter() { + if layer.depth > self.layers[container_id].depth + && layer.contains_byte_range(start, end) + { + container_id = layer_id; + } + } + + self.layers[container_id] + .tree() + .root_node() + .descendant_for_byte_range(start, end) + } + // Commenting // comment_strings_for_pos // is_commented @@ -1434,6 +1451,32 @@ impl LanguageLayer { self.tree = Some(tree); Ok(()) } + + /// Whether the layer contains the given byte range. + /// + /// If the layer has multiple ranges (i.e. combined injections), the + /// given range is considered contained if it is within the start and + /// end bytes of the first and last ranges **and** if the given range + /// starts or ends within any of the layer's ranges. + fn contains_byte_range(&self, start: usize, end: usize) -> bool { + let layer_start = self + .ranges + .first() + .expect("ranges should not be empty") + .start_byte; + let layer_end = self + .ranges + .last() + .expect("ranges should not be empty") + .end_byte; + + layer_start <= start + && layer_end >= end + && self.ranges.iter().any(|range| { + let byte_range = range.start_byte..range.end_byte; + byte_range.contains(&start) || byte_range.contains(&end) + }) + } } pub(crate) fn generate_edits( diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index f1c3cb71c..b13af03a2 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -2117,11 +2117,7 @@ fn tree_sitter_subtree( let text = doc.text(); let from = text.char_to_byte(primary_selection.from()); let to = text.char_to_byte(primary_selection.to()); - if let Some(selected_node) = syntax - .tree() - .root_node() - .descendant_for_byte_range(from, to) - { + if let Some(selected_node) = syntax.descendant_for_byte_range(from, to) { let mut contents = String::from("```tsq\n"); helix_core::syntax::pretty_print_tree(&mut contents, selected_node)?; contents.push_str("\n```"); From 6ce57b7924fc52ee9800637e4f0eae72d123fec6 Mon Sep 17 00:00:00 2001 From: JR Date: Mon, 15 Jan 2024 21:43:23 +0100 Subject: [PATCH 052/796] Fix export instructions in installation doc (#9306) --- book/src/install.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/install.md b/book/src/install.md index 2a4273b86..1f200e2ed 100644 --- a/book/src/install.md +++ b/book/src/install.md @@ -216,12 +216,12 @@ RUSTFLAGS="-C target-feature=-crt-static" #### Linux and macOS -The **runtime** directory is one below the Helix source, so either set a +The **runtime** directory is one below the Helix source, so either export a `HELIX_RUNTIME` environment variable to point to that directory and add it to your `~/.bashrc` or equivalent: ```sh -HELIX_RUNTIME=~/src/helix/runtime +export HELIX_RUNTIME=~/src/helix/runtime ``` Or, create a symbolic link: From 8a00620a7116e3804b481af41a1252e6170ccc28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 14:12:15 +0900 Subject: [PATCH 053/796] build(deps): bump smallvec from 1.11.2 to 1.12.0 (#9341) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- helix-core/Cargo.toml | 2 +- helix-term/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d07d5fa91..3dd25ed49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1920,9 +1920,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e" [[package]] name = "smartstring" diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index be41fa010..eb14cda6c 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -19,7 +19,7 @@ integration = [] helix-loader = { path = "../helix-loader" } ropey = { version = "1.6.1", default-features = false, features = ["simd"] } -smallvec = "1.11" +smallvec = "1.12" smartstring = "1.0.1" unicode-segmentation = "1.10" unicode-width = "0.1" diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 0859dbd74..80bda2b6c 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -82,6 +82,6 @@ crossterm = { version = "0.27", features = ["event-stream", "use-dev-tty"] } helix-loader = { path = "../helix-loader" } [dev-dependencies] -smallvec = "1.11" +smallvec = "1.12" indoc = "2.0.4" tempfile = "3.9.0" From 2d8d16ff5ef149c659aaa5b805db542cbd839c38 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 14:12:25 +0900 Subject: [PATCH 054/796] build(deps): bump rustix from 0.38.28 to 0.38.30 (#9342) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3dd25ed49..d4c348f03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1413,9 +1413,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "lock_api" @@ -1763,9 +1763,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ "bitflags 2.4.1", "errno", From fa1d8dfabc0b8ee0b074928412ddd4083412c20d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 14:12:32 +0900 Subject: [PATCH 055/796] build(deps): bump anyhow from 1.0.78 to 1.0.79 (#9344) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d4c348f03..78b650b13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,9 +62,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca87830a3e3fb156dc96cfbd31cb620265dd053be734723f22b760d6cc3c3051" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "arc-swap" From 0e7f5d604ed411439c1cb3b73d11eae1223b2203 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 14:12:39 +0900 Subject: [PATCH 056/796] build(deps): bump thiserror from 1.0.52 to 1.0.56 (#9345) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78b650b13..da0dc3612 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2029,18 +2029,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.52" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.52" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", From bd9eef1f90409fda5790e4b560ef68b7f9df672a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 14:13:04 +0900 Subject: [PATCH 057/796] build(deps): bump cachix/install-nix-action from 24 to 25 (#9346) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/cachix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cachix.yml b/.github/workflows/cachix.yml index 0620cbf12..08f24e1e5 100644 --- a/.github/workflows/cachix.yml +++ b/.github/workflows/cachix.yml @@ -14,7 +14,7 @@ jobs: uses: actions/checkout@v4 - name: Install nix - uses: cachix/install-nix-action@v24 + uses: cachix/install-nix-action@v25 - name: Authenticate with Cachix uses: cachix/cachix-action@v13 From eef46b1aeda8b7061657622c19867663193ecb4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 14:13:12 +0900 Subject: [PATCH 058/796] build(deps): bump cachix/cachix-action from 13 to 14 (#9347) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/cachix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cachix.yml b/.github/workflows/cachix.yml index 08f24e1e5..57f0a0db4 100644 --- a/.github/workflows/cachix.yml +++ b/.github/workflows/cachix.yml @@ -17,7 +17,7 @@ jobs: uses: cachix/install-nix-action@v25 - name: Authenticate with Cachix - uses: cachix/cachix-action@v13 + uses: cachix/cachix-action@v14 with: name: helix authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} From 44cb8e547550648bf942b452acaac0268e30afa9 Mon Sep 17 00:00:00 2001 From: Kirawi <67773714+kirawi@users.noreply.github.com> Date: Tue, 16 Jan 2024 19:32:05 -0500 Subject: [PATCH 059/796] update tempfile dev-dependency to 3.9 (#9359) --- helix-core/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index eb14cda6c..07c801b89 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -55,4 +55,4 @@ parking_lot = "0.12" [dev-dependencies] quickcheck = { version = "1", default-features = false } indoc = "2.0.4" -tempfile = "3.7.0" +tempfile = "3.9" From 6339a8c95a969a40a0db92d6dddd48a1617a1b37 Mon Sep 17 00:00:00 2001 From: Kirawi <67773714+kirawi@users.noreply.github.com> Date: Wed, 17 Jan 2024 09:15:09 -0500 Subject: [PATCH 060/796] Delete .ignore (#9363) This isn't being used for anything anymore. --- .ignore | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .ignore diff --git a/.ignore b/.ignore deleted file mode 100644 index 0c4493ee8..000000000 --- a/.ignore +++ /dev/null @@ -1,2 +0,0 @@ -# Things that we don't want ripgrep to search that we do want in git -# https://github.com/BurntSushi/ripgrep/blob/master/GUIDE.md#automatic-filtering From f41727cc9c3a71ccaec837f358cb3b2362c4c303 Mon Sep 17 00:00:00 2001 From: Jeremy Brudvik Date: Wed, 17 Jan 2024 09:15:38 -0500 Subject: [PATCH 061/796] Support PureScript's new spago.yaml configs (#9362) --- languages.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index 7983e75ac..1638cac49 100644 --- a/languages.toml +++ b/languages.toml @@ -1119,7 +1119,7 @@ name = "purescript" scope = "source.purescript" injection-regex = "purescript" file-types = ["purs"] -roots = ["spago.dhall", "bower.json"] +roots = ["spago.yaml", "spago.dhall", "bower.json"] comment-token = "--" language-servers = [ "purescript-language-server" ] indent = { tab-width = 2, unit = " " } From dcdecaab22e87ae8046e15aa461dc2604b1c67ad Mon Sep 17 00:00:00 2001 From: Ben Dennis Date: Wed, 17 Jan 2024 08:49:25 -0600 Subject: [PATCH 062/796] Exit a language server if it sends a message with invalid json (#9332) * Keep lsp event listener thread alive when malformed json is encountered from the lsp server * Update unexpected error flow in recv() to close outstanding requests and close the language server * Log malformed notifications as info instead of error * Make close_language_server a nested function inside recv, similar to what's done in send * Update malformed notification log text * Clean up new log text a bit * Initialize recv_buffer closer to where it's used * Use "exit" instead of "close" * Remove whitespace * Remove the need for a helper method to exit the language server * Match on Unhandled error explicitly and keep catch-all error case around --- helix-lsp/src/transport.rs | 13 ++++++++----- helix-term/src/application.rs | 10 +++++++--- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/helix-lsp/src/transport.rs b/helix-lsp/src/transport.rs index 9fdd30aa0..f2f35d6ab 100644 --- a/helix-lsp/src/transport.rs +++ b/helix-lsp/src/transport.rs @@ -270,7 +270,14 @@ impl Transport { } }; } - Err(Error::StreamClosed) => { + Err(err) => { + if !matches!(err, Error::StreamClosed) { + error!( + "Exiting {} after unexpected error: {err:?}", + &transport.name + ); + } + // Close any outstanding requests. for (id, tx) in transport.pending_requests.lock().await.drain() { match tx.send(Err(Error::StreamClosed)).await { @@ -300,10 +307,6 @@ impl Transport { } break; } - Err(err) => { - error!("{} err: <- {err:?}", transport.name); - break; - } } } } diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 4eda8097c..01c120d06 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -27,7 +27,7 @@ use crate::{ ui::{self, overlay::overlaid}, }; -use log::{debug, error, warn}; +use log::{debug, error, info, warn}; #[cfg(not(feature = "integration"))] use std::io::stdout; use std::{collections::btree_map::Entry, io::stdin, path::Path, sync::Arc}; @@ -683,9 +683,13 @@ impl Application { Call::Notification(helix_lsp::jsonrpc::Notification { method, params, .. }) => { let notification = match Notification::parse(&method, params) { Ok(notification) => notification, + Err(helix_lsp::Error::Unhandled) => { + info!("Ignoring Unhandled notification from Language Server"); + return; + } Err(err) => { - log::error!( - "received malformed notification from Language Server: {}", + error!( + "Ignoring unknown notification from Language Server: {}", err ); return; From 6754acd83ff0f6edf837611679c5f4424e14492e Mon Sep 17 00:00:00 2001 From: HumanEntity <102693471+HumanEntity@users.noreply.github.com> Date: Wed, 17 Jan 2024 18:23:46 +0100 Subject: [PATCH 063/796] Made inlay-hints not look like normal code (#9370) Added `"ui.virtual.whitespace" = { fg = "grey_dim" }` line --- runtime/themes/sonokai.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/themes/sonokai.toml b/runtime/themes/sonokai.toml index f60bd4dbf..c7c9adc06 100644 --- a/runtime/themes/sonokai.toml +++ b/runtime/themes/sonokai.toml @@ -72,6 +72,7 @@ "ui.menu.selected" = { fg = "bg0", bg = "green" } "ui.virtual.whitespace" = { fg = "grey_dim" } "ui.virtual.ruler" = { bg = "grey_dim" } +"ui.virtual.inlay-hint" = { fg = "grey_dim" } info = { fg = 'green', bg = 'bg2' } hint = { fg = 'blue', bg = 'bg2', modifiers = ['bold'] } From c60ba4ba04de56f37f4f4b19051bc4a1e68b3484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Dzivjak?= Date: Wed, 17 Jan 2024 17:24:38 +0000 Subject: [PATCH 064/796] feat(lsp): implement show document request (#8865) * feat(lsp): implement show document request Implement [window.showDocument](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#window_showDocument) LSP server-sent request. This PR builds on top of helix-editor#5820, moves the external-URL opening functionality into shared crate-level function that returns a callback that is now used by both the `open_file` command as well as the window.showDocument handler if the URL is marked as external. * add return * use vertical split * refactor --------- Co-authored-by: Michael Davis --- helix-lsp/src/lib.rs | 5 +++ helix-term/src/application.rs | 70 +++++++++++++++++++++++++++++++++++ helix-term/src/commands.rs | 23 ++---------- helix-term/src/lib.rs | 23 ++++++++++++ 4 files changed, 102 insertions(+), 19 deletions(-) diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index 34278cd54..83625897e 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -549,6 +549,7 @@ pub enum MethodCall { WorkspaceConfiguration(lsp::ConfigurationParams), RegisterCapability(lsp::RegistrationParams), UnregisterCapability(lsp::UnregistrationParams), + ShowDocument(lsp::ShowDocumentParams), } impl MethodCall { @@ -576,6 +577,10 @@ impl MethodCall { let params: lsp::UnregistrationParams = params.parse()?; Self::UnregisterCapability(params) } + lsp::request::ShowDocument::METHOD => { + let params: lsp::ShowDocumentParams = params.parse()?; + Self::ShowDocument(params) + } _ => { return Err(Error::Unhandled); } diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 01c120d06..1b0a06dd9 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -3,6 +3,7 @@ use futures_util::Stream; use helix_core::{path::get_relative_path, pos_at_coords, syntax, Selection}; use helix_lsp::{ lsp::{self, notification::Notification}, + util::lsp_range_to_range, LspProgressMap, }; use helix_view::{ @@ -1100,6 +1101,13 @@ impl Application { } Ok(serde_json::Value::Null) } + Ok(MethodCall::ShowDocument(params)) => { + let language_server = language_server!(); + let offset_encoding = language_server.offset_encoding(); + + let result = self.handle_show_document(params, offset_encoding); + Ok(json!(result)) + } }; tokio::spawn(language_server!().reply(id, reply)); @@ -1108,6 +1116,68 @@ impl Application { } } + fn handle_show_document( + &mut self, + params: lsp::ShowDocumentParams, + offset_encoding: helix_lsp::OffsetEncoding, + ) -> lsp::ShowDocumentResult { + if let lsp::ShowDocumentParams { + external: Some(true), + uri, + .. + } = params + { + self.jobs.callback(crate::open_external_url_callback(uri)); + return lsp::ShowDocumentResult { success: true }; + }; + + let lsp::ShowDocumentParams { + uri, + selection, + take_focus, + .. + } = params; + + let path = match uri.to_file_path() { + Ok(path) => path, + Err(err) => { + log::error!("unsupported file URI: {}: {:?}", uri, err); + return lsp::ShowDocumentResult { success: false }; + } + }; + + let action = match take_focus { + Some(true) => helix_view::editor::Action::Replace, + _ => helix_view::editor::Action::VerticalSplit, + }; + + let doc_id = match self.editor.open(&path, action) { + Ok(id) => id, + Err(err) => { + log::error!("failed to open path: {:?}: {:?}", uri, err); + return lsp::ShowDocumentResult { success: false }; + } + }; + + let doc = doc_mut!(self.editor, &doc_id); + if let Some(range) = selection { + // TODO: convert inside server + if let Some(new_range) = lsp_range_to_range(doc.text(), range, offset_encoding) { + let view = view_mut!(self.editor); + + // we flip the range so that the cursor sits on the start of the symbol + // (for example start of the function). + doc.set_selection(view.id, Selection::single(new_range.head, new_range.anchor)); + if action.align_view(view, doc.id()) { + align_view(doc, view, Align::Center); + } + } else { + log::warn!("lsp position out of bounds - {:?}", range); + }; + }; + lsp::ShowDocumentResult { success: true } + } + async fn claim_term(&mut self) -> std::io::Result<()> { let terminal_config = self.config.load().editor.clone().into(); self.terminal.claim(terminal_config) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index c95933804..e436e1cfc 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -1227,7 +1227,7 @@ fn open_url(cx: &mut Context, url: Url, action: Action) { .unwrap_or_default(); if url.scheme() != "file" { - return open_external_url(cx, url); + return cx.jobs.callback(crate::open_external_url_callback(url)); } let content_type = std::fs::File::open(url.path()).and_then(|file| { @@ -1240,7 +1240,9 @@ fn open_url(cx: &mut Context, url: Url, action: Action) { // we attempt to open binary files - files that can't be open in helix - using external // program as well, e.g. pdf files or images match content_type { - Ok(content_inspector::ContentType::BINARY) => open_external_url(cx, url), + Ok(content_inspector::ContentType::BINARY) => { + cx.jobs.callback(crate::open_external_url_callback(url)) + } Ok(_) | Err(_) => { let path = &rel_path.join(url.path()); if path.is_dir() { @@ -1253,23 +1255,6 @@ fn open_url(cx: &mut Context, url: Url, action: Action) { } } -/// Opens URL in external program. -fn open_external_url(cx: &mut Context, url: Url) { - let commands = open::commands(url.as_str()); - cx.jobs.callback(async { - for cmd in commands { - let mut command = tokio::process::Command::new(cmd.get_program()); - command.args(cmd.get_args()); - if command.output().await.is_ok() { - return Ok(job::Callback::Editor(Box::new(|_| {}))); - } - } - Ok(job::Callback::Editor(Box::new(move |editor| { - editor.set_error("Opening URL in external program failed") - }))) - }); -} - fn extend_word_impl(cx: &mut Context, extend_fn: F) where F: Fn(RopeSlice, Range, usize) -> Range, diff --git a/helix-term/src/lib.rs b/helix-term/src/lib.rs index a94c5e494..a1d603298 100644 --- a/helix-term/src/lib.rs +++ b/helix-term/src/lib.rs @@ -12,7 +12,11 @@ pub mod keymap; pub mod ui; use std::path::Path; +use futures_util::Future; use ignore::DirEntry; +use url::Url; + +pub use keymap::macros::*; #[cfg(not(windows))] fn true_color() -> bool { @@ -46,3 +50,22 @@ fn filter_picker_entry(entry: &DirEntry, root: &Path, dedup_symlinks: bool) -> b true } + +/// Opens URL in external program. +fn open_external_url_callback( + url: Url, +) -> impl Future> + Send + 'static { + let commands = open::commands(url.as_str()); + async { + for cmd in commands { + let mut command = tokio::process::Command::new(cmd.get_program()); + command.args(cmd.get_args()); + if command.output().await.is_ok() { + return Ok(job::Callback::Editor(Box::new(|_| {}))); + } + } + Ok(job::Callback::Editor(Box::new(move |editor| { + editor.set_error("Opening URL in external program failed") + }))) + } +} From af8e524a7d06253fa854bf8954f64312e11d0ea0 Mon Sep 17 00:00:00 2001 From: Daniel Sedlak Date: Wed, 17 Jan 2024 19:40:45 +0100 Subject: [PATCH 065/796] Address clippy lints (#9371) --- helix-term/src/commands.rs | 4 ++-- helix-term/src/commands/dap.rs | 4 ++-- helix-term/src/commands/typed.rs | 6 +++--- helix-term/src/keymap.rs | 2 +- helix-term/src/ui/spinner.rs | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index e436e1cfc..937326f6e 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -795,7 +795,7 @@ fn goto_buffer(editor: &mut Editor, direction: Direction) { let iter = editor.documents.keys(); let mut iter = iter.rev().skip_while(|id| *id != ¤t); iter.next(); // skip current item - iter.next().or_else(|| editor.documents.keys().rev().next()) + iter.next().or_else(|| editor.documents.keys().next_back()) } } .unwrap(); @@ -2789,7 +2789,7 @@ fn buffer_picker(cx: &mut Context) { .editor .documents .values() - .map(|doc| new_meta(doc)) + .map(new_meta) .collect::>(); // mru diff --git a/helix-term/src/commands/dap.rs b/helix-term/src/commands/dap.rs index e9fde4767..dec25cbd9 100644 --- a/helix-term/src/commands/dap.rs +++ b/helix-term/src/commands/dap.rs @@ -78,7 +78,7 @@ fn thread_picker( }) .with_preview(move |editor, thread| { let frames = editor.debugger.as_ref()?.stack_frames.get(&thread.id)?; - let frame = frames.get(0)?; + let frame = frames.first()?; let path = frame.source.as_ref()?.path.clone()?; let pos = Some(( frame.line.saturating_sub(1), @@ -166,7 +166,7 @@ pub fn dap_start_impl( // TODO: avoid refetching all of this... pass a config in let template = match name { Some(name) => config.templates.iter().find(|t| t.name == name), - None => config.templates.get(0), + None => config.templates.first(), } .ok_or_else(|| anyhow!("No debug config with given name"))?; diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index b13af03a2..eb88e0411 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -483,7 +483,7 @@ fn set_indent_style( } // Attempt to parse argument as an indent style. - let style = match args.get(0) { + let style = match args.first() { Some(arg) if "tabs".starts_with(&arg.to_lowercase()) => Some(Tabs), Some(Cow::Borrowed("0")) => Some(Tabs), Some(arg) => arg @@ -535,7 +535,7 @@ fn set_line_ending( } let arg = args - .get(0) + .first() .context("argument missing")? .to_ascii_lowercase(); @@ -2078,7 +2078,7 @@ fn reflow( // - The configured text-width for this language in languages.toml // - The configured text-width in the config.toml let text_width: usize = args - .get(0) + .first() .map(|num| num.parse::()) .transpose()? .or_else(|| doc.language_config().and_then(|config| config.text_width)) diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index 598be55b5..d9297e08d 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -319,7 +319,7 @@ impl Keymaps { self.sticky = None; } - let first = self.state.get(0).unwrap_or(&key); + let first = self.state.first().unwrap_or(&key); let trie_node = match self.sticky { Some(ref trie) => Cow::Owned(KeyTrie::Node(trie.clone())), None => Cow::Borrowed(keymap), diff --git a/helix-term/src/ui/spinner.rs b/helix-term/src/ui/spinner.rs index 68965469d..379c4489f 100644 --- a/helix-term/src/ui/spinner.rs +++ b/helix-term/src/ui/spinner.rs @@ -11,7 +11,7 @@ impl ProgressSpinners { } pub fn get_or_create(&mut self, id: usize) -> &mut Spinner { - self.inner.entry(id).or_insert_with(Spinner::default) + self.inner.entry(id).or_default() } } From 1f916e65cff4459698d465b2f4558da1e1bf6e44 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Tue, 16 Jan 2024 13:59:48 -0500 Subject: [PATCH 066/796] Create helix-stdx crate for stdlib extensions helix-stdx is meant to carry extensions to the stdlib or low-level dependencies that are useful in all other crates. This commit starts with all of the path functions from helix-core and the CWD tracking that lived in helix-loader. The CWD tracking in helix-loader was previously unable to call the canonicalization functions in helix-core. Switching to our custom canonicalization code should make no noticeable difference though since `std::env::current_dir` returns a canonicalized path with symlinks resolved (at least on unix). --- Cargo.lock | 14 ++++++- Cargo.toml | 1 + helix-core/Cargo.toml | 2 +- helix-core/src/lib.rs | 1 - helix-loader/src/lib.rs | 44 ++-------------------- helix-lsp/Cargo.toml | 1 + helix-lsp/src/client.rs | 7 ++-- helix-lsp/src/lib.rs | 12 +++--- helix-stdx/Cargo.toml | 19 ++++++++++ helix-stdx/src/env.rs | 48 ++++++++++++++++++++++++ helix-stdx/src/lib.rs | 2 + {helix-core => helix-stdx}/src/path.rs | 34 +++++++++-------- {helix-core => helix-stdx}/tests/path.rs | 10 ++--- helix-term/Cargo.toml | 1 + helix-term/src/application.rs | 3 +- helix-term/src/commands.rs | 10 ++--- helix-term/src/commands/dap.rs | 2 +- helix-term/src/commands/lsp.rs | 7 ++-- helix-term/src/commands/typed.rs | 16 ++++---- helix-term/src/main.rs | 6 +-- helix-term/src/ui/mod.rs | 4 +- helix-term/src/ui/picker.rs | 2 +- helix-term/tests/test/commands/write.rs | 11 +++--- helix-term/tests/test/splits.rs | 8 ++-- helix-view/Cargo.toml | 1 + helix-view/src/document.rs | 6 +-- helix-view/src/editor.rs | 2 +- 27 files changed, 163 insertions(+), 111 deletions(-) create mode 100644 helix-stdx/Cargo.toml create mode 100644 helix-stdx/src/env.rs create mode 100644 helix-stdx/src/lib.rs rename {helix-core => helix-stdx}/src/path.rs (88%) rename {helix-core => helix-stdx}/tests/path.rs (93%) diff --git a/Cargo.lock b/Cargo.lock index da0dc3612..09bec59f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1060,6 +1060,7 @@ dependencies = [ "etcetera", "hashbrown 0.14.3", "helix-loader", + "helix-stdx", "imara-diff", "indoc", "log", @@ -1074,7 +1075,6 @@ dependencies = [ "slotmap", "smallvec", "smartstring", - "tempfile", "textwrap", "toml", "tree-sitter", @@ -1136,6 +1136,7 @@ dependencies = [ "helix-core", "helix-loader", "helix-parsec", + "helix-stdx", "log", "lsp-types", "parking_lot", @@ -1151,6 +1152,15 @@ dependencies = [ name = "helix-parsec" version = "23.10.0" +[[package]] +name = "helix-stdx" +version = "23.10.0" +dependencies = [ + "dunce", + "etcetera", + "tempfile", +] + [[package]] name = "helix-term" version = "23.10.0" @@ -1169,6 +1179,7 @@ dependencies = [ "helix-event", "helix-loader", "helix-lsp", + "helix-stdx", "helix-tui", "helix-vcs", "helix-view", @@ -1241,6 +1252,7 @@ dependencies = [ "helix-event", "helix-loader", "helix-lsp", + "helix-stdx", "helix-tui", "helix-vcs", "libc", diff --git a/Cargo.toml b/Cargo.toml index f59896ecb..91f6e7cae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ "helix-loader", "helix-vcs", "helix-parsec", + "helix-stdx", "xtask", ] diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index 07c801b89..42c88f4bc 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -16,6 +16,7 @@ unicode-lines = ["ropey/unicode_lines"] integration = [] [dependencies] +helix-stdx = { path = "../helix-stdx" } helix-loader = { path = "../helix-loader" } ropey = { version = "1.6.1", default-features = false, features = ["simd"] } @@ -55,4 +56,3 @@ parking_lot = "0.12" [dev-dependencies] quickcheck = { version = "1", default-features = false } indoc = "2.0.4" -tempfile = "3.9" diff --git a/helix-core/src/lib.rs b/helix-core/src/lib.rs index 0acdb2380..94802eba9 100644 --- a/helix-core/src/lib.rs +++ b/helix-core/src/lib.rs @@ -17,7 +17,6 @@ pub mod macros; pub mod match_brackets; pub mod movement; pub mod object; -pub mod path; mod position; pub mod search; pub mod selection; diff --git a/helix-loader/src/lib.rs b/helix-loader/src/lib.rs index 5337d6027..991504fb8 100644 --- a/helix-loader/src/lib.rs +++ b/helix-loader/src/lib.rs @@ -1,14 +1,13 @@ pub mod config; pub mod grammar; +use helix_stdx::{env::current_working_dir, path}; + use etcetera::base_strategy::{choose_base_strategy, BaseStrategy}; use std::path::{Path, PathBuf}; -use std::sync::RwLock; pub const VERSION_AND_GIT_HASH: &str = env!("VERSION_AND_GIT_HASH"); -static CWD: RwLock> = RwLock::new(None); - static RUNTIME_DIRS: once_cell::sync::Lazy> = once_cell::sync::Lazy::new(prioritize_runtime_dirs); @@ -16,31 +15,6 @@ static CONFIG_FILE: once_cell::sync::OnceCell = once_cell::sync::OnceCe static LOG_FILE: once_cell::sync::OnceCell = once_cell::sync::OnceCell::new(); -// Get the current working directory. -// This information is managed internally as the call to std::env::current_dir -// might fail if the cwd has been deleted. -pub fn current_working_dir() -> PathBuf { - if let Some(path) = &*CWD.read().unwrap() { - return path.clone(); - } - - let path = std::env::current_dir() - .and_then(dunce::canonicalize) - .expect("Couldn't determine current working directory"); - let mut cwd = CWD.write().unwrap(); - *cwd = Some(path.clone()); - - path -} - -pub fn set_current_working_dir(path: impl AsRef) -> std::io::Result<()> { - let path = dunce::canonicalize(path)?; - std::env::set_current_dir(&path)?; - let mut cwd = CWD.write().unwrap(); - *cwd = Some(path); - Ok(()) -} - pub fn initialize_config_file(specified_file: Option) { let config_file = specified_file.unwrap_or_else(default_config_file); ensure_parent_dir(&config_file); @@ -280,21 +254,9 @@ fn ensure_parent_dir(path: &Path) { mod merge_toml_tests { use std::str; - use super::{current_working_dir, merge_toml_values, set_current_working_dir}; + use super::merge_toml_values; use toml::Value; - #[test] - fn current_dir_is_set() { - let new_path = dunce::canonicalize(std::env::temp_dir()).unwrap(); - let cwd = current_working_dir(); - assert_ne!(cwd, new_path); - - set_current_working_dir(&new_path).expect("Couldn't set new path"); - - let cwd = current_working_dir(); - assert_eq!(cwd, new_path); - } - #[test] fn language_toml_map_merges() { const USER: &str = r#" diff --git a/helix-lsp/Cargo.toml b/helix-lsp/Cargo.toml index 851351e0e..510be6eec 100644 --- a/helix-lsp/Cargo.toml +++ b/helix-lsp/Cargo.toml @@ -13,6 +13,7 @@ homepage.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +helix-stdx = { path = "../helix-stdx" } helix-core = { path = "../helix-core" } helix-loader = { path = "../helix-loader" } helix-parsec = { path = "../helix-parsec" } diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 682d4db66..1af27c1d4 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -4,8 +4,9 @@ use crate::{ Call, Error, OffsetEncoding, Result, }; -use helix_core::{find_workspace, path, syntax::LanguageServerFeature, ChangeSet, Rope}; +use helix_core::{find_workspace, syntax::LanguageServerFeature, ChangeSet, Rope}; use helix_loader::{self, VERSION_AND_GIT_HASH}; +use helix_stdx::path; use lsp::{ notification::DidChangeWorkspaceFolders, CodeActionCapabilityResolveSupport, DidChangeWorkspaceFoldersParams, OneOf, PositionEncodingKind, WorkspaceFolder, @@ -68,7 +69,7 @@ impl Client { may_support_workspace: bool, ) -> bool { let (workspace, workspace_is_cwd) = find_workspace(); - let workspace = path::get_normalized_path(&workspace); + let workspace = path::normalize(workspace); let root = find_lsp_workspace( doc_path .and_then(|x| x.parent().and_then(|x| x.to_str())) @@ -204,7 +205,7 @@ impl Client { let (server_rx, server_tx, initialize_notify) = Transport::start(reader, writer, stderr, id, name.clone()); let (workspace, workspace_is_cwd) = find_workspace(); - let workspace = path::get_normalized_path(&workspace); + let workspace = path::normalize(workspace); let root = find_lsp_workspace( doc_path .and_then(|x| x.parent().and_then(|x| x.to_str())) diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index 83625897e..c99ec217b 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -11,10 +11,10 @@ pub use lsp::{Position, Url}; pub use lsp_types as lsp; use futures_util::stream::select_all::SelectAll; -use helix_core::{ - path, - syntax::{LanguageConfiguration, LanguageServerConfiguration, LanguageServerFeatures}, +use helix_core::syntax::{ + LanguageConfiguration, LanguageServerConfiguration, LanguageServerFeatures, }; +use helix_stdx::path; use tokio::sync::mpsc::UnboundedReceiver; use std::{ @@ -958,10 +958,10 @@ pub fn find_lsp_workspace( let mut file = if file.is_absolute() { file.to_path_buf() } else { - let current_dir = helix_loader::current_working_dir(); + let current_dir = helix_stdx::env::current_working_dir(); current_dir.join(file) }; - file = path::get_normalized_path(&file); + file = path::normalize(&file); if !file.starts_with(workspace) { return None; @@ -978,7 +978,7 @@ pub fn find_lsp_workspace( if root_dirs .iter() - .any(|root_dir| path::get_normalized_path(&workspace.join(root_dir)) == ancestor) + .any(|root_dir| path::normalize(workspace.join(root_dir)) == ancestor) { // if the worskapce is the cwd do not search any higher for workspaces // but specify diff --git a/helix-stdx/Cargo.toml b/helix-stdx/Cargo.toml new file mode 100644 index 000000000..216a3b407 --- /dev/null +++ b/helix-stdx/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "helix-stdx" +description = "Standard library extensions" +include = ["src/**/*", "README.md"] +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true +categories.workspace = true +repository.workspace = true +homepage.workspace = true + +[dependencies] +dunce = "1.0" +etcetera = "0.8" + +[dev-dependencies] +tempfile = "3.9" diff --git a/helix-stdx/src/env.rs b/helix-stdx/src/env.rs new file mode 100644 index 000000000..864ba828b --- /dev/null +++ b/helix-stdx/src/env.rs @@ -0,0 +1,48 @@ +use std::{ + path::{Path, PathBuf}, + sync::RwLock, +}; + +static CWD: RwLock> = RwLock::new(None); + +// Get the current working directory. +// This information is managed internally as the call to std::env::current_dir +// might fail if the cwd has been deleted. +pub fn current_working_dir() -> PathBuf { + if let Some(path) = &*CWD.read().unwrap() { + return path.clone(); + } + + let path = std::env::current_dir() + .map(crate::path::normalize) + .expect("Couldn't determine current working directory"); + let mut cwd = CWD.write().unwrap(); + *cwd = Some(path.clone()); + + path +} + +pub fn set_current_working_dir(path: impl AsRef) -> std::io::Result<()> { + let path = crate::path::canonicalize(path); + std::env::set_current_dir(&path)?; + let mut cwd = CWD.write().unwrap(); + *cwd = Some(path); + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::{current_working_dir, set_current_working_dir}; + + #[test] + fn current_dir_is_set() { + let new_path = dunce::canonicalize(std::env::temp_dir()).unwrap(); + let cwd = current_working_dir(); + assert_ne!(cwd, new_path); + + set_current_working_dir(&new_path).expect("Couldn't set new path"); + + let cwd = current_working_dir(); + assert_eq!(cwd, new_path); + } +} diff --git a/helix-stdx/src/lib.rs b/helix-stdx/src/lib.rs new file mode 100644 index 000000000..ae3c3a981 --- /dev/null +++ b/helix-stdx/src/lib.rs @@ -0,0 +1,2 @@ +pub mod env; +pub mod path; diff --git a/helix-core/src/path.rs b/helix-stdx/src/path.rs similarity index 88% rename from helix-core/src/path.rs rename to helix-stdx/src/path.rs index 0cf6f812f..5746657c3 100644 --- a/helix-core/src/path.rs +++ b/helix-stdx/src/path.rs @@ -1,6 +1,9 @@ -use etcetera::home_dir; +pub use etcetera::home_dir; + use std::path::{Component, Path, PathBuf}; +use crate::env::current_working_dir; + /// Replaces users home directory from `path` with tilde `~` if the directory /// is available, otherwise returns the path unchanged. pub fn fold_home_dir(path: &Path) -> PathBuf { @@ -16,7 +19,8 @@ pub fn fold_home_dir(path: &Path) -> PathBuf { /// Expands tilde `~` into users home directory if available, otherwise returns the path /// unchanged. The tilde will only be expanded when present as the first component of the path /// and only slash follows it. -pub fn expand_tilde(path: &Path) -> PathBuf { +pub fn expand_tilde(path: impl AsRef) -> PathBuf { + let path = path.as_ref(); let mut components = path.components().peekable(); if let Some(Component::Normal(c)) = components.peek() { if c == &"~" { @@ -33,8 +37,8 @@ pub fn expand_tilde(path: &Path) -> PathBuf { /// Normalize a path without resolving symlinks. // Strategy: start from the first component and move up. Cannonicalize previous path, // join component, cannonicalize new path, strip prefix and join to the final result. -pub fn get_normalized_path(path: &Path) -> PathBuf { - let mut components = path.components().peekable(); +pub fn normalize(path: impl AsRef) -> PathBuf { + let mut components = path.as_ref().components().peekable(); let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { components.next(); PathBuf::from(c.as_os_str()) @@ -104,22 +108,22 @@ pub fn get_normalized_path(path: &Path) -> PathBuf { /// /// This function is used instead of [`std::fs::canonicalize`] because we don't want to verify /// here if the path exists, just normalize it's components. -pub fn get_canonicalized_path(path: &Path) -> PathBuf { +pub fn canonicalize(path: impl AsRef) -> PathBuf { let path = expand_tilde(path); let path = if path.is_relative() { - helix_loader::current_working_dir().join(path) + current_working_dir().join(path) } else { path }; - get_normalized_path(path.as_path()) + normalize(path) } -pub fn get_relative_path(path: &Path) -> PathBuf { - let path = PathBuf::from(path); +pub fn get_relative_path(path: impl AsRef) -> PathBuf { + let path = PathBuf::from(path.as_ref()); let path = if path.is_absolute() { - let cwdir = get_normalized_path(&helix_loader::current_working_dir()); - get_normalized_path(&path) + let cwdir = normalize(current_working_dir()); + normalize(&path) .strip_prefix(cwdir) .map(PathBuf::from) .unwrap_or(path) @@ -135,8 +139,8 @@ pub fn get_relative_path(path: &Path) -> PathBuf { /// Also strip the current working directory from the beginning of the path. /// Note that this function does not check if the truncated path is unambiguous. /// -/// ``` -/// use helix_core::path::get_truncated_path; +/// ``` +/// use helix_stdx::path::get_truncated_path; /// use std::path::Path; /// /// assert_eq!( @@ -158,8 +162,8 @@ pub fn get_relative_path(path: &Path) -> PathBuf { /// assert_eq!(get_truncated_path("").as_path(), Path::new("")); /// ``` /// -pub fn get_truncated_path>(path: P) -> PathBuf { - let cwd = helix_loader::current_working_dir(); +pub fn get_truncated_path(path: impl AsRef) -> PathBuf { + let cwd = current_working_dir(); let path = path .as_ref() .strip_prefix(cwd) diff --git a/helix-core/tests/path.rs b/helix-stdx/tests/path.rs similarity index 93% rename from helix-core/tests/path.rs rename to helix-stdx/tests/path.rs index cbda5e1ab..cc3c15cba 100644 --- a/helix-core/tests/path.rs +++ b/helix-stdx/tests/path.rs @@ -6,7 +6,7 @@ use std::{ path::{Component, Path, PathBuf}, }; -use helix_core::path::get_normalized_path; +use helix_stdx::path; use tempfile::Builder; // Paths on Windows are almost always case-insensitive. @@ -34,7 +34,7 @@ fn test_case_folding_windows() -> Result<(), Box> { ); let test_path = root_without_prefix.join(lowercase_case); assert_eq!( - get_normalized_path(&test_path), + path::normalize(&test_path), case.path().strip_prefix(&tmp_prefix)? ); @@ -80,7 +80,7 @@ fn test_normalize_path() -> Result<(), Box> { // root/link let path = link.strip_prefix(&tmp_prefix)?; assert_eq!( - get_normalized_path(path), + path::normalize(path), path, "input {:?} and symlink last component shouldn't be resolved", path @@ -98,7 +98,7 @@ fn test_normalize_path() -> Result<(), Box> { .unwrap() .join(Component::ParentDir); assert_eq!( - get_normalized_path(&path), + path::normalize(&path), expected, "input {:?} and \"..\" should not erase the simlink that goes ahead", &path @@ -118,7 +118,7 @@ fn test_normalize_path() -> Result<(), Box> { .unwrap() .join(Component::ParentDir) .join(Component::ParentDir); - assert_eq!(get_normalized_path(&path), expected, "input {:?}", &path); + assert_eq!(path::normalize(&path), expected, "input {:?}", &path); Ok(()) } diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 80bda2b6c..21c35553c 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -23,6 +23,7 @@ name = "hx" path = "src/main.rs" [dependencies] +helix-stdx = { path = "../helix-stdx" } helix-core = { path = "../helix-core" } helix-event = { path = "../helix-event" } helix-view = { path = "../helix-view" } diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 1b0a06dd9..290441b41 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -1,11 +1,12 @@ use arc_swap::{access::Map, ArcSwap}; use futures_util::Stream; -use helix_core::{path::get_relative_path, pos_at_coords, syntax, Selection}; +use helix_core::{pos_at_coords, syntax, Selection}; use helix_lsp::{ lsp::{self, notification::Notification}, util::lsp_range_to_range, LspProgressMap, }; +use helix_stdx::path::get_relative_path; use helix_view::{ align_view, document::DocumentSavedEventResult, diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 937326f6e..53783e4e3 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2169,7 +2169,7 @@ fn global_search(cx: &mut Context) { type Data = Option; fn format(&self, current_path: &Self::Data) -> Row { - let relative_path = helix_core::path::get_relative_path(&self.path) + let relative_path = helix_stdx::path::get_relative_path(&self.path) .to_string_lossy() .into_owned(); if current_path @@ -2218,7 +2218,7 @@ fn global_search(cx: &mut Context) { .case_smart(smart_case) .build(regex.as_str()) { - let search_root = helix_loader::current_working_dir(); + let search_root = helix_stdx::env::current_working_dir(); if !search_root.exists() { cx.editor .set_error("Current working directory does not exist"); @@ -2731,7 +2731,7 @@ fn file_picker_in_current_buffer_directory(cx: &mut Context) { } fn file_picker_in_current_directory(cx: &mut Context) { - let cwd = helix_loader::current_working_dir(); + let cwd = helix_stdx::env::current_working_dir(); if !cwd.exists() { cx.editor .set_error("Current working directory does not exist"); @@ -2759,7 +2759,7 @@ fn buffer_picker(cx: &mut Context) { let path = self .path .as_deref() - .map(helix_core::path::get_relative_path); + .map(helix_stdx::path::get_relative_path); let path = match path.as_deref().and_then(Path::to_str) { Some(path) => path, None => SCRATCH_BUFFER_NAME, @@ -2826,7 +2826,7 @@ fn jumplist_picker(cx: &mut Context) { let path = self .path .as_deref() - .map(helix_core::path::get_relative_path); + .map(helix_stdx::path::get_relative_path); let path = match path.as_deref().and_then(Path::to_str) { Some(path) => path, None => SCRATCH_BUFFER_NAME, diff --git a/helix-term/src/commands/dap.rs b/helix-term/src/commands/dap.rs index dec25cbd9..d62b0a4e5 100644 --- a/helix-term/src/commands/dap.rs +++ b/helix-term/src/commands/dap.rs @@ -217,7 +217,7 @@ pub fn dap_start_impl( } } - args.insert("cwd", to_value(helix_loader::current_working_dir())?); + args.insert("cwd", to_value(helix_stdx::env::current_working_dir())?); let args = to_value(args).unwrap(); diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index 0096e6aa9..051cdcd35 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -17,9 +17,8 @@ use tui::{ use super::{align_view, push_jump, Align, Context, Editor, Open}; -use helix_core::{ - path, syntax::LanguageServerFeature, text_annotations::InlineAnnotation, Selection, -}; +use helix_core::{syntax::LanguageServerFeature, text_annotations::InlineAnnotation, Selection}; +use helix_stdx::path; use helix_view::{ document::{DocumentInlayHints, DocumentInlayHintsId, Mode}, editor::Action, @@ -1018,7 +1017,7 @@ fn goto_impl( locations: Vec, offset_encoding: OffsetEncoding, ) { - let cwdir = helix_loader::current_working_dir(); + let cwdir = helix_stdx::env::current_working_dir(); match locations.as_slice() { [location] => { diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index eb88e0411..ee02a7d25 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -7,7 +7,7 @@ use super::*; use helix_core::fuzzy::fuzzy_match; use helix_core::indent::MAX_INDENT; -use helix_core::{encoding, line_ending, path::get_canonicalized_path, shellwords::Shellwords}; +use helix_core::{encoding, line_ending, shellwords::Shellwords}; use helix_lsp::{OffsetEncoding, Url}; use helix_view::document::DEFAULT_LANGUAGE_NAME; use helix_view::editor::{Action, CloseError, ConfigEvent}; @@ -111,7 +111,7 @@ fn open(cx: &mut compositor::Context, args: &[Cow], event: PromptEvent) -> ensure!(!args.is_empty(), "wrong argument count"); for arg in args { let (path, pos) = args::parse_file(arg); - let path = helix_core::path::expand_tilde(&path); + let path = helix_stdx::path::expand_tilde(&path); // If the path is a directory, open a file picker on that directory and update the status // message if let Ok(true) = std::fs::canonicalize(&path).map(|p| p.is_dir()) { @@ -1079,18 +1079,17 @@ fn change_current_directory( return Ok(()); } - let dir = helix_core::path::expand_tilde( + let dir = helix_stdx::path::expand_tilde( args.first() .context("target directory not provided")? - .as_ref() .as_ref(), ); - helix_loader::set_current_working_dir(dir)?; + helix_stdx::env::set_current_working_dir(dir)?; cx.editor.set_status(format!( "Current working directory is now {}", - helix_loader::current_working_dir().display() + helix_stdx::env::current_working_dir().display() )); Ok(()) } @@ -1104,7 +1103,7 @@ fn show_current_directory( return Ok(()); } - let cwd = helix_loader::current_working_dir(); + let cwd = helix_stdx::env::current_working_dir(); let message = format!("Current working directory is {}", cwd.display()); if cwd.exists() { @@ -2409,7 +2408,8 @@ fn move_buffer( ensure!(args.len() == 1, format!(":move takes one argument")); let doc = doc!(cx.editor); - let new_path = get_canonicalized_path(&PathBuf::from(args.first().unwrap().to_string())); + let new_path = + helix_stdx::path::canonicalize(&PathBuf::from(args.first().unwrap().to_string())); let old_path = doc .path() .ok_or_else(|| anyhow!("Scratch buffer cannot be moved. Use :write instead"))? diff --git a/helix-term/src/main.rs b/helix-term/src/main.rs index a62c54a40..132ee796f 100644 --- a/helix-term/src/main.rs +++ b/helix-term/src/main.rs @@ -118,16 +118,16 @@ FLAGS: // Before setting the working directory, resolve all the paths in args.files for (path, _) in args.files.iter_mut() { - *path = helix_core::path::get_canonicalized_path(path); + *path = helix_stdx::path::canonicalize(&path); } // NOTE: Set the working directory early so the correct configuration is loaded. Be aware that // Application::new() depends on this logic so it must be updated if this changes. if let Some(path) = &args.working_directory { - helix_loader::set_current_working_dir(path)?; + helix_stdx::env::set_current_working_dir(path)?; } else if let Some((path, _)) = args.files.first().filter(|p| p.0.is_dir()) { // If the first file is a directory, it will be the working directory unless -w was specified - helix_loader::set_current_working_dir(path)?; + helix_stdx::env::set_current_working_dir(path)?; } let config = match Config::load_default() { diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 660bbfea3..efa2473e0 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -409,7 +409,7 @@ pub mod completers { use std::path::Path; let is_tilde = input == "~"; - let path = helix_core::path::expand_tilde(Path::new(input)); + let path = helix_stdx::path::expand_tilde(Path::new(input)); let (dir, file_name) = if input.ends_with(std::path::MAIN_SEPARATOR) { (path, None) @@ -430,7 +430,7 @@ pub mod completers { match path.parent() { Some(path) if !path.as_os_str().is_empty() => path.to_path_buf(), // Path::new("h")'s parent is Some("")... - _ => helix_loader::current_working_dir(), + _ => helix_stdx::env::current_working_dir(), } }; diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 08a367ba9..4be5a11ef 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -63,7 +63,7 @@ impl PathOrId { fn get_canonicalized(self) -> Self { use PathOrId::*; match self { - Path(path) => Path(helix_core::path::get_canonicalized_path(&path)), + Path(path) => Path(helix_stdx::path::canonicalize(path)), Id(id) => Id(id), } } diff --git a/helix-term/tests/test/commands/write.rs b/helix-term/tests/test/commands/write.rs index 376ba5e7b..adc721c5f 100644 --- a/helix-term/tests/test/commands/write.rs +++ b/helix-term/tests/test/commands/write.rs @@ -3,7 +3,8 @@ use std::{ ops::RangeInclusive, }; -use helix_core::{diagnostic::Severity, path::get_normalized_path}; +use helix_core::diagnostic::Severity; +use helix_stdx::path; use helix_view::doc; use super::*; @@ -23,7 +24,7 @@ async fn test_write_quit_fail() -> anyhow::Result<()> { assert_eq!(1, docs.len()); let doc = docs.pop().unwrap(); - assert_eq!(Some(&get_normalized_path(file.path())), doc.path()); + assert_eq!(Some(&path::normalize(file.path())), doc.path()); assert_eq!(&Severity::Error, app.editor.get_status().unwrap().1); }), false, @@ -269,7 +270,7 @@ async fn test_write_scratch_to_new_path() -> anyhow::Result<()> { assert_eq!(1, docs.len()); let doc = docs.pop().unwrap(); - assert_eq!(Some(&get_normalized_path(file.path())), doc.path()); + assert_eq!(Some(&path::normalize(file.path())), doc.path()); }), false, ) @@ -341,7 +342,7 @@ async fn test_write_new_path() -> anyhow::Result<()> { Some(&|app| { let doc = doc!(app.editor); assert!(!app.editor.is_err()); - assert_eq!(&get_normalized_path(file1.path()), doc.path().unwrap()); + assert_eq!(&path::normalize(file1.path()), doc.path().unwrap()); }), ), ( @@ -349,7 +350,7 @@ async fn test_write_new_path() -> anyhow::Result<()> { Some(&|app| { let doc = doc!(app.editor); assert!(!app.editor.is_err()); - assert_eq!(&get_normalized_path(file2.path()), doc.path().unwrap()); + assert_eq!(&path::normalize(file2.path()), doc.path().unwrap()); assert!(app.editor.document_by_path(file1.path()).is_none()); }), ), diff --git a/helix-term/tests/test/splits.rs b/helix-term/tests/test/splits.rs index f010c86ba..3b66c0486 100644 --- a/helix-term/tests/test/splits.rs +++ b/helix-term/tests/test/splits.rs @@ -1,6 +1,6 @@ use super::*; -use helix_core::path::get_normalized_path; +use helix_stdx::path; #[tokio::test(flavor = "multi_thread")] async fn test_split_write_quit_all() -> anyhow::Result<()> { @@ -27,21 +27,21 @@ async fn test_split_write_quit_all() -> anyhow::Result<()> { let doc1 = docs .iter() - .find(|doc| doc.path().unwrap() == &get_normalized_path(file1.path())) + .find(|doc| doc.path().unwrap() == &path::normalize(file1.path())) .unwrap(); assert_eq!("hello1", doc1.text().to_string()); let doc2 = docs .iter() - .find(|doc| doc.path().unwrap() == &get_normalized_path(file2.path())) + .find(|doc| doc.path().unwrap() == &path::normalize(file2.path())) .unwrap(); assert_eq!("hello2", doc2.text().to_string()); let doc3 = docs .iter() - .find(|doc| doc.path().unwrap() == &get_normalized_path(file3.path())) + .find(|doc| doc.path().unwrap() == &path::normalize(file3.path())) .unwrap(); assert_eq!("hello3", doc3.text().to_string()); diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index db53b54cc..0dc18b373 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -15,6 +15,7 @@ default = [] term = ["crossterm"] [dependencies] +helix-stdx = { path = "../helix-stdx" } helix-core = { path = "../helix-core" } helix-event = { path = "../helix-event" } helix-loader = { path = "../helix-loader" } diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 0de0cd172..6473c2d18 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -855,7 +855,7 @@ impl Document { let text = self.text().clone(); let path = match path { - Some(path) => helix_core::path::get_canonicalized_path(&path), + Some(path) => helix_stdx::path::canonicalize(path), None => { if self.path.is_none() { bail!("Can't save with no path set!"); @@ -1049,7 +1049,7 @@ impl Document { } pub fn set_path(&mut self, path: Option<&Path>) { - let path = path.map(helix_core::path::get_canonicalized_path); + let path = path.map(helix_stdx::path::canonicalize); // if parent doesn't exist we still want to open the document // and error out when document is saved @@ -1672,7 +1672,7 @@ impl Document { pub fn relative_path(&self) -> Option { self.path .as_deref() - .map(helix_core::path::get_relative_path) + .map(helix_stdx::path::get_relative_path) } pub fn display_name(&self) -> Cow<'static, str> { diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index f13df2135..0ab4be8b2 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -1464,7 +1464,7 @@ impl Editor { // ??? possible use for integration tests pub fn open(&mut self, path: &Path, action: Action) -> Result { - let path = helix_core::path::get_canonicalized_path(path); + let path = helix_stdx::path::canonicalize(path); let id = self.document_by_path(&path).map(|doc| doc.id); let id = if let Some(id) = id { From 1bc7aac7805285cea9ef6dc8adcaabf87a18ce67 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Tue, 16 Jan 2024 14:00:06 -0500 Subject: [PATCH 067/796] Use helix-stdx tilde expansion and normalization for HELIX_RUNTIME paths Previously this wasn't possible since helix-core depends on helix-loader, so helix-loader couldn't use helix-core's path extensions. We use the path normalization/canonicalization for the runtime directory provided by the HELIX_RUNTIME environment variable. This improves a scenario where you set a path containing a tilde. Now that path will be expanded and normalized. --- Cargo.lock | 1 + helix-loader/Cargo.toml | 2 ++ helix-loader/src/lib.rs | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 09bec59f0..9884dadfb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1114,6 +1114,7 @@ dependencies = [ "cc", "dunce", "etcetera", + "helix-stdx", "libloading", "log", "once_cell", diff --git a/helix-loader/Cargo.toml b/helix-loader/Cargo.toml index 187475385..08da7f295 100644 --- a/helix-loader/Cargo.toml +++ b/helix-loader/Cargo.toml @@ -15,6 +15,8 @@ name = "hx-loader" path = "src/main.rs" [dependencies] +helix-stdx = { path = "../helix-stdx" } + anyhow = "1" serde = { version = "1.0", features = ["derive"] } toml = "0.7" diff --git a/helix-loader/src/lib.rs b/helix-loader/src/lib.rs index 991504fb8..f8fac6703 100644 --- a/helix-loader/src/lib.rs +++ b/helix-loader/src/lib.rs @@ -53,7 +53,8 @@ fn prioritize_runtime_dirs() -> Vec { rt_dirs.push(conf_rt_dir); if let Ok(dir) = std::env::var("HELIX_RUNTIME") { - rt_dirs.push(dir.into()); + let dir = path::expand_tilde(dir); + rt_dirs.push(path::normalize(dir)); } // If this variable is set during build time, it will always be included From 9c56afeff36489ff73a9b160fe7d57360a3f9952 Mon Sep 17 00:00:00 2001 From: Ahmed Hagi Date: Thu, 18 Jan 2024 12:34:06 +0000 Subject: [PATCH 068/796] Handle failure when enabling bracketed paste (#9353) * match instead of crash * pulling bracketedpaste out, refactor, tracking for bracketed paste * sending disable bracketed paste only when supports true * move disable bracketed paste to throwaway --- helix-tui/src/backend/crossterm.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/helix-tui/src/backend/crossterm.rs b/helix-tui/src/backend/crossterm.rs index c55ab6bbd..88e70f32e 100644 --- a/helix-tui/src/backend/crossterm.rs +++ b/helix-tui/src/backend/crossterm.rs @@ -79,6 +79,7 @@ pub struct CrosstermBackend { capabilities: Capabilities, supports_keyboard_enhancement_protocol: OnceCell, mouse_capture_enabled: bool, + supports_bracketed_paste: bool, } impl CrosstermBackend @@ -91,6 +92,7 @@ where capabilities: Capabilities::from_env_or_default(config), supports_keyboard_enhancement_protocol: OnceCell::new(), mouse_capture_enabled: false, + supports_bracketed_paste: true, } } @@ -134,9 +136,16 @@ where execute!( self.buffer, terminal::EnterAlternateScreen, - EnableBracketedPaste, EnableFocusChange )?; + match execute!(self.buffer, EnableBracketedPaste,) { + Err(err) if err.kind() == io::ErrorKind::Unsupported => { + log::warn!("Bracketed paste is not supported on this terminal."); + self.supports_bracketed_paste = false; + } + Err(err) => return Err(err), + Ok(_) => (), + }; execute!(self.buffer, terminal::Clear(terminal::ClearType::All))?; if config.enable_mouse_capture { execute!(self.buffer, EnableMouseCapture)?; @@ -177,9 +186,11 @@ where if self.supports_keyboard_enhancement_protocol() { execute!(self.buffer, PopKeyboardEnhancementFlags)?; } + if self.supports_bracketed_paste { + execute!(self.buffer, DisableBracketedPaste,)?; + } execute!( self.buffer, - DisableBracketedPaste, DisableFocusChange, terminal::LeaveAlternateScreen )?; @@ -195,12 +206,8 @@ where // disable without calling enable previously let _ = execute!(stdout, DisableMouseCapture); let _ = execute!(stdout, PopKeyboardEnhancementFlags); - execute!( - stdout, - DisableBracketedPaste, - DisableFocusChange, - terminal::LeaveAlternateScreen - )?; + let _ = execute!(stdout, DisableBracketedPaste); + execute!(stdout, DisableFocusChange, terminal::LeaveAlternateScreen)?; terminal::disable_raw_mode() } From f5f08becef0c27f183a7772f6ec6f5442eca2bab Mon Sep 17 00:00:00 2001 From: Matthew Toohey Date: Mon, 22 Jan 2024 09:06:20 -0500 Subject: [PATCH 069/796] Fix typo in string representation of GotoReference (#9395) --- helix-core/src/syntax.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 83bd09b4d..e543df06e 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -263,7 +263,7 @@ impl Display for LanguageServerFeature { GotoDeclaration => "goto-declaration", GotoDefinition => "goto-definition", GotoTypeDefinition => "goto-type-definition", - GotoReference => "goto-type-definition", + GotoReference => "goto-reference", GotoImplementation => "goto-implementation", SignatureHelp => "signature-help", Hover => "hover", From 52a43bcdfcc258a2871be468a3e31496dd2d80c8 Mon Sep 17 00:00:00 2001 From: woojiq <122799969+woojiq@users.noreply.github.com> Date: Mon, 22 Jan 2024 20:51:12 +0200 Subject: [PATCH 070/796] bash, make, css: highlight and indent queries improvement (#9393) * highlights(bash): rework keywords section * Use more specified scope when possible for keywords like @keyword.repeat. * Add more keywords like "local" or "unsetenv". Limitation: * Bash doesn't allow you to have a local variable outside of a function, so maybe we need to have better queries to not highlight the local in this case. * If we name a function with a keyword (such as unset or local), it will use the highlight scope "keyword" instead of "function". * indents(css, make): add basic queries * Despite the fact that queries look simple, they improve indentation in some edge cases that helix couldn't handle correctly by default. --- book/src/generated/lang-support.md | 4 ++-- runtime/queries/bash/highlights.scm | 34 ++++++++++++++++++++--------- runtime/queries/css/indents.scm | 7 ++++++ runtime/queries/make/indents.scm | 8 +++++++ 4 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 runtime/queries/css/indents.scm create mode 100644 runtime/queries/make/indents.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index ee01c4030..6fedace63 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -21,7 +21,7 @@ | cpon | ✓ | | ✓ | | | cpp | ✓ | ✓ | ✓ | `clangd` | | crystal | ✓ | ✓ | | `crystalline` | -| css | ✓ | | | `vscode-css-language-server` | +| css | ✓ | | ✓ | `vscode-css-language-server` | | cue | ✓ | | | `cuelsp` | | d | ✓ | ✓ | ✓ | `serve-d` | | dart | ✓ | | ✓ | `dart` | @@ -97,7 +97,7 @@ | log | ✓ | | | | | lpf | ✓ | | | | | lua | ✓ | ✓ | ✓ | `lua-language-server` | -| make | ✓ | | | | +| make | ✓ | | ✓ | | | markdoc | ✓ | | | `markdoc-ls` | | markdown | ✓ | | | `marksman` | | markdown.inline | ✓ | | | | diff --git a/runtime/queries/bash/highlights.scm b/runtime/queries/bash/highlights.scm index 3f2df6386..92d61e8b8 100644 --- a/runtime/queries/bash/highlights.scm +++ b/runtime/queries/bash/highlights.scm @@ -10,23 +10,37 @@ (variable_name) @variable.other.member [ + "if" + "then" + "else" + "elif" + "fi" "case" + "in" + "esac" +] @keyword.control.conditional + +[ + "for" "do" "done" - "elif" - "else" - "esac" + "select" + "until" + "while" +] @keyword.control.repeat + +[ + "declare" + "typeset" "export" - "fi" - "for" - "function" - "if" - "in" + "readonly" + "local" "unset" - "while" - "then" + "unsetenv" ] @keyword +"function" @keyword.function + (comment) @comment (function_definition name: (word) @function) diff --git a/runtime/queries/css/indents.scm b/runtime/queries/css/indents.scm new file mode 100644 index 000000000..1dfd977d9 --- /dev/null +++ b/runtime/queries/css/indents.scm @@ -0,0 +1,7 @@ +[ + (block) +] @indent + +[ + "}" +] @outdent diff --git a/runtime/queries/make/indents.scm b/runtime/queries/make/indents.scm new file mode 100644 index 000000000..42b2c60e6 --- /dev/null +++ b/runtime/queries/make/indents.scm @@ -0,0 +1,8 @@ +[ + (define_directive) + (rule) +] @indent + +[ + "endef" +] @outdent From 9ed3dc52e0d56f0f0fd721d7f10da5f935f56ef9 Mon Sep 17 00:00:00 2001 From: Jaakko Paju Date: Mon, 22 Jan 2024 20:51:56 +0200 Subject: [PATCH 071/796] Update Scala tree-sitter grammar (#9348) * Update Scala tree-sitter grammar * Support block comments Modify comment handling in textobjects and highlights to support new TS-scala node type 'block_comment' --- languages.toml | 2 +- runtime/queries/scala/highlights.scm | 2 +- runtime/queries/scala/textobjects.scm | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/languages.toml b/languages.toml index 1638cac49..a785828c1 100644 --- a/languages.toml +++ b/languages.toml @@ -1393,7 +1393,7 @@ language-servers = [ "metals" ] [[grammar]] name = "scala" -source = { git = "https://github.com/tree-sitter/tree-sitter-scala", rev = "23d21310fe4ab4b3273e7a6810e781224a3e7fe1" } +source = { git = "https://github.com/tree-sitter/tree-sitter-scala", rev = "7891815f42dca9ed6aeb464c2edc39d479ab965c" } [[language]] name = "dockerfile" diff --git a/runtime/queries/scala/highlights.scm b/runtime/queries/scala/highlights.scm index 67603fdda..40b230ec4 100644 --- a/runtime/queries/scala/highlights.scm +++ b/runtime/queries/scala/highlights.scm @@ -263,7 +263,7 @@ "return" @keyword.control.return -(comment) @comment +[(comment) (block_comment)] @comment ;; `case` is a conditional keyword in case_block diff --git a/runtime/queries/scala/textobjects.scm b/runtime/queries/scala/textobjects.scm index fe0b8c255..6e551c417 100644 --- a/runtime/queries/scala/textobjects.scm +++ b/runtime/queries/scala/textobjects.scm @@ -51,8 +51,8 @@ ; Comment queries -(comment) @comment.inside -(comment) @comment.around ; Does not match consecutive block comments +[(comment) (block_comment)] @comment.inside +[(comment) (block_comment)] @comment.around ; Does not match consecutive block comments ; Test queries From 7d7ace551cd58f0b6d65af7a6dfa8f896d94724a Mon Sep 17 00:00:00 2001 From: Boris Verkhovskiy Date: Mon, 22 Jan 2024 15:19:28 -0800 Subject: [PATCH 072/796] Highlight .bash_history as bash (#9401) --- languages.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/languages.toml b/languages.toml index a785828c1..4ff529c47 100644 --- a/languages.toml +++ b/languages.toml @@ -796,6 +796,7 @@ file-types = [ "sh", "bash", "zsh", + ".bash_history", ".bash_login", ".bash_logout", ".bash_profile", From 13ed4f6c4748019787d24c2b686d417b71604242 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Fri, 1 Dec 2023 00:03:26 +0100 Subject: [PATCH 073/796] Add hook/event system --- .cargo/config.toml | 14 +++ Cargo.lock | 6 + helix-event/Cargo.toml | 17 ++- helix-event/src/cancel.rs | 19 ++++ helix-event/src/debounce.rs | 67 +++++++++++ helix-event/src/hook.rs | 91 +++++++++++++++ helix-event/src/lib.rs | 201 ++++++++++++++++++++++++++++++++- helix-event/src/redraw.rs | 24 ++-- helix-event/src/registry.rs | 131 +++++++++++++++++++++ helix-event/src/runtime.rs | 88 +++++++++++++++ helix-event/src/status.rs | 68 +++++++++++ helix-event/src/test.rs | 90 +++++++++++++++ helix-term/Cargo.toml | 2 +- helix-term/src/application.rs | 24 +++- helix-term/src/commands.rs | 26 +++-- helix-term/src/events.rs | 20 ++++ helix-term/src/handlers.rs | 15 +++ helix-term/src/job.rs | 55 +++++++-- helix-term/src/lib.rs | 4 + helix-term/src/ui/editor.rs | 24 +++- helix-view/src/document.rs | 18 +++ helix-view/src/editor.rs | 2 + helix-view/src/events.rs | 9 ++ helix-view/src/handlers.rs | 12 ++ helix-view/src/handlers/lsp.rs | 39 +++++++ helix-view/src/lib.rs | 8 +- 26 files changed, 1024 insertions(+), 50 deletions(-) create mode 100644 helix-event/src/cancel.rs create mode 100644 helix-event/src/debounce.rs create mode 100644 helix-event/src/hook.rs create mode 100644 helix-event/src/registry.rs create mode 100644 helix-event/src/runtime.rs create mode 100644 helix-event/src/status.rs create mode 100644 helix-event/src/test.rs create mode 100644 helix-term/src/events.rs create mode 100644 helix-term/src/handlers.rs create mode 100644 helix-view/src/events.rs create mode 100644 helix-view/src/handlers.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index b016eca31..af4312dc8 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,17 @@ +# we use tokio_unstable to enable runtime::Handle::id so we can separate +# globals from multiple parallel tests. If that function ever does get removed +# its possible to replace (with some additional overhead and effort) +# Annoyingly build.rustflags doesn't work here because it gets overwritten +# if people have their own global target.<..> config (for example to enable mold) +# specifying flags this way is more robust as they get merged +# This still gets overwritten by RUST_FLAGS though, luckily it shouldn't be necessary +# to set those most of the time. If downstream does overwrite this its not a huge +# deal since it will only break tests anyway +[target."cfg(all())"] +rustflags = ["--cfg", "tokio_unstable", "-C", "target-feature=-crt-static"] + + [alias] xtask = "run --package xtask --" integration-test = "test --features integration --profile integration --workspace --test integration" + diff --git a/Cargo.lock b/Cargo.lock index 9884dadfb..4969ef46b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1102,6 +1102,12 @@ dependencies = [ name = "helix-event" version = "23.10.0" dependencies = [ + "ahash", + "anyhow", + "futures-executor", + "hashbrown 0.14.3", + "log", + "once_cell", "parking_lot", "tokio", ] diff --git a/helix-event/Cargo.toml b/helix-event/Cargo.toml index c20328246..a5c88e93d 100644 --- a/helix-event/Cargo.toml +++ b/helix-event/Cargo.toml @@ -12,5 +12,18 @@ homepage.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "parking_lot"] } -parking_lot = { version = "0.12", features = ["send_guard"] } +ahash = "0.8.3" +hashbrown = "0.14.0" +tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "parking_lot", "macros"] } +# the event registry is essentially read only but must be an rwlock so we can +# setup new events on initialization, hardware-lock-elision hugely benefits this case +# as it essentially makes the lock entirely free as long as there is no writes +parking_lot = { version = "0.12", features = ["hardware-lock-elision"] } +once_cell = "1.18" + +anyhow = "1" +log = "0.4" +futures-executor = "0.3.28" + +[features] +integration_test = [] diff --git a/helix-event/src/cancel.rs b/helix-event/src/cancel.rs new file mode 100644 index 000000000..f027be80e --- /dev/null +++ b/helix-event/src/cancel.rs @@ -0,0 +1,19 @@ +use std::future::Future; + +pub use oneshot::channel as cancelation; +use tokio::sync::oneshot; + +pub type CancelTx = oneshot::Sender<()>; +pub type CancelRx = oneshot::Receiver<()>; + +pub async fn cancelable_future(future: impl Future, cancel: CancelRx) -> Option { + tokio::select! { + biased; + _ = cancel => { + None + } + res = future => { + Some(res) + } + } +} diff --git a/helix-event/src/debounce.rs b/helix-event/src/debounce.rs new file mode 100644 index 000000000..30b6f671b --- /dev/null +++ b/helix-event/src/debounce.rs @@ -0,0 +1,67 @@ +//! Utilities for declaring an async (usually debounced) hook + +use std::time::Duration; + +use futures_executor::block_on; +use tokio::sync::mpsc::{self, error::TrySendError, Sender}; +use tokio::time::Instant; + +/// Async hooks provide a convenient framework for implementing (debounced) +/// async event handlers. Most synchronous event hooks will likely need to +/// debounce their events, coordinate multiple different hooks and potentially +/// track some state. `AsyncHooks` facilitate these use cases by running as +/// a background tokio task that waits for events (usually an enum) to be +/// sent through a channel. +pub trait AsyncHook: Sync + Send + 'static + Sized { + type Event: Sync + Send + 'static; + /// Called immediately whenever an event is received, this function can + /// consume the event immediately or debounce it. In case of debouncing, + /// it can either define a new debounce timeout or continue the current one + fn handle_event(&mut self, event: Self::Event, timeout: Option) -> Option; + + /// Called whenever the debounce timeline is reached + fn finish_debounce(&mut self); + + fn spawn(self) -> mpsc::Sender { + // the capacity doesn't matter too much here, unless the cpu is totally overwhelmed + // the cap will never be reached since we always immediately drain the channel + // so it should only be reached in case of total CPU overload. + // However, a bounded channel is much more efficient so it's nice to use here + let (tx, rx) = mpsc::channel(128); + tokio::spawn(run(self, rx)); + tx + } +} + +async fn run(mut hook: Hook, mut rx: mpsc::Receiver) { + let mut deadline = None; + loop { + let event = match deadline { + Some(deadline_) => { + let res = tokio::time::timeout_at(deadline_, rx.recv()).await; + match res { + Ok(event) => event, + Err(_) => { + hook.finish_debounce(); + deadline = None; + continue; + } + } + } + None => rx.recv().await, + }; + let Some(event) = event else { + break; + }; + deadline = hook.handle_event(event, deadline); + } +} + +pub fn send_blocking(tx: &Sender, data: T) { + // block_on has some overhead and in practice the channel should basically + // never be full anyway so first try sending without blocking + if let Err(TrySendError::Full(data)) = tx.try_send(data) { + // set a timeout so that we just drop a message instead of freezing the editor in the worst case + let _ = block_on(tx.send_timeout(data, Duration::from_millis(10))); + } +} diff --git a/helix-event/src/hook.rs b/helix-event/src/hook.rs new file mode 100644 index 000000000..7fb681483 --- /dev/null +++ b/helix-event/src/hook.rs @@ -0,0 +1,91 @@ +//! rust dynamic dispatch is extremely limited so we have to build our +//! own vtable implementation. Otherwise implementing the event system would not be possible. +//! A nice bonus of this approach is that we can optimize the vtable a bit more. Normally +//! a dyn Trait fat pointer contains two pointers: A pointer to the data itself and a +//! pointer to a global (static) vtable entry which itself contains multiple other pointers +//! (the various functions of the trait, drop, size and align). That makes dynamic +//! dispatch pretty slow (double pointer indirections). However, we only have a single function +//! in the hook trait and don't need a drop implementation (event system is global anyway +//! and never dropped) so we can just store the entire vtable inline. + +use anyhow::Result; +use std::ptr::{self, NonNull}; + +use crate::Event; + +/// Opaque handle type that represents an erased type parameter. +/// +/// If extern types were stable, this could be implemented as `extern { pub type Opaque; }` but +/// until then we can use this. +/// +/// Care should be taken that we don't use a concrete instance of this. It should only be used +/// through a reference, so we can maintain something else's lifetime. +struct Opaque(()); + +pub(crate) struct ErasedHook { + data: NonNull, + call: unsafe fn(NonNull, NonNull, NonNull), +} + +impl ErasedHook { + pub(crate) fn new_dynamic Result<()> + 'static + Send + Sync>( + hook: H, + ) -> ErasedHook { + unsafe fn call Result<()> + 'static + Send + Sync>( + hook: NonNull, + _event: NonNull, + result: NonNull, + ) { + let hook: NonNull = hook.cast(); + let result: NonNull> = result.cast(); + let hook: &F = hook.as_ref(); + let res = hook(); + ptr::write(result.as_ptr(), res) + } + + unsafe { + ErasedHook { + data: NonNull::new_unchecked(Box::into_raw(Box::new(hook)) as *mut Opaque), + call: call::, + } + } + } + + pub(crate) fn new Result<()>>(hook: F) -> ErasedHook { + unsafe fn call Result<()>>( + hook: NonNull, + event: NonNull, + result: NonNull, + ) { + let hook: NonNull = hook.cast(); + let mut event: NonNull = event.cast(); + let result: NonNull> = result.cast(); + let hook: &F = hook.as_ref(); + let res = hook(event.as_mut()); + ptr::write(result.as_ptr(), res) + } + + unsafe { + ErasedHook { + data: NonNull::new_unchecked(Box::into_raw(Box::new(hook)) as *mut Opaque), + call: call::, + } + } + } + + pub(crate) unsafe fn call(&self, event: &mut E) -> Result<()> { + let mut res = Ok(()); + + unsafe { + (self.call)( + self.data, + NonNull::from(event).cast(), + NonNull::from(&mut res).cast(), + ); + } + res + } +} + +unsafe impl Sync for ErasedHook {} +unsafe impl Send for ErasedHook {} diff --git a/helix-event/src/lib.rs b/helix-event/src/lib.rs index 9c082b93a..894de5e8d 100644 --- a/helix-event/src/lib.rs +++ b/helix-event/src/lib.rs @@ -1,8 +1,203 @@ //! `helix-event` contains systems that allow (often async) communication between -//! different editor components without strongly coupling them. Currently this -//! crate only contains some smaller facilities but the intend is to add more -//! functionality in the future ( like a generic hook system) +//! different editor components without strongly coupling them. Specifically +//! it allows defining synchronous hooks that run when certain editor events +//! occur. +//! +//! The core of the event system are hook callbacks and the [`Event`] trait. A +//! hook is essentially just a closure `Fn(event: &mut impl Event) -> Result<()>` +//! that gets called every time an appropriate event is dispatched. The implementation +//! details of the [`Event`] trait are considered private. The [`events`] macro is +//! provided which automatically declares event types. Similarly the `register_hook` +//! macro should be used to (safely) declare event hooks. +//! +//! Hooks run synchronously which can be advantageous since they can modify the +//! current editor state right away (for example to immediately hide the completion +//! popup). However, they can not contain their own state without locking since +//! they only receive immutable references. For handler that want to track state, do +//! expensive background computations or debouncing an [`AsyncHook`] is preferable. +//! Async hooks are based around a channels that receive events specific to +//! that `AsyncHook` (usually an enum). These events can be sent by synchronous +//! hooks. Due to some limitations around tokio channels the [`send_blocking`] +//! function exported in this crate should be used instead of the builtin +//! `blocking_send`. +//! +//! In addition to the core event system, this crate contains some message queues +//! that allow transfer of data back to the main event loop from async hooks and +//! hooks that may not have access to all application data (for example in helix-view). +//! This include the ability to control rendering ([`lock_frame`], [`request_redraw`]) and +//! display status messages ([`status`]). +//! +//! Hooks declared in helix-term can furthermore dispatch synchronous jobs to be run on the +//! main loop (including access to the compositor). Ideally that queue will be moved +//! to helix-view in the future if we manage to detach the compositor from its rendering backend. +use anyhow::Result; +pub use cancel::{cancelable_future, cancelation, CancelRx, CancelTx}; +pub use debounce::{send_blocking, AsyncHook}; pub use redraw::{lock_frame, redraw_requested, request_redraw, start_frame, RenderLockGuard}; +pub use registry::Event; +mod cancel; +mod debounce; +mod hook; mod redraw; +mod registry; +#[doc(hidden)] +pub mod runtime; +pub mod status; + +#[cfg(test)] +mod test; + +pub fn register_event() { + registry::with_mut(|registry| registry.register_event::()) +} + +/// Registers a hook that will be called when an event of type `E` is dispatched. +/// This function should usually not be used directly, use the [`register_hook`] +/// macro instead. +/// +/// +/// # Safety +/// +/// `hook` must be totally generic over all lifetime parameters of `E`. For +/// example if `E` was a known type `Foo<'a, 'b>`, then the correct trait bound +/// would be `F: for<'a, 'b, 'c> Fn(&'a mut Foo<'b, 'c>)`, but there is no way to +/// express that kind of constraint for a generic type with the Rust type system +/// as of this writing. +pub unsafe fn register_hook_raw( + hook: impl Fn(&mut E) -> Result<()> + 'static + Send + Sync, +) { + registry::with_mut(|registry| registry.register_hook(hook)) +} + +/// Register a hook solely by event name +pub fn register_dynamic_hook( + hook: impl Fn() -> Result<()> + 'static + Send + Sync, + id: &str, +) -> Result<()> { + registry::with_mut(|reg| reg.register_dynamic_hook(hook, id)) +} + +pub fn dispatch(e: impl Event) { + registry::with(|registry| registry.dispatch(e)); +} + +/// Macro to declare events +/// +/// # Examples +/// +/// ``` no-compile +/// events! { +/// FileWrite(&Path) +/// ViewScrolled{ view: View, new_pos: ViewOffset } +/// DocumentChanged<'a> { old_doc: &'a Rope, doc: &'a mut Document, changes: &'a ChangeSet } +/// } +/// +/// fn init() { +/// register_event::(); +/// register_event::(); +/// register_event::(); +/// } +/// +/// fn save(path: &Path, content: &str){ +/// std::fs::write(path, content); +/// dispatch(FileWrite(path)); +/// } +/// ``` +#[macro_export] +macro_rules! events { + ($name: ident<$($lt: lifetime),*> { $($data:ident : $data_ty:ty),* } $($rem:tt)*) => { + pub struct $name<$($lt),*> { $(pub $data: $data_ty),* } + unsafe impl<$($lt),*> $crate::Event for $name<$($lt),*> { + const ID: &'static str = stringify!($name); + const LIFETIMES: usize = $crate::events!(@sum $(1, $lt),*); + type Static = $crate::events!(@replace_lt $name, $('static, $lt),*); + } + $crate::events!{ $($rem)* } + }; + ($name: ident { $($data:ident : $data_ty:ty),* } $($rem:tt)*) => { + pub struct $name { $(pub $data: $data_ty),* } + unsafe impl $crate::Event for $name { + const ID: &'static str = stringify!($name); + const LIFETIMES: usize = 0; + type Static = Self; + } + $crate::events!{ $($rem)* } + }; + () => {}; + (@replace_lt $name: ident, $($lt1: lifetime, $lt2: lifetime),* ) => {$name<$($lt1),*>}; + (@sum $($val: expr, $lt1: lifetime),* ) => {0 $(+ $val)*}; +} + +/// Safely register statically typed event hooks +#[macro_export] +macro_rules! register_hook { + // Safety: this is safe because we fully control the type of the event here and + // ensure all lifetime arguments are fully generic and the correct number of lifetime arguments + // is present + (move |$event:ident: &mut $event_ty: ident<$($lt: lifetime),*>| $body: expr) => { + let val = move |$event: &mut $event_ty<$($lt),*>| $body; + unsafe { + // Lifetimes are a bit of a pain. We want to allow events being + // non-static. Lifetimes don't actually exist at runtime so its + // fine to essentially transmute the lifetimes as long as we can + // prove soundness. The hook must therefore accept any combination + // of lifetimes. In other words fn(&'_ mut Event<'_, '_>) is ok + // but examples like fn(&'_ mut Event<'_, 'static>) or fn<'a>(&'a + // mut Event<'a, 'a>) are not. To make this safe we use a macro to + // forbid the user from specifying lifetimes manually (all lifetimes + // specified are always function generics and passed to the event so + // lifetimes can't be used multiple times and using 'static causes a + // syntax error). + // + // There is one soundness hole tough: Type Aliases allow + // "accidentally" creating these problems. For example: + // + // type Event2 = Event<'static>. + // type Event2<'a> = Event<'a, a>. + // + // These cases can be caught by counting the number of lifetimes + // parameters at the parameter declaration site and then at the hook + // declaration site. By asserting the number of lifetime parameters + // are equal we can catch all bad type aliases under one assumption: + // There are no unused lifetime parameters. Introducing a static + // would reduce the number of arguments of the alias by one in the + // above example Event2 has zero lifetime arguments while the original + // event has one lifetime argument. Similar logic applies to using + // a lifetime argument multiple times. The ASSERT below performs a + // a compile time assertion to ensure exactly this property. + // + // With unused lifetime arguments it is still one way to cause unsound code: + // + // type Event2<'a, 'b> = Event<'a, 'a>; + // + // However, this case will always emit a compiler warning/cause CI + // failures so a user would have to introduce #[allow(unused)] which + // is easily caught in review (and a very theoretical case anyway). + // If we want to be pedantic we can simply compile helix with + // forbid(unused). All of this is just a safety net to prevent + // very theoretical misuse. This won't come up in real code (and is + // easily caught in review). + #[allow(unused)] + const ASSERT: () = { + if <$event_ty as $crate::Event>::LIFETIMES != 0 + $crate::events!(@sum $(1, $lt),*){ + panic!("invalid type alias"); + } + }; + $crate::register_hook_raw::<$crate::events!(@replace_lt $event_ty, $('static, $lt),*)>(val); + } + }; + (move |$event:ident: &mut $event_ty: ident| $body: expr) => { + let val = move |$event: &mut $event_ty| $body; + unsafe { + #[allow(unused)] + const ASSERT: () = { + if <$event_ty as $crate::Event>::LIFETIMES != 0{ + panic!("invalid type alias"); + } + }; + $crate::register_hook_raw::<$event_ty>(val); + } + }; +} diff --git a/helix-event/src/redraw.rs b/helix-event/src/redraw.rs index a99152238..8fadb8aea 100644 --- a/helix-event/src/redraw.rs +++ b/helix-event/src/redraw.rs @@ -5,16 +5,20 @@ use std::future::Future; use parking_lot::{RwLock, RwLockReadGuard}; use tokio::sync::Notify; -/// A `Notify` instance that can be used to (asynchronously) request -/// the editor the render a new frame. -static REDRAW_NOTIFY: Notify = Notify::const_new(); - -/// A `RwLock` that prevents the next frame from being -/// drawn until an exclusive (write) lock can be acquired. -/// This allows asynchsonous tasks to acquire `non-exclusive` -/// locks (read) to prevent the next frame from being drawn -/// until a certain computation has finished. -static RENDER_LOCK: RwLock<()> = RwLock::new(()); +use crate::runtime_local; + +runtime_local! { + /// A `Notify` instance that can be used to (asynchronously) request + /// the editor to render a new frame. + static REDRAW_NOTIFY: Notify = Notify::const_new(); + + /// A `RwLock` that prevents the next frame from being + /// drawn until an exclusive (write) lock can be acquired. + /// This allows asynchronous tasks to acquire `non-exclusive` + /// locks (read) to prevent the next frame from being drawn + /// until a certain computation has finished. + static RENDER_LOCK: RwLock<()> = RwLock::new(()); +} pub type RenderLockGuard = RwLockReadGuard<'static, ()>; diff --git a/helix-event/src/registry.rs b/helix-event/src/registry.rs new file mode 100644 index 000000000..d43c48ac4 --- /dev/null +++ b/helix-event/src/registry.rs @@ -0,0 +1,131 @@ +//! A global registry where events are registered and can be +//! subscribed to by registering hooks. The registry identifies event +//! types using their type name so multiple event with the same type name +//! may not be registered (will cause a panic to ensure soundness) + +use std::any::TypeId; + +use anyhow::{bail, Result}; +use hashbrown::hash_map::Entry; +use hashbrown::HashMap; +use parking_lot::RwLock; + +use crate::hook::ErasedHook; +use crate::runtime_local; + +pub struct Registry { + events: HashMap<&'static str, TypeId, ahash::RandomState>, + handlers: HashMap<&'static str, Vec, ahash::RandomState>, +} + +impl Registry { + pub fn register_event(&mut self) { + let ty = TypeId::of::(); + assert_eq!(ty, TypeId::of::()); + match self.events.entry(E::ID) { + Entry::Occupied(entry) => { + if entry.get() == &ty { + // don't warn during tests to avoid log spam + #[cfg(not(feature = "integration_test"))] + panic!("Event {} was registered multiple times", E::ID); + } else { + panic!("Multiple events with ID {} were registered", E::ID); + } + } + Entry::Vacant(ent) => { + ent.insert(ty); + self.handlers.insert(E::ID, Vec::new()); + } + } + } + + /// # Safety + /// + /// `hook` must be totally generic over all lifetime parameters of `E`. For + /// example if `E` was a known type `Foo<'a, 'b> then the correct trait bound + /// would be `F: for<'a, 'b, 'c> Fn(&'a mut Foo<'b, 'c>)` but there is no way to + /// express that kind of constraint for a generic type with the rust type system + /// right now. + pub unsafe fn register_hook( + &mut self, + hook: impl Fn(&mut E) -> Result<()> + 'static + Send + Sync, + ) { + // ensure event type ids match so we can rely on them always matching + let id = E::ID; + let Some(&event_id) = self.events.get(id) else { + panic!("Tried to register handler for unknown event {id}"); + }; + assert!( + TypeId::of::() == event_id, + "Tried to register invalid hook for event {id}" + ); + let hook = ErasedHook::new(hook); + self.handlers.get_mut(id).unwrap().push(hook); + } + + pub fn register_dynamic_hook( + &mut self, + hook: impl Fn() -> Result<()> + 'static + Send + Sync, + id: &str, + ) -> Result<()> { + // ensure event type ids match so we can rely on them always matching + if self.events.get(id).is_none() { + bail!("Tried to register handler for unknown event {id}"); + }; + let hook = ErasedHook::new_dynamic(hook); + self.handlers.get_mut(id).unwrap().push(hook); + Ok(()) + } + + pub fn dispatch(&self, mut event: E) { + let Some(hooks) = self.handlers.get(E::ID) else { + log::error!("Dispatched unknown event {}", E::ID); + return; + }; + let event_id = self.events[E::ID]; + + assert_eq!( + TypeId::of::(), + event_id, + "Tried to dispatch invalid event {}", + E::ID + ); + + for hook in hooks { + // safety: event type is the same + if let Err(err) = unsafe { hook.call(&mut event) } { + log::error!("{} hook failed: {err:#?}", E::ID); + crate::status::report_blocking(err); + } + } + } +} + +runtime_local! { + static REGISTRY: RwLock = RwLock::new(Registry { + // hardcoded random number is good enough here we don't care about DOS resistance + // and avoids the additional complexity of `Option` + events: HashMap::with_hasher(ahash::RandomState::with_seeds(423, 9978, 38322, 3280080)), + handlers: HashMap::with_hasher(ahash::RandomState::with_seeds(423, 99078, 382322, 3282938)), + }); +} + +pub(crate) fn with(f: impl FnOnce(&Registry) -> T) -> T { + f(®ISTRY.read()) +} + +pub(crate) fn with_mut(f: impl FnOnce(&mut Registry) -> T) -> T { + f(&mut REGISTRY.write()) +} + +/// # Safety +/// The number of specified lifetimes and the static type *must* be correct. +/// This is ensured automatically by the [`events`](crate::events) +/// macro. +pub unsafe trait Event: Sized { + /// Globally unique (case sensitive) string that identifies this type. + /// A good candidate is the events type name + const ID: &'static str; + const LIFETIMES: usize; + type Static: Event + 'static; +} diff --git a/helix-event/src/runtime.rs b/helix-event/src/runtime.rs new file mode 100644 index 000000000..8da465ef3 --- /dev/null +++ b/helix-event/src/runtime.rs @@ -0,0 +1,88 @@ +//! The event system makes use of global to decouple different systems. +//! However, this can cause problems for the integration test system because +//! it runs multiple helix applications in parallel. Making the globals +//! thread-local does not work because a applications can/does have multiple +//! runtime threads. Instead this crate implements a similar notion to a thread +//! local but instead of being local to a single thread, the statics are local to +//! a single tokio-runtime. The implementation requires locking so it's not exactly efficient. +//! +//! Therefore this function is only enabled during integration tests and behaves like +//! a normal static otherwise. I would prefer this module to be fully private and to only +//! export the macro but the macro still need to construct these internals so it's marked +//! `doc(hidden)` instead + +use std::ops::Deref; + +#[cfg(not(feature = "integration_test"))] +pub struct RuntimeLocal { + /// inner API used in the macro, not part of public API + #[doc(hidden)] + pub __data: T, +} + +#[cfg(not(feature = "integration_test"))] +impl Deref for RuntimeLocal { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.__data + } +} + +#[cfg(not(feature = "integration_test"))] +#[macro_export] +macro_rules! runtime_local { + ($($(#[$attr:meta])* $vis: vis static $name:ident: $ty: ty = $init: expr;)*) => { + $($(#[$attr])* $vis static $name: $crate::runtime::RuntimeLocal<$ty> = $crate::runtime::RuntimeLocal { + __data: $init + };)* + }; +} + +#[cfg(feature = "integration_test")] +pub struct RuntimeLocal { + data: + parking_lot::RwLock>, + init: fn() -> T, +} + +#[cfg(feature = "integration_test")] +impl RuntimeLocal { + /// inner API used in the macro, not part of public API + #[doc(hidden)] + pub const fn __new(init: fn() -> T) -> Self { + Self { + data: parking_lot::RwLock::new(hashbrown::HashMap::with_hasher( + ahash::RandomState::with_seeds(423, 9978, 38322, 3280080), + )), + init, + } + } +} + +#[cfg(feature = "integration_test")] +impl Deref for RuntimeLocal { + type Target = T; + fn deref(&self) -> &T { + let id = tokio::runtime::Handle::current().id(); + let guard = self.data.read(); + match guard.get(&id) { + Some(res) => res, + None => { + drop(guard); + let data = Box::leak(Box::new((self.init)())); + let mut guard = self.data.write(); + guard.insert(id, data); + data + } + } + } +} + +#[cfg(feature = "integration_test")] +#[macro_export] +macro_rules! runtime_local { + ($($(#[$attr:meta])* $vis: vis static $name:ident: $ty: ty = $init: expr;)*) => { + $($(#[$attr])* $vis static $name: $crate::runtime::RuntimeLocal<$ty> = $crate::runtime::RuntimeLocal::__new(|| $init);)* + }; +} diff --git a/helix-event/src/status.rs b/helix-event/src/status.rs new file mode 100644 index 000000000..fdca67624 --- /dev/null +++ b/helix-event/src/status.rs @@ -0,0 +1,68 @@ +//! A queue of async messages/errors that will be shown in the editor + +use std::borrow::Cow; +use std::time::Duration; + +use crate::{runtime_local, send_blocking}; +use once_cell::sync::OnceCell; +use tokio::sync::mpsc::{Receiver, Sender}; + +/// Describes the severity level of a [`StatusMessage`]. +#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)] +pub enum Severity { + Hint, + Info, + Warning, + Error, +} + +pub struct StatusMessage { + pub severity: Severity, + pub message: Cow<'static, str>, +} + +impl From for StatusMessage { + fn from(err: anyhow::Error) -> Self { + StatusMessage { + severity: Severity::Error, + message: err.to_string().into(), + } + } +} + +impl From<&'static str> for StatusMessage { + fn from(msg: &'static str) -> Self { + StatusMessage { + severity: Severity::Info, + message: msg.into(), + } + } +} + +runtime_local! { + static MESSAGES: OnceCell> = OnceCell::new(); +} + +pub async fn report(msg: impl Into) { + // if the error channel overflows just ignore it + let _ = MESSAGES + .wait() + .send_timeout(msg.into(), Duration::from_millis(10)) + .await; +} + +pub fn report_blocking(msg: impl Into) { + let messages = MESSAGES.wait(); + send_blocking(messages, msg.into()) +} + +/// Must be called once during editor startup exactly once +/// before any of the messages in this module can be used +/// +/// # Panics +/// If called multiple times +pub fn setup() -> Receiver { + let (tx, rx) = tokio::sync::mpsc::channel(128); + let _ = MESSAGES.set(tx); + rx +} diff --git a/helix-event/src/test.rs b/helix-event/src/test.rs new file mode 100644 index 000000000..a1283ada1 --- /dev/null +++ b/helix-event/src/test.rs @@ -0,0 +1,90 @@ +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; +use std::time::Duration; + +use parking_lot::Mutex; + +use crate::{dispatch, events, register_dynamic_hook, register_event, register_hook}; +#[test] +fn smoke_test() { + events! { + Event1 { content: String } + Event2 { content: usize } + } + register_event::(); + register_event::(); + + // setup hooks + let res1: Arc> = Arc::default(); + let acc = Arc::clone(&res1); + register_hook!(move |event: &mut Event1| { + acc.lock().push_str(&event.content); + Ok(()) + }); + let res2: Arc = Arc::default(); + let acc = Arc::clone(&res2); + register_hook!(move |event: &mut Event2| { + acc.fetch_add(event.content, Ordering::Relaxed); + Ok(()) + }); + + // triggers events + let thread = std::thread::spawn(|| { + for i in 0..1000 { + dispatch(Event2 { content: i }); + } + }); + std::thread::sleep(Duration::from_millis(1)); + dispatch(Event1 { + content: "foo".to_owned(), + }); + dispatch(Event2 { content: 42 }); + dispatch(Event1 { + content: "bar".to_owned(), + }); + dispatch(Event1 { + content: "hello world".to_owned(), + }); + thread.join().unwrap(); + + // check output + assert_eq!(&**res1.lock(), "foobarhello world"); + assert_eq!( + res2.load(Ordering::Relaxed), + 42 + (0..1000usize).sum::() + ); +} + +#[test] +fn dynamic() { + events! { + Event3 {} + Event4 { count: usize } + }; + register_event::(); + register_event::(); + + let count = Arc::new(AtomicUsize::new(0)); + let count1 = count.clone(); + let count2 = count.clone(); + register_dynamic_hook( + move || { + count1.fetch_add(2, Ordering::Relaxed); + Ok(()) + }, + "Event3", + ) + .unwrap(); + register_dynamic_hook( + move || { + count2.fetch_add(3, Ordering::Relaxed); + Ok(()) + }, + "Event4", + ) + .unwrap(); + dispatch(Event3 {}); + dispatch(Event4 { count: 0 }); + dispatch(Event3 {}); + assert_eq!(count.load(Ordering::Relaxed), 7) +} diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 21c35553c..7bdd433ed 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -15,7 +15,7 @@ homepage.workspace = true [features] default = ["git"] unicode-lines = ["helix-core/unicode-lines"] -integration = [] +integration = ["helix-event/integration_test"] git = ["helix-vcs/git"] [[bin]] diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 290441b41..8215eeaa5 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -1,6 +1,10 @@ use arc_swap::{access::Map, ArcSwap}; use futures_util::Stream; -use helix_core::{pos_at_coords, syntax, Selection}; +use helix_core::{ + chars::char_is_word, + diagnostic::{DiagnosticTag, NumberOrString}, + pos_at_coords, syntax, Selection, +}; use helix_lsp::{ lsp::{self, notification::Notification}, util::lsp_range_to_range, @@ -24,6 +28,7 @@ use crate::{ commands::apply_workspace_edit, compositor::{Compositor, Event}, config::Config, + handlers, job::Jobs, keymap::Keymaps, ui::{self, overlay::overlaid}, @@ -138,6 +143,7 @@ impl Application { let area = terminal.size().expect("couldn't get terminal size"); let mut compositor = Compositor::new(area); let config = Arc::new(ArcSwap::from_pointee(config)); + let handlers = handlers::setup(config.clone()); let mut editor = Editor::new( area, theme_loader.clone(), @@ -145,6 +151,7 @@ impl Application { Arc::new(Map::new(Arc::clone(&config), |config: &Config| { &config.editor })), + handlers, ); let keys = Box::new(Map::new(Arc::clone(&config), |config: &Config| { @@ -321,10 +328,21 @@ impl Application { Some(event) = input_stream.next() => { self.handle_terminal_events(event).await; } - Some(callback) = self.jobs.futures.next() => { - self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback); + Some(callback) = self.jobs.callbacks.recv() => { + self.jobs.handle_callback(&mut self.editor, &mut self.compositor, Ok(Some(callback))); self.render().await; } + Some(msg) = self.jobs.status_messages.recv() => { + let severity = match msg.severity{ + helix_event::status::Severity::Hint => Severity::Hint, + helix_event::status::Severity::Info => Severity::Info, + helix_event::status::Severity::Warning => Severity::Warning, + helix_event::status::Severity::Error => Severity::Error, + }; + // TODO: show multiple status messages at once to avoid clobbering + self.editor.status_msg = Some((msg.message, severity)); + helix_event::request_redraw(); + } Some(callback) = self.jobs.wait_futures.next() => { self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback); self.render().await; diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 53783e4e3..48ceb23ba 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -88,7 +88,7 @@ pub struct Context<'a> { pub count: Option, pub editor: &'a mut Editor, - pub callback: Option, + pub callback: Vec, pub on_next_key_callback: Option, pub jobs: &'a mut Jobs, } @@ -96,16 +96,18 @@ pub struct Context<'a> { impl<'a> Context<'a> { /// Push a new component onto the compositor. pub fn push_layer(&mut self, component: Box) { - self.callback = Some(Box::new(|compositor: &mut Compositor, _| { - compositor.push(component) - })); + self.callback + .push(Box::new(|compositor: &mut Compositor, _| { + compositor.push(component) + })); } /// Call `replace_or_push` on the Compositor pub fn replace_or_push_layer(&mut self, id: &'static str, component: T) { - self.callback = Some(Box::new(move |compositor: &mut Compositor, _| { - compositor.replace_or_push(id, component); - })); + self.callback + .push(Box::new(move |compositor: &mut Compositor, _| { + compositor.replace_or_push(id, component); + })); } #[inline] @@ -2934,7 +2936,7 @@ pub fn command_palette(cx: &mut Context) { let register = cx.register; let count = cx.count; - cx.callback = Some(Box::new( + cx.callback.push(Box::new( move |compositor: &mut Compositor, cx: &mut compositor::Context| { let keymap = compositor.find::().unwrap().keymaps.map() [&cx.editor.mode] @@ -2954,7 +2956,7 @@ pub fn command_palette(cx: &mut Context) { register, count, editor: cx.editor, - callback: None, + callback: Vec::new(), on_next_key_callback: None, jobs: cx.jobs, }; @@ -2982,7 +2984,7 @@ pub fn command_palette(cx: &mut Context) { fn last_picker(cx: &mut Context) { // TODO: last picker does not seem to work well with buffer_picker - cx.callback = Some(Box::new(|compositor, cx| { + cx.callback.push(Box::new(|compositor, cx| { if let Some(picker) = compositor.last_picker.take() { compositor.push(picker); } else { @@ -3494,6 +3496,7 @@ fn hunk_range(hunk: Hunk, text: RopeSlice) -> Range { } pub mod insert { + use crate::events::PostInsertChar; use super::*; pub type Hook = fn(&Rope, &Selection, char) -> Option; pub type PostHook = fn(&mut Context, char); @@ -3627,6 +3630,7 @@ pub mod insert { for hook in &[language_server_completion, signature_help] { hook(cx, c); } + helix_event::dispatch(PostInsertChar { c, cx }); } pub fn smart_tab(cx: &mut Context) { @@ -5820,7 +5824,7 @@ fn replay_macro(cx: &mut Context) { cx.editor.macro_replaying.push(reg); let count = cx.count(); - cx.callback = Some(Box::new(move |compositor, cx| { + cx.callback.push(Box::new(move |compositor, cx| { for _ in 0..count { for &key in keys.iter() { compositor.handle_event(&compositor::Event::Key(key), cx); diff --git a/helix-term/src/events.rs b/helix-term/src/events.rs new file mode 100644 index 000000000..49b44f775 --- /dev/null +++ b/helix-term/src/events.rs @@ -0,0 +1,20 @@ +use helix_event::{events, register_event}; +use helix_view::document::Mode; +use helix_view::events::{DocumentDidChange, SelectionDidChange}; + +use crate::commands; +use crate::keymap::MappableCommand; + +events! { + OnModeSwitch<'a, 'cx> { old_mode: Mode, new_mode: Mode, cx: &'a mut commands::Context<'cx> } + PostInsertChar<'a, 'cx> { c: char, cx: &'a mut commands::Context<'cx> } + PostCommand<'a, 'cx> { command: & 'a MappableCommand, cx: &'a mut commands::Context<'cx> } +} + +pub fn register() { + register_event::(); + register_event::(); + register_event::(); + register_event::(); + register_event::(); +} diff --git a/helix-term/src/handlers.rs b/helix-term/src/handlers.rs new file mode 100644 index 000000000..ab2d724ff --- /dev/null +++ b/helix-term/src/handlers.rs @@ -0,0 +1,15 @@ +use std::sync::Arc; + +use arc_swap::ArcSwap; + +use crate::config::Config; +use crate::events; + + + } +pub fn setup(config: Arc>) -> Handlers { + events::register(); + let handlers = Handlers { + }; + handlers +} diff --git a/helix-term/src/job.rs b/helix-term/src/job.rs index 19f2521a5..72ed892dd 100644 --- a/helix-term/src/job.rs +++ b/helix-term/src/job.rs @@ -1,13 +1,37 @@ +use helix_event::status::StatusMessage; +use helix_event::{runtime_local, send_blocking}; use helix_view::Editor; +use once_cell::sync::OnceCell; use crate::compositor::Compositor; use futures_util::future::{BoxFuture, Future, FutureExt}; use futures_util::stream::{FuturesUnordered, StreamExt}; +use tokio::sync::mpsc::{channel, Receiver, Sender}; pub type EditorCompositorCallback = Box; pub type EditorCallback = Box; +runtime_local! { + static JOB_QUEUE: OnceCell> = OnceCell::new(); +} + +pub async fn dispatch_callback(job: Callback) { + let _ = JOB_QUEUE.wait().send(job).await; +} + +pub async fn dispatch(job: impl FnOnce(&mut Editor, &mut Compositor) + Send + 'static) { + let _ = JOB_QUEUE + .wait() + .send(Callback::EditorCompositor(Box::new(job))) + .await; +} + +pub fn dispatch_blocking(job: impl FnOnce(&mut Editor, &mut Compositor) + Send + 'static) { + let jobs = JOB_QUEUE.wait(); + send_blocking(jobs, Callback::EditorCompositor(Box::new(job))) +} + pub enum Callback { EditorCompositor(EditorCompositorCallback), Editor(EditorCallback), @@ -21,11 +45,11 @@ pub struct Job { pub wait: bool, } -#[derive(Default)] pub struct Jobs { - pub futures: FuturesUnordered, - /// These are the ones that need to complete before we exit. + /// jobs that need to complete before we exit. pub wait_futures: FuturesUnordered, + pub callbacks: Receiver, + pub status_messages: Receiver, } impl Job { @@ -52,8 +76,16 @@ impl Job { } impl Jobs { + #[allow(clippy::new_without_default)] pub fn new() -> Self { - Self::default() + let (tx, rx) = channel(1024); + let _ = JOB_QUEUE.set(tx); + let status_messages = helix_event::status::setup(); + Self { + wait_futures: FuturesUnordered::new(), + callbacks: rx, + status_messages, + } } pub fn spawn> + Send + 'static>(&mut self, f: F) { @@ -85,18 +117,17 @@ impl Jobs { } } - pub async fn next_job(&mut self) -> Option>> { - tokio::select! { - event = self.futures.next() => { event } - event = self.wait_futures.next() => { event } - } - } - pub fn add(&self, j: Job) { if j.wait { self.wait_futures.push(j.future); } else { - self.futures.push(j.future); + tokio::spawn(async move { + match j.future.await { + Ok(Some(cb)) => dispatch_callback(cb).await, + Ok(None) => (), + Err(err) => helix_event::status::report(err).await, + } + }); } } diff --git a/helix-term/src/lib.rs b/helix-term/src/lib.rs index a1d603298..b1413ed0d 100644 --- a/helix-term/src/lib.rs +++ b/helix-term/src/lib.rs @@ -6,13 +6,17 @@ pub mod args; pub mod commands; pub mod compositor; pub mod config; +pub mod events; pub mod health; pub mod job; pub mod keymap; pub mod ui; + use std::path::Path; use futures_util::Future; +mod handlers; + use ignore::DirEntry; use url::Url; diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 24fcdb014..9f186d148 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -2,6 +2,7 @@ use crate::{ commands::{self, OnKeyCallback}, compositor::{Component, Context, Event, EventResult}, job::{self, Callback}, + events::{OnModeSwitch, PostCommand}, key, keymap::{KeymapResult, Keymaps}, ui::{ @@ -835,11 +836,18 @@ impl EditorView { let mut execute_command = |command: &commands::MappableCommand| { command.execute(cxt); + helix_event::dispatch(PostCommand { command, cx: cxt }); let current_mode = cxt.editor.mode(); match (last_mode, current_mode) { (Mode::Normal, Mode::Insert) => { // HAXX: if we just entered insert mode from normal, clear key buf // and record the command that got us into this mode. + if current_mode != last_mode { + helix_event::dispatch(OnModeSwitch { + old_mode: last_mode, + new_mode: current_mode, + cx: cxt, + }); // how we entered insert mode is important, and we should track that so // we can repeat the side effect. @@ -1004,7 +1012,7 @@ impl EditorView { } let area = completion.area(size, editor); - editor.last_completion = None; + editor.last_completion = Some(CompleteAction::Triggered); self.last_insert.1.push(InsertEvent::TriggerCompletion); // TODO : propagate required size on resize to completion too @@ -1265,7 +1273,7 @@ impl Component for EditorView { editor: context.editor, count: None, register: None, - callback: None, + callback: Vec::new(), on_next_key_callback: None, jobs: context.jobs, }; @@ -1375,7 +1383,7 @@ impl Component for EditorView { } // appease borrowck - let callback = cx.callback.take(); + let callbacks = take(&mut cx.callback); // if the command consumed the last view, skip the render. // on the next loop cycle the Application will then terminate. @@ -1394,6 +1402,16 @@ impl Component for EditorView { if mode != Mode::Insert { doc.append_changes_to_history(view); } + let callback = if callbacks.is_empty() { + None + } else { + let callback: crate::compositor::Callback = Box::new(move |compositor, cx| { + for callback in callbacks { + callback(compositor, cx) + } + }); + Some(callback) + }; EventResult::Consumed(callback) } diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 6473c2d18..93b83da4f 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -36,6 +36,7 @@ use helix_core::{ }; use crate::editor::Config; +use crate::events::{DocumentDidChange, SelectionDidChange}; use crate::{DocumentId, Editor, Theme, View, ViewId}; /// 8kB of buffer space for encoding and decoding `Rope`s. @@ -1096,6 +1097,10 @@ impl Document { // TODO: use a transaction? self.selections .insert(view_id, selection.ensure_invariants(self.text().slice(..))); + helix_event::dispatch(SelectionDidChange { + doc: self, + view: view_id, + }) } /// Find the origin selection of the text in a document, i.e. where @@ -1149,6 +1154,14 @@ impl Document { let success = transaction.changes().apply(&mut self.text); if success { + if emit_lsp_notification { + helix_event::dispatch(DocumentDidChange { + doc: self, + view: view_id, + old_text: &old_doc, + }); + } + for selection in self.selections.values_mut() { *selection = selection .clone() @@ -1164,6 +1177,10 @@ impl Document { view_id, selection.clone().ensure_invariants(self.text.slice(..)), ); + helix_event::dispatch(SelectionDidChange { + doc: self, + view: view_id, + }); } self.modified_since_accessed = true; @@ -1276,6 +1293,7 @@ impl Document { } if emit_lsp_notification { + // TODO: move to hook // emit lsp notification for language_server in self.language_servers() { let notify = language_server.text_document_did_change( diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 0ab4be8b2..44c706d7d 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -2,6 +2,7 @@ use crate::{ align_view, document::{DocumentSavedEventFuture, DocumentSavedEventResult, Mode, SavePoint}, graphics::{CursorKind, Rect}, + handlers::Handlers, info::Info, input::KeyEvent, register::Registers, @@ -960,6 +961,7 @@ pub struct Editor { /// field is set and any old requests are automatically /// canceled as a result pub completion_request_handle: Option>, + pub handlers: Handlers, } pub type Motion = Box; diff --git a/helix-view/src/events.rs b/helix-view/src/events.rs new file mode 100644 index 000000000..8b789cc0d --- /dev/null +++ b/helix-view/src/events.rs @@ -0,0 +1,9 @@ +use helix_core::Rope; +use helix_event::events; + +use crate::{Document, ViewId}; + +events! { + DocumentDidChange<'a> { doc: &'a mut Document, view: ViewId, old_text: &'a Rope } + SelectionDidChange<'a> { doc: &'a mut Document, view: ViewId } +} diff --git a/helix-view/src/handlers.rs b/helix-view/src/handlers.rs new file mode 100644 index 000000000..ae3eb545a --- /dev/null +++ b/helix-view/src/handlers.rs @@ -0,0 +1,12 @@ +use std::sync::Arc; + +use helix_event::send_blocking; +use tokio::sync::mpsc::Sender; + +use crate::handlers::lsp::SignatureHelpInvoked; +use crate::Editor; + +pub mod dap; +pub mod lsp; + +pub struct Handlers {} diff --git a/helix-view/src/handlers/lsp.rs b/helix-view/src/handlers/lsp.rs index 8b1378917..958385646 100644 --- a/helix-view/src/handlers/lsp.rs +++ b/helix-view/src/handlers/lsp.rs @@ -1 +1,40 @@ +use crate::{DocumentId, ViewId}; +#[derive(Debug, Clone, Copy)] +pub struct CompletionTrigger { + /// The char position of the primary cursor when the + /// completion was triggered + pub trigger_pos: usize, + pub doc: DocumentId, + pub view: ViewId, + /// Whether the cause of the trigger was an automatic completion (any word + /// char for words longer than minimum word length). + /// This is false for trigger chars send by the LS + pub auto: bool, +} + +pub enum CompletionEvent { + /// Auto completion was triggered by typing a word char + /// or a completion trigger + Trigger(CompletionTrigger), + /// A completion was manually requested (c-x) + Manual, + /// Some text was deleted and the cursor is now at `pos` + DeleteText { pos: usize }, + /// Invalidate the current auto completion trigger + Cancel, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum SignatureHelpInvoked { + Automatic, + Manual, +} + +pub enum SignatureHelpEvent { + Invoked, + Trigger, + ReTrigger, + Cancel, + RequestComplete { open: bool }, +} diff --git a/helix-view/src/lib.rs b/helix-view/src/lib.rs index 6a68e7d6f..82827b5d5 100644 --- a/helix-view/src/lib.rs +++ b/helix-view/src/lib.rs @@ -1,17 +1,15 @@ #[macro_use] pub mod macros; +pub mod base64; pub mod clipboard; pub mod document; pub mod editor; pub mod env; +pub mod events; pub mod graphics; pub mod gutter; -pub mod handlers { - pub mod dap; - pub mod lsp; -} -pub mod base64; +pub mod handlers; pub mod info; pub mod input; pub mod keyboard; From 8e592a151fe7adfbf3fb35ae134b7f2a70700f09 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Fri, 1 Dec 2023 00:03:27 +0100 Subject: [PATCH 074/796] refactor completion and signature help using hooks --- Cargo.lock | 1 + book/src/configuration.md | 3 +- helix-lsp/src/client.rs | 11 +- helix-stdx/Cargo.toml | 1 + helix-stdx/src/lib.rs | 1 + helix-stdx/src/rope.rs | 26 ++ helix-term/src/application.rs | 6 +- helix-term/src/commands.rs | 259 +----------- helix-term/src/commands/lsp.rs | 156 +------- helix-term/src/handlers.rs | 17 +- helix-term/src/handlers/completion.rs | 465 ++++++++++++++++++++++ helix-term/src/handlers/signature_help.rs | 335 ++++++++++++++++ helix-term/src/ui/completion.rs | 87 ++-- helix-term/src/ui/editor.rs | 59 +-- helix-term/src/ui/menu.rs | 34 +- helix-view/src/document.rs | 14 - helix-view/src/editor.rs | 35 +- helix-view/src/handlers.rs | 37 +- helix-view/src/handlers/lsp.rs | 35 +- 19 files changed, 1022 insertions(+), 560 deletions(-) create mode 100644 helix-stdx/src/rope.rs create mode 100644 helix-term/src/handlers/completion.rs create mode 100644 helix-term/src/handlers/signature_help.rs diff --git a/Cargo.lock b/Cargo.lock index 4969ef46b..96496125a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1165,6 +1165,7 @@ version = "23.10.0" dependencies = [ "dunce", "etcetera", + "ropey", "tempfile", ] diff --git a/book/src/configuration.md b/book/src/configuration.md index 36e2fee2e..a43ede76a 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -51,7 +51,8 @@ Its settings will be merged with the configuration directory `config.toml` and t | `auto-completion` | Enable automatic pop up of auto-completion | `true` | | `auto-format` | Enable automatic formatting on save | `true` | | `auto-save` | Enable automatic saving on the focus moving away from Helix. Requires [focus event support](https://github.com/helix-editor/helix/wiki/Terminal-Support) from your terminal | `false` | -| `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant | `250` | +| `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. | `250` | +| `completion-timeout` | Time in milliseconds after typing a word character before completions are shown, set to 5 for instant. | `250` | | `preview-completion-insert` | Whether to apply completion item instantly when selected | `true` | | `completion-trigger-len` | The min-length of word under cursor to trigger autocompletion | `2` | | `completion-replace` | Set to `true` to make completions always replace the entire word and not just the part before the cursor | `false` | diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 1af27c1d4..7eef2bf7a 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -9,7 +9,7 @@ use helix_loader::{self, VERSION_AND_GIT_HASH}; use helix_stdx::path; use lsp::{ notification::DidChangeWorkspaceFolders, CodeActionCapabilityResolveSupport, - DidChangeWorkspaceFoldersParams, OneOf, PositionEncodingKind, WorkspaceFolder, + DidChangeWorkspaceFoldersParams, OneOf, PositionEncodingKind, SignatureHelp, WorkspaceFolder, WorkspaceFoldersChangeEvent, }; use lsp_types as lsp; @@ -999,6 +999,7 @@ impl Client { text_document: lsp::TextDocumentIdentifier, position: lsp::Position, work_done_token: Option, + context: lsp::CompletionContext, ) -> Option>> { let capabilities = self.capabilities.get().unwrap(); @@ -1010,13 +1011,12 @@ impl Client { text_document, position, }, + context: Some(context), // TODO: support these tokens by async receiving and updating the choice list work_done_progress_params: lsp::WorkDoneProgressParams { work_done_token }, partial_result_params: lsp::PartialResultParams { partial_result_token: None, }, - context: None, - // lsp::CompletionContext { trigger_kind: , trigger_character: Some(), } }; Some(self.call::(params)) @@ -1063,7 +1063,7 @@ impl Client { text_document: lsp::TextDocumentIdentifier, position: lsp::Position, work_done_token: Option, - ) -> Option>> { + ) -> Option>>> { let capabilities = self.capabilities.get().unwrap(); // Return early if the server does not support signature help. @@ -1079,7 +1079,8 @@ impl Client { // lsp::SignatureHelpContext }; - Some(self.call::(params)) + let res = self.call::(params); + Some(async move { Ok(serde_json::from_value(res.await?)?) }) } pub fn text_document_range_inlay_hints( diff --git a/helix-stdx/Cargo.toml b/helix-stdx/Cargo.toml index 216a3b407..9b4de9fef 100644 --- a/helix-stdx/Cargo.toml +++ b/helix-stdx/Cargo.toml @@ -14,6 +14,7 @@ homepage.workspace = true [dependencies] dunce = "1.0" etcetera = "0.8" +ropey = { version = "1.6.1", default-features = false } [dev-dependencies] tempfile = "3.9" diff --git a/helix-stdx/src/lib.rs b/helix-stdx/src/lib.rs index ae3c3a981..68fe3ec37 100644 --- a/helix-stdx/src/lib.rs +++ b/helix-stdx/src/lib.rs @@ -1,2 +1,3 @@ pub mod env; pub mod path; +pub mod rope; diff --git a/helix-stdx/src/rope.rs b/helix-stdx/src/rope.rs new file mode 100644 index 000000000..4ee39d4a8 --- /dev/null +++ b/helix-stdx/src/rope.rs @@ -0,0 +1,26 @@ +use ropey::RopeSlice; + +pub trait RopeSliceExt: Sized { + fn ends_with(self, text: &str) -> bool; + fn starts_with(self, text: &str) -> bool; +} + +impl RopeSliceExt for RopeSlice<'_> { + fn ends_with(self, text: &str) -> bool { + let len = self.len_bytes(); + if len < text.len() { + return false; + } + self.get_byte_slice(len - text.len()..) + .map_or(false, |end| end == text) + } + + fn starts_with(self, text: &str) -> bool { + let len = self.len_bytes(); + if len < text.len() { + return false; + } + self.get_byte_slice(..len - text.len()) + .map_or(false, |start| start == text) + } +} diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 8215eeaa5..3f3e59c6a 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -1,10 +1,6 @@ use arc_swap::{access::Map, ArcSwap}; use futures_util::Stream; -use helix_core::{ - chars::char_is_word, - diagnostic::{DiagnosticTag, NumberOrString}, - pos_at_coords, syntax, Selection, -}; +use helix_core::{diagnostic::Severity, pos_at_coords, syntax, Selection}; use helix_lsp::{ lsp::{self, notification::Notification}, util::lsp_range_to_range, diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 48ceb23ba..4df3278b8 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -5,7 +5,6 @@ pub(crate) mod typed; pub use dap::*; use helix_vcs::Hunk; pub use lsp::*; -use tokio::sync::oneshot; use tui::widgets::Row; pub use typed::*; @@ -33,7 +32,7 @@ use helix_core::{ }; use helix_view::{ document::{FormatterError, Mode, SCRATCH_BUFFER_NAME}, - editor::{Action, CompleteAction}, + editor::Action, info::Info, input::KeyEvent, keyboard::KeyCode, @@ -52,14 +51,10 @@ use crate::{ filter_picker_entry, job::Callback, keymap::ReverseKeymap, - ui::{ - self, editor::InsertEvent, lsp::SignatureHelp, overlay::overlaid, CompletionItem, Picker, - Popup, Prompt, PromptEvent, - }, + ui::{self, overlay::overlaid, Picker, Popup, Prompt, PromptEvent}, }; use crate::job::{self, Jobs}; -use futures_util::{stream::FuturesUnordered, TryStreamExt}; use std::{ collections::{HashMap, HashSet}, fmt, @@ -2593,7 +2588,6 @@ fn delete_by_selection_insert_mode( ); } doc.apply(&transaction, view.id); - lsp::signature_help_impl(cx, SignatureHelpInvoked::Automatic); } fn delete_selection(cx: &mut Context) { @@ -2667,10 +2661,6 @@ fn insert_mode(cx: &mut Context) { .transform(|range| Range::new(range.to(), range.from())); doc.set_selection(view.id, selection); - - // [TODO] temporary workaround until we're not using the idle timer to - // trigger auto completions any more - cx.editor.clear_idle_timer(); } // inserts at the end of each selection @@ -3497,9 +3487,9 @@ fn hunk_range(hunk: Hunk, text: RopeSlice) -> Range { pub mod insert { use crate::events::PostInsertChar; + use super::*; pub type Hook = fn(&Rope, &Selection, char) -> Option; - pub type PostHook = fn(&mut Context, char); /// Exclude the cursor in range. fn exclude_cursor(text: RopeSlice, range: Range, cursor: Range) -> Range { @@ -3513,88 +3503,6 @@ pub mod insert { } } - // It trigger completion when idle timer reaches deadline - // Only trigger completion if the word under cursor is longer than n characters - pub fn idle_completion(cx: &mut Context) { - let config = cx.editor.config(); - let (view, doc) = current!(cx.editor); - let text = doc.text().slice(..); - let cursor = doc.selection(view.id).primary().cursor(text); - - use helix_core::chars::char_is_word; - let mut iter = text.chars_at(cursor); - iter.reverse(); - for _ in 0..config.completion_trigger_len { - match iter.next() { - Some(c) if char_is_word(c) => {} - _ => return, - } - } - super::completion(cx); - } - - fn language_server_completion(cx: &mut Context, ch: char) { - let config = cx.editor.config(); - if !config.auto_completion { - return; - } - - use helix_lsp::lsp; - // if ch matches completion char, trigger completion - let doc = doc_mut!(cx.editor); - let trigger_completion = doc - .language_servers_with_feature(LanguageServerFeature::Completion) - .any(|ls| { - // TODO: what if trigger is multiple chars long - matches!(&ls.capabilities().completion_provider, Some(lsp::CompletionOptions { - trigger_characters: Some(triggers), - .. - }) if triggers.iter().any(|trigger| trigger.contains(ch))) - }); - - if trigger_completion { - cx.editor.clear_idle_timer(); - super::completion(cx); - } - } - - fn signature_help(cx: &mut Context, ch: char) { - use helix_lsp::lsp; - // if ch matches signature_help char, trigger - let doc = doc_mut!(cx.editor); - // TODO support multiple language servers (not just the first that is found), likely by merging UI somehow - let Some(language_server) = doc - .language_servers_with_feature(LanguageServerFeature::SignatureHelp) - .next() - else { - return; - }; - - let capabilities = language_server.capabilities(); - - if let lsp::ServerCapabilities { - signature_help_provider: - Some(lsp::SignatureHelpOptions { - trigger_characters: Some(triggers), - // TODO: retrigger_characters - .. - }), - .. - } = capabilities - { - // TODO: what if trigger is multiple chars long - let is_trigger = triggers.iter().any(|trigger| trigger.contains(ch)); - // lsp doesn't tell us when to close the signature help, so we request - // the help information again after common close triggers which should - // return None, which in turn closes the popup. - let close_triggers = &[')', ';', '.']; - - if is_trigger || close_triggers.contains(&ch) { - super::signature_help_impl(cx, SignatureHelpInvoked::Automatic); - } - } - } - // The default insert hook: simply insert the character #[allow(clippy::unnecessary_wraps)] // need to use Option<> because of the Hook signature fn insert(doc: &Rope, selection: &Selection, ch: char) -> Option { @@ -3624,12 +3532,6 @@ pub mod insert { doc.apply(&t, view.id); } - // TODO: need a post insert hook too for certain triggers (autocomplete, signature help, etc) - // this could also generically look at Transaction, but it's a bit annoying to look at - // Operation instead of Change. - for hook in &[language_server_completion, signature_help] { - hook(cx, c); - } helix_event::dispatch(PostInsertChar { c, cx }); } @@ -3855,8 +3757,6 @@ pub mod insert { }); let (view, doc) = current!(cx.editor); doc.apply(&transaction, view.id); - - lsp::signature_help_impl(cx, SignatureHelpInvoked::Automatic); } pub fn delete_char_forward(cx: &mut Context) { @@ -4510,151 +4410,14 @@ fn remove_primary_selection(cx: &mut Context) { } pub fn completion(cx: &mut Context) { - use helix_lsp::{lsp, util::pos_to_lsp_pos}; - let (view, doc) = current!(cx.editor); + let range = doc.selection(view.id).primary(); + let text = doc.text().slice(..); + let cursor = range.cursor(text); - let savepoint = if let Some(CompleteAction::Selected { savepoint }) = &cx.editor.last_completion - { - savepoint.clone() - } else { - doc.savepoint(view) - }; - - let text = savepoint.text.clone(); - let cursor = savepoint.cursor(); - - let mut seen_language_servers = HashSet::new(); - - let mut futures: FuturesUnordered<_> = doc - .language_servers_with_feature(LanguageServerFeature::Completion) - .filter(|ls| seen_language_servers.insert(ls.id())) - .map(|language_server| { - let language_server_id = language_server.id(); - let offset_encoding = language_server.offset_encoding(); - let pos = pos_to_lsp_pos(&text, cursor, offset_encoding); - let doc_id = doc.identifier(); - let completion_request = language_server.completion(doc_id, pos, None).unwrap(); - - async move { - let json = completion_request.await?; - let response: Option = serde_json::from_value(json)?; - - let items = match response { - Some(lsp::CompletionResponse::Array(items)) => items, - // TODO: do something with is_incomplete - Some(lsp::CompletionResponse::List(lsp::CompletionList { - is_incomplete: _is_incomplete, - items, - })) => items, - None => Vec::new(), - } - .into_iter() - .map(|item| CompletionItem { - item, - language_server_id, - resolved: false, - }) - .collect(); - - anyhow::Ok(items) - } - }) - .collect(); - - // setup a channel that allows the request to be canceled - let (tx, rx) = oneshot::channel(); - // set completion_request so that this request can be canceled - // by setting completion_request, the old channel stored there is dropped - // and the associated request is automatically dropped - cx.editor.completion_request_handle = Some(tx); - let future = async move { - let items_future = async move { - let mut items = Vec::new(); - // TODO if one completion request errors, all other completion requests are discarded (even if they're valid) - while let Some(mut lsp_items) = futures.try_next().await? { - items.append(&mut lsp_items); - } - anyhow::Ok(items) - }; - tokio::select! { - biased; - _ = rx => { - Ok(Vec::new()) - } - res = items_future => { - res - } - } - }; - - let trigger_offset = cursor; - - // TODO: trigger_offset should be the cursor offset but we also need a starting offset from where we want to apply - // completion filtering. For example logger.te| should filter the initial suggestion list with "te". - - use helix_core::chars; - let mut iter = text.chars_at(cursor); - iter.reverse(); - let offset = iter.take_while(|ch| chars::char_is_word(*ch)).count(); - let start_offset = cursor.saturating_sub(offset); - - let trigger_doc = doc.id(); - let trigger_view = view.id; - - // FIXME: The commands Context can only have a single callback - // which means it gets overwritten when executing keybindings - // with multiple commands or macros. This would mean that completion - // might be incorrectly applied when repeating the insertmode action - // - // TODO: to solve this either make cx.callback a Vec of callbacks or - // alternatively move `last_insert` to `helix_view::Editor` - cx.callback = Some(Box::new( - move |compositor: &mut Compositor, _cx: &mut compositor::Context| { - let ui = compositor.find::().unwrap(); - ui.last_insert.1.push(InsertEvent::RequestCompletion); - }, - )); - - cx.jobs.callback(async move { - let items = future.await?; - let call = move |editor: &mut Editor, compositor: &mut Compositor| { - let (view, doc) = current_ref!(editor); - // check if the completion request is stale. - // - // Completions are completed asynchronously and therefore the user could - //switch document/view or leave insert mode. In all of thoise cases the - // completion should be discarded - if editor.mode != Mode::Insert || view.id != trigger_view || doc.id() != trigger_doc { - return; - } - - if items.is_empty() { - // editor.set_error("No completion available"); - return; - } - let size = compositor.size(); - let ui = compositor.find::().unwrap(); - let completion_area = ui.set_completion( - editor, - savepoint, - items, - start_offset, - trigger_offset, - size, - ); - let size = compositor.size(); - let signature_help_area = compositor - .find_id::>(SignatureHelp::ID) - .map(|signature_help| signature_help.area(size, editor)); - // Delete the signature help popup if they intersect. - if matches!((completion_area, signature_help_area),(Some(a), Some(b)) if a.intersects(b)) - { - compositor.remove(SignatureHelp::ID); - } - }; - Ok(Callback::EditorCompositor(Box::new(call))) - }); + cx.editor + .handlers + .trigger_completions(cursor, doc.id(), view.id); } // comments @@ -4833,10 +4596,6 @@ fn move_node_bound_impl(cx: &mut Context, dir: Direction, movement: Movement) { ); doc.set_selection(view.id, selection); - - // [TODO] temporary workaround until we're not using the idle timer to - // trigger auto completions any more - editor.clear_idle_timer(); } }; diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index 051cdcd35..de2f0e5ec 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -1,4 +1,4 @@ -use futures_util::{future::BoxFuture, stream::FuturesUnordered, FutureExt}; +use futures_util::{stream::FuturesUnordered, FutureExt}; use helix_lsp::{ block_on, lsp::{ @@ -8,21 +8,21 @@ use helix_lsp::{ util::{diagnostic_to_lsp_diagnostic, lsp_range_to_range, range_to_lsp_range}, Client, OffsetEncoding, }; -use serde_json::Value; use tokio_stream::StreamExt; use tui::{ text::{Span, Spans}, widgets::Row, }; -use super::{align_view, push_jump, Align, Context, Editor, Open}; +use super::{align_view, push_jump, Align, Context, Editor}; use helix_core::{syntax::LanguageServerFeature, text_annotations::InlineAnnotation, Selection}; use helix_stdx::path; use helix_view::{ - document::{DocumentInlayHints, DocumentInlayHintsId, Mode}, + document::{DocumentInlayHints, DocumentInlayHintsId}, editor::Action, graphics::Margin, + handlers::lsp::SignatureHelpInvoked, theme::Style, Document, View, }; @@ -30,10 +30,7 @@ use helix_view::{ use crate::{ compositor::{self, Compositor}, job::Callback, - ui::{ - self, lsp::SignatureHelp, overlay::overlaid, DynamicPicker, FileLocation, Picker, Popup, - PromptEvent, - }, + ui::{self, overlay::overlaid, DynamicPicker, FileLocation, Picker, Popup, PromptEvent}, }; use std::{ @@ -42,7 +39,6 @@ use std::{ fmt::Write, future::Future, path::PathBuf, - sync::Arc, }; /// Gets the first language server that is attached to a document which supports a specific feature. @@ -1132,146 +1128,10 @@ pub fn goto_reference(cx: &mut Context) { ); } -#[derive(PartialEq, Eq, Clone, Copy)] -pub enum SignatureHelpInvoked { - Manual, - Automatic, -} - pub fn signature_help(cx: &mut Context) { - signature_help_impl(cx, SignatureHelpInvoked::Manual) -} - -pub fn signature_help_impl(cx: &mut Context, invoked: SignatureHelpInvoked) { - let (view, doc) = current!(cx.editor); - - // TODO merge multiple language server signature help into one instead of just taking the first language server that supports it - let future = doc - .language_servers_with_feature(LanguageServerFeature::SignatureHelp) - .find_map(|language_server| { - let pos = doc.position(view.id, language_server.offset_encoding()); - language_server.text_document_signature_help(doc.identifier(), pos, None) - }); - - let Some(future) = future else { - // Do not show the message if signature help was invoked - // automatically on backspace, trigger characters, etc. - if invoked == SignatureHelpInvoked::Manual { - cx.editor - .set_error("No configured language server supports signature-help"); - } - return; - }; - signature_help_impl_with_future(cx, future.boxed(), invoked); -} - -pub fn signature_help_impl_with_future( - cx: &mut Context, - future: BoxFuture<'static, helix_lsp::Result>, - invoked: SignatureHelpInvoked, -) { - cx.callback( - future, - move |editor, compositor, response: Option| { - let config = &editor.config(); - - if !(config.lsp.auto_signature_help - || SignatureHelp::visible_popup(compositor).is_some() - || invoked == SignatureHelpInvoked::Manual) - { - return; - } - - // If the signature help invocation is automatic, don't show it outside of Insert Mode: - // it very probably means the server was a little slow to respond and the user has - // already moved on to something else, making a signature help popup will just be an - // annoyance, see https://github.com/helix-editor/helix/issues/3112 - if invoked == SignatureHelpInvoked::Automatic && editor.mode != Mode::Insert { - return; - } - - let response = match response { - // According to the spec the response should be None if there - // are no signatures, but some servers don't follow this. - Some(s) if !s.signatures.is_empty() => s, - _ => { - compositor.remove(SignatureHelp::ID); - return; - } - }; - let doc = doc!(editor); - let language = doc.language_name().unwrap_or(""); - - let signature = match response - .signatures - .get(response.active_signature.unwrap_or(0) as usize) - { - Some(s) => s, - None => return, - }; - let mut contents = SignatureHelp::new( - signature.label.clone(), - language.to_string(), - Arc::clone(&editor.syn_loader), - ); - - let signature_doc = if config.lsp.display_signature_help_docs { - signature.documentation.as_ref().map(|doc| match doc { - lsp::Documentation::String(s) => s.clone(), - lsp::Documentation::MarkupContent(markup) => markup.value.clone(), - }) - } else { - None - }; - - contents.set_signature_doc(signature_doc); - - let active_param_range = || -> Option<(usize, usize)> { - let param_idx = signature - .active_parameter - .or(response.active_parameter) - .unwrap_or(0) as usize; - let param = signature.parameters.as_ref()?.get(param_idx)?; - match ¶m.label { - lsp::ParameterLabel::Simple(string) => { - let start = signature.label.find(string.as_str())?; - Some((start, start + string.len())) - } - lsp::ParameterLabel::LabelOffsets([start, end]) => { - // LS sends offsets based on utf-16 based string representation - // but highlighting in helix is done using byte offset. - use helix_core::str_utils::char_to_byte_idx; - let from = char_to_byte_idx(&signature.label, *start as usize); - let to = char_to_byte_idx(&signature.label, *end as usize); - Some((from, to)) - } - } - }; - contents.set_active_param_range(active_param_range()); - - let old_popup = compositor.find_id::>(SignatureHelp::ID); - let mut popup = Popup::new(SignatureHelp::ID, contents) - .position(old_popup.and_then(|p| p.get_position())) - .position_bias(Open::Above) - .ignore_escape_key(true); - - // Don't create a popup if it intersects the auto-complete menu. - let size = compositor.size(); - if compositor - .find::() - .unwrap() - .completion - .as_mut() - .map(|completion| completion.area(size, editor)) - .filter(|area| area.intersects(popup.area(size, editor))) - .is_some() - { - return; - } - - compositor.replace_or_push(SignatureHelp::ID, popup); - }, - ); + cx.editor + .handlers + .trigger_signature_help(SignatureHelpInvoked::Manual, cx.editor) } pub fn hover(cx: &mut Context) { diff --git a/helix-term/src/handlers.rs b/helix-term/src/handlers.rs index ab2d724ff..ef5369f85 100644 --- a/helix-term/src/handlers.rs +++ b/helix-term/src/handlers.rs @@ -1,15 +1,30 @@ use std::sync::Arc; use arc_swap::ArcSwap; +use helix_event::AsyncHook; use crate::config::Config; use crate::events; +use crate::handlers::completion::CompletionHandler; +use crate::handlers::signature_help::SignatureHelpHandler; +pub use completion::trigger_auto_completion; +pub use helix_view::handlers::lsp::SignatureHelpInvoked; +pub use helix_view::handlers::Handlers; + +mod completion; +mod signature_help; - } pub fn setup(config: Arc>) -> Handlers { events::register(); + + let completions = CompletionHandler::new(config).spawn(); + let signature_hints = SignatureHelpHandler::new().spawn(); let handlers = Handlers { + completions, + signature_hints, }; + completion::register_hooks(&handlers); + signature_help::register_hooks(&handlers); handlers } diff --git a/helix-term/src/handlers/completion.rs b/helix-term/src/handlers/completion.rs new file mode 100644 index 000000000..d71fd24fc --- /dev/null +++ b/helix-term/src/handlers/completion.rs @@ -0,0 +1,465 @@ +use std::collections::HashSet; +use std::sync::Arc; +use std::time::Duration; + +use arc_swap::ArcSwap; +use futures_util::stream::FuturesUnordered; +use helix_core::chars::char_is_word; +use helix_core::syntax::LanguageServerFeature; +use helix_event::{ + cancelable_future, cancelation, register_hook, send_blocking, CancelRx, CancelTx, +}; +use helix_lsp::lsp; +use helix_lsp::util::pos_to_lsp_pos; +use helix_stdx::rope::RopeSliceExt; +use helix_view::document::{Mode, SavePoint}; +use helix_view::handlers::lsp::CompletionEvent; +use helix_view::{DocumentId, Editor, ViewId}; +use tokio::sync::mpsc::Sender; +use tokio::time::Instant; +use tokio_stream::StreamExt; + +use crate::commands; +use crate::compositor::Compositor; +use crate::config::Config; +use crate::events::{OnModeSwitch, PostCommand, PostInsertChar}; +use crate::job::{dispatch, dispatch_blocking}; +use crate::keymap::MappableCommand; +use crate::ui::editor::InsertEvent; +use crate::ui::lsp::SignatureHelp; +use crate::ui::{self, CompletionItem, Popup}; + +use super::Handlers; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum TriggerKind { + Auto, + TriggerChar, + Manual, +} + +#[derive(Debug, Clone, Copy)] +struct Trigger { + pos: usize, + view: ViewId, + doc: DocumentId, + kind: TriggerKind, +} + +#[derive(Debug)] +pub(super) struct CompletionHandler { + /// currently active trigger which will cause a + /// completion request after the timeout + trigger: Option, + /// A handle for currently active completion request. + /// This can be used to determine whether the current + /// request is still active (and new triggers should be + /// ignored) and can also be used to abort the current + /// request (by dropping the handle) + request: Option, + config: Arc>, +} + +impl CompletionHandler { + pub fn new(config: Arc>) -> CompletionHandler { + Self { + config, + request: None, + trigger: None, + } + } +} + +impl helix_event::AsyncHook for CompletionHandler { + type Event = CompletionEvent; + + fn handle_event( + &mut self, + event: Self::Event, + _old_timeout: Option, + ) -> Option { + match event { + CompletionEvent::AutoTrigger { + cursor: trigger_pos, + doc, + view, + } => { + // techically it shouldn't be possible to switch views/documents in insert mode + // but people may create weird keymaps/use the mouse so lets be extra careful + if self + .trigger + .as_ref() + .map_or(true, |trigger| trigger.doc != doc || trigger.view != view) + { + self.trigger = Some(Trigger { + pos: trigger_pos, + view, + doc, + kind: TriggerKind::Auto, + }); + } + } + CompletionEvent::TriggerChar { cursor, doc, view } => { + // immediately request completions and drop all auto completion requests + self.request = None; + self.trigger = Some(Trigger { + pos: cursor, + view, + doc, + kind: TriggerKind::TriggerChar, + }); + } + CompletionEvent::ManualTrigger { cursor, doc, view } => { + // immediately request completions and drop all auto completion requests + self.request = None; + self.trigger = Some(Trigger { + pos: cursor, + view, + doc, + kind: TriggerKind::Manual, + }); + // stop debouncing immediately and request the completion + self.finish_debounce(); + return None; + } + CompletionEvent::Cancel => { + self.trigger = None; + self.request = None; + } + CompletionEvent::DeleteText { cursor } => { + // if we deleted the original trigger, abort the completion + if matches!(self.trigger, Some(Trigger{ pos, .. }) if cursor < pos) { + self.trigger = None; + self.request = None; + } + } + } + self.trigger.map(|trigger| { + // if the current request was closed forget about it + // otherwise immediately restart the completion request + let cancel = self.request.take().map_or(false, |req| !req.is_closed()); + let timeout = if trigger.kind == TriggerKind::Auto && !cancel { + self.config.load().editor.completion_timeout + } else { + // we want almost instant completions for trigger chars + // and restarting completion requests. The small timeout here mainly + // serves to better handle cases where the completion handler + // may fall behind (so multiple events in the channel) and macros + Duration::from_millis(5) + }; + Instant::now() + timeout + }) + } + + fn finish_debounce(&mut self) { + let trigger = self.trigger.take().expect("debounce always has a trigger"); + let (tx, rx) = cancelation(); + self.request = Some(tx); + dispatch_blocking(move |editor, compositor| { + request_completion(trigger, rx, editor, compositor) + }); + } +} + +fn request_completion( + mut trigger: Trigger, + cancel: CancelRx, + editor: &mut Editor, + compositor: &mut Compositor, +) { + let (view, doc) = current!(editor); + + if compositor + .find::() + .unwrap() + .completion + .is_some() + || editor.mode != Mode::Insert + { + return; + } + + let text = doc.text(); + let cursor = doc.selection(view.id).primary().cursor(text.slice(..)); + if trigger.view != view.id || trigger.doc != doc.id() || cursor < trigger.pos { + return; + } + // this looks odd... Why are we not using the trigger position from + // the `trigger` here? Won't that mean that the trigger char doesn't get + // send to the LS if we type fast enougn? Yes that is true but it's + // not actually a problem. The LSP will resolve the completion to the identifier + // anyway (in fact sending the later position is necessary to get the right results + // from LSPs that provide incomplete completion list). We rely on trigger offset + // and primary cursor matching for multi-cursor completions so this is definitely + // necessary from our side too. + trigger.pos = cursor; + let trigger_text = text.slice(..cursor); + + let mut seen_language_servers = HashSet::new(); + let mut futures: FuturesUnordered<_> = doc + .language_servers_with_feature(LanguageServerFeature::Completion) + .filter(|ls| seen_language_servers.insert(ls.id())) + .map(|ls| { + let language_server_id = ls.id(); + let offset_encoding = ls.offset_encoding(); + let pos = pos_to_lsp_pos(text, cursor, offset_encoding); + let doc_id = doc.identifier(); + let context = if trigger.kind == TriggerKind::Manual { + lsp::CompletionContext { + trigger_kind: lsp::CompletionTriggerKind::INVOKED, + trigger_character: None, + } + } else { + let trigger_char = + ls.capabilities() + .completion_provider + .as_ref() + .and_then(|provider| { + provider + .trigger_characters + .as_deref()? + .iter() + .find(|&trigger| trigger_text.ends_with(trigger)) + }); + lsp::CompletionContext { + trigger_kind: lsp::CompletionTriggerKind::TRIGGER_CHARACTER, + trigger_character: trigger_char.cloned(), + } + }; + + let completion_response = ls.completion(doc_id, pos, None, context).unwrap(); + async move { + let json = completion_response.await?; + let response: Option = serde_json::from_value(json)?; + let items = match response { + Some(lsp::CompletionResponse::Array(items)) => items, + // TODO: do something with is_incomplete + Some(lsp::CompletionResponse::List(lsp::CompletionList { + is_incomplete: _is_incomplete, + items, + })) => items, + None => Vec::new(), + } + .into_iter() + .map(|item| CompletionItem { + item, + language_server_id, + resolved: false, + }) + .collect(); + anyhow::Ok(items) + } + }) + .collect(); + + let future = async move { + let mut items = Vec::new(); + while let Some(lsp_items) = futures.next().await { + match lsp_items { + Ok(mut lsp_items) => items.append(&mut lsp_items), + Err(err) => { + log::debug!("completion request failed: {err:?}"); + } + }; + } + items + }; + + let savepoint = doc.savepoint(view); + + let ui = compositor.find::().unwrap(); + ui.last_insert.1.push(InsertEvent::RequestCompletion); + tokio::spawn(async move { + let items = cancelable_future(future, cancel).await.unwrap_or_default(); + if items.is_empty() { + return; + } + dispatch(move |editor, compositor| { + show_completion(editor, compositor, items, trigger, savepoint) + }) + .await + }); +} + +fn show_completion( + editor: &mut Editor, + compositor: &mut Compositor, + items: Vec, + trigger: Trigger, + savepoint: Arc, +) { + let (view, doc) = current_ref!(editor); + // check if the completion request is stale. + // + // Completions are completed asynchronously and therefore the user could + //switch document/view or leave insert mode. In all of thoise cases the + // completion should be discarded + if editor.mode != Mode::Insert || view.id != trigger.view || doc.id() != trigger.doc { + return; + } + + let size = compositor.size(); + let ui = compositor.find::().unwrap(); + if ui.completion.is_some() { + return; + } + + let completion_area = ui.set_completion(editor, savepoint, items, trigger.pos, size); + let signature_help_area = compositor + .find_id::>(SignatureHelp::ID) + .map(|signature_help| signature_help.area(size, editor)); + // Delete the signature help popup if they intersect. + if matches!((completion_area, signature_help_area),(Some(a), Some(b)) if a.intersects(b)) { + compositor.remove(SignatureHelp::ID); + } +} + +pub fn trigger_auto_completion( + tx: &Sender, + editor: &Editor, + trigger_char_only: bool, +) { + let config = editor.config.load(); + if !config.auto_completion { + return; + } + let (view, doc): (&helix_view::View, &helix_view::Document) = current_ref!(editor); + let mut text = doc.text().slice(..); + let cursor = doc.selection(view.id).primary().cursor(text); + text = doc.text().slice(..cursor); + + let is_trigger_char = doc + .language_servers_with_feature(LanguageServerFeature::Completion) + .any(|ls| { + matches!(&ls.capabilities().completion_provider, Some(lsp::CompletionOptions { + trigger_characters: Some(triggers), + .. + }) if triggers.iter().any(|trigger| text.ends_with(trigger))) + }); + if is_trigger_char { + send_blocking( + tx, + CompletionEvent::TriggerChar { + cursor, + doc: doc.id(), + view: view.id, + }, + ); + return; + } + + let is_auto_trigger = !trigger_char_only + && doc + .text() + .chars_at(cursor) + .reversed() + .take(config.completion_trigger_len as usize) + .all(char_is_word); + + if is_auto_trigger { + send_blocking( + tx, + CompletionEvent::AutoTrigger { + cursor, + doc: doc.id(), + view: view.id, + }, + ); + } +} + +fn update_completions(cx: &mut commands::Context, c: Option) { + cx.callback.push(Box::new(move |compositor, cx| { + let editor_view = compositor.find::().unwrap(); + if let Some(completion) = &mut editor_view.completion { + completion.update_filter(c); + if completion.is_empty() { + editor_view.clear_completion(cx.editor); + // clearing completions might mean we want to immediately rerequest them (usually + // this occurs if typing a trigger char) + if c.is_some() { + trigger_auto_completion(&cx.editor.handlers.completions, cx.editor, false); + } + } + } + })) +} + +fn clear_completions(cx: &mut commands::Context) { + cx.callback.push(Box::new(|compositor, cx| { + let editor_view = compositor.find::().unwrap(); + editor_view.clear_completion(cx.editor); + })) +} + +fn completion_post_command_hook( + tx: &Sender, + PostCommand { command, cx }: &mut PostCommand<'_, '_>, +) -> anyhow::Result<()> { + if cx.editor.mode == Mode::Insert { + if cx.editor.last_completion.is_some() { + match command { + MappableCommand::Static { + name: "delete_word_forward" | "delete_char_forward" | "completion", + .. + } => (), + MappableCommand::Static { + name: "delete_char_backward", + .. + } => update_completions(cx, None), + _ => clear_completions(cx), + } + } else { + let event = match command { + MappableCommand::Static { + name: "delete_char_backward" | "delete_word_forward" | "delete_char_forward", + .. + } => { + let (view, doc) = current!(cx.editor); + let primary_cursor = doc + .selection(view.id) + .primary() + .cursor(doc.text().slice(..)); + CompletionEvent::DeleteText { + cursor: primary_cursor, + } + } + // hacks: some commands are handeled elsewhere and we don't want to + // cancel in that case + MappableCommand::Static { + name: "completion" | "insert_mode" | "append_mode", + .. + } => return Ok(()), + _ => CompletionEvent::Cancel, + }; + send_blocking(tx, event); + } + } + Ok(()) +} + +pub(super) fn register_hooks(handlers: &Handlers) { + let tx = handlers.completions.clone(); + register_hook!(move |event: &mut PostCommand<'_, '_>| completion_post_command_hook(&tx, event)); + + let tx = handlers.completions.clone(); + register_hook!(move |event: &mut OnModeSwitch<'_, '_>| { + if event.old_mode == Mode::Insert { + send_blocking(&tx, CompletionEvent::Cancel); + clear_completions(event.cx); + } else if event.new_mode == Mode::Insert { + trigger_auto_completion(&tx, event.cx.editor, false) + } + Ok(()) + }); + + let tx = handlers.completions.clone(); + register_hook!(move |event: &mut PostInsertChar<'_, '_>| { + if event.cx.editor.last_completion.is_some() { + update_completions(event.cx, Some(event.c)) + } else { + trigger_auto_completion(&tx, event.cx.editor, false); + } + Ok(()) + }); +} diff --git a/helix-term/src/handlers/signature_help.rs b/helix-term/src/handlers/signature_help.rs new file mode 100644 index 000000000..3c746548a --- /dev/null +++ b/helix-term/src/handlers/signature_help.rs @@ -0,0 +1,335 @@ +use std::sync::Arc; +use std::time::Duration; + +use helix_core::syntax::LanguageServerFeature; +use helix_event::{ + cancelable_future, cancelation, register_hook, send_blocking, CancelRx, CancelTx, +}; +use helix_lsp::lsp; +use helix_stdx::rope::RopeSliceExt; +use helix_view::document::Mode; +use helix_view::events::{DocumentDidChange, SelectionDidChange}; +use helix_view::handlers::lsp::{SignatureHelpEvent, SignatureHelpInvoked}; +use helix_view::Editor; +use tokio::sync::mpsc::Sender; +use tokio::time::Instant; + +use crate::commands::Open; +use crate::compositor::Compositor; +use crate::events::{OnModeSwitch, PostInsertChar}; +use crate::handlers::Handlers; +use crate::ui::lsp::SignatureHelp; +use crate::ui::Popup; +use crate::{job, ui}; + +#[derive(Debug)] +enum State { + Open, + Closed, + Pending { request: CancelTx }, +} + +/// debounce timeout in ms, value taken from VSCode +/// TODO: make this configurable? +const TIMEOUT: u64 = 120; + +#[derive(Debug)] +pub(super) struct SignatureHelpHandler { + trigger: Option, + state: State, +} + +impl SignatureHelpHandler { + pub fn new() -> SignatureHelpHandler { + SignatureHelpHandler { + trigger: None, + state: State::Closed, + } + } +} + +impl helix_event::AsyncHook for SignatureHelpHandler { + type Event = SignatureHelpEvent; + + fn handle_event( + &mut self, + event: Self::Event, + timeout: Option, + ) -> Option { + match event { + SignatureHelpEvent::Invoked => { + self.trigger = Some(SignatureHelpInvoked::Manual); + self.state = State::Closed; + self.finish_debounce(); + return None; + } + SignatureHelpEvent::Trigger => {} + SignatureHelpEvent::ReTrigger => { + // don't retrigger if we aren't open/pending yet + if matches!(self.state, State::Closed) { + return timeout; + } + } + SignatureHelpEvent::Cancel => { + self.state = State::Closed; + return None; + } + SignatureHelpEvent::RequestComplete { open } => { + // don't cancel rerequest that was already triggered + if let State::Pending { request } = &self.state { + if !request.is_closed() { + return timeout; + } + } + self.state = if open { State::Open } else { State::Closed }; + return timeout; + } + } + if self.trigger.is_none() { + self.trigger = Some(SignatureHelpInvoked::Automatic) + } + Some(Instant::now() + Duration::from_millis(TIMEOUT)) + } + + fn finish_debounce(&mut self) { + let invocation = self.trigger.take().unwrap(); + let (tx, rx) = cancelation(); + self.state = State::Pending { request: tx }; + job::dispatch_blocking(move |editor, _| request_signature_help(editor, invocation, rx)) + } +} + +pub fn request_signature_help( + editor: &mut Editor, + invoked: SignatureHelpInvoked, + cancel: CancelRx, +) { + let (view, doc) = current!(editor); + + // TODO merge multiple language server signature help into one instead of just taking the first language server that supports it + let future = doc + .language_servers_with_feature(LanguageServerFeature::SignatureHelp) + .find_map(|language_server| { + let pos = doc.position(view.id, language_server.offset_encoding()); + language_server.text_document_signature_help(doc.identifier(), pos, None) + }); + + let Some(future) = future else { + // Do not show the message if signature help was invoked + // automatically on backspace, trigger characters, etc. + if invoked == SignatureHelpInvoked::Manual { + editor + .set_error("No configured language server supports signature-help"); + } + return; + }; + + tokio::spawn(async move { + match cancelable_future(future, cancel).await { + Some(Ok(res)) => { + job::dispatch(move |editor, compositor| { + show_signature_help(editor, compositor, invoked, res) + }) + .await + } + Some(Err(err)) => log::error!("signature help request failed: {err}"), + None => (), + } + }); +} + +pub fn show_signature_help( + editor: &mut Editor, + compositor: &mut Compositor, + invoked: SignatureHelpInvoked, + response: Option, +) { + let config = &editor.config(); + + if !(config.lsp.auto_signature_help + || SignatureHelp::visible_popup(compositor).is_some() + || invoked == SignatureHelpInvoked::Manual) + { + return; + } + + // If the signature help invocation is automatic, don't show it outside of Insert Mode: + // it very probably means the server was a little slow to respond and the user has + // already moved on to something else, making a signature help popup will just be an + // annoyance, see https://github.com/helix-editor/helix/issues/3112 + // For the most part this should not be needed as the request gets canceled automatically now + // but it's technically possible for the mode change to just preempt this callback so better safe than sorry + if invoked == SignatureHelpInvoked::Automatic && editor.mode != Mode::Insert { + return; + } + + let response = match response { + // According to the spec the response should be None if there + // are no signatures, but some servers don't follow this. + Some(s) if !s.signatures.is_empty() => s, + _ => { + send_blocking( + &editor.handlers.signature_hints, + SignatureHelpEvent::RequestComplete { open: false }, + ); + compositor.remove(SignatureHelp::ID); + return; + } + }; + send_blocking( + &editor.handlers.signature_hints, + SignatureHelpEvent::RequestComplete { open: true }, + ); + + let doc = doc!(editor); + let language = doc.language_name().unwrap_or(""); + + let signature = match response + .signatures + .get(response.active_signature.unwrap_or(0) as usize) + { + Some(s) => s, + None => return, + }; + let mut contents = SignatureHelp::new( + signature.label.clone(), + language.to_string(), + Arc::clone(&editor.syn_loader), + ); + + let signature_doc = if config.lsp.display_signature_help_docs { + signature.documentation.as_ref().map(|doc| match doc { + lsp::Documentation::String(s) => s.clone(), + lsp::Documentation::MarkupContent(markup) => markup.value.clone(), + }) + } else { + None + }; + + contents.set_signature_doc(signature_doc); + + let active_param_range = || -> Option<(usize, usize)> { + let param_idx = signature + .active_parameter + .or(response.active_parameter) + .unwrap_or(0) as usize; + let param = signature.parameters.as_ref()?.get(param_idx)?; + match ¶m.label { + lsp::ParameterLabel::Simple(string) => { + let start = signature.label.find(string.as_str())?; + Some((start, start + string.len())) + } + lsp::ParameterLabel::LabelOffsets([start, end]) => { + // LS sends offsets based on utf-16 based string representation + // but highlighting in helix is done using byte offset. + use helix_core::str_utils::char_to_byte_idx; + let from = char_to_byte_idx(&signature.label, *start as usize); + let to = char_to_byte_idx(&signature.label, *end as usize); + Some((from, to)) + } + } + }; + contents.set_active_param_range(active_param_range()); + + let old_popup = compositor.find_id::>(SignatureHelp::ID); + let mut popup = Popup::new(SignatureHelp::ID, contents) + .position(old_popup.and_then(|p| p.get_position())) + .position_bias(Open::Above) + .ignore_escape_key(true); + + // Don't create a popup if it intersects the auto-complete menu. + let size = compositor.size(); + if compositor + .find::() + .unwrap() + .completion + .as_mut() + .map(|completion| completion.area(size, editor)) + .filter(|area| area.intersects(popup.area(size, editor))) + .is_some() + { + return; + } + + compositor.replace_or_push(SignatureHelp::ID, popup); +} + +fn signature_help_post_insert_char_hook( + tx: &Sender, + PostInsertChar { cx, .. }: &mut PostInsertChar<'_, '_>, +) -> anyhow::Result<()> { + if !cx.editor.config().lsp.auto_signature_help { + return Ok(()); + } + let (view, doc) = current!(cx.editor); + // TODO support multiple language servers (not just the first that is found), likely by merging UI somehow + let Some(language_server) = doc + .language_servers_with_feature(LanguageServerFeature::SignatureHelp) + .next() + else { + return Ok(()); + }; + + let capabilities = language_server.capabilities(); + + if let lsp::ServerCapabilities { + signature_help_provider: + Some(lsp::SignatureHelpOptions { + trigger_characters: Some(triggers), + // TODO: retrigger_characters + .. + }), + .. + } = capabilities + { + let mut text = doc.text().slice(..); + let cursor = doc.selection(view.id).primary().cursor(text); + text = text.slice(..cursor); + if triggers.iter().any(|trigger| text.ends_with(trigger)) { + send_blocking(tx, SignatureHelpEvent::Trigger) + } + } + Ok(()) +} + +pub(super) fn register_hooks(handlers: &Handlers) { + let tx = handlers.signature_hints.clone(); + register_hook!(move |event: &mut OnModeSwitch<'_, '_>| { + match (event.old_mode, event.new_mode) { + (Mode::Insert, _) => { + send_blocking(&tx, SignatureHelpEvent::Cancel); + event.cx.callback.push(Box::new(|compositor, _| { + compositor.remove(SignatureHelp::ID); + })); + } + (_, Mode::Insert) => { + if event.cx.editor.config().lsp.auto_signature_help { + send_blocking(&tx, SignatureHelpEvent::Trigger); + } + } + _ => (), + } + Ok(()) + }); + + let tx = handlers.signature_hints.clone(); + register_hook!( + move |event: &mut PostInsertChar<'_, '_>| signature_help_post_insert_char_hook(&tx, event) + ); + + let tx = handlers.signature_hints.clone(); + register_hook!(move |event: &mut DocumentDidChange<'_>| { + if event.doc.config.load().lsp.auto_signature_help { + send_blocking(&tx, SignatureHelpEvent::ReTrigger); + } + Ok(()) + }); + + let tx = handlers.signature_hints.clone(); + register_hook!(move |event: &mut SelectionDidChange<'_>| { + if event.doc.config.load().lsp.auto_signature_help { + send_blocking(&tx, SignatureHelpEvent::ReTrigger); + } + Ok(()) + }); +} diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index 7c6a0055e..48d97fbd8 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -1,8 +1,12 @@ -use crate::compositor::{Component, Context, Event, EventResult}; +use crate::{ + compositor::{Component, Context, Event, EventResult}, + handlers::trigger_auto_completion, +}; use helix_view::{ document::SavePoint, editor::CompleteAction, graphics::Margin, + handlers::lsp::SignatureHelpInvoked, theme::{Modifier, Style}, ViewId, }; @@ -10,7 +14,7 @@ use tui::{buffer::Buffer as Surface, text::Span}; use std::{borrow::Cow, sync::Arc}; -use helix_core::{Change, Transaction}; +use helix_core::{chars, Change, Transaction}; use helix_view::{graphics::Rect, Document, Editor}; use crate::commands; @@ -95,10 +99,9 @@ pub struct CompletionItem { /// Wraps a Menu. pub struct Completion { popup: Popup>, - start_offset: usize, #[allow(dead_code)] trigger_offset: usize, - // TODO: maintain a completioncontext with trigger kind & trigger char + filter: String, } impl Completion { @@ -108,7 +111,6 @@ impl Completion { editor: &Editor, savepoint: Arc, mut items: Vec, - start_offset: usize, trigger_offset: usize, ) -> Self { let preview_completion_insert = editor.config().preview_completion_insert; @@ -246,7 +248,7 @@ impl Completion { // (also without sending the transaction to the LS) *before any further transaction is applied*. // Otherwise incremental sync breaks (since the state of the LS doesn't match the state the transaction // is applied to). - if editor.last_completion.is_none() { + if matches!(editor.last_completion, Some(CompleteAction::Triggered)) { editor.last_completion = Some(CompleteAction::Selected { savepoint: doc.savepoint(view), }) @@ -324,8 +326,18 @@ impl Completion { doc.apply(&transaction, view.id); } } + // we could have just inserted a trigger char (like a `crate::` completion for rust + // so we want to retrigger immediately when accepting a completion. + trigger_auto_completion(&editor.handlers.completions, editor, true); } }; + + // In case the popup was deleted because of an intersection w/ the auto-complete menu. + if event != PromptEvent::Update { + editor + .handlers + .trigger_signature_help(SignatureHelpInvoked::Automatic, editor); + } }); let margin = if editor.menu_border() { @@ -339,14 +351,30 @@ impl Completion { .ignore_escape_key(true) .margin(margin); + let (view, doc) = current_ref!(editor); + let text = doc.text().slice(..); + let cursor = doc.selection(view.id).primary().cursor(text); + let offset = text + .chars_at(cursor) + .reversed() + .take_while(|ch| chars::char_is_word(*ch)) + .count(); + let start_offset = cursor.saturating_sub(offset); + + let fragment = doc.text().slice(start_offset..cursor); let mut completion = Self { popup, - start_offset, trigger_offset, + // TODO: expand nucleo api to allow moving straight to a Utf32String here + // and avoid allocation during matching + filter: String::from(fragment), }; // need to recompute immediately in case start_offset != trigger_offset - completion.recompute_filter(editor); + completion + .popup + .contents_mut() + .score(&completion.filter, false); completion } @@ -366,39 +394,22 @@ impl Completion { } } - pub fn recompute_filter(&mut self, editor: &Editor) { + /// Appends (`c: Some(c)`) or removes (`c: None`) a character to/from the filter + /// this should be called whenever the user types or deletes a character in insert mode. + pub fn update_filter(&mut self, c: Option) { // recompute menu based on matches let menu = self.popup.contents_mut(); - let (view, doc) = current_ref!(editor); - - // cx.hooks() - // cx.add_hook(enum type, ||) - // cx.trigger_hook(enum type, &str, ...) <-- there has to be enough to identify doc/view - // callback with editor & compositor - // - // trigger_hook sends event into channel, that's consumed in the global loop and - // triggers all registered callbacks - // TODO: hooks should get processed immediately so maybe do it after select!(), before - // looping? - - let cursor = doc - .selection(view.id) - .primary() - .cursor(doc.text().slice(..)); - if self.trigger_offset <= cursor { - let fragment = doc.text().slice(self.start_offset..cursor); - let text = Cow::from(fragment); - // TODO: logic is same as ui/picker - menu.score(&text); - } else { - // we backspaced before the start offset, clear the menu - // this will cause the editor to remove the completion popup - menu.clear(); + match c { + Some(c) => self.filter.push(c), + None => { + self.filter.pop(); + if self.filter.is_empty() { + menu.clear(); + return; + } + } } - } - - pub fn update(&mut self, cx: &mut commands::Context) { - self.recompute_filter(cx.editor) + menu.score(&self.filter, c.is_some()); } pub fn is_empty(&self) -> bool { diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 9f186d148..fef62a292 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -1,7 +1,6 @@ use crate::{ commands::{self, OnKeyCallback}, compositor::{Component, Context, Event, EventResult}, - job::{self, Callback}, events::{OnModeSwitch, PostCommand}, key, keymap::{KeymapResult, Keymaps}, @@ -34,8 +33,8 @@ use std::{mem::take, num::NonZeroUsize, path::PathBuf, rc::Rc, sync::Arc}; use tui::{buffer::Buffer as Surface, text::Span}; +use super::document::LineDecoration; use super::{completion::CompletionItem, statusline}; -use super::{document::LineDecoration, lsp::SignatureHelp}; pub struct EditorView { pub keymaps: Keymaps, @@ -837,11 +836,8 @@ impl EditorView { let mut execute_command = |command: &commands::MappableCommand| { command.execute(cxt); helix_event::dispatch(PostCommand { command, cx: cxt }); + let current_mode = cxt.editor.mode(); - match (last_mode, current_mode) { - (Mode::Normal, Mode::Insert) => { - // HAXX: if we just entered insert mode from normal, clear key buf - // and record the command that got us into this mode. if current_mode != last_mode { helix_event::dispatch(OnModeSwitch { old_mode: last_mode, @@ -849,29 +845,16 @@ impl EditorView { cx: cxt, }); + // HAXX: if we just entered insert mode from normal, clear key buf + // and record the command that got us into this mode. + if current_mode == Mode::Insert { // how we entered insert mode is important, and we should track that so // we can repeat the side effect. self.last_insert.0 = command.clone(); self.last_insert.1.clear(); - - commands::signature_help_impl(cxt, commands::SignatureHelpInvoked::Automatic); - } - (Mode::Insert, Mode::Normal) => { - // if exiting insert mode, remove completion - self.clear_completion(cxt.editor); - cxt.editor.completion_request_handle = None; - - // TODO: Use an on_mode_change hook to remove signature help - cxt.jobs.callback(async { - let call: job::Callback = - Callback::EditorCompositor(Box::new(|_editor, compositor| { - compositor.remove(SignatureHelp::ID); - })); - Ok(call) - }); } - _ => (), } + last_mode = current_mode; }; @@ -999,12 +982,10 @@ impl EditorView { editor: &mut Editor, savepoint: Arc, items: Vec, - start_offset: usize, trigger_offset: usize, size: Rect, ) -> Option { - let mut completion = - Completion::new(editor, savepoint, items, start_offset, trigger_offset); + let mut completion = Completion::new(editor, savepoint, items, trigger_offset); if completion.is_empty() { // skip if we got no completion results @@ -1025,6 +1006,7 @@ impl EditorView { self.completion = None; if let Some(last_completion) = editor.last_completion.take() { match last_completion { + CompleteAction::Triggered => (), CompleteAction::Applied { trigger_offset, changes, @@ -1038,9 +1020,6 @@ impl EditorView { } } } - - // Clear any savepoints - editor.clear_idle_timer(); // don't retrigger } pub fn handle_idle_timeout(&mut self, cx: &mut commands::Context) -> EventResult { @@ -1054,13 +1033,7 @@ impl EditorView { }; } - if cx.editor.mode != Mode::Insert || !cx.editor.config().auto_completion { - return EventResult::Ignored(None); - } - - crate::commands::insert::idle_completion(cx); - - EventResult::Consumed(None) + EventResult::Ignored(None) } } @@ -1346,12 +1319,6 @@ impl Component for EditorView { if callback.is_some() { // assume close_fn self.clear_completion(cx.editor); - - // In case the popup was deleted because of an intersection w/ the auto-complete menu. - commands::signature_help_impl( - &mut cx, - commands::SignatureHelpInvoked::Automatic, - ); } } } @@ -1362,14 +1329,6 @@ impl Component for EditorView { // record last_insert key self.last_insert.1.push(InsertEvent::Key(key)); - - // lastly we recalculate completion - if let Some(completion) = &mut self.completion { - completion.update(&mut cx); - if completion.is_empty() { - self.clear_completion(cx.editor); - } - } } } mode => self.command_mode(mode, &mut cx, key), diff --git a/helix-term/src/ui/menu.rs b/helix-term/src/ui/menu.rs index 0ee64ce9e..64127e3af 100644 --- a/helix-term/src/ui/menu.rs +++ b/helix-term/src/ui/menu.rs @@ -96,20 +96,34 @@ impl Menu { } } - pub fn score(&mut self, pattern: &str) { - // reuse the matches allocation - self.matches.clear(); + pub fn score(&mut self, pattern: &str, incremental: bool) { let mut matcher = MATCHER.lock(); matcher.config = Config::DEFAULT; let pattern = Atom::new(pattern, CaseMatching::Ignore, AtomKind::Fuzzy, false); let mut buf = Vec::new(); - let matches = self.options.iter().enumerate().filter_map(|(i, option)| { - let text = option.filter_text(&self.editor_data); - pattern - .score(Utf32Str::new(&text, &mut buf), &mut matcher) - .map(|score| (i as u32, score as u32)) - }); - self.matches.extend(matches); + if incremental { + self.matches.retain_mut(|(index, score)| { + let option = &self.options[*index as usize]; + let text = option.filter_text(&self.editor_data); + let new_score = pattern.score(Utf32Str::new(&text, &mut buf), &mut matcher); + match new_score { + Some(new_score) => { + *score = new_score as u32; + true + } + None => false, + } + }) + } else { + self.matches.clear(); + let matches = self.options.iter().enumerate().filter_map(|(i, option)| { + let text = option.filter_text(&self.editor_data); + pattern + .score(Utf32Str::new(&text, &mut buf), &mut matcher) + .map(|score| (i as u32, score as u32)) + }); + self.matches.extend(matches); + } self.matches .sort_unstable_by_key(|&(i, score)| (Reverse(score), i)); diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 93b83da4f..388810b13 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -115,19 +115,6 @@ pub struct SavePoint { /// The view this savepoint is associated with pub view: ViewId, revert: Mutex, - pub text: Rope, -} - -impl SavePoint { - pub fn cursor(&self) -> usize { - // we always create transactions with selections - self.revert - .lock() - .selection() - .unwrap() - .primary() - .cursor(self.text.slice(..)) - } } pub struct Document { @@ -1404,7 +1391,6 @@ impl Document { let savepoint = Arc::new(SavePoint { view: view.id, revert: Mutex::new(revert), - text: self.text.clone(), }); self.savepoints.push(Arc::downgrade(&savepoint)); savepoint diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 44c706d7d..dc10a6044 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -31,10 +31,7 @@ use std::{ }; use tokio::{ - sync::{ - mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, - oneshot, - }, + sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, time::{sleep, Duration, Instant, Sleep}, }; @@ -244,12 +241,19 @@ pub struct Config { /// Set a global text_width pub text_width: usize, /// Time in milliseconds since last keypress before idle timers trigger. - /// Used for autocompletion, set to 0 for instant. Defaults to 250ms. + /// Used for various UI timeouts. Defaults to 250ms. #[serde( serialize_with = "serialize_duration_millis", deserialize_with = "deserialize_duration_millis" )] pub idle_timeout: Duration, + /// Time in milliseconds after typing a word character before auto completions + /// are shown, set to 5 for instant. Defaults to 250ms. + #[serde( + serialize_with = "serialize_duration_millis", + deserialize_with = "deserialize_duration_millis" + )] + pub completion_timeout: Duration, /// Whether to insert the completion suggestion on hover. Defaults to true. pub preview_completion_insert: bool, pub completion_trigger_len: u8, @@ -829,6 +833,7 @@ impl Default for Config { auto_format: true, auto_save: false, idle_timeout: Duration::from_millis(250), + completion_timeout: Duration::from_millis(250), preview_completion_insert: true, completion_trigger_len: 2, auto_info: true, @@ -953,14 +958,6 @@ pub struct Editor { /// avoid calculating the cursor position multiple /// times during rendering and should not be set by other functions. pub cursor_cache: Cell>>, - /// When a new completion request is sent to the server old - /// unfinished request must be dropped. Each completion - /// request is associated with a channel that cancels - /// when the channel is dropped. That channel is stored - /// here. When a new completion request is sent this - /// field is set and any old requests are automatically - /// canceled as a result - pub completion_request_handle: Option>, pub handlers: Handlers, } @@ -989,13 +986,16 @@ enum ThemeAction { #[derive(Debug, Clone)] pub enum CompleteAction { + Triggered, + /// A savepoint of the currently selected completion. The savepoint + /// MUST be restored before sending any event to the LSP + Selected { + savepoint: Arc, + }, Applied { trigger_offset: usize, changes: Vec, }, - /// A savepoint of the currently selected completion. The savepoint - /// MUST be restored before sending any event to the LSP - Selected { savepoint: Arc }, } #[derive(Debug, Copy, Clone)] @@ -1029,6 +1029,7 @@ impl Editor { theme_loader: Arc, syn_loader: Arc, config: Arc>, + handlers: Handlers, ) -> Self { let language_servers = helix_lsp::Registry::new(syn_loader.clone()); let conf = config.load(); @@ -1073,7 +1074,7 @@ impl Editor { config_events: unbounded_channel(), needs_redraw: false, cursor_cache: Cell::new(None), - completion_request_handle: None, + handlers, } } diff --git a/helix-view/src/handlers.rs b/helix-view/src/handlers.rs index ae3eb545a..724e7b192 100644 --- a/helix-view/src/handlers.rs +++ b/helix-view/src/handlers.rs @@ -1,12 +1,41 @@ -use std::sync::Arc; - use helix_event::send_blocking; use tokio::sync::mpsc::Sender; use crate::handlers::lsp::SignatureHelpInvoked; -use crate::Editor; +use crate::{DocumentId, Editor, ViewId}; pub mod dap; pub mod lsp; -pub struct Handlers {} +pub struct Handlers { + // only public because most of the actual implementation is in helix-term right now :/ + pub completions: Sender, + pub signature_hints: Sender, +} + +impl Handlers { + /// Manually trigger completion (c-x) + pub fn trigger_completions(&self, trigger_pos: usize, doc: DocumentId, view: ViewId) { + send_blocking( + &self.completions, + lsp::CompletionEvent::ManualTrigger { + cursor: trigger_pos, + doc, + view, + }, + ); + } + + pub fn trigger_signature_help(&self, invocation: SignatureHelpInvoked, editor: &Editor) { + let event = match invocation { + SignatureHelpInvoked::Automatic => { + if !editor.config().lsp.auto_signature_help { + return; + } + lsp::SignatureHelpEvent::Trigger + } + SignatureHelpInvoked::Manual => lsp::SignatureHelpEvent::Invoked, + }; + send_blocking(&self.signature_hints, event) + } +} diff --git a/helix-view/src/handlers/lsp.rs b/helix-view/src/handlers/lsp.rs index 958385646..1dae45dd5 100644 --- a/helix-view/src/handlers/lsp.rs +++ b/helix-view/src/handlers/lsp.rs @@ -1,26 +1,27 @@ use crate::{DocumentId, ViewId}; -#[derive(Debug, Clone, Copy)] -pub struct CompletionTrigger { - /// The char position of the primary cursor when the - /// completion was triggered - pub trigger_pos: usize, - pub doc: DocumentId, - pub view: ViewId, - /// Whether the cause of the trigger was an automatic completion (any word - /// char for words longer than minimum word length). - /// This is false for trigger chars send by the LS - pub auto: bool, -} - pub enum CompletionEvent { /// Auto completion was triggered by typing a word char - /// or a completion trigger - Trigger(CompletionTrigger), + AutoTrigger { + cursor: usize, + doc: DocumentId, + view: ViewId, + }, + /// Auto completion was triggered by typing a trigger char + /// specified by the LSP + TriggerChar { + cursor: usize, + doc: DocumentId, + view: ViewId, + }, /// A completion was manually requested (c-x) - Manual, + ManualTrigger { + cursor: usize, + doc: DocumentId, + view: ViewId, + }, /// Some text was deleted and the cursor is now at `pos` - DeleteText { pos: usize }, + DeleteText { cursor: usize }, /// Invalidate the current auto completion trigger Cancel, } From 960cda60abf8eb84a7369079f906a0bc53e8088c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 11:22:42 +0900 Subject: [PATCH 075/796] build(deps): bump chrono from 0.4.31 to 0.4.32 (#9409) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 96496125a..4226e06ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -171,14 +171,14 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "41daef31d7a747c5c847246f36de49ced6f7403b4cdabc807a97b5cc184cda7a" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", - "windows-targets 0.48.0", + "windows-targets 0.52.0", ] [[package]] From e85507ccac5ad6771b66d2b85e7de29fa08a5b49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 11:24:29 +0900 Subject: [PATCH 076/796] build(deps): bump regex from 1.10.2 to 1.10.3 (#9408) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4226e06ac..839245f60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1738,9 +1738,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", @@ -1750,9 +1750,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a" dependencies = [ "aho-corasick", "memchr", From ae8042bb83e34b7cf085ec8792fcbf529896d998 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 11:24:38 +0900 Subject: [PATCH 077/796] build(deps): bump bitflags from 2.4.1 to 2.4.2 (#9404) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 839245f60..d9519aa21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,9 +101,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "bstr" @@ -285,7 +285,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "crossterm_winapi", "filedescriptor", "futures-core", @@ -625,7 +625,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52e0be46f4cf1f8f9e88d0e3eb7b29718aff23889563249f379119bd1ab6910e" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "bstr", "gix-path", "libc", @@ -704,7 +704,7 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae6232f18b262770e343dcdd461c0011c9b9ae27f0c805e115012aa2b902c1b8" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "bstr", "gix-features", "gix-path", @@ -907,7 +907,7 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78f6dce0c6683e2219e8169aac4b1c29e89540a8262fef7056b31d80d969408c" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "gix-path", "libc", "windows 0.52.0", @@ -1053,7 +1053,7 @@ version = "23.10.0" dependencies = [ "ahash", "arc-swap", - "bitflags 2.4.1", + "bitflags 2.4.2", "chrono", "dunce", "encoding_rs", @@ -1216,7 +1216,7 @@ dependencies = [ name = "helix-tui" version = "23.10.0" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "cassowary", "crossterm", "helix-core", @@ -1250,7 +1250,7 @@ version = "23.10.0" dependencies = [ "anyhow", "arc-swap", - "bitflags 2.4.1", + "bitflags 2.4.2", "chardetng", "clipboard-win", "crossterm", @@ -1787,7 +1787,7 @@ version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "errno", "libc", "linux-raw-sys", From 4168864572ac29c64d82031ba9edfbda0a521839 Mon Sep 17 00:00:00 2001 From: Jaakko Paju Date: Tue, 23 Jan 2024 16:19:22 +0200 Subject: [PATCH 078/796] Add bufferline config for onedark & onedarker themes (#9397) --- runtime/themes/onedark.toml | 4 ++++ runtime/themes/onedarker.toml | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/runtime/themes/onedark.toml b/runtime/themes/onedark.toml index 1db2aff86..a50b5d354 100644 --- a/runtime/themes/onedark.toml +++ b/runtime/themes/onedark.toml @@ -77,6 +77,10 @@ "ui.statusline.insert" = { fg = "light-black", bg = "green" } "ui.statusline.select" = { fg = "light-black", bg = "purple" } +"ui.bufferline" = { fg = "light-gray", bg = "light-black" } +"ui.bufferline.active" = { fg = "light-black", bg = "blue", underline = { color = "light-black", style = "line" } } +"ui.bufferline.background" = { bg = "light-black" } + "ui.text" = { fg = "white" } "ui.text.focus" = { fg = "white", bg = "light-black", modifiers = ["bold"] } diff --git a/runtime/themes/onedarker.toml b/runtime/themes/onedarker.toml index 88b2871ab..307871445 100644 --- a/runtime/themes/onedarker.toml +++ b/runtime/themes/onedarker.toml @@ -75,6 +75,11 @@ "ui.statusline.normal" = { fg = "light-black", bg = "purple" } "ui.statusline.insert" = { fg = "light-black", bg = "green" } "ui.statusline.select" = { fg = "light-black", bg = "cyan" } + +"ui.bufferline" = { fg = "light-gray", bg = "light-black" } +"ui.bufferline.active" = { fg = "light-black", bg = "blue", underline = { color = "light-black", style = "line" } } +"ui.bufferline.background" = { bg = "light-black" } + "ui.text" = { fg = "white" } "ui.text.focus" = { fg = "white", bg = "light-black", modifiers = ["bold"] } From cbd8602018efd90506fe684ea58a35dd4b40518f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 23:19:36 +0900 Subject: [PATCH 079/796] build(deps): bump smallvec from 1.12.0 to 1.13.1 (#9405) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- helix-core/Cargo.toml | 2 +- helix-term/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d9519aa21..d3da63613 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1940,9 +1940,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.12.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "smartstring" diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index 42c88f4bc..8c63af8ef 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -20,7 +20,7 @@ helix-stdx = { path = "../helix-stdx" } helix-loader = { path = "../helix-loader" } ropey = { version = "1.6.1", default-features = false, features = ["simd"] } -smallvec = "1.12" +smallvec = "1.13" smartstring = "1.0.1" unicode-segmentation = "1.10" unicode-width = "0.1" diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 7bdd433ed..683d6dc53 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -83,6 +83,6 @@ crossterm = { version = "0.27", features = ["event-stream", "use-dev-tty"] } helix-loader = { path = "../helix-loader" } [dev-dependencies] -smallvec = "1.12" +smallvec = "1.13" indoc = "2.0.4" tempfile = "3.9.0" From 7caae13465dd14ae105c69280bbe40774956eda1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 16:56:16 +0100 Subject: [PATCH 080/796] build(deps): bump gix from 0.57.1 to 0.58.0 (#9407) Bumps [gix](https://github.com/Byron/gitoxide) from 0.57.1 to 0.58.0. - [Release notes](https://github.com/Byron/gitoxide/releases) - [Changelog](https://github.com/Byron/gitoxide/blob/main/CHANGELOG.md) - [Commits](https://github.com/Byron/gitoxide/compare/gix-v0.57.1...gix-v0.58.0) --- updated-dependencies: - dependency-name: gix dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 130 +++++++++++++++++++------------------------ helix-vcs/Cargo.toml | 2 +- 2 files changed, 59 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d3da63613..9f62947f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -522,9 +522,9 @@ checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "gix" -version = "0.57.1" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd025382892c7b500a9ce1582cd803f9c2ebfe44aff52e9c7f86feee7ced75e" +checksum = "31887c304d9a935f3e5494fb5d6a0106c34e965168ec0db9b457424eedd0c741" dependencies = [ "gix-actor", "gix-commitgraph", @@ -558,14 +558,13 @@ dependencies = [ "parking_lot", "smallvec", "thiserror", - "unicode-normalization", ] [[package]] name = "gix-actor" -version = "0.29.1" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da27b5ab4ab5c75ff891dccd48409f8cc53c28a79480f1efdd33184b2dc1d958" +checksum = "0a7bb9fad6125c81372987c06469601d37e1a2d421511adb69971b9083517a8a" dependencies = [ "bstr", "btoi", @@ -586,9 +585,9 @@ dependencies = [ [[package]] name = "gix-commitgraph" -version = "0.23.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a39c675fd737cb43a2120eddf1aa652c19d76b28d79783a198ac1b398ed9ce6" +checksum = "82dbd7fb959862e3df2583331f0ad032ac93533e8a52f1b0694bc517f5d292bc" dependencies = [ "bstr", "gix-chunk", @@ -600,9 +599,9 @@ dependencies = [ [[package]] name = "gix-config" -version = "0.33.1" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "367304855b369cadcac4ee5fb5a3a20da9378dd7905106141070b79f85241079" +checksum = "e62bf2073b6ce3921ffa6d8326f645f30eec5fc4a8e8a4bc0fcb721a2f3f69dc" dependencies = [ "bstr", "gix-config-value", @@ -621,9 +620,9 @@ dependencies = [ [[package]] name = "gix-config-value" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e0be46f4cf1f8f9e88d0e3eb7b29718aff23889563249f379119bd1ab6910e" +checksum = "5b8a1e7bfb37a46ed0b8468db37a6d8a0a61d56bdbe4603ae492cb322e5f3958" dependencies = [ "bitflags 2.4.2", "bstr", @@ -646,9 +645,9 @@ dependencies = [ [[package]] name = "gix-diff" -version = "0.39.1" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6a0454f8c42d686f17e7f084057c717c082b7dbb8209729e4e8f26749eb93a" +checksum = "cbdcb5e49c4b9729dd1c361040ae5c3cd7c497b2260b18c954f62db3a63e98cf" dependencies = [ "bstr", "gix-hash", @@ -658,12 +657,13 @@ dependencies = [ [[package]] name = "gix-discover" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8d7b2896edc3d899d28a646ccc6df729827a6600e546570b2783466404a42d6" +checksum = "b4669218f3ec0cbbf8f16857b32200890f8ca585f36f5817242e4115fe4551af" dependencies = [ "bstr", "dunce", + "gix-fs", "gix-hash", "gix-path", "gix-ref", @@ -673,14 +673,15 @@ dependencies = [ [[package]] name = "gix-features" -version = "0.37.1" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a80f0fe688d654c2a741751578b11131071026d1934d03c1820d6d767525ce" +checksum = "184f7f7d4e45db0e2a362aeaf12c06c5e84817d0ef91d08e8e90170dad9f0b07" dependencies = [ "crc32fast", "flate2", "gix-hash", "gix-trace", + "gix-utils", "libc", "once_cell", "prodash", @@ -691,18 +692,19 @@ dependencies = [ [[package]] name = "gix-fs" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7555c23a005537434bbfcb8939694e18cad42602961d0de617f8477cc2adecdd" +checksum = "4436e883d5769f9fb18677b8712b49228357815f9e4104174a6fc2d8461a437b" dependencies = [ "gix-features", + "gix-utils", ] [[package]] name = "gix-glob" -version = "0.15.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae6232f18b262770e343dcdd461c0011c9b9ae27f0c805e115012aa2b902c1b8" +checksum = "4965a1d06d0ab84a29d4a67697a97352ab14ae1da821084e5afb1fd6d8191ca0" dependencies = [ "bitflags 2.4.2", "bstr", @@ -733,9 +735,9 @@ dependencies = [ [[package]] name = "gix-lock" -version = "12.0.0" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cf112ddee94223c119a8534dad027740dc3aba3365ac5edeef8a7f6660c74db" +checksum = "651e46174dc5e7d18b7b809d31937b6de3681b1debd78618c99162cc30fcf3e1" dependencies = [ "gix-tempfile", "gix-utils", @@ -755,9 +757,9 @@ dependencies = [ [[package]] name = "gix-object" -version = "0.40.1" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c89402e8faa41b49fde348665a8f38589e461036475af43b6b70615a6a313a2" +checksum = "693ce9d30741506cb082ef2d8b797415b48e032cce0ab23eff894c19a7e4777b" dependencies = [ "bstr", "btoi", @@ -774,13 +776,14 @@ dependencies = [ [[package]] name = "gix-odb" -version = "0.56.1" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46ae6da873de41c6c2b73570e82c571b69df5154dcd8f46dfafc6687767c33b1" +checksum = "8ba2fa9e81f2461b78b4d81a807867667326c84cdab48e0aed7b73a593aa1be4" dependencies = [ "arc-swap", "gix-date", "gix-features", + "gix-fs", "gix-hash", "gix-object", "gix-pack", @@ -793,9 +796,9 @@ dependencies = [ [[package]] name = "gix-pack" -version = "0.46.1" +version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "782b4d42790a14072d5c400deda9851f5765f50fe72bca6dece0da1cd6f05a9a" +checksum = "8da5f3e78c96b76c4e6fe5e8e06b76221e4a0ee9a255aa935ed1fdf68988dfd8" dependencies = [ "clru", "gix-chunk", @@ -813,9 +816,9 @@ dependencies = [ [[package]] name = "gix-path" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dd0998ab245f33d40ca2267e58d542fe54185ebd1dc41923346cf28d179fb6" +checksum = "14a6282621aed1becc3f83d64099a564b3b9063f22783d9a87ea502a3e9f2e40" dependencies = [ "bstr", "gix-trace", @@ -837,9 +840,9 @@ dependencies = [ [[package]] name = "gix-ref" -version = "0.40.1" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d9bd1984638d8f3511a2fcbe84fcedb8a5b5d64df677353620572383f42649" +checksum = "5818958994ad7879fa566f5441ebcc48f0926aa027b28948e6fbf6578894dc31" dependencies = [ "gix-actor", "gix-date", @@ -850,6 +853,7 @@ dependencies = [ "gix-object", "gix-path", "gix-tempfile", + "gix-utils", "gix-validate", "memmap2", "thiserror", @@ -858,9 +862,9 @@ dependencies = [ [[package]] name = "gix-refspec" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be219df5092c1735abb2a53eccdf775e945eea6986ee1b6e7a5896dccc0be704" +checksum = "613aa4d93034c5791d13bdc635e530f4ddab1412ddfb4a8215f76213177b61c7" dependencies = [ "bstr", "gix-hash", @@ -872,9 +876,9 @@ dependencies = [ [[package]] name = "gix-revision" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa78e1df3633bc937d4db15f8dca2abdb1300ca971c0fabcf9fa97e38cf4cd9f" +checksum = "288f6549d7666db74dc3f169a9a333694fc28ecd2f5aa7b2c979c89eb556751a" dependencies = [ "bstr", "gix-date", @@ -888,9 +892,9 @@ dependencies = [ [[package]] name = "gix-revwalk" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702de5fe5c2bbdde80219f3a8b9723eb927466e7ecd187cfd1b45d986408e45f" +checksum = "5b9b4d91dfc5c14fee61a28c65113ded720403b65a0f46169c0460f731a5d03c" dependencies = [ "gix-commitgraph", "gix-date", @@ -903,21 +907,21 @@ dependencies = [ [[package]] name = "gix-sec" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78f6dce0c6683e2219e8169aac4b1c29e89540a8262fef7056b31d80d969408c" +checksum = "f8d9bf462feaf05f2121cba7399dbc6c34d88a9cad58fc1e95027791d6a3c6d2" dependencies = [ "bitflags 2.4.2", "gix-path", "libc", - "windows 0.52.0", + "windows-sys 0.52.0", ] [[package]] name = "gix-tempfile" -version = "12.0.0" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e76a494bd530e1a1309188ff971825a24f159c76c2db0bf71fa5dfb469a2c915" +checksum = "2d337955b7af00fb87120d053d87cdfb422a80b9ff7a3aa4057a99c79422dc30" dependencies = [ "gix-fs", "libc", @@ -928,15 +932,15 @@ dependencies = [ [[package]] name = "gix-trace" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8e1127ede0475b58f4fe9c0aaa0d9bb0bad2af90bbd93ccd307c8632b863d89" +checksum = "02b202d766a7fefc596e2cc6a89cda8ad8ad733aed82da635ac120691112a9b1" [[package]] name = "gix-traverse" -version = "0.36.1" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb64213e52e1b726cb04581690c1e98b5910f983b977d5e9f2eb09f1a7fea6d2" +checksum = "bfc30c5b5e4e838683b59e1b0574ce6bc1c35916df9709aaab32bb7751daf08b" dependencies = [ "gix-commitgraph", "gix-date", @@ -950,9 +954,9 @@ dependencies = [ [[package]] name = "gix-url" -version = "0.26.1" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f0f17cceb7552a231d1fec690bc2740c346554e3be6f5d2c41dfa809594dc44" +checksum = "26f1981ecc700f4fd73ae62b9ca2da7c8816c8fd267f0185e3f8c21e967984ac" dependencies = [ "bstr", "gix-features", @@ -964,11 +968,12 @@ dependencies = [ [[package]] name = "gix-utils" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de6225e2de30b6e9bca2d9f1cc4731640fcef0fb3cabddceee366e7e85d3e94f" +checksum = "56e839f3d0798b296411263da6bee780a176ef8008a5dfc31287f7eda9266ab8" dependencies = [ "fastrand", + "unicode-normalization", ] [[package]] @@ -1307,7 +1312,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows 0.48.0", + "windows", ] [[package]] @@ -2405,25 +2410,6 @@ dependencies = [ "windows-targets 0.48.0", ] -[[package]] -name = "windows" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core", - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.0", -] - [[package]] name = "windows-sys" version = "0.45.0" diff --git a/helix-vcs/Cargo.toml b/helix-vcs/Cargo.toml index f6dbe076d..051134e4a 100644 --- a/helix-vcs/Cargo.toml +++ b/helix-vcs/Cargo.toml @@ -19,7 +19,7 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "p parking_lot = "0.12" arc-swap = { version = "1.6.0" } -gix = { version = "0.57.1", default-features = false , optional = true } +gix = { version = "0.58.0", default-features = false , optional = true } imara-diff = "0.1.5" anyhow = "1" From 2058b3732c340f5bc29eee2aca1d8edae15e1314 Mon Sep 17 00:00:00 2001 From: melted-brownie <66597133+melted-brownie@users.noreply.github.com> Date: Tue, 23 Jan 2024 17:27:09 +0000 Subject: [PATCH 081/796] Add text object queries for dart (#9411) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add text object queries for dart * Update runtime/queries/dart/textobjects.scm Co-authored-by: Michael Davis * Clean up internal capture name --------- Co-authored-by: Sébastien Blondiau Co-authored-by: Michael Davis --- book/src/generated/lang-support.md | 2 +- runtime/queries/dart/textobjects.scm | 68 ++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 runtime/queries/dart/textobjects.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 6fedace63..a78dd7935 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -24,7 +24,7 @@ | css | ✓ | | ✓ | `vscode-css-language-server` | | cue | ✓ | | | `cuelsp` | | d | ✓ | ✓ | ✓ | `serve-d` | -| dart | ✓ | | ✓ | `dart` | +| dart | ✓ | ✓ | ✓ | `dart` | | dbml | ✓ | | | | | devicetree | ✓ | | | | | dhall | ✓ | ✓ | | `dhall-lsp-server` | diff --git a/runtime/queries/dart/textobjects.scm b/runtime/queries/dart/textobjects.scm new file mode 100644 index 000000000..028276156 --- /dev/null +++ b/runtime/queries/dart/textobjects.scm @@ -0,0 +1,68 @@ +(class_definition + body: (_) @class.inside) @class.around + +(mixin_declaration + (class_body) @class.inside) @class.around + +(extension_declaration + (extension_body) @class.inside) @class.around + +(enum_declaration + body: (_) @class.inside) @class.around + +(type_alias) @class.around + +(_ + ( + [ + (getter_signature) + (setter_signature) + (function_signature) + (method_signature) + (constructor_signature) + ] + . + (function_body) @function.inside @function.around + ) @function.around +) + +(declaration + [ + (constant_constructor_signature) + (constructor_signature) + (factory_constructor_signature) + (redirecting_factory_constructor_signature) + (getter_signature) + (setter_signature) + (operator_signature) + (function_signature) + ] +) @function.around + +(lambda_expression + body: (_) @function.inside +) @function.around + +(function_expression + body: (_) @function.inside +) @function.around + +[ + (comment) + (documentation_comment) +] @comment.inside + +(comment)+ @comment.around + +(documentation_comment)+ @comment.around + +(formal_parameter) @parameter.inside + +(formal_parameter_list) @parameter.around + +(expression_statement + ((identifier) @_name (#any-of? @_name "test" "testWidgets")) + . + (selector (argument_part (arguments . (_) . (argument) @test.inside))) +) @test.around + From 299bcce481abe7e13f2023ec6e82c59b43f12f47 Mon Sep 17 00:00:00 2001 From: Idobenhamo <135357971+Idobenhamo@users.noreply.github.com> Date: Tue, 23 Jan 2024 19:27:46 +0200 Subject: [PATCH 082/796] Update Typst Tree-Sitter grammar (#9403) Co-authored-by: Idobenhamo --- languages.toml | 2 +- runtime/queries/typst/highlights.scm | 7 ++++++- runtime/queries/typst/injections.scm | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/languages.toml b/languages.toml index 4ff529c47..83c770479 100644 --- a/languages.toml +++ b/languages.toml @@ -2865,7 +2865,7 @@ indent = { tab-width = 2, unit = " " } [[grammar]] name = "typst" -source = { git = "https://github.com/uben0/tree-sitter-typst", rev = "e35aa22395fdde82bbc4b5700c324ce346dfc9e5" } +source = { git = "https://github.com/uben0/tree-sitter-typst", rev = "ecf8596336857adfcd5f7cbb3b2aa11a67badc37" } [[language]] name = "nunjucks" diff --git a/runtime/queries/typst/highlights.scm b/runtime/queries/typst/highlights.scm index b422e05b3..0bbccede0 100644 --- a/runtime/queries/typst/highlights.scm +++ b/runtime/queries/typst/highlights.scm @@ -55,7 +55,12 @@ ; MARKUP (item "-" @markup.list) (term ["/" ":"] @markup.list) -(heading ["=" "==" "===" "====" "====="] @markup.heading.marker) @markup.heading +(heading "=" @markup.heading.marker) @markup.heading.1 +(heading "==" @markup.heading.marker) @markup.heading.2 +(heading "===" @markup.heading.marker) @markup.heading.3 +(heading "====" @markup.heading.marker) @markup.heading.4 +(heading "=====" @markup.heading.marker) @markup.heading.5 +(heading "======" @markup.heading.marker) @markup.heading.6 (url) @tag (emph) @markup.italic (strong) @markup.bold diff --git a/runtime/queries/typst/injections.scm b/runtime/queries/typst/injections.scm index 8039b4cab..06a250979 100644 --- a/runtime/queries/typst/injections.scm +++ b/runtime/queries/typst/injections.scm @@ -3,4 +3,5 @@ (raw_blck lang: (ident) @injection.language - (blob) @injection.content) \ No newline at end of file + (blob) @injection.content) + From b606c052467396042b1dbba10f19fd3c114fd42a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:23:39 +0100 Subject: [PATCH 083/796] build(deps): bump actions/cache from 3 to 4 (#9402) Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 22151c378..3d47c2088 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,7 +46,7 @@ jobs: shared-key: "build" - name: Cache test tree-sitter grammar - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: runtime/grammars key: ${{ runner.os }}-stable-v${{ env.CACHE_VERSION }}-tree-sitter-grammars-${{ hashFiles('languages.toml') }} From 6bfe1ddc53f542d62e242fd4aaf6748dda1b0e71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Wed, 24 Jan 2024 15:31:16 +0900 Subject: [PATCH 084/796] minor: Silence noisy set_error log Outside of debugging tests, it makes no sense to log this. --- helix-view/src/editor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index dc10a6044..fbfcb3560 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -1147,7 +1147,7 @@ impl Editor { #[inline] pub fn set_error>>(&mut self, error: T) { let error = error.into(); - log::error!("editor error: {}", error); + log::debug!("editor error: {}", error); self.status_msg = Some((error, Severity::Error)); } From 6d724a8f331f4b2a8f1a001e990cf6129dc50b00 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Tue, 23 Jan 2024 09:29:07 -0500 Subject: [PATCH 085/796] Re-export `which` from `helix-stdx::env` We use `which::which` in many crates, so `which` was a separate dependency across all of them. We can centralize `which` into the stdx crate so it's easy for all crates to depend on it. I also moved the rest of `helix-view/src/env.rs` into helix-stdx's `env` module since it only contained a thin wrapper around `which` and `std::env`. --- Cargo.lock | 23 ++++++++++------------- helix-dap/Cargo.toml | 2 +- helix-dap/src/client.rs | 2 +- helix-loader/Cargo.toml | 1 - helix-loader/src/grammar.rs | 2 +- helix-lsp/Cargo.toml | 1 - helix-lsp/src/client.rs | 2 +- helix-stdx/Cargo.toml | 1 + helix-stdx/src/env.rs | 10 ++++++++++ helix-term/Cargo.toml | 2 -- helix-term/src/health.rs | 6 +++--- helix-view/Cargo.toml | 1 - helix-view/src/clipboard.rs | 4 ++-- helix-view/src/document.rs | 7 ++++++- helix-view/src/editor.rs | 4 ++-- helix-view/src/env.rs | 8 -------- helix-view/src/lib.rs | 1 - 17 files changed, 38 insertions(+), 39 deletions(-) delete mode 100644 helix-view/src/env.rs diff --git a/Cargo.lock b/Cargo.lock index 9f62947f0..d4ac12e43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -358,9 +358,9 @@ checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encoding_rs" @@ -1095,12 +1095,12 @@ dependencies = [ "anyhow", "fern", "helix-core", + "helix-stdx", "log", "serde", "serde_json", "thiserror", "tokio", - "which", ] [[package]] @@ -1134,7 +1134,6 @@ dependencies = [ "threadpool", "toml", "tree-sitter", - "which", ] [[package]] @@ -1157,7 +1156,6 @@ dependencies = [ "thiserror", "tokio", "tokio-stream", - "which", ] [[package]] @@ -1172,6 +1170,7 @@ dependencies = [ "etcetera", "ropey", "tempfile", + "which", ] [[package]] @@ -1214,7 +1213,6 @@ dependencies = [ "tokio-stream", "toml", "url", - "which", ] [[package]] @@ -1280,7 +1278,6 @@ dependencies = [ "tokio-stream", "toml", "url", - "which", ] [[package]] @@ -1294,11 +1291,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2359,15 +2356,15 @@ checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "which" -version = "5.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14" +checksum = "7fa5e0c10bf77f44aac573e498d1a82d5fbd5e91f6fc0a99e7be4b38e85e101c" dependencies = [ "either", "home", "once_cell", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] diff --git a/helix-dap/Cargo.toml b/helix-dap/Cargo.toml index f7acb0032..3521f5890 100644 --- a/helix-dap/Cargo.toml +++ b/helix-dap/Cargo.toml @@ -13,6 +13,7 @@ homepage.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +helix-stdx = { path = "../helix-stdx" } helix-core = { path = "../helix-core" } anyhow = "1.0" @@ -21,7 +22,6 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" thiserror = "1.0" tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "net", "sync"] } -which = "5.0.0" [dev-dependencies] fern = "0.6" diff --git a/helix-dap/src/client.rs b/helix-dap/src/client.rs index 55ebab577..579811df7 100644 --- a/helix-dap/src/client.rs +++ b/helix-dap/src/client.rs @@ -113,7 +113,7 @@ impl Client { id: usize, ) -> Result<(Self, UnboundedReceiver)> { // Resolve path to the binary - let cmd = which::which(cmd).map_err(|err| anyhow::anyhow!(err))?; + let cmd = helix_stdx::env::which(cmd).map_err(|err| anyhow::anyhow!(err))?; let process = Command::new(cmd) .args(args) diff --git a/helix-loader/Cargo.toml b/helix-loader/Cargo.toml index 08da7f295..469bedc10 100644 --- a/helix-loader/Cargo.toml +++ b/helix-loader/Cargo.toml @@ -24,7 +24,6 @@ etcetera = "0.8" tree-sitter.workspace = true once_cell = "1.19" log = "0.4" -which = "5.0.0" # TODO: these two should be on !wasm32 only diff --git a/helix-loader/src/grammar.rs b/helix-loader/src/grammar.rs index 66111aebb..537e12828 100644 --- a/helix-loader/src/grammar.rs +++ b/helix-loader/src/grammar.rs @@ -86,7 +86,7 @@ pub fn get_language(name: &str) -> Result { } fn ensure_git_is_available() -> Result<()> { - match which::which("git") { + match helix_stdx::env::which("git") { Ok(_cmd) => Ok(()), Err(err) => Err(anyhow::anyhow!("'git' could not be found ({err})")), } diff --git a/helix-lsp/Cargo.toml b/helix-lsp/Cargo.toml index 510be6eec..8e9e3407c 100644 --- a/helix-lsp/Cargo.toml +++ b/helix-lsp/Cargo.toml @@ -29,5 +29,4 @@ serde_json = "1.0" thiserror = "1.0" tokio = { version = "1.35", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] } tokio-stream = "0.1.14" -which = "5.0.0" parking_lot = "0.12.1" diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 7eef2bf7a..f8c2912e7 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -183,7 +183,7 @@ impl Client { doc_path: Option<&std::path::PathBuf>, ) -> Result<(Self, UnboundedReceiver<(usize, Call)>, Arc)> { // Resolve path to the binary - let cmd = which::which(cmd).map_err(|err| anyhow::anyhow!(err))?; + let cmd = helix_stdx::env::which(cmd).map_err(|err| anyhow::anyhow!(err))?; let process = Command::new(cmd) .envs(server_environment) diff --git a/helix-stdx/Cargo.toml b/helix-stdx/Cargo.toml index 9b4de9fef..e77f8b91f 100644 --- a/helix-stdx/Cargo.toml +++ b/helix-stdx/Cargo.toml @@ -15,6 +15,7 @@ homepage.workspace = true dunce = "1.0" etcetera = "0.8" ropey = { version = "1.6.1", default-features = false } +which = "6.0" [dev-dependencies] tempfile = "3.9" diff --git a/helix-stdx/src/env.rs b/helix-stdx/src/env.rs index 864ba828b..3676727f2 100644 --- a/helix-stdx/src/env.rs +++ b/helix-stdx/src/env.rs @@ -1,3 +1,5 @@ +pub use which::which; + use std::{ path::{Path, PathBuf}, sync::RwLock, @@ -30,6 +32,14 @@ pub fn set_current_working_dir(path: impl AsRef) -> std::io::Result<()> { Ok(()) } +pub fn env_var_is_set(env_var_name: &str) -> bool { + std::env::var_os(env_var_name).is_some() +} + +pub fn binary_exists(binary_name: &str) -> bool { + which::which(binary_name).is_ok() +} + #[cfg(test)] mod tests { use super::{current_working_dir, set_current_working_dir}; diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 683d6dc53..9a7162ac1 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -35,8 +35,6 @@ helix-loader = { path = "../helix-loader" } anyhow = "1" once_cell = "1.19" -which = "5.0.0" - tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] } tui = { path = "../helix-tui", package = "helix-tui", default-features = false, features = ["crossterm"] } crossterm = { version = "0.27", features = ["event-stream"] } diff --git a/helix-term/src/health.rs b/helix-term/src/health.rs index 44ae2a2f7..5f2019265 100644 --- a/helix-term/src/health.rs +++ b/helix-term/src/health.rs @@ -182,7 +182,7 @@ pub fn languages_all() -> std::io::Result<()> { .sort_unstable_by_key(|l| l.language_id.clone()); let check_binary = |cmd: Option<&str>| match cmd { - Some(cmd) => match which::which(cmd) { + Some(cmd) => match helix_stdx::env::which(cmd) { Ok(_) => column(&format!("✓ {}", cmd), Color::Green), Err(_) => column(&format!("✘ {}", cmd), Color::Red), }, @@ -322,7 +322,7 @@ fn probe_protocols<'a, I: Iterator + 'a>( writeln!(stdout)?; for cmd in server_cmds { - let (path, icon) = match which::which(cmd) { + let (path, icon) = match helix_stdx::env::which(cmd) { Ok(path) => (path.display().to_string().green(), "✓".green()), Err(_) => (format!("'{}' not found in $PATH", cmd).red(), "✘".red()), }; @@ -344,7 +344,7 @@ fn probe_protocol(protocol_name: &str, server_cmd: Option) -> std::io::R writeln!(stdout, "Configured {}: {}", protocol_name, cmd_name)?; if let Some(cmd) = server_cmd { - let path = match which::which(&cmd) { + let path = match helix_stdx::env::which(&cmd) { Ok(path) => path.display().to_string().green(), Err(_) => format!("'{}' not found in $PATH", cmd).red(), }; diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index 0dc18b373..2e6893414 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -46,7 +46,6 @@ serde_json = "1.0" toml = "0.7" log = "~0.4" -which = "5.0.0" parking_lot = "0.12.1" diff --git a/helix-view/src/clipboard.rs b/helix-view/src/clipboard.rs index 812c803e9..9ff2fd788 100644 --- a/helix-view/src/clipboard.rs +++ b/helix-view/src/clipboard.rs @@ -73,7 +73,7 @@ pub fn get_clipboard_provider() -> Box { #[cfg(target_os = "macos")] pub fn get_clipboard_provider() -> Box { - use crate::env::{binary_exists, env_var_is_set}; + use helix_stdx::env::{binary_exists, env_var_is_set}; if env_var_is_set("TMUX") && binary_exists("tmux") { command_provider! { @@ -98,7 +98,7 @@ pub fn get_clipboard_provider() -> Box { #[cfg(not(any(windows, target_os = "wasm32", target_os = "macos")))] pub fn get_clipboard_provider() -> Box { - use crate::env::{binary_exists, env_var_is_set}; + use helix_stdx::env::{binary_exists, env_var_is_set}; use provider::command::is_exit_success; // TODO: support for user-defined provider, probably when we have plugin support by setting a // variable? diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 388810b13..88653948c 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -726,7 +726,12 @@ impl Document { if let Some((fmt_cmd, fmt_args)) = self .language_config() .and_then(|c| c.formatter.as_ref()) - .and_then(|formatter| Some((which::which(&formatter.command).ok()?, &formatter.args))) + .and_then(|formatter| { + Some(( + helix_stdx::env::which(&formatter.command).ok()?, + &formatter.args, + )) + }) { use std::process::Stdio; let text = self.text().clone(); diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index fbfcb3560..f605cbb5f 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -329,7 +329,7 @@ pub struct TerminalConfig { #[cfg(windows)] pub fn get_terminal_provider() -> Option { - use crate::env::binary_exists; + use helix_stdx::env::binary_exists; if binary_exists("wt") { return Some(TerminalConfig { @@ -352,7 +352,7 @@ pub fn get_terminal_provider() -> Option { #[cfg(not(any(windows, target_os = "wasm32")))] pub fn get_terminal_provider() -> Option { - use crate::env::{binary_exists, env_var_is_set}; + use helix_stdx::env::{binary_exists, env_var_is_set}; if env_var_is_set("TMUX") && binary_exists("tmux") { return Some(TerminalConfig { diff --git a/helix-view/src/env.rs b/helix-view/src/env.rs deleted file mode 100644 index c68cc609a..000000000 --- a/helix-view/src/env.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub fn binary_exists(binary_name: &str) -> bool { - which::which(binary_name).is_ok() -} - -#[cfg(not(windows))] -pub fn env_var_is_set(env_var_name: &str) -> bool { - std::env::var_os(env_var_name).is_some() -} diff --git a/helix-view/src/lib.rs b/helix-view/src/lib.rs index 82827b5d5..14b6e1ce8 100644 --- a/helix-view/src/lib.rs +++ b/helix-view/src/lib.rs @@ -5,7 +5,6 @@ pub mod base64; pub mod clipboard; pub mod document; pub mod editor; -pub mod env; pub mod events; pub mod graphics; pub mod gutter; From cb25d13028ec1cdf986a3567ea52562ea654a7b8 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Tue, 23 Jan 2024 13:36:53 -0500 Subject: [PATCH 086/796] Improve error handling for `which::which` failures Co-authored-by: Pascal Kuthe --- helix-dap/src/client.rs | 2 +- helix-dap/src/lib.rs | 2 ++ helix-loader/src/grammar.rs | 6 ++---- helix-lsp/src/client.rs | 2 +- helix-lsp/src/lib.rs | 2 ++ helix-stdx/src/env.rs | 28 +++++++++++++++++++++++++--- 6 files changed, 33 insertions(+), 9 deletions(-) diff --git a/helix-dap/src/client.rs b/helix-dap/src/client.rs index 579811df7..18af13ae7 100644 --- a/helix-dap/src/client.rs +++ b/helix-dap/src/client.rs @@ -113,7 +113,7 @@ impl Client { id: usize, ) -> Result<(Self, UnboundedReceiver)> { // Resolve path to the binary - let cmd = helix_stdx::env::which(cmd).map_err(|err| anyhow::anyhow!(err))?; + let cmd = helix_stdx::env::which(cmd)?; let process = Command::new(cmd) .args(args) diff --git a/helix-dap/src/lib.rs b/helix-dap/src/lib.rs index 21162cb86..d0229249d 100644 --- a/helix-dap/src/lib.rs +++ b/helix-dap/src/lib.rs @@ -19,6 +19,8 @@ pub enum Error { #[error("server closed the stream")] StreamClosed, #[error(transparent)] + ExecutableNotFound(#[from] helix_stdx::env::ExecutableNotFoundError), + #[error(transparent)] Other(#[from] anyhow::Error), } pub type Result = core::result::Result; diff --git a/helix-loader/src/grammar.rs b/helix-loader/src/grammar.rs index 537e12828..7977c6df8 100644 --- a/helix-loader/src/grammar.rs +++ b/helix-loader/src/grammar.rs @@ -86,10 +86,8 @@ pub fn get_language(name: &str) -> Result { } fn ensure_git_is_available() -> Result<()> { - match helix_stdx::env::which("git") { - Ok(_cmd) => Ok(()), - Err(err) => Err(anyhow::anyhow!("'git' could not be found ({err})")), - } + helix_stdx::env::which("git")?; + Ok(()) } pub fn fetch_grammars() -> Result<()> { diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index f8c2912e7..fb32f6eb3 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -183,7 +183,7 @@ impl Client { doc_path: Option<&std::path::PathBuf>, ) -> Result<(Self, UnboundedReceiver<(usize, Call)>, Arc)> { // Resolve path to the binary - let cmd = helix_stdx::env::which(cmd).map_err(|err| anyhow::anyhow!(err))?; + let cmd = helix_stdx::env::which(cmd)?; let process = Command::new(cmd) .envs(server_environment) diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index c99ec217b..53b2712d0 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -44,6 +44,8 @@ pub enum Error { #[error("Unhandled")] Unhandled, #[error(transparent)] + ExecutableNotFound(#[from] helix_stdx::env::ExecutableNotFoundError), + #[error(transparent)] Other(#[from] anyhow::Error), } diff --git a/helix-stdx/src/env.rs b/helix-stdx/src/env.rs index 3676727f2..90a0aee87 100644 --- a/helix-stdx/src/env.rs +++ b/helix-stdx/src/env.rs @@ -1,6 +1,5 @@ -pub use which::which; - use std::{ + ffi::OsStr, path::{Path, PathBuf}, sync::RwLock, }; @@ -36,10 +35,33 @@ pub fn env_var_is_set(env_var_name: &str) -> bool { std::env::var_os(env_var_name).is_some() } -pub fn binary_exists(binary_name: &str) -> bool { +pub fn binary_exists>(binary_name: T) -> bool { which::which(binary_name).is_ok() } +pub fn which>( + binary_name: T, +) -> Result { + which::which(binary_name.as_ref()).map_err(|err| ExecutableNotFoundError { + command: binary_name.as_ref().to_string_lossy().into_owned(), + inner: err, + }) +} + +#[derive(Debug)] +pub struct ExecutableNotFoundError { + command: String, + inner: which::Error, +} + +impl std::fmt::Display for ExecutableNotFoundError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "command '{}' not found: {}", self.command, self.inner) + } +} + +impl std::error::Error for ExecutableNotFoundError {} + #[cfg(test)] mod tests { use super::{current_working_dir, set_current_working_dir}; From 83f09ecbff5160e4350c8099be1ad4c64513f665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Wed, 24 Jan 2024 16:00:21 +0900 Subject: [PATCH 087/796] minor: Silence noisy language server not found error in log --- helix-view/src/editor.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index f605cbb5f..eca488e74 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -1238,12 +1238,19 @@ impl Editor { .filter_map(|(lang, client)| match client { Ok(client) => Some((lang, client)), Err(err) => { - log::error!( - "Failed to initialize the language servers for `{}` - `{}` {{ {} }}", - language.scope(), - lang, - err - ); + if let helix_lsp::Error::ExecutableNotFound(err) = err { + // Silence by default since some language servers might just not be installed + log::debug!( + "Language server not found for `{}` {} {}", language.scope(), lang, err, + ); + } else { + log::error!( + "Failed to initialize the language servers for `{}` - `{}` {{ {} }}", + language.scope(), + lang, + err + ); + } None } }) From cda8ea991e885a84f03d7ffcf4cf046bfbf77490 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Thu, 25 Jan 2024 00:10:58 -0500 Subject: [PATCH 088/796] highlighting: Gate multiple captures behind `#is-not? local` predicates (#9390) --- helix-core/src/syntax.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index e543df06e..a5a85c575 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -2306,6 +2306,7 @@ impl<'a> Iterator for HighlightIter<'a> { // highlighting patterns that are disabled for local variables. if definition_highlight.is_some() || reference_highlight.is_some() { while layer.config.non_local_variable_patterns[match_.pattern_index] { + match_.remove(); if let Some((next_match, next_capture_index)) = captures.peek() { let next_capture = next_match.captures[*next_capture_index]; if next_capture.node == capture.node { From d8b8d2fda6a10f25c9704fc95c069a4c457752e6 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 25 Jan 2024 10:41:12 +0530 Subject: [PATCH 089/796] Fix error message shown for goto references (#9382) --- helix-term/src/commands/lsp.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index de2f0e5ec..c694ba25c 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -1007,6 +1007,7 @@ pub fn apply_workspace_edit( Ok(()) } +/// Precondition: `locations` should be non-empty. fn goto_impl( editor: &mut Editor, compositor: &mut Compositor, @@ -1019,9 +1020,7 @@ fn goto_impl( [location] => { jump_to_location(editor, location, offset_encoding, Action::Replace); } - [] => { - editor.set_error("No definition found."); - } + [] => unreachable!("`locations` should be non-empty for `goto_impl`"), _locations => { let picker = Picker::new(locations, cwdir, move |cx, location, action| { jump_to_location(cx.editor, location, offset_encoding, action) @@ -1063,7 +1062,11 @@ where future, move |editor, compositor, response: Option| { let items = to_locations(response); - goto_impl(editor, compositor, items, offset_encoding); + if items.is_empty() { + editor.set_error("No definition found."); + } else { + goto_impl(editor, compositor, items, offset_encoding); + } }, ); } @@ -1123,7 +1126,11 @@ pub fn goto_reference(cx: &mut Context) { future, move |editor, compositor, response: Option>| { let items = response.unwrap_or_default(); - goto_impl(editor, compositor, items, offset_encoding); + if items.is_empty() { + editor.set_error("No references found."); + } else { + goto_impl(editor, compositor, items, offset_encoding); + } }, ); } From 2661e05b3405c216a2303c20e71830b4bde7ac35 Mon Sep 17 00:00:00 2001 From: blinxen Date: Thu, 25 Jan 2024 06:12:17 +0100 Subject: [PATCH 090/796] Update some grammars to a commit where the license file is included (#9279) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Blaž Hrastnik Co-authored-by: Michael Davis --- languages.toml | 8 +- runtime/queries/pod/highlights.scm | 122 +++++++++++++++++--------- runtime/queries/unison/highlights.scm | 2 +- runtime/queries/vhs/highlights.scm | 21 +++-- 4 files changed, 99 insertions(+), 54 deletions(-) diff --git a/languages.toml b/languages.toml index 83c770479..87d4ae235 100644 --- a/languages.toml +++ b/languages.toml @@ -1249,7 +1249,7 @@ file-types = ["pod"] [[grammar]] name = "pod" -source = { git = "https://github.com/tree-sitter-perl/tree-sitter-pod", rev = "d466b84009a63986834498073ec05d58d727d55f" } +source = { git = "https://github.com/tree-sitter-perl/tree-sitter-pod", rev = "39da859947b94abdee43e431368e1ae975c0a424" } [[language]] name = "racket" @@ -2259,7 +2259,7 @@ grammar = "vhs" [[grammar]] name = "vhs" -source = { git = "https://github.com/charmbracelet/tree-sitter-vhs", rev = "c6d81f34c011c29ee86dd73b45a8ecc9f2e2bdaf" } +source = { git = "https://github.com/charmbracelet/tree-sitter-vhs", rev = "9534865e614c95eb9418e5e73f061c32fa4d9540" } [[language]] name = "kdl" @@ -2666,7 +2666,7 @@ language-servers = [ "cs" ] [[grammar]] name = "smithy" -source = { git = "https://github.com/indoorvivants/tree-sitter-smithy", rev = "cf8c7eb9faf7c7049839585eac19c94af231e6a0" } +source = { git = "https://github.com/indoorvivants/tree-sitter-smithy", rev = "8327eb84d55639ffbe08c9dc82da7fff72a1ad07" } [[language]] name = "vhdl" @@ -2917,7 +2917,7 @@ indent = { tab-width = 4, unit = " " } [[grammar]] name = "unison" -source = { git = "https://github.com/kylegoetz/tree-sitter-unison", rev = "98c4e8bc5c9f5989814a720457cf36963cf4043d" } +source = { git = "https://github.com/kylegoetz/tree-sitter-unison", rev = "aaec316774c8b50d367ec7cf26523aac5ef0cfc5" } [[language]] name = "todotxt" diff --git a/runtime/queries/pod/highlights.scm b/runtime/queries/pod/highlights.scm index e8bd4b546..d88d9ffa7 100644 --- a/runtime/queries/pod/highlights.scm +++ b/runtime/queries/pod/highlights.scm @@ -1,61 +1,97 @@ -[(pod_directive) - (head_directive) - (over_directive) - (item_directive) - (back_directive) - (encoding_directive) - (cut_directive)] @tag - -(head_paragraph - (head_directive) @directive - (#eq? @directive "=head1") +; A highlight file for nvim-treesitter to use + +[(pod_command) + (command) + (cut_command)] @keyword + +(command_paragraph + (command) @keyword + (#eq? @keyword "=head1") (content) @markup.heading.1) -(head_paragraph - (head_directive) @directive - (#eq? @directive "=head2") + +(command_paragraph + (command) @keyword + (#eq? @keyword "=head2") (content) @markup.heading.2) -(head_paragraph - (head_directive) @directive - (#eq? @directive "=head3") + +(command_paragraph + (command) @keyword + (#eq? @keyword "=head3") (content) @markup.heading.3) -(head_paragraph - (head_directive) @directive - (#eq? @directive "=head4") + +(command_paragraph + (command) @keyword + (#eq? @keyword "=head4") (content) @markup.heading.4) -(head_paragraph - (head_directive) @directive - (#eq? @directive "=head5") + +(command_paragraph + (command) @keyword + (#eq? @keyword "=head5") (content) @markup.heading.5) -(head_paragraph - (head_directive) @directive - (#eq? @directive "=head6") + +(command_paragraph + (command) @keyword + (#eq? @keyword "=head6") (content) @markup.heading.6) -(over_paragraph (content) @constant.numeric.integer) -(item_paragraph (content) @markup.list) -(encoding_paragraph (content) @string) +(command_paragraph + (command) @keyword + (#match? @keyword "^=over") + (content) @constant.numeric) + +(command_paragraph + (command) @keyword + (#match? @keyword "^=item") + (content) @markup) + +(command_paragraph + (command) @keyword + (#match? @keyword "^=encoding") + (content) @string.special) + +(command_paragraph + (command) @keyword + (#not-match? @keyword "^=(head|over|item|encoding)") + (content) @string) (verbatim_paragraph (content) @markup.raw) -(interior_sequence) @tag +(interior_sequence + (sequence_letter) @constant.character + ["<" ">"] @punctuation.delimiter +) (interior_sequence - (sequence_letter) @letter - (#eq? @letter "B") + (sequence_letter) @character + (#eq? @character "B") (content) @markup.bold) + (interior_sequence - (sequence_letter) @letter - (#eq? @letter "C") - (content) @markup.raw) + (sequence_letter) @character + (#eq? @character "C") + (content) @markup.literal) + (interior_sequence - (sequence_letter) @letter - (#eq? @letter "F") - (content) @markup.italic) + (sequence_letter) @character + (#eq? @character "F") + (content) @markup.underline @string.special) + (interior_sequence - (sequence_letter) @letter - (#eq? @letter "I") - (content) @markup.italic) + (sequence_letter) @character + (#eq? @character "I") + (content) @markup.bold) + (interior_sequence - (sequence_letter) @letter - (#eq? @letter "L") + (sequence_letter) @character + (#eq? @character "L") (content) @markup.link.url) + +(interior_sequence + (sequence_letter) @character + (#eq? @character "X") + (content) @markup.reference) + +(interior_sequence + (sequence_letter) @character + (#eq? @character "E") + (content) @string.special.escape) diff --git a/runtime/queries/unison/highlights.scm b/runtime/queries/unison/highlights.scm index 956dc5824..d58285ed8 100644 --- a/runtime/queries/unison/highlights.scm +++ b/runtime/queries/unison/highlights.scm @@ -63,7 +63,7 @@ ;; Terms (type_signature term_name: (path)? @variable term_name: (wordy_id) @variable) (type_signature (wordy_id) @type) -(type_signature (delayed (wordy_id)) @type) +(type_signature (term_type(delayed(wordy_id))) @type) (term_definition param: (wordy_id) @variable.parameter) diff --git a/runtime/queries/vhs/highlights.scm b/runtime/queries/vhs/highlights.scm index 9a2d05cf4..a7e1af301 100644 --- a/runtime/queries/vhs/highlights.scm +++ b/runtime/queries/vhs/highlights.scm @@ -1,4 +1,4 @@ -[ +[ "Output" "Backspace" "Down" @@ -15,22 +15,31 @@ "Hide" "Show" ] @keyword -[ "FontFamily" +[ "Shell" + "FontFamily" "FontSize" "Framerate" + "PlaybackSpeed" "Height" "LetterSpacing" "TypingSpeed" "LineHeight" "Padding" "Theme" - "Width" ] @type + "LoopOffset" + "Width" + "BorderRadius" + "Margin" + "MarginFill" + "WindowBar" + "WindowBarSize" + "CursorBlink" ] @type [ "@" ] @operator (control) @function.macro (float) @constant.numeric.float (integer) @constant.numeric.integer (comment) @comment -(path) @string.special.path -[(string) (json)] @string -(time) @string.special.symbol \ No newline at end of file +[(path) (string) (json)] @string.special.path +(time) @string.special.symbol +(boolean) @constant.builtin.boolean From 0d09fb4f550eb87a02f5f17dffb0800220f2e937 Mon Sep 17 00:00:00 2001 From: Poliorcetics Date: Sat, 27 Jan 2024 20:17:37 +0100 Subject: [PATCH 091/796] lang(git-ignore): add `helix/ignore` to git-ignore file types (#9447) --- languages.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index 87d4ae235..785bc9701 100644 --- a/languages.toml +++ b/languages.toml @@ -1484,7 +1484,7 @@ source = { git = "https://github.com/mtoohey31/tree-sitter-gitattributes", rev = [[language]] name = "git-ignore" scope = "source.gitignore" -file-types = [".gitignore", ".gitignore_global", ".ignore", ".prettierignore", ".eslintignore", ".npmignore", "CODEOWNERS"] +file-types = [".gitignore", ".gitignore_global", ".ignore", ".prettierignore", ".eslintignore", ".npmignore", "CODEOWNERS", { suffix = ".config/helix/ignore" }, { suffix = ".helix/ignore" }] injection-regex = "git-ignore" comment-token = "#" grammar = "gitignore" From 1616021a5a5378118678b57bca0ae844af5922ab Mon Sep 17 00:00:00 2001 From: Chirikumbrah <78883260+Chirikumbrah@users.noreply.github.com> Date: Sun, 28 Jan 2024 12:11:05 +0300 Subject: [PATCH 092/796] Make status line modes bold (#9449) --- runtime/themes/dracula.toml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/runtime/themes/dracula.toml b/runtime/themes/dracula.toml index 1253544f2..1473c1a83 100644 --- a/runtime/themes/dracula.toml +++ b/runtime/themes/dracula.toml @@ -60,10 +60,10 @@ "diff.minus" = { fg = "red" } "ui.background" = { fg = "foreground", bg = "background" } "ui.cursor.match" = { fg = "foreground", bg = "grey" } -"ui.cursor" = { fg = "background", bg = "purple", modifiers = ["dim"] } -"ui.cursor.normal" = { fg = "background", bg = "purple", modifiers = ["dim"] } -"ui.cursor.insert" = { fg = "background", bg = "green", modifiers = ["dim"] } -"ui.cursor.select" = { fg = "background", bg = "cyan", modifiers = ["dim"] } +"ui.cursor" = { fg = "background", bg = "purple", modifiers = ["dim"] } +"ui.cursor.normal" = { fg = "background", bg = "purple", modifiers = ["dim"] } +"ui.cursor.insert" = { fg = "background", bg = "green", modifiers = ["dim"] } +"ui.cursor.select" = { fg = "background", bg = "cyan", modifiers = ["dim"] } "ui.cursor.primary.normal" = { fg = "background", bg = "purple" } "ui.cursor.primary.insert" = { fg = "background", bg = "green" } "ui.cursor.primary.select" = { fg = "background", bg = "cyan" } @@ -74,16 +74,16 @@ "ui.linenr" = { fg = "comment" } "ui.linenr.selected" = { fg = "foreground" } "ui.menu" = { fg = "foreground", bg = "current_line" } -"ui.menu.selected" = { fg = "current_line", bg = "purple", modifiers = ["dim"] } +"ui.menu.selected" = { fg = "current_line", bg = "purple", modifiers = ["dim"] } "ui.menu.scroll" = { fg = "foreground", bg = "current_line" } "ui.popup" = { fg = "foreground", bg = "black" } "ui.selection.primary" = { bg = "current_line" } "ui.selection" = { bg = "selection" } "ui.statusline" = { fg = "foreground", bg = "darker" } "ui.statusline.inactive" = { fg = "comment", bg = "darker" } -"ui.statusline.normal" = { fg = "black", bg = "purple" } -"ui.statusline.insert" = { fg = "black", bg = "green" } -"ui.statusline.select" = { fg = "black", bg = "cyan" } +"ui.statusline.normal" = { fg = "black", bg = "purple", modifiers = ["bold"] } +"ui.statusline.insert" = { fg = "black", bg = "green", modifiers = ["bold"] } +"ui.statusline.select" = { fg = "black", bg = "cyan", modifiers = ["bold"] } "ui.text" = { fg = "foreground" } "ui.text.focus" = { fg = "cyan" } "ui.window" = { fg = "foreground" } @@ -133,3 +133,4 @@ green = "#50fa7b" purple = "#BD93F9" cyan = "#8be9fd" pink = "#ff79c6" + From f0be0c6d4a6c253e258b977436144796b8bd086f Mon Sep 17 00:00:00 2001 From: Travis Harmon Date: Sun, 28 Jan 2024 03:11:31 -0600 Subject: [PATCH 093/796] Make status line modes bold for theme onedark (#9435) * Make status line modes bold * Revert change to onedarker --- runtime/themes/onedark.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/themes/onedark.toml b/runtime/themes/onedark.toml index a50b5d354..eae11172e 100644 --- a/runtime/themes/onedark.toml +++ b/runtime/themes/onedark.toml @@ -73,9 +73,9 @@ "ui.statusline" = { fg = "white", bg = "light-black" } "ui.statusline.inactive" = { fg = "light-gray", bg = "light-black" } -"ui.statusline.normal" = { fg = "light-black", bg = "blue" } -"ui.statusline.insert" = { fg = "light-black", bg = "green" } -"ui.statusline.select" = { fg = "light-black", bg = "purple" } +"ui.statusline.normal" = { fg = "light-black", bg = "blue", modifiers = ["bold"] } +"ui.statusline.insert" = { fg = "light-black", bg = "green", modifiers = ["bold"] } +"ui.statusline.select" = { fg = "light-black", bg = "purple", modifiers = ["bold"] } "ui.bufferline" = { fg = "light-gray", bg = "light-black" } "ui.bufferline.active" = { fg = "light-black", bg = "blue", underline = { color = "light-black", style = "line" } } From 9978d421fe7237c74932c34263d91445728d65d1 Mon Sep 17 00:00:00 2001 From: Jaakko Paju Date: Sun, 28 Jan 2024 11:12:07 +0200 Subject: [PATCH 094/796] Include interpolated SQL strings in Scala injection queries (#9428) * Change Scala injection queries to include SQL strings * Include block comments in comment injection * Change #match predicate to #any-of Co-authored-by: Kirawi <67773714+kirawi@users.noreply.github.com> --------- Co-authored-by: Kirawi <67773714+kirawi@users.noreply.github.com> --- runtime/queries/scala/injections.scm | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/runtime/queries/scala/injections.scm b/runtime/queries/scala/injections.scm index 321c90add..1ad68557e 100644 --- a/runtime/queries/scala/injections.scm +++ b/runtime/queries/scala/injections.scm @@ -1,2 +1,16 @@ -((comment) @injection.content +([(comment) (block_comment)] @injection.content (#set! injection.language "comment")) + + +; TODO for some reason multiline string (triple quotes) interpolation works only if it contains interpolated value +; Matches these SQL interpolators: +; - Doobie: 'sql', 'fr' +; - Quill: 'sql', 'infix' +; - Slick: 'sql', 'sqlu' +(interpolated_string_expression + interpolator: + ((identifier) @interpolator + (#any-of? @interpolator "fr" "infix" "sql" "sqlu")) + (interpolated_string) @injection.content + (#set! injection.language "sql")) + From 5e0b3cc28b8ae6da1f41b47080a695c99bbc844a Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Thu, 25 Jan 2024 14:05:42 -0500 Subject: [PATCH 095/796] Use injection syntax trees for bracket matching Previously we used the root syntax tree for bracket matching. We can use the new functionality in `Syntax` for finding the correct syntax tree for a given byte range though so we use the correct syntax tree within injections. This improves bracket matching behavior within HTML injections like script or style tags for example. --- helix-core/src/match_brackets.rs | 8 +++----- helix-core/src/syntax.rs | 9 ++++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/helix-core/src/match_brackets.rs b/helix-core/src/match_brackets.rs index 150679b5c..b8bcc28ca 100644 --- a/helix-core/src/match_brackets.rs +++ b/helix-core/src/match_brackets.rs @@ -57,10 +57,10 @@ fn find_pair( pos_: usize, traverse_parents: bool, ) -> Option { - let tree = syntax.tree(); let pos = doc.char_to_byte(pos_); - let mut node = tree.root_node().descendant_for_byte_range(pos, pos + 1)?; + let root = syntax.tree_for_byte_range(pos, pos + 1).root_node(); + let mut node = root.descendant_for_byte_range(pos, pos + 1)?; loop { if node.is_named() { @@ -118,9 +118,7 @@ fn find_pair( }; node = parent; } - let node = tree - .root_node() - .named_descendant_for_byte_range(pos, pos + 1)?; + let node = root.named_descendant_for_byte_range(pos, pos + 1)?; if node.child_count() != 0 { return None; } diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index a5a85c575..a403e7ccf 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -1338,7 +1338,7 @@ impl Syntax { result } - pub fn descendant_for_byte_range(&self, start: usize, end: usize) -> Option> { + pub fn tree_for_byte_range(&self, start: usize, end: usize) -> &Tree { let mut container_id = self.root; for (layer_id, layer) in self.layers.iter() { @@ -1349,8 +1349,11 @@ impl Syntax { } } - self.layers[container_id] - .tree() + self.layers[container_id].tree() + } + + pub fn descendant_for_byte_range(&self, start: usize, end: usize) -> Option> { + self.tree_for_byte_range(start, end) .root_node() .descendant_for_byte_range(start, end) } From 8b6565c83924e7ef9176ae4611ec368fbf75ab8a Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Thu, 25 Jan 2024 14:29:49 -0500 Subject: [PATCH 096/796] Respect injections in :tree-sitter-highlight-name --- helix-term/src/commands/typed.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index ee02a7d25..81ffdf875 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -1546,10 +1546,7 @@ fn tree_sitter_highlight_name( let text = doc.text().slice(..); let cursor = doc.selection(view.id).primary().cursor(text); let byte = text.char_to_byte(cursor); - let node = syntax - .tree() - .root_node() - .descendant_for_byte_range(byte, byte)?; + let node = syntax.descendant_for_byte_range(byte, byte)?; // Query the same range as the one used in syntax highlighting. let range = { // Calculate viewport byte ranges: From 035b8eabdbad02bdcf4f254162623f14767e62de Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Thu, 25 Jan 2024 14:33:37 -0500 Subject: [PATCH 097/796] Respect injections in movement::move_parent_node_end --- helix-core/src/movement.rs | 7 +------ helix-core/src/syntax.rs | 6 ++++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/helix-core/src/movement.rs b/helix-core/src/movement.rs index 6c4f3f535..54eb02fd0 100644 --- a/helix-core/src/movement.rs +++ b/helix-core/src/movement.rs @@ -573,16 +573,11 @@ pub fn move_parent_node_end( dir: Direction, movement: Movement, ) -> Selection { - let tree = syntax.tree(); - selection.transform(|range| { let start_from = text.char_to_byte(range.from()); let start_to = text.char_to_byte(range.to()); - let mut node = match tree - .root_node() - .named_descendant_for_byte_range(start_from, start_to) - { + let mut node = match syntax.named_descendant_for_byte_range(start_from, start_to) { Some(node) => node, None => { log::debug!( diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index a403e7ccf..24de1a338 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -1352,6 +1352,12 @@ impl Syntax { self.layers[container_id].tree() } + pub fn named_descendant_for_byte_range(&self, start: usize, end: usize) -> Option> { + self.tree_for_byte_range(start, end) + .root_node() + .named_descendant_for_byte_range(start, end) + } + pub fn descendant_for_byte_range(&self, start: usize, end: usize) -> Option> { self.tree_for_byte_range(start, end) .root_node() From 0328fa4d02ff7fcab221fcc6d9051bdafce63a79 Mon Sep 17 00:00:00 2001 From: Abderrahmane TAHRI JOUTI <302837+atahrijouti@users.noreply.github.com> Date: Sun, 28 Jan 2024 10:14:12 +0100 Subject: [PATCH 098/796] adjust color darkness on ruler & inlay-hints (#9375) --- runtime/themes/cyan_light.toml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/runtime/themes/cyan_light.toml b/runtime/themes/cyan_light.toml index e18c46a9f..45cb6539d 100644 --- a/runtime/themes/cyan_light.toml +++ b/runtime/themes/cyan_light.toml @@ -95,7 +95,8 @@ "ui.text" = "shade05" "ui.text.focus" = { fg = "shade07", bg = "light_blue" } "ui.virtual" = "shade03" -"ui.virtual.ruler" = { bg = "shade04" } +"ui.virtual.ruler" = { bg = "shade01" } +"ui.virtual.inlay-hint" = { fg = "shade03_darker" } "ui.menu" = { fg = "shade05", bg = "shade01" } "ui.menu.selected" = { fg = "shade07", bg = "light_blue" } @@ -119,6 +120,9 @@ shade05 = "#434b6c" shade06 = "#343a54" shade07 = "#25293c" +shade03_darker = "#9199bb" +shade04_lighter = "#616d9d" + background = "#f2f3f7" foreground = "#25293c" @@ -133,7 +137,6 @@ blue = "#0073E6" dark_blue = "#185b93" darker_blue = "#000080" - purple = "#660E7A" light_purple = "#ED9CFF" @@ -142,7 +145,6 @@ green = "#00733B" light_green = "#5DCE87" green_blue = "#458383" - yellow = "#808000" dark_yellow = "#7A7A43" From ee68fd09ac51b613061d6eb0680d8d42cc21260e Mon Sep 17 00:00:00 2001 From: Jaakko Paju Date: Sun, 28 Jan 2024 11:18:35 +0200 Subject: [PATCH 099/796] highlight(scala): highlight abstract methods in traits and classes (#9340) --- runtime/queries/scala/highlights.scm | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/runtime/queries/scala/highlights.scm b/runtime/queries/scala/highlights.scm index 40b230ec4..4f90bfda6 100644 --- a/runtime/queries/scala/highlights.scm +++ b/runtime/queries/scala/highlights.scm @@ -57,13 +57,21 @@ (class_definition body: (template_body - (function_definition - name: (identifier) @function.method))) -(object_definition - body: (template_body - (function_definition - name: (identifier) @function.method))) + [ + (function_definition + name: (identifier) @function.method) + (function_declaration + name: (identifier) @function.method) + ])) (trait_definition + body: (template_body + [ + (function_definition + name: (identifier) @function.method) + (function_declaration + name: (identifier) @function.method) + ])) +(object_definition body: (template_body (function_definition name: (identifier) @function.method))) From fe443910160e145c69f32a028b313d6f596e3fbe Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Sun, 28 Jan 2024 04:19:25 -0500 Subject: [PATCH 100/796] Add argument to textobject in gdscript. (#9288) Currently `maa` only selects parameters in a function definition. Allow it to also select arguments inside a function call. --- runtime/queries/gdscript/textobjects.scm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/queries/gdscript/textobjects.scm b/runtime/queries/gdscript/textobjects.scm index 089544682..47512bba7 100644 --- a/runtime/queries/gdscript/textobjects.scm +++ b/runtime/queries/gdscript/textobjects.scm @@ -13,5 +13,7 @@ (typed_default_parameter) ] @parameter.inside @parameter.around) +(arguments (_expression) @parameter.inside @parameter.around) + (comment) @comment.inside (comment)+ @comment.around From eb3c4e9f005d323ea8de828c23b4a7c0903a5702 Mon Sep 17 00:00:00 2001 From: Twinkle Date: Sun, 28 Jan 2024 17:20:51 +0800 Subject: [PATCH 101/796] feat: add hard/soft contrast for gruvbox light mode (#9266) --- runtime/themes/gruvbox_light_hard.toml | 7 +++++++ runtime/themes/gruvbox_light_soft.toml | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 runtime/themes/gruvbox_light_hard.toml create mode 100644 runtime/themes/gruvbox_light_soft.toml diff --git a/runtime/themes/gruvbox_light_hard.toml b/runtime/themes/gruvbox_light_hard.toml new file mode 100644 index 000000000..4a48116ed --- /dev/null +++ b/runtime/themes/gruvbox_light_hard.toml @@ -0,0 +1,7 @@ +# Author : Twinkle +# The theme uses the gruvbox light palette with hard contrast: github.com/morhetz/gruvbox + +inherits = "gruvbox_light" + +[palette] +bg0 = "#f9f5d7" # main background diff --git a/runtime/themes/gruvbox_light_soft.toml b/runtime/themes/gruvbox_light_soft.toml new file mode 100644 index 000000000..a29b23734 --- /dev/null +++ b/runtime/themes/gruvbox_light_soft.toml @@ -0,0 +1,7 @@ +# Author : Twinkle +# The theme uses the gruvbox light palette with soft contrast: github.com/morhetz/gruvbox + +inherits = "gruvbox_light" + +[palette] +bg0 = "#f2e5bc" # main background From 4ab70295352f6e4f86c4d170a04ceb858ee79d5b Mon Sep 17 00:00:00 2001 From: NitinKM <70827815+NewtonChutney@users.noreply.github.com> Date: Sun, 28 Jan 2024 15:01:10 +0530 Subject: [PATCH 102/796] Add icon to Windows executable (#9104) * injecting the icon through a resource file, no extra deps * formatted * scripted rc compilation * formatted and restructured * simplified conditional func call --- contrib/helix-256p.ico | Bin 0 -> 270398 bytes helix-term/build.rs | 146 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 contrib/helix-256p.ico diff --git a/contrib/helix-256p.ico b/contrib/helix-256p.ico new file mode 100644 index 0000000000000000000000000000000000000000..16781cc10b53d50b51f56c7a1d95473eec2a8fb8 GIT binary patch literal 270398 zcmeHw4Qv!wwx)-N7!)BEp$J7N!U#nXiXs$6(FmbYKoN=%LTr{8md7w`CNGa6#$f(2 zZHI1bLm0wj2w}kXFj0&P0H7e0XbpP!ofYSw_OG;pJ3(J1i!W#AKl`uKwDdY{aHSJfUDhBc78 z1_oQoo(13E2S&2+Uibp$f9~q$D_H}s&;YppBtY-;x(xE3`R|IF<3g|oa@W96>*BHC z`)fcm@W>?h$p`TBKX>)>m8^k+X#m{)32+R!4~XZv*-z$w!D^5*W(}Cv0JuIC*ah6o zgZIKuF#pY00bjuya9jiOoZZ&NE5Z587Vw_=?|2PzhO7be8W?I_G6sB~2b=;PTF8C! z0sQ(qP60>bUpjZe9#h+GtlH89Y+_&M)ojV@O~2bz6sEa%N6{z`;(vl1vZb?t@8NZ5^%jM#(UyEA%bWBa|JFQXK%3v z23udJxZEW`3y{m4T`PS8^WWn7HIrOtppnM*sh@u*#(BYg`aBDaHs}2Ew90Y0 zv%vWi!1sWaJkB*O=6?ZA2K-(M=y9L;f3&@Z$NdUeJ(guX&|3Bk_`Vkyb_wr=zhM4b zR?FrR0>5Vf$!&i1^Z#&oTLZuEH>XxtosBhkC@!}Gxau> z4`1uN2Eg~1fTKV@ak+Y2wc{A`-(eH!*m{V@{Ptye_LC170cJT|JkBmh4S?@cfO=pM zu!F01q{;lxvA%P?EF5Tf3cS8xfdAwJcIBE6U+0Vl!1W2hTfij`@m}~6=D#y$63+$v zp9x-PYx|2nKp)P1A$~D`ag7ELk3SlGe--!wusSYR@YC*3=D))h1wPYUp9g^8c(%_^ z+5{EUjk0StOmgKalmq*(yf@5&pg5B+&D?WMB-I0HN^a_*C_ zTIG$J^G_)q=D+X*SCMZ8#s;4Bk0#CC32dF>a*qIBh|6`N9YO1iFEyt{K099x6o~&~ zY=G7jTnIlfHsAa3%Jr~uip#A9`T-|+<&``rc7fx|9X8|w@?W$C?xPGx0bkE0#+d$i z*!P>jMdrP(-=lct$qt);(P!rZ{tG`ah_ck9Eilzjm4dam)N#2^?xo|E=e{WCfdRbx z^i$n?;a*(Cf8hsckFk~T0~5TUzf5Js^Xz$`0q}ZUuHdL$pQN9;nRLlAdoQ3J_Y<3 zmur^~qjvv_q9dJ)ZrCII7k;1<tOqaM{G1OMZd&w8j zm_5dS@&h!N_#`kVyzNlIY-g}V-Y=Kpa=V!Kru`npGB4|ITg>DA1>`+RiX0oF#m-g=*B&404CX~!{L@P z>f_G@J_d>{E;nEPQ*6UoVBtVpS<%e(w9_7~O4|qgCqF=O#x1}S>}6>AdnAnOPlN65 z25tiRa-rC+r&-gF=0L2;afgi}p_As_bak=w>(*U(yi|v`6&;3&0=r}N| zzvK11eX!N`&gp42;8Xq!KX664zXro?PuT4Xz(>G6ARitS+qE%Umg_O^|Tkk0gRJ8m#`@Q<|Pdj5&wlBpgoI17k0k? z%usmI+`+cRpTmY9F!xRQKGN4o;FUq1zvm^or>Hm+vm0-ig z_T0|remlYY<$!DEV5b;0A26(eV&y;XTk3e{kaTI^PO0bQDrul)3HyP}{htUpUjtw1 z$`~xe2G1vIGyoo~A}$my0!>n9=(p0>4cn#fPW?i8F1DF!3+MAJ^}@CE9;69e&)Z> z0onpIHt?7IbyDxY{Jk_-JYxfc;C($XRcj^u%dZ;fsBRh!?$dj5=6|vdXl!7|0qM%` zs-?)kzu|?xdah-pxLmBkxA-T1wR66&^cn#77XcJI!#*I54leBajdbnUMydZ#Kl4+c zfadHr1FvCxkK=OFQ^eO2T5B8J|D5@s!T+Hx5Uy#EzWVjAr0f4{HO2=1tJnR&Rq%c- zcwe-8u=$$QCT|Yhp9_qz56IvHsBJ)F1KSTumw&rM>igHXl=il_<_}R^?p`3L*hrIR z#ix5j1MPvPQQ&?R@PK_l79SAv1Lte^OW%C7O^W<+g%{cav@Y=p;HM+J&yGh}>1;9E zs~evH_i2A_o)3^_96--Ee!2VC66O+1{mskV=m)5e--dSnQq0}sxLju`<+;p{r zK=^_4bstDqk8G80ocXzveFBQh{S43hxr42*7i@0??^{0Kfm#FL{&axW+NU-5xh-Hs z2h=CPd0+l&uk`IdRZ0%T8D9YR-@w|11Lz$1 z0Xfe2gPl_EKmXE>IAdC`jMgoC+KPJSG*|JGuX9|k9mdUR8rA@~KLcn2IEL6nTY!A##rO7@jWecwNLHbb&vChiEtF5RTLYcd zjbmWX^MF$T&mA)12jn>853|M@)4pT7fN6HuC8y6C$W;S)Ur2F8D}hS@j|~{r0gVmp z_)xlX7;(m@|C_qsPkZTn3~*d-u13k%Ij(`WEx(_FSh-yt!Grf`{F{A&abHMr#$Eq& zBdtMp0qc{$cI*5?j>~o2f;q!{G;p)?KR=CURz2e5?p;0@lHdpA_h9S?4EO=^mxwd& z2>$j$d)4n2wro7Y{LjZ2`C7*`0M1VU-U4X-ICJQ{qT2Ydn0`zduFR`vt^e_Doi?FQ+>yYF*J!lX>+R?haQu&+;qtM(H4yE7=Y^5$I}VTZ?6`OHt5w>uy-eITDf|bemM~;(W zKVaGqT!0@~0YAX|^5$*voB(Mc`fc^oc;?rP^wdR0dh4a(t6MOZk3F$W^4jqEVr<~E zUrLt{FH3V{*$<8^Ru7(!UxbNjSc*to@ z+`6`PeDr$g0C@jM&Ha(yooM@S1s}1lt(u=se5XDE^7#(yY0|oyw7w?$0aJe95qtox zrgC55|E)svU*!jApN+16TrbhS z8?a}FIUNi`XL|wO|0@e8ibWjl3OtFizO(w=k3Ijt*^Kso0iXYZ|Kbzv0or$q_TQp? zxVSA~+7DiZ52(O?FTDR}u@F7YeMY;3%h2wBkct1%u4-wh9s64e{<+~ZHa0-}aMFI9 zJeSzC51_Guv%o^k8{jzOH2m=M;alHq9vki1aUwnU#rwY<(#;FBr;P*e|IGRp8XLfV z+_Wz@#~GXU0n~q@IOC(hOMbR>zPD5jM8Bo>j?4m5C_u^&mVx~J&%;7agcnd2ws`epcxUfYIwK+E0P4^aC68XHi@8S`9Xvuz=Y zGyVd2HN35X=Mt+pWR=KJZN# zADrPQyXSjRG=OKnY5tQhp!Weczx?~yoIn?j398!yF+YGcb>)3`X#XA9G{c+@D9*SB zeqb_l$3Gg#mH%>kpr=+E{$`Uj)Tvps-d*nqll=gGe|PE^wDwRR?3tcz><7&0fcAY{ zjqxX%@3eH!M8P%ygo=ftr6h5KEZwXhNFPy9XD)vJn#l^5#Y9f zIUP`(@&0gi!*nlmpqR^UCI6K%!a8aA+E%e@W)VZRN;{jK}`wHxrJJD#hz>Z%8 zjsUd2CbtKS>wxAGpF>-qEF3t*amGdsuE6Kn!+%wupgSnt`b!1I1{{qY7zXe6q7RZ& zj9Xja5bbBY05}8CdvNvx#&tk(#>atK9f3m}XIx-@XUpDh{)>47^83PzzeJzF*0BNF zFZTp^KP%F@IB(m74NnBhfhz#}0CPH^IO9F&6XfKBv)L`nttSS3bvD4LHw#;Ro=(uzS07>(XkhEAY3uyf3^1-X8!QUE{XBx^XOQc@coIu0|U3 zVn1M92lt^b8e4tdS}W$%a@9Z~`JWgY2$-2m`~aN)40z>6%j*S;OMwkf0yY5j?u&hZ zIUSJBmSSum?=hNO?VhhwG~fpQD`NvRmw2;;y~Q$(4O{^4R}8e4xn}MuY z*avn&SGP`;LtzMTsJ&x{+xo+kqVfMN`}Es#wIfrex4TYqEud)oq0 z%2V08pZ{{3fc7Fg2K>ZJ$_4jl01W{3b=eQ3)j`9(@U{cSAj18jx$c@0|Ez|!W-)g_CCie_GEQAJ;s0G2QI@0tc>>7duDw~aDP1T z7H|>Zwm`BD0*9`2RDHnf4qHv|Ea`ca|KtZ~pTg6?{Af?zm@K#H8Zo#(1*ioE*$=4w zpxg(T0%$HA&57goKy||-i~+n^B(!tQZTf)! z!Vg5?2eyy&)Vp$yfL?`%w>46n@j`&+#y>22KM*)np{FN4W}gOp#DC!jF2E1009-Ri zFk9^*u4p2-&vC}sk2IUESZ=Kbe8_+D1GJavXTU2XJv%)!rVJl24cG$=1I5@UVE$W6 z@2si$l>eeFa0fo%0LBI!jS1C|1wnQPi!-B3(%#>{^M zoVD_lBI3Vj3;Y27p8$S(tGC`Wv8QMQP@M68fMVwSIyS)kx02ij(kep!3qLRnAFvnj z%=(n)mAS-oz<=6%$B%P~ng0ejYvn0L%zxnruEGaYU~IrM>r-M4JrQTz>9e)~^WREx z8%Qgc{lCE%!5zPR(p=)Rz`|&6op<7lu@^7+PjSW(pZEdhzX8r#c?#Cxd=qT|8XNNq zm-D#~iZeb6X!Z!SQr~u@1@6xPnt;0=_XEs-I}9X4n&AH&@So<;=ffMnT{{SU)B~Q0 zN9zd4amMq&|5Lz2kNN@Te+I7FA&Pd@OhF%j`oMnUuod@n33_=8@KBs_!vt`j;*2kO z&<8O8?I3xEG^3Z-j>di*hnfFr{U41Dd;!e6-RGV4DZu@yKt154IAi922CmvAigpL* zV{b0%`&#kL^lKV-p`RvThF$ewN4nrY#Tg$3ybx#1{I|mhvZNWkR`mq>03S2|4f#Kc zGY$Zgv#8ZIVsL+KxTa|V;tbE>yPM|@G5-r>HL&LwfNsFA&u^>ms}s6fKGIh=u0U00 zD%BE?UKTQ-@45YF#oNkw5il?_tcGoEtdoA1I+nAiZk91OgE)) z8&7Uuy>1-1z6=P*c`x`cJ}b9L;VtjM4;*rfA7K95XcI<~j`oJg2T+V0_XW)P0BYlq z&NcuAi!%OX02i+~5P4|3*maz&Wtt8Nf#X&kZu? z2WT$kIiPIhy0dY{;nkJX!S{W@NLJnp{)^9wO;UJ6ofO{oVIlnh^WOn(8_fdy@{WNW zF96N}4*|dSgdb>f z+7~eYjgr&B^KSRnPlO%QxWE;_4zBn)O^P$#16Uqs+`hVUEcjjqoC6-^&3)kmR={yWHPvsuB0rvbZxn}DA=YsbBj?<~hYrg_I1cdRU@KK@MLW8i!1 zcprN|Ap8K04Xh7J;i?au@&U|$v!rzR{J-|q()i#^fZDdy#EExBN51_Gubv1Zj_`V%u1I&My@Y-xKV8_#e zeZUA{$1~H4Hl=kD~SA-?#T&4C47pTq{p{?k4lkEx*@dF7f6)`OYO~ z{_7FSqsQO}CWHS0ppX54IX`&w;tr|ne{7S^Z^1h_ulN9YYyfkKbAMmR{P!r=GZh=$ zr@6#Uz+LtO2K_)E&g-d{23kYXl>=L)&UF}f^NJs!IfU}~pvkocc>nKAH1f18inX)A z{Z|26OY0%~0pork`fZ(rK7n-UUG%jpywE48l)@BaY$~P*^9;8gSm|ja=fY>z0LBN$ zgZs3m<|Xz427LgHp`bl*>q@P3{gZ0x%Xcs~Ky3lHw+Cnru^dNWq#e-kJ@`K_s{!!| zzR&=;KLw}-2H6i7^aIo<0ERB?kgok6V*{JqJ~n_p3(XfYvgTNzp$G30Esv4#1zCK2 z6X574QL90c)O>#gGpCz^%)*Qupy{=|aG5V*_ZLY}t!GK!$jMz@f7kD}2&b z#>j;!m?mq!60a^f#GdAG|hOgC0y`R-cU+q92yijwAX$=v19if!x|0BE`JQ&{c zz9#0W;0DJ9aHR%r_wJko{%-=h0UjGLsspqKZeFUDzC9L@E>soZec>9+SxQ+`6z}>2 z2X`~~T}d@p7Yf{`IOF{Qt+~&Bz=#fFV*>;2A?cfgm`l9gsj&gP|67lE0QDMzfkPM3 z-(QM(0XeOILNBF2fmNC{*iFihlbZW^S$Bn4O*%JaDOsT14P&l81w_wCjf>h&iIH8amJN_hj^!d z2G9Hj;Z5&yTi;TP=fw0h0Pa(q@nL}XEHcs`pmWF%AkMhI1#!mvw@MvW#2K&Id^x;v zS9!SVK;CPe>(w(KV-2L!0Ju+ciRS^Q0G>;1!Vk!C#;4wqF73)W&KT>Jtq+AO15?x0 z4w!V`0|xz|7#pBCOVc)tp~pWt|9a)zvd95nEMM|O_6ePibW%!>_UZ*FDm%P1F$`8>1- z00aXPpPV@%Ufi&7DgS(*TW2f$whtmr{5y_%A-;2WZ|4&41}{3CiO{`eAuBcd29fDHajjt^RKr99OF|^78GaZ z;QI`q3AhVr+xkX-$JW%RxEP9$xrI0xZVQ<90~g=}-T-)QkK&}Fqj%lDdfjC3y#|Qn z%=={jKz#zl$5Nau#miFMEc*ddet_b#KLcKgUf;>Zkr{Bhv>a-h>P-s|`Q zS}%jv%@}A4DeYl?UudRZKi zZT17E`~d9(UxT?rPgrj$oT8sJfIj{c;CmU+0z4`>_tm}tet`Dbp#3%mJMg}c-j{Ko zz-XU<<__(N_SCZv@DpJzx|jBfjnv1V1sn&ycNOoGd;qlvXy2{we+%Xf84B!%6#bQoJ1d0YiR(_IR7?1GD8@ z$WsI8<4*+N%YiGN;k}L@p#8WhUcMi3^AtbNejuwK_#8UoIHElHCpf=pgLLipm`jXxbYvgEV*}~N255iB*Su=KT<+X80It6X90Bh8g7<14 z1Rp?S19F`49}s7Jh2xCD^K?GoNVKPp=LzOcB~M)mu1^6%K)>&Jul51-o%{gB8NZ7- zW2~>samKnn;3|9ruPNdw7Mm|DxIP|u6S(Mm-s|`QInMY)%q7Np`#d(F@dFe?{IYqA z;w!wMf%fvs(HP%*6`cP9cu;iQSNj4Q8xV2E*nfw|24p`#^8uE5!Irs<<}?7_KM%e) z0J+EIs`=xG@6;#2eOwGGamKVCAHOH4UuDj+_-r?80QOCBxm7?P**Zf<2k--OobevS z8J~&88K)m3OE-4rm*W*~wr4JzF%7h@mg91lf$w4FybkB}d?EY*#Tj31th$ReNBT01 zllz71diTNnH%2j6p4IX8TAH)_GVm$jOkA!Wm%MrGA>I>wf%VB>9qb4_1OA7A{vz`O z%zsxhJA0wP_h*59fb(&=-sHQwKSXPkVy#l`UOM3Zi@--fvFvBe{LfA`SDgX7eG*s) zbOCCc=I`0{F$kU20%_lS-|pKv2JZ z0CaiW2QdHh;H#CF;$1Y&+5HH($GkV?_h>AX)+$_rXTGCr+kyMjfqlS;NBscv-%1ka zkXGBO%2BZ0r2xOrGvD6nhTb-{m#>?YgX%50WTdYSV*_Ue|Yspox;pNPIQ*C{k<9rgp zUPP6yT)PI`pA1w1UWh4R{#(Li^NGNQUj%vpFO9JkvaFrZ75U3?=C$qo72yB#Km$N~ zZ@Be60rTH^(a0qz&r4>12zDQfEx%=4KA8|Svaw*5nKx%I$QTb1vk#KHS9z(SyqamLJl zhiw8jPIG-808Y27-IyQ6{_*t)mF} zPjhxZ0e;%Hy294DT!*XD_UyoY5og?u?{@SFnE$rdqBesf<9{3OcWJnMt)p?dT8(@E z7r0Mx#`}Rg7W)C_zr*HHB>bnihAqHT4y)I<*8b(FBmVc}UbbWJ(Wh0n$6pJe z0dW6GfYur?>jPF6ptwbyr4RT|ak*Hh)cbL{eloiJx@fg?sTZ z|0yo_G%z1+eP6}pdckgtlo5QUzJSqn$LL++Oe5*?Io4^wqx`>ua#P$w-h1g-r%6sp zPXpleZoo(zfYu`6_p^FRwDFk7_)q(e?ZzHj(`-};CvBkyz-fvfFtQ)PzQ6YDfT6F9^pUjr9=CT&J4f3hHb=1#k%AiyceXgz%Y=h4M1)GIW8%3Q5D1G{HM6wb6BHf zQMe+v{c?+{GF^DH;6JS^av}@=j|}(JbKG?S>d(de??Tzu6>=|~0#t|ZvK;BF#ePKf zhcob>Vu?Jzrm9yf&V~G^xZHifbg$}^%bm~wp8pgla4Ze~@8bBf#M%7KX$`oD{}h+| zDeyADeSD`4!KVCTYmJ}P;XiSIRkV-C{A^Oap=4dc|1iq23~l{!h6>Fmc}4?x{yztv zC&vU(n}6v@Z!P=z0@q}L`QL~9t1!OD?|lnw2XryN>H{pi(^t=PHCmy53xV^OfeKnz-0D0yz5F%cApa>Y_f2r0<8t$_Hdm)G(kGAk&jatV9zfkQt}bkm z7tR6xQ(SH>`uJ0dT-8N!`!uEp-cJUe9l2i5Z9JFPx{ds&y>yNMFS%R=Ts#Xk@Yk-| zQPJ)?9@n=}r*2ZQaByffI}L{k^dBz+X9pU zyqAtcb;a4R2CUXV4*c)NH5<^!pJa7eI6c+?Yrt6znBe~%oVy?3xLjweh;w5N*rovk z{HM6wlbF9Vr?Y%L&*!kMCOBc%fb$y2!v9Xlm!qxEakg)=1FV69X&{OJ6qnls%qUoaIAhiTYrruLfa?{25|_Jr-K!m|IWE^R<#R@?fdXp) zduc2J|2H5WpXclrxGp$*?`lBG_3v>o^OX;>$nRy5H)oNTWR^e5B7cxY9?2pPWtKlK z$t-`AMSd@fyg7?Jlv$4AY0LNDCC%T1B>Cgn8nF~fk|$SobCNu{vLV*w9|>yGhjz5` zX006B(aJ+wc_dk`$xM<$ndPJ%P5O_u*OPXV8!Q;tG^FQ9wmI$?_k? z&u-RDKapHWUtStZAf}z<^y3ALX(vT4G!%&?pU6;>Z=&R!$J&lf#Cc-$;>o`m;sZ=u;^l zRNfqytHp8o?6_D{ytq8^Hc73$MEdc|HQLdW2X*sLT(1$w^N-6l+R;g0qa7W2X_oYZ zT6v<{loDvQgLC5rQ@&`m19?b$LQ?V6W+R_CJ1Kiry~*-qvC1q-o}?X|ov<5{rxY(q z!KEqbYi}@^DsQHAV}B{y!RZO!=oL@RyOOx4Qs*Kpn6j9y7KrbmSmyW$ieu`}NL;R0 zJaqw6wL|G7Za`n2E&WhDf8FwByT62A($`O-c>4NnPGn!ArvQ~$Pl4*6?xEETrXPDUg_IM5^2zdl;+`}b z(vd3#jA=+muF;U5yi}sUwE4%3u30`ID4#3;C(0LV87LakldG#xB^N4*J$sepbmgTI z{e`FiLHVolpL~E0Dpe@|RLh%5Mt_QiuPu2b*BX5>d zQ(qrfkgTClid^H^VkfJw*Z7sx`qlcCh@8AeKPuY(Sfr&%KT!ie5?{4)rOcGFj(tm# z6d{rPH#7FBy*^gxn2bKO@`Q%ODO$NGm5?T1FYYR>JW++QQJ`X^OmA zBTv<^Mx5FSRci;QrWlx7JCLVXKt!8<$_<7x%eC6Uxk(DpYA02mRJ@eyo0GCnPCr?k zBu^<`l02n&N%BZM{TE{2wD%W^%SAnC<#Dn8^F>@lPw-~tT+Q_f?UV`@YUHtNaAi=~ zp++83(!~GqHnduv&<>f6aU(2oA|JyE>bxL*0CzCIGqo{lOvtClwlxnh;_FSR@* zB}0@ zOJ5wL0*ddrL|h-|EFI91i{i;us3TAKl!)&2vyqqbhbjz;?}_{qN1&lpdAxWyPfsqi zL&-GjTwh9p*k43P9uz`+-qVq*#HeD`*^2%W+QEsM_jigsp&fmBDGtZ~G_P!w>w|G2 zzC2d@Pg$om;)nWnv_kxXe0It~)d9$Zs-G!e zAE!PoHRn7|){aI>`Ijag(f&+&f2S4Vm&TYyLS4&)iN19_=VqN+pL-o>j io::Result { + let find_reg_key = process::Command::new("reg") + .arg("query") + .arg(r"HKLM\SOFTWARE\Microsoft\Windows Kits\Installed Roots") + .arg("/reg:32") + .arg("/v") + .arg("KitsRoot10") + .output(); + + match find_reg_key { + Err(find_reg_key) => { + return Err(io::Error::new( + io::ErrorKind::Other, + format!("Failed to run registry query: {}", find_reg_key), + )) + } + Ok(find_reg_key) => { + if find_reg_key.status.code().unwrap() != 0 { + return Err(io::Error::new( + io::ErrorKind::Other, + "Can not find Windows SDK", + )); + } else { + let lines = String::from_utf8(find_reg_key.stdout) + .expect("Should be able to parse the output"); + let mut lines: Vec<&str> = lines.lines().collect(); + let mut rc_exe_paths: Vec = Vec::new(); + lines.reverse(); + for line in lines { + if line.trim().starts_with("KitsRoot") { + let kit: String = line + .chars() + .skip(line.find("REG_SZ").unwrap() + 6) + .skip_while(|c| c.is_whitespace()) + .collect(); + + let p = PathBuf::from(&kit); + let rc = if cfg!(target_arch = "x86_64") { + p.join(r"bin\x64\rc.exe") + } else { + p.join(r"bin\x86\rc.exe") + }; + + if rc.exists() { + println!("{:?}", rc); + rc_exe_paths.push(rc.to_owned()); + } + + if let Ok(bin) = p.join("bin").read_dir() { + for e in bin.filter_map(|e| e.ok()) { + let p = if cfg!(target_arch = "x86_64") { + e.path().join(r"x64\rc.exe") + } else { + e.path().join(r"x86\rc.exe") + }; + if p.exists() { + println!("{:?}", p); + rc_exe_paths.push(p.to_owned()); + } + } + } + } + } + if rc_exe_paths.is_empty() { + return Err(io::Error::new( + io::ErrorKind::Other, + "Can not find Windows SDK", + )); + } + + println!("{:?}", rc_exe_paths); + let rc_path = rc_exe_paths.pop().unwrap(); + + let rc_exe = if !rc_path.exists() { + if cfg!(target_arch = "x86_64") { + PathBuf::from(rc_path.parent().unwrap()).join(r"bin\x64\rc.exe") + } else { + PathBuf::from(rc_path.parent().unwrap()).join(r"bin\x86\rc.exe") + } + } else { + rc_path + }; + + println!("Selected RC path: '{}'", rc_exe.display()); + Ok(rc_exe) + } + } + } + } + + fn write_resource_file(rc_path: &Path, icon_path: &str) -> io::Result<()> { + let mut f = std::fs::File::create(rc_path)?; + writeln!(f, "{} ICON \"{}\"", 1, icon_path)?; + + Ok(()) + } } From f5b67d9acb89ea54e7226111e3e4d8c3a008144b Mon Sep 17 00:00:00 2001 From: sogaiu <33044872+sogaiu@users.noreply.github.com> Date: Sun, 28 Jan 2024 19:17:46 +0900 Subject: [PATCH 103/796] Use janet-simple grammar for Janet (#9247) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use janet-simple grammar for Janet * Update book * Tweak language name and related * Rename janet-simple to janet in book * Remove spurious language section for janet * Drop quote_lit and qq_lit related highlighting --------- Co-authored-by: sogaiu <983021772@users.noreply.github.com> Co-authored-by: Blaž Hrastnik --- languages.toml | 32 +++++++++---- runtime/queries/janet/highlights.scm | 67 +++++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/languages.toml b/languages.toml index 785bc9701..ba3b19066 100644 --- a/languages.toml +++ b/languages.toml @@ -326,6 +326,29 @@ comment-token = "//" language-servers = [ "mint" ] indent = { tab-width = 2, unit = " " } +[[language]] +name = "janet" +scope = "source.janet" +injection-regex = "janet" +file-types = ["cgen", "janet", "jdn"] +shebangs = ["janet"] +roots = ["project.janet"] +comment-token = "#" +indent = { tab-width = 2, unit = " " } +formatter = { command = "janet-format" } +grammar = "janet-simple" + +[language.auto-pairs] +'"' = '"' +'(' = ')' +'[' = ']' +'{' = '}' +"`" = "`" + +[[grammar]] +name = "janet-simple" +source = { git = "https://github.com/sogaiu/tree-sitter-janet-simple", rev = "51271e260346878e1a1aa6c506ce6a797b7c25e2" } + [[language]] name = "json" scope = "source.json" @@ -3008,15 +3031,6 @@ file-types = ["log"] name = "log" source = { git = "https://github.com/Tudyx/tree-sitter-log", rev = "62cfe307e942af3417171243b599cc7deac5eab9" } -[[language]] -name = "janet" -scope = "source.janet" -injection-regex = "janet" -file-types = ["janet"] -comment-token = "#" -indent = { tab-width = 2, unit = " " } -grammar = "clojure" - [[language]] name = "hocon" scope = "source.conf" diff --git a/runtime/queries/janet/highlights.scm b/runtime/queries/janet/highlights.scm index a036368a1..133559439 100644 --- a/runtime/queries/janet/highlights.scm +++ b/runtime/queries/janet/highlights.scm @@ -1 +1,66 @@ -; inherits: clojure +(kwd_lit) @string.special.symbol + +(str_lit) @string + +(long_str_lit) @string + +(buf_lit) @string + +(long_buf_lit) @string + +(num_lit) @constant.numeric + +[(bool_lit) (nil_lit)] @constant.builtin + +(comment) @comment + +((sym_lit) @variable + (#match? @variable "^\\*.+\\*$")) + +(short_fn_lit + . + (sym_lit) @function) + +;; special forms +(par_tup_lit + . + (sym_lit) @function.macro + (#match? @function.macro + "^(break|def|do|fn|if|quasiquote|quote|set|splice|unquote|upscope|var|while)$")) + +;; for macros +;; +;; (each name (all-bindings) +;; (when-let [info (dyn (symbol name))] +;; (when (info :macro) +;; (print name)))) +(par_tup_lit + . + (sym_lit) @function.macro + (#match? @function.macro + "^(%=|\\*=|\\+\\+|\\+=|\\-\\-|\\-=|\\->|\\->>|\\-\\?>|\\-\\?>>|/=|and|as\\->|as\\-macro|as\\?\\->|assert|case|catseq|chr|comment|compif|comptime|compwhen|cond|coro|def\\-|default|defdyn|defer|defmacro|defmacro\\-|defn|defn\\-|delay|doc|each|eachk|eachp|edefer|ev/do\\-thread|ev/gather|ev/spawn|ev/spawn\\-thread|ev/with\\-deadline|ffi/defbind|fiber\\-fn|for|forever|forv|generate|if\\-let|if\\-not|if\\-with|import|juxt|label|let|loop|match|or|prompt|protect|repeat|seq|short\\-fn|tabseq|toggle|tracev|try|unless|use|var\\-|varfn|when|when\\-let|when\\-with|with|with\\-dyns|with\\-syms|with\\-vars)$")) + +;; builtin functions +;; +;; (each name (all-bindings) +;; (when-let [info (dyn (symbol name))] +;; (when (and (nil? (info :macro)) +;; (or (function? (info :value)) +;; (cfunction? (info :value)))) +;; (print name)))) +((sym_lit) @function.builtin + (#match? @function.builtin + "^(%|\\*|\\+|\\-|/|<|<=|=|>|>=|\\.break|\\.breakall|\\.bytecode|\\.clear|\\.clearall|\\.disasm|\\.fiber|\\.fn|\\.frame|\\.locals|\\.next|\\.nextc|\\.ppasm|\\.signal|\\.slot|\\.slots|\\.source|\\.stack|\\.step|abstract\\?|accumulate|accumulate2|all|all\\-bindings|all\\-dynamics|any\\?|apply|array|array/clear|array/concat|array/ensure|array/fill|array/insert|array/new|array/new\\-filled|array/peek|array/pop|array/push|array/remove|array/slice|array/trim|array/weak|array\\?|asm|bad\\-compile|bad\\-parse|band|blshift|bnot|boolean\\?|bor|brshift|brushift|buffer|buffer/bit|buffer/bit\\-clear|buffer/bit\\-set|buffer/bit\\-toggle|buffer/blit|buffer/clear|buffer/fill|buffer/format|buffer/from\\-bytes|buffer/new|buffer/new\\-filled|buffer/popn|buffer/push|buffer/push\\-at|buffer/push\\-byte|buffer/push\\-string|buffer/push\\-word|buffer/slice|buffer/trim|buffer\\?|bxor|bytes\\?|cancel|cfunction\\?|cli\\-main|cmp|comp|compare|compare<|compare<=|compare=|compare>|compare>=|compile|complement|count|curenv|debug|debug/arg\\-stack|debug/break|debug/fbreak|debug/lineage|debug/stack|debug/stacktrace|debug/step|debug/unbreak|debug/unfbreak|debugger|debugger\\-on\\-status|dec|deep\\-not=|deep=|defglobal|describe|dictionary\\?|disasm|distinct|div|doc\\*|doc\\-format|doc\\-of|dofile|drop|drop\\-until|drop\\-while|dyn|eflush|empty\\?|env\\-lookup|eprin|eprinf|eprint|eprintf|error|errorf|ev/acquire\\-lock|ev/acquire\\-rlock|ev/acquire\\-wlock|ev/all\\-tasks|ev/call|ev/cancel|ev/capacity|ev/chan|ev/chan\\-close|ev/chunk|ev/close|ev/count|ev/deadline|ev/full|ev/give|ev/give\\-supervisor|ev/go|ev/lock|ev/read|ev/release\\-lock|ev/release\\-rlock|ev/release\\-wlock|ev/rselect|ev/rwlock|ev/select|ev/sleep|ev/take|ev/thread|ev/thread\\-chan|ev/write|eval|eval\\-string|even\\?|every\\?|extreme|false\\?|ffi/align|ffi/call|ffi/calling\\-conventions|ffi/close|ffi/context|ffi/free|ffi/jitfn|ffi/lookup|ffi/malloc|ffi/native|ffi/pointer\\-buffer|ffi/pointer\\-cfunction|ffi/read|ffi/signature|ffi/size|ffi/struct|ffi/trampoline|ffi/write|fiber/can\\-resume\\?|fiber/current|fiber/getenv|fiber/last\\-value|fiber/maxstack|fiber/new|fiber/root|fiber/setenv|fiber/setmaxstack|fiber/status|fiber\\?|file/close|file/flush|file/lines|file/open|file/read|file/seek|file/tell|file/temp|file/write|filter|find|find\\-index|first|flatten|flatten\\-into|flush|flycheck|freeze|frequencies|from\\-pairs|function\\?|gccollect|gcinterval|gcsetinterval|gensym|get|get\\-in|getline|getproto|group\\-by|has\\-key\\?|has\\-value\\?|hash|idempotent\\?|identity|import\\*|in|inc|index\\-of|indexed\\?|int/s64|int/to\\-bytes|int/to\\-number|int/u64|int\\?|interleave|interpose|invert|juxt\\*|keep|keep\\-syntax|keep\\-syntax!|keys|keyword|keyword/slice|keyword\\?|kvs|last|length|lengthable\\?|load\\-image|macex|macex1|maclintf|make\\-env|make\\-image|map|mapcat|marshal|math/abs|math/acos|math/acosh|math/asin|math/asinh|math/atan|math/atan2|math/atanh|math/cbrt|math/ceil|math/cos|math/cosh|math/erf|math/erfc|math/exp|math/exp2|math/expm1|math/floor|math/gamma|math/gcd|math/hypot|math/lcm|math/log|math/log\\-gamma|math/log10|math/log1p|math/log2|math/next|math/pow|math/random|math/rng|math/rng\\-buffer|math/rng\\-int|math/rng\\-uniform|math/round|math/seedrandom|math/sin|math/sinh|math/sqrt|math/tan|math/tanh|math/trunc|max|max\\-of|mean|memcmp|merge|merge\\-into|merge\\-module|min|min\\-of|mod|module/add\\-paths|module/expand\\-path|module/find|module/value|nan\\?|nat\\?|native|neg\\?|net/accept|net/accept\\-loop|net/address|net/address\\-unpack|net/chunk|net/close|net/connect|net/flush|net/listen|net/localname|net/peername|net/read|net/recv\\-from|net/send\\-to|net/server|net/setsockopt|net/shutdown|net/write|next|nil\\?|not|not=|number\\?|odd\\?|one\\?|os/arch|os/cd|os/chmod|os/clock|os/compiler|os/cpu\\-count|os/cryptorand|os/cwd|os/date|os/dir|os/environ|os/execute|os/exit|os/getenv|os/isatty|os/link|os/lstat|os/mkdir|os/mktime|os/open|os/perm\\-int|os/perm\\-string|os/pipe|os/posix\\-exec|os/posix\\-fork|os/proc\\-close|os/proc\\-kill|os/proc\\-wait|os/readlink|os/realpath|os/rename|os/rm|os/rmdir|os/setenv|os/shell|os/sigaction|os/sleep|os/spawn|os/stat|os/strftime|os/symlink|os/time|os/touch|os/umask|os/which|pairs|parse|parse\\-all|parser/byte|parser/clone|parser/consume|parser/eof|parser/error|parser/flush|parser/has\\-more|parser/insert|parser/new|parser/produce|parser/state|parser/status|parser/where|partial|partition|partition\\-by|peg/compile|peg/find|peg/find\\-all|peg/match|peg/replace|peg/replace\\-all|pos\\?|postwalk|pp|prewalk|prin|prinf|print|printf|product|propagate|put|put\\-in|quit|range|reduce|reduce2|repl|require|resume|return|reverse|reverse!|run\\-context|sandbox|scan\\-number|setdyn|signal|slice|slurp|some|sort|sort\\-by|sorted|sorted\\-by|spit|string|string/ascii\\-lower|string/ascii\\-upper|string/bytes|string/check\\-set|string/find|string/find\\-all|string/format|string/from\\-bytes|string/has\\-prefix\\?|string/has\\-suffix\\?|string/join|string/repeat|string/replace|string/replace\\-all|string/reverse|string/slice|string/split|string/trim|string/triml|string/trimr|string\\?|struct|struct/getproto|struct/proto\\-flatten|struct/to\\-table|struct/with\\-proto|struct\\?|sum|symbol|symbol/slice|symbol\\?|table|table/clear|table/clone|table/getproto|table/new|table/proto\\-flatten|table/rawget|table/setproto|table/to\\-struct|table/weak|table/weak\\-keys|table/weak\\-values|table\\?|take|take\\-until|take\\-while|tarray/buffer|tarray/copy\\-bytes|tarray/length|tarray/new|tarray/properties|tarray/slice|tarray/swap\\-bytes|thread/close|thread/current|thread/exit|thread/new|thread/receive|thread/send|thaw|trace|true\\?|truthy\\?|tuple|tuple/brackets|tuple/setmap|tuple/slice|tuple/sourcemap|tuple/type|tuple\\?|type|unmarshal|untrace|update|update\\-in|values|varglobal|walk|warn\\-compile|xprin|xprinf|xprint|xprintf|yield|zero\\?|zipcoll)$")) + +;; other calls +(par_tup_lit + . + (sym_lit) @function) + +(sym_lit) @variable + +["{" "@{" "}" + "[" "@[" "]" + "(" "@(" ")"] @punctuation.bracket + +["~" "'" "|" ";" ","] @operator From 87a720c3a13ccc7245f5b0befc008db5bd039032 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Sun, 28 Jan 2024 17:34:45 +0100 Subject: [PATCH 104/796] make path changes LSP spec conform (#8949) Currently, helix implements operations which change the paths of files incorrectly and inconsistently. This PR ensures that we do the following whenever a buffer is renamed (`:move` and workspace edits) * always send did_open/did_close notifications * send will_rename/did_rename requests correctly * send them to all LSP servers not just those that are active for a buffer * also send these requests for paths that are not yet open in a buffer (if triggered from workspace edit). * only send these if the server registered interests in the path * autodetect language, indent, line ending, .. This PR also centralizes the infrastructure for path setting and therefore `:w ` benefits from similar fixed (but without didRename) --- helix-lsp/src/client.rs | 83 ++++++----- helix-lsp/src/file_operations.rs | 105 ++++++++++++++ helix-lsp/src/lib.rs | 1 + helix-term/src/application.rs | 31 +---- helix-term/src/commands/lsp.rs | 198 +------------------------- helix-term/src/commands/typed.rs | 62 +-------- helix-view/src/document.rs | 3 + helix-view/src/editor.rs | 90 +++++++++++- helix-view/src/handlers/lsp.rs | 229 +++++++++++++++++++++++++++++++ 9 files changed, 483 insertions(+), 319 deletions(-) create mode 100644 helix-lsp/src/file_operations.rs diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index fb32f6eb3..94bad6faf 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -1,4 +1,5 @@ use crate::{ + file_operations::FileOperationsInterest, find_lsp_workspace, jsonrpc, transport::{Payload, Transport}, Call, Error, OffsetEncoding, Result, @@ -9,20 +10,20 @@ use helix_loader::{self, VERSION_AND_GIT_HASH}; use helix_stdx::path; use lsp::{ notification::DidChangeWorkspaceFolders, CodeActionCapabilityResolveSupport, - DidChangeWorkspaceFoldersParams, OneOf, PositionEncodingKind, SignatureHelp, WorkspaceFolder, - WorkspaceFoldersChangeEvent, + DidChangeWorkspaceFoldersParams, OneOf, PositionEncodingKind, SignatureHelp, Url, + WorkspaceFolder, WorkspaceFoldersChangeEvent, }; use lsp_types as lsp; use parking_lot::Mutex; use serde::Deserialize; use serde_json::Value; -use std::future::Future; -use std::process::Stdio; use std::sync::{ atomic::{AtomicU64, Ordering}, Arc, }; use std::{collections::HashMap, path::PathBuf}; +use std::{future::Future, sync::OnceLock}; +use std::{path::Path, process::Stdio}; use tokio::{ io::{BufReader, BufWriter}, process::{Child, Command}, @@ -51,6 +52,7 @@ pub struct Client { server_tx: UnboundedSender, request_counter: AtomicU64, pub(crate) capabilities: OnceCell, + pub(crate) file_operation_interest: OnceLock, config: Option, root_path: std::path::PathBuf, root_uri: Option, @@ -233,6 +235,7 @@ impl Client { server_tx, request_counter: AtomicU64::new(0), capabilities: OnceCell::new(), + file_operation_interest: OnceLock::new(), config, req_timeout, root_path, @@ -278,6 +281,11 @@ impl Client { .expect("language server not yet initialized!") } + pub(crate) fn file_operations_intests(&self) -> &FileOperationsInterest { + self.file_operation_interest + .get_or_init(|| FileOperationsInterest::new(self.capabilities())) + } + /// Client has to be initialized otherwise this function panics #[inline] pub fn supports_feature(&self, feature: LanguageServerFeature) -> bool { @@ -717,27 +725,27 @@ impl Client { }) } - pub fn prepare_file_rename( + pub fn will_rename( &self, - old_uri: &lsp::Url, - new_uri: &lsp::Url, + old_path: &Path, + new_path: &Path, + is_dir: bool, ) -> Option>> { - let capabilities = self.capabilities.get().unwrap(); - - // Return early if the server does not support willRename feature - match &capabilities.workspace { - Some(workspace) => match &workspace.file_operations { - Some(op) => { - op.will_rename.as_ref()?; - } - _ => return None, - }, - _ => return None, + let capabilities = self.file_operations_intests(); + if !capabilities.will_rename.has_interest(old_path, is_dir) { + return None; } - + let url_from_path = |path| { + let url = if is_dir { + Url::from_directory_path(path) + } else { + Url::from_file_path(path) + }; + Some(url.ok()?.to_string()) + }; let files = vec![lsp::FileRename { - old_uri: old_uri.to_string(), - new_uri: new_uri.to_string(), + old_uri: url_from_path(old_path)?, + new_uri: url_from_path(new_path)?, }]; let request = self.call_with_timeout::( lsp::RenameFilesParams { files }, @@ -751,27 +759,28 @@ impl Client { }) } - pub fn did_file_rename( + pub fn did_rename( &self, - old_uri: &lsp::Url, - new_uri: &lsp::Url, + old_path: &Path, + new_path: &Path, + is_dir: bool, ) -> Option>> { - let capabilities = self.capabilities.get().unwrap(); - - // Return early if the server does not support DidRename feature - match &capabilities.workspace { - Some(workspace) => match &workspace.file_operations { - Some(op) => { - op.did_rename.as_ref()?; - } - _ => return None, - }, - _ => return None, + let capabilities = self.file_operations_intests(); + if !capabilities.did_rename.has_interest(new_path, is_dir) { + return None; } + let url_from_path = |path| { + let url = if is_dir { + Url::from_directory_path(path) + } else { + Url::from_file_path(path) + }; + Some(url.ok()?.to_string()) + }; let files = vec![lsp::FileRename { - old_uri: old_uri.to_string(), - new_uri: new_uri.to_string(), + old_uri: url_from_path(old_path)?, + new_uri: url_from_path(new_path)?, }]; Some(self.notify::(lsp::RenameFilesParams { files })) } diff --git a/helix-lsp/src/file_operations.rs b/helix-lsp/src/file_operations.rs new file mode 100644 index 000000000..98ac32a40 --- /dev/null +++ b/helix-lsp/src/file_operations.rs @@ -0,0 +1,105 @@ +use std::path::Path; + +use globset::{GlobBuilder, GlobSet}; + +use crate::lsp; + +#[derive(Default, Debug)] +pub(crate) struct FileOperationFilter { + dir_globs: GlobSet, + file_globs: GlobSet, +} + +impl FileOperationFilter { + fn new(capability: Option<&lsp::FileOperationRegistrationOptions>) -> FileOperationFilter { + let Some(cap) = capability else { + return FileOperationFilter::default(); + }; + let mut dir_globs = GlobSet::builder(); + let mut file_globs = GlobSet::builder(); + for filter in &cap.filters { + // TODO: support other url schemes + let is_non_file_schema = filter + .scheme + .as_ref() + .is_some_and(|schema| schema != "file"); + if is_non_file_schema { + continue; + } + let ignore_case = filter + .pattern + .options + .as_ref() + .and_then(|opts| opts.ignore_case) + .unwrap_or(false); + let mut glob_builder = GlobBuilder::new(&filter.pattern.glob); + glob_builder.case_insensitive(!ignore_case); + let glob = match glob_builder.build() { + Ok(glob) => glob, + Err(err) => { + log::error!("invalid glob send by LS: {err}"); + continue; + } + }; + match filter.pattern.matches { + Some(lsp::FileOperationPatternKind::File) => { + file_globs.add(glob); + } + Some(lsp::FileOperationPatternKind::Folder) => { + dir_globs.add(glob); + } + None => { + file_globs.add(glob.clone()); + dir_globs.add(glob); + } + }; + } + let file_globs = file_globs.build().unwrap_or_else(|err| { + log::error!("invalid globs send by LS: {err}"); + GlobSet::empty() + }); + let dir_globs = dir_globs.build().unwrap_or_else(|err| { + log::error!("invalid globs send by LS: {err}"); + GlobSet::empty() + }); + FileOperationFilter { + dir_globs, + file_globs, + } + } + + pub(crate) fn has_interest(&self, path: &Path, is_dir: bool) -> bool { + if is_dir { + self.dir_globs.is_match(path) + } else { + self.file_globs.is_match(path) + } + } +} + +#[derive(Default, Debug)] +pub(crate) struct FileOperationsInterest { + // TODO: support other notifications + // did_create: FileOperationFilter, + // will_create: FileOperationFilter, + pub did_rename: FileOperationFilter, + pub will_rename: FileOperationFilter, + // did_delete: FileOperationFilter, + // will_delete: FileOperationFilter, +} + +impl FileOperationsInterest { + pub fn new(capabilities: &lsp::ServerCapabilities) -> FileOperationsInterest { + let capabilities = capabilities + .workspace + .as_ref() + .and_then(|capabilities| capabilities.file_operations.as_ref()); + let Some(capabilities) = capabilities else { + return FileOperationsInterest::default(); + }; + FileOperationsInterest { + did_rename: FileOperationFilter::new(capabilities.did_rename.as_ref()), + will_rename: FileOperationFilter::new(capabilities.will_rename.as_ref()), + } + } +} diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index 53b2712d0..4ce445aee 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -1,5 +1,6 @@ mod client; pub mod file_event; +mod file_operations; pub mod jsonrpc; pub mod snippet; mod transport; diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 3f3e59c6a..b5150a13a 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -21,7 +21,6 @@ use tui::backend::Backend; use crate::{ args::Args, - commands::apply_workspace_edit, compositor::{Compositor, Event}, config::Config, handlers, @@ -573,26 +572,8 @@ impl Application { let lines = doc_save_event.text.len_lines(); let bytes = doc_save_event.text.len_bytes(); - if doc.path() != Some(&doc_save_event.path) { - doc.set_path(Some(&doc_save_event.path)); - - let loader = self.editor.syn_loader.clone(); - - // borrowing the same doc again to get around the borrow checker - let doc = doc_mut!(self.editor, &doc_save_event.doc_id); - let id = doc.id(); - doc.detect_language(loader); - self.editor.refresh_language_servers(id); - // and again a borrow checker workaround... - let doc = doc_mut!(self.editor, &doc_save_event.doc_id); - let diagnostics = Editor::doc_diagnostics( - &self.editor.language_servers, - &self.editor.diagnostics, - doc, - ); - doc.replace_diagnostics(diagnostics, &[], None); - } - + self.editor + .set_doc_path(doc_save_event.doc_id, &doc_save_event.path); // TODO: fix being overwritten by lsp self.editor.set_status(format!( "'{}' written, {}L {}B", @@ -1011,11 +992,9 @@ impl Application { let language_server = language_server!(); if language_server.is_initialized() { let offset_encoding = language_server.offset_encoding(); - let res = apply_workspace_edit( - &mut self.editor, - offset_encoding, - ¶ms.edit, - ); + let res = self + .editor + .apply_workspace_edit(offset_encoding, ¶ms.edit); Ok(json!(lsp::ApplyWorkspaceEditResponse { applied: res.is_ok(), diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index c694ba25c..a1f7bf17d 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -726,8 +726,7 @@ pub fn code_action(cx: &mut Context) { resolved_code_action.as_ref().unwrap_or(code_action); if let Some(ref workspace_edit) = resolved_code_action.edit { - log::debug!("edit: {:?}", workspace_edit); - let _ = apply_workspace_edit(editor, offset_encoding, workspace_edit); + let _ = editor.apply_workspace_edit(offset_encoding, workspace_edit); } // if code action provides both edit and command first the edit @@ -787,63 +786,6 @@ pub fn execute_lsp_command(editor: &mut Editor, language_server_id: usize, cmd: }); } -pub fn apply_document_resource_op(op: &lsp::ResourceOp) -> std::io::Result<()> { - use lsp::ResourceOp; - use std::fs; - match op { - ResourceOp::Create(op) => { - let path = op.uri.to_file_path().unwrap(); - let ignore_if_exists = op.options.as_ref().map_or(false, |options| { - !options.overwrite.unwrap_or(false) && options.ignore_if_exists.unwrap_or(false) - }); - if ignore_if_exists && path.exists() { - Ok(()) - } else { - // Create directory if it does not exist - if let Some(dir) = path.parent() { - if !dir.is_dir() { - fs::create_dir_all(dir)?; - } - } - - fs::write(&path, []) - } - } - ResourceOp::Delete(op) => { - let path = op.uri.to_file_path().unwrap(); - if path.is_dir() { - let recursive = op - .options - .as_ref() - .and_then(|options| options.recursive) - .unwrap_or(false); - - if recursive { - fs::remove_dir_all(&path) - } else { - fs::remove_dir(&path) - } - } else if path.is_file() { - fs::remove_file(&path) - } else { - Ok(()) - } - } - ResourceOp::Rename(op) => { - let from = op.old_uri.to_file_path().unwrap(); - let to = op.new_uri.to_file_path().unwrap(); - let ignore_if_exists = op.options.as_ref().map_or(false, |options| { - !options.overwrite.unwrap_or(false) && options.ignore_if_exists.unwrap_or(false) - }); - if ignore_if_exists && to.exists() { - Ok(()) - } else { - fs::rename(from, &to) - } - } - } -} - #[derive(Debug)] pub struct ApplyEditError { pub kind: ApplyEditErrorKind, @@ -871,142 +813,6 @@ impl ToString for ApplyEditErrorKind { } } -///TODO make this transactional (and set failureMode to transactional) -pub fn apply_workspace_edit( - editor: &mut Editor, - offset_encoding: OffsetEncoding, - workspace_edit: &lsp::WorkspaceEdit, -) -> Result<(), ApplyEditError> { - let mut apply_edits = |uri: &helix_lsp::Url, - version: Option, - text_edits: Vec| - -> Result<(), ApplyEditErrorKind> { - let path = match uri.to_file_path() { - Ok(path) => path, - Err(_) => { - let err = format!("unable to convert URI to filepath: {}", uri); - log::error!("{}", err); - editor.set_error(err); - return Err(ApplyEditErrorKind::UnknownURISchema); - } - }; - - let doc_id = match editor.open(&path, Action::Load) { - Ok(doc_id) => doc_id, - Err(err) => { - let err = format!("failed to open document: {}: {}", uri, err); - log::error!("{}", err); - editor.set_error(err); - return Err(ApplyEditErrorKind::FileNotFound); - } - }; - - let doc = doc!(editor, &doc_id); - if let Some(version) = version { - if version != doc.version() { - let err = format!("outdated workspace edit for {path:?}"); - log::error!("{err}, expected {} but got {version}", doc.version()); - editor.set_error(err); - return Err(ApplyEditErrorKind::DocumentChanged); - } - } - - // Need to determine a view for apply/append_changes_to_history - let view_id = editor.get_synced_view_id(doc_id); - let doc = doc_mut!(editor, &doc_id); - - let transaction = helix_lsp::util::generate_transaction_from_edits( - doc.text(), - text_edits, - offset_encoding, - ); - let view = view_mut!(editor, view_id); - doc.apply(&transaction, view.id); - doc.append_changes_to_history(view); - Ok(()) - }; - - if let Some(ref document_changes) = workspace_edit.document_changes { - match document_changes { - lsp::DocumentChanges::Edits(document_edits) => { - for (i, document_edit) in document_edits.iter().enumerate() { - let edits = document_edit - .edits - .iter() - .map(|edit| match edit { - lsp::OneOf::Left(text_edit) => text_edit, - lsp::OneOf::Right(annotated_text_edit) => { - &annotated_text_edit.text_edit - } - }) - .cloned() - .collect(); - apply_edits( - &document_edit.text_document.uri, - document_edit.text_document.version, - edits, - ) - .map_err(|kind| ApplyEditError { - kind, - failed_change_idx: i, - })?; - } - } - lsp::DocumentChanges::Operations(operations) => { - log::debug!("document changes - operations: {:?}", operations); - for (i, operation) in operations.iter().enumerate() { - match operation { - lsp::DocumentChangeOperation::Op(op) => { - apply_document_resource_op(op).map_err(|io| ApplyEditError { - kind: ApplyEditErrorKind::IoError(io), - failed_change_idx: i, - })?; - } - - lsp::DocumentChangeOperation::Edit(document_edit) => { - let edits = document_edit - .edits - .iter() - .map(|edit| match edit { - lsp::OneOf::Left(text_edit) => text_edit, - lsp::OneOf::Right(annotated_text_edit) => { - &annotated_text_edit.text_edit - } - }) - .cloned() - .collect(); - apply_edits( - &document_edit.text_document.uri, - document_edit.text_document.version, - edits, - ) - .map_err(|kind| ApplyEditError { - kind, - failed_change_idx: i, - })?; - } - } - } - } - } - - return Ok(()); - } - - if let Some(ref changes) = workspace_edit.changes { - log::debug!("workspace changes: {:?}", changes); - for (i, (uri, text_edits)) in changes.iter().enumerate() { - let text_edits = text_edits.to_vec(); - apply_edits(uri, None, text_edits).map_err(|kind| ApplyEditError { - kind, - failed_change_idx: i, - })?; - } - } - - Ok(()) -} - /// Precondition: `locations` should be non-empty. fn goto_impl( editor: &mut Editor, @@ -1263,7 +1069,7 @@ pub fn rename_symbol(cx: &mut Context) { match block_on(future) { Ok(edits) => { - let _ = apply_workspace_edit(cx.editor, offset_encoding, &edits); + let _ = cx.editor.apply_workspace_edit(offset_encoding, &edits); } Err(err) => cx.editor.set_error(err.to_string()), } diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 81ffdf875..b7ceeba59 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -8,7 +8,6 @@ use super::*; use helix_core::fuzzy::fuzzy_match; use helix_core::indent::MAX_INDENT; use helix_core::{encoding, line_ending, shellwords::Shellwords}; -use helix_lsp::{OffsetEncoding, Url}; use helix_view::document::DEFAULT_LANGUAGE_NAME; use helix_view::editor::{Action, CloseError, ConfigEvent}; use serde_json::Value; @@ -2404,67 +2403,14 @@ fn move_buffer( ensure!(args.len() == 1, format!(":move takes one argument")); let doc = doc!(cx.editor); - - let new_path = - helix_stdx::path::canonicalize(&PathBuf::from(args.first().unwrap().to_string())); let old_path = doc .path() - .ok_or_else(|| anyhow!("Scratch buffer cannot be moved. Use :write instead"))? + .context("Scratch buffer cannot be moved. Use :write instead")? .clone(); - let old_path_as_url = doc.url().unwrap(); - let new_path_as_url = Url::from_file_path(&new_path).unwrap(); - - let edits: Vec<( - helix_lsp::Result, - OffsetEncoding, - String, - )> = doc - .language_servers() - .map(|lsp| { - ( - lsp.prepare_file_rename(&old_path_as_url, &new_path_as_url), - lsp.offset_encoding(), - lsp.name().to_owned(), - ) - }) - .filter(|(f, _, _)| f.is_some()) - .map(|(f, encoding, name)| (helix_lsp::block_on(f.unwrap()), encoding, name)) - .collect(); - - for (lsp_reply, encoding, name) in edits { - match lsp_reply { - Ok(edit) => { - if let Err(e) = apply_workspace_edit(cx.editor, encoding, &edit) { - log::error!( - ":move command failed to apply edits from lsp {}: {:?}", - name, - e - ); - }; - } - Err(e) => { - log::error!("LSP {} failed to treat willRename request: {:?}", name, e); - } - }; + let new_path = args.first().unwrap().to_string(); + if let Err(err) = cx.editor.move_path(&old_path, new_path.as_ref()) { + bail!("Could not move file: {err}"); } - - let doc = doc_mut!(cx.editor); - - doc.set_path(Some(new_path.as_path())); - if let Err(e) = std::fs::rename(&old_path, &new_path) { - doc.set_path(Some(old_path.as_path())); - bail!("Could not move file: {}", e); - }; - - doc.language_servers().for_each(|lsp| { - lsp.did_file_rename(&old_path_as_url, &new_path_as_url); - }); - - cx.editor - .language_servers - .file_event_handler - .file_changed(new_path); - Ok(()) } diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 88653948c..33137c6c9 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -1041,6 +1041,9 @@ impl Document { self.encoding } + /// sets the document path without sending events to various + /// observers (like LSP), in most cases `Editor::set_doc_path` + /// should be used instead pub fn set_path(&mut self, path: Option<&Path>) { let path = path.map(helix_stdx::path::canonicalize); diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index eca488e74..db0d4030b 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -23,7 +23,8 @@ use std::{ borrow::Cow, cell::Cell, collections::{BTreeMap, HashMap}, - io::stdin, + fs, + io::{self, stdin}, num::NonZeroUsize, path::{Path, PathBuf}, pin::Pin, @@ -45,6 +46,7 @@ use helix_core::{ }; use helix_dap as dap; use helix_lsp::lsp; +use helix_stdx::path::canonicalize; use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer}; @@ -1215,6 +1217,90 @@ impl Editor { self.launch_language_servers(doc_id) } + /// moves/renames a path, invoking any event handlers (currently only lsp) + /// and calling `set_doc_path` if the file is open in the editor + pub fn move_path(&mut self, old_path: &Path, new_path: &Path) -> io::Result<()> { + let new_path = canonicalize(new_path); + // sanity check + if old_path == new_path { + return Ok(()); + } + let is_dir = old_path.is_dir(); + let language_servers: Vec<_> = self + .language_servers + .iter_clients() + .filter(|client| client.is_initialized()) + .cloned() + .collect(); + for language_server in language_servers { + let Some(request) = language_server.will_rename(old_path, &new_path, is_dir) else { + continue; + }; + let edit = match helix_lsp::block_on(request) { + Ok(edit) => edit, + Err(err) => { + log::error!("invalid willRename response: {err:?}"); + continue; + } + }; + if let Err(err) = self.apply_workspace_edit(language_server.offset_encoding(), &edit) { + log::error!("failed to apply workspace edit: {err:?}") + } + } + fs::rename(old_path, &new_path)?; + if let Some(doc) = self.document_by_path(old_path) { + self.set_doc_path(doc.id(), &new_path); + } + let is_dir = new_path.is_dir(); + for ls in self.language_servers.iter_clients() { + if let Some(notification) = ls.did_rename(old_path, &new_path, is_dir) { + tokio::spawn(notification); + }; + } + self.language_servers + .file_event_handler + .file_changed(old_path.to_owned()); + self.language_servers + .file_event_handler + .file_changed(new_path); + Ok(()) + } + + pub fn set_doc_path(&mut self, doc_id: DocumentId, path: &Path) { + let doc = doc_mut!(self, &doc_id); + let old_path = doc.path(); + + if let Some(old_path) = old_path { + // sanity check, should not occur but some callers (like an LSP) may + // create bogus calls + if old_path == path { + return; + } + // if we are open in LSPs send did_close notification + for language_server in doc.language_servers() { + tokio::spawn(language_server.text_document_did_close(doc.identifier())); + } + } + // we need to clear the list of language servers here so that + // refresh_doc_language/refresh_language_servers doesn't resend + // text_document_did_close. Since we called `text_document_did_close` + // we have fully unregistered this document from its LS + doc.language_servers.clear(); + doc.set_path(Some(path)); + self.refresh_doc_language(doc_id) + } + + pub fn refresh_doc_language(&mut self, doc_id: DocumentId) { + let loader = self.syn_loader.clone(); + let doc = doc_mut!(self, &doc_id); + doc.detect_language(loader); + doc.detect_indent_and_line_ending(); + self.refresh_language_servers(doc_id); + let doc = doc_mut!(self, &doc_id); + let diagnostics = Editor::doc_diagnostics(&self.language_servers, &self.diagnostics, doc); + doc.replace_diagnostics(diagnostics, &[], None); + } + /// Launch a language server for a given document fn launch_language_servers(&mut self, doc_id: DocumentId) { if !self.config().lsp.enable { @@ -1257,7 +1343,7 @@ impl Editor { .collect::>() }); - if language_servers.is_empty() { + if language_servers.is_empty() && doc.language_servers.is_empty() { return; } diff --git a/helix-view/src/handlers/lsp.rs b/helix-view/src/handlers/lsp.rs index 1dae45dd5..beb106b2b 100644 --- a/helix-view/src/handlers/lsp.rs +++ b/helix-view/src/handlers/lsp.rs @@ -1,4 +1,8 @@ +use crate::editor::Action; +use crate::Editor; use crate::{DocumentId, ViewId}; +use helix_lsp::util::generate_transaction_from_edits; +use helix_lsp::{lsp, OffsetEncoding}; pub enum CompletionEvent { /// Auto completion was triggered by typing a word char @@ -39,3 +43,228 @@ pub enum SignatureHelpEvent { Cancel, RequestComplete { open: bool }, } + +#[derive(Debug)] +pub struct ApplyEditError { + pub kind: ApplyEditErrorKind, + pub failed_change_idx: usize, +} + +#[derive(Debug)] +pub enum ApplyEditErrorKind { + DocumentChanged, + FileNotFound, + UnknownURISchema, + IoError(std::io::Error), + // TODO: check edits before applying and propagate failure + // InvalidEdit, +} + +impl ToString for ApplyEditErrorKind { + fn to_string(&self) -> String { + match self { + ApplyEditErrorKind::DocumentChanged => "document has changed".to_string(), + ApplyEditErrorKind::FileNotFound => "file not found".to_string(), + ApplyEditErrorKind::UnknownURISchema => "URI schema not supported".to_string(), + ApplyEditErrorKind::IoError(err) => err.to_string(), + } + } +} + +impl Editor { + fn apply_text_edits( + &mut self, + uri: &helix_lsp::Url, + version: Option, + text_edits: Vec, + offset_encoding: OffsetEncoding, + ) -> Result<(), ApplyEditErrorKind> { + let path = match uri.to_file_path() { + Ok(path) => path, + Err(_) => { + let err = format!("unable to convert URI to filepath: {}", uri); + log::error!("{}", err); + self.set_error(err); + return Err(ApplyEditErrorKind::UnknownURISchema); + } + }; + + let doc_id = match self.open(&path, Action::Load) { + Ok(doc_id) => doc_id, + Err(err) => { + let err = format!("failed to open document: {}: {}", uri, err); + log::error!("{}", err); + self.set_error(err); + return Err(ApplyEditErrorKind::FileNotFound); + } + }; + + let doc = doc_mut!(self, &doc_id); + if let Some(version) = version { + if version != doc.version() { + let err = format!("outdated workspace edit for {path:?}"); + log::error!("{err}, expected {} but got {version}", doc.version()); + self.set_error(err); + return Err(ApplyEditErrorKind::DocumentChanged); + } + } + + // Need to determine a view for apply/append_changes_to_history + let view_id = self.get_synced_view_id(doc_id); + let doc = doc_mut!(self, &doc_id); + + let transaction = generate_transaction_from_edits(doc.text(), text_edits, offset_encoding); + let view = view_mut!(self, view_id); + doc.apply(&transaction, view.id); + doc.append_changes_to_history(view); + Ok(()) + } + + // TODO make this transactional (and set failureMode to transactional) + pub fn apply_workspace_edit( + &mut self, + offset_encoding: OffsetEncoding, + workspace_edit: &lsp::WorkspaceEdit, + ) -> Result<(), ApplyEditError> { + if let Some(ref document_changes) = workspace_edit.document_changes { + match document_changes { + lsp::DocumentChanges::Edits(document_edits) => { + for (i, document_edit) in document_edits.iter().enumerate() { + let edits = document_edit + .edits + .iter() + .map(|edit| match edit { + lsp::OneOf::Left(text_edit) => text_edit, + lsp::OneOf::Right(annotated_text_edit) => { + &annotated_text_edit.text_edit + } + }) + .cloned() + .collect(); + self.apply_text_edits( + &document_edit.text_document.uri, + document_edit.text_document.version, + edits, + offset_encoding, + ) + .map_err(|kind| ApplyEditError { + kind, + failed_change_idx: i, + })?; + } + } + lsp::DocumentChanges::Operations(operations) => { + log::debug!("document changes - operations: {:?}", operations); + for (i, operation) in operations.iter().enumerate() { + match operation { + lsp::DocumentChangeOperation::Op(op) => { + self.apply_document_resource_op(op).map_err(|io| { + ApplyEditError { + kind: ApplyEditErrorKind::IoError(io), + failed_change_idx: i, + } + })?; + } + + lsp::DocumentChangeOperation::Edit(document_edit) => { + let edits = document_edit + .edits + .iter() + .map(|edit| match edit { + lsp::OneOf::Left(text_edit) => text_edit, + lsp::OneOf::Right(annotated_text_edit) => { + &annotated_text_edit.text_edit + } + }) + .cloned() + .collect(); + self.apply_text_edits( + &document_edit.text_document.uri, + document_edit.text_document.version, + edits, + offset_encoding, + ) + .map_err(|kind| { + ApplyEditError { + kind, + failed_change_idx: i, + } + })?; + } + } + } + } + } + + return Ok(()); + } + + if let Some(ref changes) = workspace_edit.changes { + log::debug!("workspace changes: {:?}", changes); + for (i, (uri, text_edits)) in changes.iter().enumerate() { + let text_edits = text_edits.to_vec(); + self.apply_text_edits(uri, None, text_edits, offset_encoding) + .map_err(|kind| ApplyEditError { + kind, + failed_change_idx: i, + })?; + } + } + + Ok(()) + } + + fn apply_document_resource_op(&mut self, op: &lsp::ResourceOp) -> std::io::Result<()> { + use lsp::ResourceOp; + use std::fs; + match op { + ResourceOp::Create(op) => { + let path = op.uri.to_file_path().unwrap(); + let ignore_if_exists = op.options.as_ref().map_or(false, |options| { + !options.overwrite.unwrap_or(false) && options.ignore_if_exists.unwrap_or(false) + }); + if !ignore_if_exists || !path.exists() { + // Create directory if it does not exist + if let Some(dir) = path.parent() { + if !dir.is_dir() { + fs::create_dir_all(dir)?; + } + } + + fs::write(&path, [])?; + self.language_servers.file_event_handler.file_changed(path); + } + } + ResourceOp::Delete(op) => { + let path = op.uri.to_file_path().unwrap(); + if path.is_dir() { + let recursive = op + .options + .as_ref() + .and_then(|options| options.recursive) + .unwrap_or(false); + + if recursive { + fs::remove_dir_all(&path)? + } else { + fs::remove_dir(&path)? + } + self.language_servers.file_event_handler.file_changed(path); + } else if path.is_file() { + fs::remove_file(&path)?; + } + } + ResourceOp::Rename(op) => { + let from = op.old_uri.to_file_path().unwrap(); + let to = op.new_uri.to_file_path().unwrap(); + let ignore_if_exists = op.options.as_ref().map_or(false, |options| { + !options.overwrite.unwrap_or(false) && options.ignore_if_exists.unwrap_or(false) + }); + if !ignore_if_exists || !to.exists() { + self.move_path(&from, &to)?; + } + } + } + Ok(()) + } +} From cf4492174d0ee27bd3c73a5fa57fe3a26aa064be Mon Sep 17 00:00:00 2001 From: Waleed Dahshan <58462210+wmstack@users.noreply.github.com> Date: Tue, 30 Jan 2024 08:58:33 +1100 Subject: [PATCH 105/796] Use range positions to determine insert_newline motion (#9448) * use anchor and head positions to determine motion * use range cursor to decide extending or shifting * add condition to cursor moving back on normal --- helix-term/src/commands.rs | 2 +- helix-view/src/editor.rs | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 4df3278b8..d44f477b7 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -3651,7 +3651,7 @@ pub mod insert { (pos, pos, local_offs) }; - let new_range = if doc.restore_cursor { + let new_range = if range.cursor(text) > range.anchor { // when appending, extend the range by local_offs Range::new( range.anchor + global_offs, diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index db0d4030b..0fa6d67c9 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -1990,10 +1990,12 @@ impl Editor { if doc.restore_cursor { let text = doc.text().slice(..); let selection = doc.selection(view.id).clone().transform(|range| { - Range::new( - range.from(), - graphemes::prev_grapheme_boundary(text, range.to()), - ) + let mut head = range.to(); + if range.head > range.anchor { + head = graphemes::prev_grapheme_boundary(text, head); + } + + Range::new(range.from(), head) }); doc.set_selection(view.id, selection); From dbac78bb3c06717f1f83984da3035e28f39d49a5 Mon Sep 17 00:00:00 2001 From: Andrew Carter Date: Wed, 31 Jan 2024 17:18:53 -0800 Subject: [PATCH 106/796] Set ui.virtual.ruler background for GitHub themes (#9487) Turning on a ruler does not show a visible ruler line for the GitHub themes. This change renders rulers using the `canvas.subtle` color. This matches the color used for the `cursorline` and creates a visible ruler that fits the theme. --- runtime/themes/github_dark.toml | 1 + runtime/themes/github_light.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/runtime/themes/github_dark.toml b/runtime/themes/github_dark.toml index 4f9aa562b..6b3354848 100644 --- a/runtime/themes/github_dark.toml +++ b/runtime/themes/github_dark.toml @@ -60,6 +60,7 @@ label = "scale.red.3" "ui.text.focus" = { fg = "fg.default" } "ui.text.inactive" = "fg.subtle" "ui.virtual" = { fg = "scale.gray.6" } +"ui.virtual.ruler" = { bg = "canvas.subtle" } "ui.selection" = { bg = "scale.blue.8" } "ui.selection.primary" = { bg = "scale.blue.7" } diff --git a/runtime/themes/github_light.toml b/runtime/themes/github_light.toml index 3e2269698..e6912a987 100644 --- a/runtime/themes/github_light.toml +++ b/runtime/themes/github_light.toml @@ -60,6 +60,7 @@ label = "scale.red.5" "ui.text.focus" = { fg = "fg.default" } "ui.text.inactive" = "fg.subtle" "ui.virtual" = { fg = "scale.gray.2" } +"ui.virtual.ruler" = { bg = "canvas.subtle" } "ui.selection" = { bg = "scale.blue.0" } "ui.selection.primary" = { bg = "scale.blue.1" } From dd596028099ea8bc2aa01f980137b22b5ff7b0b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Feb 2024 10:33:46 -0700 Subject: [PATCH 107/796] build(deps): bump pulldown-cmark from 0.9.3 to 0.9.6 (#9474) --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d4ac12e43..76213cf0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1653,11 +1653,11 @@ checksum = "744a264d26b88a6a7e37cbad97953fa233b94d585236310bcbc88474b4092d79" [[package]] name = "pulldown-cmark" -version = "0.9.3" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" +checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.2", "memchr", "unicase", ] From 5ec9565ddb352744aa4d5a8d4017033d899db01d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Feb 2024 10:34:05 -0700 Subject: [PATCH 108/796] build(deps): bump serde from 1.0.195 to 1.0.196 (#9473) --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76213cf0a..dae1bece5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1825,18 +1825,18 @@ checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" [[package]] name = "serde" -version = "1.0.195" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", From aa4241c973d0f5f69ab034ce54a6d2d372e38c3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Feb 2024 10:34:24 -0700 Subject: [PATCH 109/796] build(deps): bump chrono from 0.4.32 to 0.4.33 (#9472) --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dae1bece5..09a019dc9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -171,9 +171,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.32" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41daef31d7a747c5c847246f36de49ced6f7403b4cdabc807a97b5cc184cda7a" +checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" dependencies = [ "android-tzdata", "iana-time-zone", From 70cea93bff2ae94c4da5676cb1e157a0d8117e66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Feb 2024 10:34:49 -0700 Subject: [PATCH 110/796] build(deps): bump serde_json from 1.0.111 to 1.0.113 (#9471) --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 09a019dc9..a0986dcc7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1845,9 +1845,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" dependencies = [ "itoa", "ryu", From 81ae768a4ea20543bc16246a46552a69204fd7c1 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sun, 4 Feb 2024 03:23:33 +0900 Subject: [PATCH 111/796] Use gix pipeline filter instead of manual crlf implementation (#9503) --- Cargo.lock | 192 +++++++++++++++++++++++++++++++++++++++++++ helix-vcs/Cargo.toml | 2 +- helix-vcs/src/git.rs | 38 ++++----- 3 files changed, 209 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0986dcc7..cb815ee3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -448,6 +448,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + [[package]] name = "flate2" version = "1.0.27" @@ -527,33 +539,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31887c304d9a935f3e5494fb5d6a0106c34e965168ec0db9b457424eedd0c741" dependencies = [ "gix-actor", + "gix-attributes", + "gix-command", "gix-commitgraph", "gix-config", "gix-date", "gix-diff", "gix-discover", "gix-features", + "gix-filter", "gix-fs", "gix-glob", "gix-hash", "gix-hashtable", + "gix-ignore", + "gix-index", "gix-lock", "gix-macros", "gix-object", "gix-odb", "gix-pack", "gix-path", + "gix-pathspec", "gix-ref", "gix-refspec", "gix-revision", "gix-revwalk", "gix-sec", + "gix-submodule", "gix-tempfile", "gix-trace", "gix-traverse", "gix-url", "gix-utils", "gix-validate", + "gix-worktree", "once_cell", "parking_lot", "smallvec", @@ -574,6 +594,32 @@ dependencies = [ "winnow 0.5.28", ] +[[package]] +name = "gix-attributes" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "214ee3792e504ee1ce206b36dcafa4f328ca313d1e2ac0b41433d68ef4e14260" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-quote", + "gix-trace", + "kstring", + "smallvec", + "thiserror", + "unicode-bom", +] + +[[package]] +name = "gix-bitmap" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b6cd0f246180034ddafac9b00a112f19178135b21eb031b3f79355891f7325" +dependencies = [ + "thiserror", +] + [[package]] name = "gix-chunk" version = "0.4.7" @@ -583,6 +629,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-command" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1ffc7db3fb50b7dae6ecd937a3527cb725f444614df2ad8988d81806f13f09" +dependencies = [ + "bstr", + "gix-path", + "gix-trace", + "shell-words", +] + [[package]] name = "gix-commitgraph" version = "0.24.0" @@ -690,6 +748,27 @@ dependencies = [ "walkdir", ] +[[package]] +name = "gix-filter" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9240862840fb740d209422937195e129e4ed3da49af212383260134bea8f6c1a" +dependencies = [ + "bstr", + "encoding_rs", + "gix-attributes", + "gix-command", + "gix-hash", + "gix-object", + "gix-packetline-blocking", + "gix-path", + "gix-quote", + "gix-trace", + "gix-utils", + "smallvec", + "thiserror", +] + [[package]] name = "gix-fs" version = "0.10.0" @@ -733,6 +812,44 @@ dependencies = [ "parking_lot", ] +[[package]] +name = "gix-ignore" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f7069aaca4a05784c4cb44e392f0eaf627c6e57e05d3100c0e2386a37a682f0" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-trace", + "unicode-bom", +] + +[[package]] +name = "gix-index" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7152181ba8f0a3addc5075dd612cea31fc3e252b29c8be8c45f4892bf87426" +dependencies = [ + "bitflags 2.4.2", + "bstr", + "btoi", + "filetime", + "gix-bitmap", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-traverse", + "itoa", + "libc", + "memmap2", + "rustix", + "smallvec", + "thiserror", +] + [[package]] name = "gix-lock" version = "13.0.0" @@ -814,6 +931,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-packetline-blocking" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8ef6dd3ea50e26f3bf572e90c034d033c804d340cd1eb386392f184a9ba2f7" +dependencies = [ + "bstr", + "faster-hex", + "gix-trace", + "thiserror", +] + [[package]] name = "gix-path" version = "0.10.4" @@ -827,6 +956,21 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-pathspec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cbd49750edb26b0a691e5246fc635fa554d344da825cd20fa9ee0da9c1b761f" +dependencies = [ + "bitflags 2.4.2", + "bstr", + "gix-attributes", + "gix-config-value", + "gix-glob", + "gix-path", + "thiserror", +] + [[package]] name = "gix-quote" version = "0.4.10" @@ -917,6 +1061,21 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "gix-submodule" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73182f6c1f5ed1ed94ba16581ac62593d5e29cd1c028b2af618f836283b8f8d4" +dependencies = [ + "bstr", + "gix-config", + "gix-path", + "gix-pathspec", + "gix-refspec", + "gix-url", + "thiserror", +] + [[package]] name = "gix-tempfile" version = "13.0.0" @@ -986,6 +1145,24 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-worktree" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca36bb3dc54038c66507dc75c4d8edbee2d6d5cc45227b4eb508ad13dd60a006" +dependencies = [ + "bstr", + "gix-attributes", + "gix-features", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-ignore", + "gix-index", + "gix-object", + "gix-path", +] + [[package]] name = "globset" version = "0.4.14" @@ -1408,6 +1585,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kstring" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747" +dependencies = [ + "static_assertions", +] + [[package]] name = "libc" version = "0.2.152" @@ -1880,6 +2066,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "signal-hook" version = "0.3.17" diff --git a/helix-vcs/Cargo.toml b/helix-vcs/Cargo.toml index 051134e4a..6aa50dcf7 100644 --- a/helix-vcs/Cargo.toml +++ b/helix-vcs/Cargo.toml @@ -19,7 +19,7 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "p parking_lot = "0.12" arc-swap = { version = "1.6.0" } -gix = { version = "0.58.0", default-features = false , optional = true } +gix = { version = "0.58.0", features = ["attributes"], default-features = false, optional = true } imara-diff = "0.1.5" anyhow = "1" diff --git a/helix-vcs/src/git.rs b/helix-vcs/src/git.rs index e4d45301a..995bade06 100644 --- a/helix-vcs/src/git.rs +++ b/helix-vcs/src/git.rs @@ -1,5 +1,7 @@ use anyhow::{bail, Context, Result}; use arc_swap::ArcSwap; +use gix::filter::plumbing::driver::apply::Delay; +use std::io::Read; use std::path::Path; use std::sync::Arc; @@ -76,29 +78,21 @@ impl DiffProvider for Git { let file_oid = find_file_in_commit(&repo, &head, file)?; let file_object = repo.find_object(file_oid)?; - let mut data = file_object.detach().data; - // convert LF to CRLF if configured to avoid showing every line as changed - if repo - .config_snapshot() - .boolean("core.autocrlf") - .unwrap_or(false) - { - let mut normalized_file = Vec::with_capacity(data.len()); - let mut at_cr = false; - for &byte in &data { - if byte == b'\n' { - // if this is a LF instead of a CRLF (last byte was not a CR) - // insert a new CR to generate a CRLF - if !at_cr { - normalized_file.push(b'\r'); - } - } - at_cr = byte == b'\r'; - normalized_file.push(byte) - } - data = normalized_file + let data = file_object.detach().data; + // Get the actual data that git would make out of the git object. + // This will apply the user's git config or attributes like crlf conversions. + if let Some(work_dir) = repo.work_dir() { + let rela_path = file.strip_prefix(work_dir)?; + let rela_path = gix::path::try_into_bstr(rela_path)?; + let (mut pipeline, _) = repo.filter_pipeline(None)?; + let mut worktree_outcome = + pipeline.convert_to_worktree(&data, rela_path.as_ref(), Delay::Forbid)?; + let mut buf = Vec::with_capacity(data.len()); + worktree_outcome.read_to_end(&mut buf)?; + Ok(buf) + } else { + Ok(data) } - Ok(data) } fn get_current_head_name(&self, file: &Path) -> Result>>> { From d54545281956bd92f52aad0ced008ae093df2b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Dzivjak?= Date: Sat, 3 Feb 2024 23:27:40 +0000 Subject: [PATCH 112/796] feat(queries): regex injection for golang (#9510) --- runtime/queries/go/injections.scm | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/runtime/queries/go/injections.scm b/runtime/queries/go/injections.scm index 321c90add..d7b03da35 100644 --- a/runtime/queries/go/injections.scm +++ b/runtime/queries/go/injections.scm @@ -1,2 +1,14 @@ ((comment) @injection.content (#set! injection.language "comment")) + + +(call_expression + (selector_expression) @_function + (#any-of? @_function "regexp.Match" "regexp.MatchReader" "regexp.MatchString" "regexp.Compile" "regexp.CompilePOSIX" "regexp.MustCompile" "regexp.MustCompilePOSIX") + (argument_list + . + [ + (raw_string_literal) + (interpreted_string_literal) + ] @injection.content + (#set! injection.language "regex"))) From 3f380722fbb2fd676ccc0a9dbbea4ddc4871c6ea Mon Sep 17 00:00:00 2001 From: Devyn Cairns Date: Sat, 3 Feb 2024 17:04:51 -0800 Subject: [PATCH 113/796] Update grammars for Nushell to rev 358c4f50 (#9502) --- languages.toml | 2 +- runtime/queries/nu/highlights.scm | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/languages.toml b/languages.toml index ba3b19066..1f26f1680 100644 --- a/languages.toml +++ b/languages.toml @@ -1832,7 +1832,7 @@ language-servers = [ "nu-lsp" ] [[grammar]] name = "nu" -source = { git = "https://github.com/nushell/tree-sitter-nu", rev = "98c11c491e3405c75affa1cf004097692da3dda2" } +source = { git = "https://github.com/nushell/tree-sitter-nu", rev = "358c4f509eb97f0148bbd25ad36acc729819b9c1" } [[language]] name = "vala" diff --git a/runtime/queries/nu/highlights.scm b/runtime/queries/nu/highlights.scm index 746c50251..66a305840 100644 --- a/runtime/queries/nu/highlights.scm +++ b/runtime/queries/nu/highlights.scm @@ -2,7 +2,6 @@ ;;; keywords [ "def" - "def-env" "alias" "export-env" "export" @@ -73,7 +72,6 @@ "tb" "tB" "Tb" "TB" "pb" "pB" "Pb" "PB" "eb" "eB" "Eb" "EB" - "zb" "zB" "Zb" "ZB" "kib" "kiB" "kIB" "kIb" "Kib" "KIb" "KIB" "mib" "miB" "mIB" "mIb" "Mib" "MIb" "MIB" @@ -81,7 +79,6 @@ "tib" "tiB" "tIB" "tIb" "Tib" "TIb" "TIB" "pib" "piB" "pIB" "pIb" "Pib" "PIb" "PIB" "eib" "eiB" "eIB" "eIb" "Eib" "EIb" "EIB" - "zib" "ziB" "zIB" "zIb" "Zib" "ZIb" "ZIB" ] @variable.parameter ) (val_binary From d1054de3ced44903c7bdcf5886d8481eb40a948f Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Sun, 4 Feb 2024 02:09:11 +0100 Subject: [PATCH 114/796] feat: Add `Tact` language support (#9512) Re-submitting --- book/src/generated/lang-support.md | 1 + languages.toml | 19 ++ runtime/queries/tact/highlights.scm | 298 +++++++++++++++++++++++++++ runtime/queries/tact/indents.scm | 38 ++++ runtime/queries/tact/injections.scm | 5 + runtime/queries/tact/locals.scm | 35 ++++ runtime/queries/tact/textobjects.scm | 58 ++++++ 7 files changed, 454 insertions(+) create mode 100644 runtime/queries/tact/highlights.scm create mode 100644 runtime/queries/tact/indents.scm create mode 100644 runtime/queries/tact/injections.scm create mode 100644 runtime/queries/tact/locals.scm create mode 100644 runtime/queries/tact/textobjects.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index a78dd7935..e7d29cc73 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -162,6 +162,7 @@ | swift | ✓ | | | `sourcekit-lsp` | | t32 | ✓ | | | | | tablegen | ✓ | ✓ | ✓ | | +| tact | ✓ | ✓ | ✓ | | | task | ✓ | | | | | templ | ✓ | | | `templ` | | tfvars | ✓ | | ✓ | `terraform-ls` | diff --git a/languages.toml b/languages.toml index 1f26f1680..e601c20d9 100644 --- a/languages.toml +++ b/languages.toml @@ -3042,3 +3042,22 @@ indent = { tab-width = 2, unit = " " } [[grammar]] name = "hocon" source = { git = "https://github.com/antosha417/tree-sitter-hocon", rev = "c390f10519ae69fdb03b3e5764f5592fb6924bcc" } + +[[language]] +name = "tact" +scope = "source.tact" +injection-regex = "tact" +file-types = ["tact"] +comment-token = "//" +indent = { tab-width = 4, unit = " " } + +[language.auto-pairs] +'"' = '"' +'{' = '}' +'(' = ')' +'<' = '>' + +[[grammar]] +name = "tact" +source = { git = "https://github.com/tact-lang/tree-sitter-tact", rev = "ec57ab29c86d632639726631fb2bb178d23e1c91" } + diff --git a/runtime/queries/tact/highlights.scm b/runtime/queries/tact/highlights.scm new file mode 100644 index 000000000..53bf985b5 --- /dev/null +++ b/runtime/queries/tact/highlights.scm @@ -0,0 +1,298 @@ +; See: https://docs.helix-editor.com/master/themes.html#syntax-highlighting +; ------------------------------------------------------------------------- + +; attribute +; --------- + +[ + "@name" + "@interface" +] @attribute + +; comment.line +; ------------ + +((comment) @comment.line + (#match? @comment.line "^//")) + +; comment.block +; ------------- + +(comment) @comment.block + +; function.builtin +; ---------------- + +((identifier) @function.builtin + (#any-of? @function.builtin + "send" "sender" "require" "now" + "myBalance" "myAddress" "newAddress" + "contractAddress" "contractAddressExt" + "emit" "cell" "ton" + "beginString" "beginComment" "beginTailString" "beginStringFromBuilder" "beginCell" "emptyCell" + "randomInt" "random" + "checkSignature" "checkDataSignature" "sha256" + "min" "max" "abs" "pow" + "throw" "dump" "getConfigParam" + "nativeThrowWhen" "nativeThrowUnless" "nativeReserve" + "nativeRandomize" "nativeRandomizeLt" "nativePrepareRandom" "nativeRandom" "nativeRandomInterval") + (#is-not? local)) + +; function.method +; --------------- + +(method_call_expression + name: (identifier) @function.method) + +; function +; -------- + +(func_identifier) @function + +(native_function + name: (identifier) @function) + +(static_function + name: (identifier) @function) + +(static_call_expression + name: (identifier) @function) + +(init_function + "init" @function.method) + +(receive_function + "receive" @function.method) + +(bounced_function + "bounced" @function.method) + +(external_function + "external" @function.method) + +(function + name: (identifier) @function.method) + +; keyword.control.conditional +; --------------------------- + +[ + "if" "else" +] @keyword.control.conditional + +; keyword.control.repeat +; ---------------------- + +[ + "while" "repeat" "do" "until" +] @keyword.control.repeat + +; keyword.control.import +; ---------------------- + +"import" @keyword.control.import + +; keyword.control.return +; ---------------------- + +"return" @keyword.control.return + +; keyword.operator +; ---------------- + +"initOf" @keyword.operator + +; keyword.directive +; ----------------- + +"primitive" @keyword.directive + +; keyword.function +; ---------------- + +[ + "fun" + "native" +] @keyword.function + +; keyword.storage.type +; -------------------- + +[ + "contract" "trait" "struct" "message" "with" + "const" "let" +] @keyword.storage.type + +; keyword.storage.modifier +; ------------------------ + +[ + "get" "mutates" "extends" "virtual" "override" "inline" "abstract" +] @keyword.storage.modifier + +; keyword +; ------- + +[ + "with" + ; "public" ; -- not used, but declared in grammar.ohm + ; "extend" ; -- not used, but declared in grammar.ohm +] @keyword + +; constant.builtin.boolean +; ------------------------ + +(boolean) @constant.builtin.boolean + +; constant.builtin +; ---------------- + +((identifier) @constant.builtin + (#any-of? @constant.builtin + "SendPayGasSeparately" + "SendIgnoreErrors" + "SendDestroyIfZero" + "SendRemainingValue" + "SendRemainingBalance") + (#is-not? local)) + +(null) @constant.builtin + +; constant.numeric.integer +; ------------------------ + +(integer) @constant.numeric.integer + +; constant +; -------- + +(constant + name: (identifier) @constant) + +; string.special.path +; ------------------- + +(import_statement + library: (string) @string.special.path) + +; string +; ------ + +(string) @string + +; type.builtin +; ------------ + +(tlb_serialization + "as" @keyword + type: (identifier) @type.builtin + (#any-of? @type.builtin + "int8" "int16" "int32" "int64" "int128" "int256" "int257" + "uint8" "uint16" "uint32" "uint64" "uint128" "uint256" + "coins" "remaining" "bytes32" "bytes64")) + +((type_identifier) @type.builtin + (#any-of? @type.builtin + "Address" "Bool" "Builder" "Cell" "Int" "Slice" "String" "StringBuilder")) + +(map_type + "map" @type.builtin + "<" @punctuation.bracket + ">" @punctuation.bracket) + +(bounced_type + "bounced" @type.builtin + "<" @punctuation.bracket + ">" @punctuation.bracket) + +((identifier) @type.builtin + (#eq? @type.builtin "SendParameters") + (#is-not? local)) + +; type +; ---- + +(type_identifier) @type + +; constructor +; ----------- + +(instance_expression + name: (identifier) @constructor) + +(initOf + name: (identifier) @constructor) + +; operator +; -------- + +[ + "-" "-=" + "+" "+=" + "*" "*=" + "/" "/=" + "%" "%=" + "=" "==" + "!" "!=" "!!" + "<" "<=" "<<" + ">" ">=" ">>" + "&" "|" + "&&" "||" +] @operator + +; punctuation.bracket +; ------------------- + +[ + "(" ")" + "{" "}" +] @punctuation.bracket + +; punctuation.delimiter +; --------------------- + +[ + ";" + "," + "." + ":" + "?" +] @punctuation.delimiter + +; variable.other.member +; --------------------- + +(field + name: (identifier) @variable.other.member) + +(contract_body + (constant + name: (identifier) @variable.other.member)) + +(trait_body + (constant + name: (identifier) @variable.other.member)) + +(field_access_expression + name: (identifier) @variable.other.member) + +(lvalue (_) (_) @variable.other.member) + +(instance_argument + name: (identifier) @variable.other.member) + +; variable.parameter +; ------------------ + +(parameter + name: (identifier) @variable.parameter) + +; variable.builtin +; ---------------- + +(self) @variable.builtin + +; variable +; -------- + +(identifier) @variable diff --git a/runtime/queries/tact/indents.scm b/runtime/queries/tact/indents.scm new file mode 100644 index 000000000..62c532b22 --- /dev/null +++ b/runtime/queries/tact/indents.scm @@ -0,0 +1,38 @@ +; indent +; ------ + +[ + ; (..., ...) + (parameter_list) + (argument_list) + + ; {..., ...} + (instance_argument_list) + + ; {...; ...} + (message_body) + (struct_body) + (contract_body) + (trait_body) + (function_body) + (block_statement) + + ; misc. + (binary_expression) + (return_statement) +] @indent + +; outdent +; ------- + +[ + "}" + ")" + ">" +] @outdent + +; indent.always +; outdent.always +; align +; extend +; extend.prevent-once \ No newline at end of file diff --git a/runtime/queries/tact/injections.scm b/runtime/queries/tact/injections.scm new file mode 100644 index 000000000..e61db3a56 --- /dev/null +++ b/runtime/queries/tact/injections.scm @@ -0,0 +1,5 @@ +; See: https://docs.helix-editor.com/guides/injection.html + +((comment) @injection.content + (#set! injection.language "comment") + (#match? @injection.content "^//")) \ No newline at end of file diff --git a/runtime/queries/tact/locals.scm b/runtime/queries/tact/locals.scm new file mode 100644 index 000000000..f1b3e8de5 --- /dev/null +++ b/runtime/queries/tact/locals.scm @@ -0,0 +1,35 @@ +; See: https://tree-sitter.github.io/tree-sitter/syntax-highlighting#local-variables + +; Scopes @local.scope +; ------------------------- + +[ + (static_function) + (init_function) + (bounced_function) + (receive_function) + (external_function) + (function) + (block_statement) +] @local.scope + +; Definitions @local.definition +; ------------------------------ + +(let_statement + name: (identifier) @local.definition) + +(parameter + name: (identifier) @local.definition) + +(constant + name: (identifier) @local.definition) + +; References @local.reference +; ----------------------------- + +(self) @local.reference + +(value_expression (identifier) @local.reference) + +(lvalue (identifier) @local.reference) diff --git a/runtime/queries/tact/textobjects.scm b/runtime/queries/tact/textobjects.scm new file mode 100644 index 000000000..54d07014e --- /dev/null +++ b/runtime/queries/tact/textobjects.scm @@ -0,0 +1,58 @@ +; function.inside & around +; ------------------------ + +(static_function + body: (_) @function.inside) @function.around + +(init_function + body: (_) @function.inside) @function.around + +(bounced_function + body: (_) @function.inside) @function.around + +(receive_function + body: (_) @function.inside) @function.around + +(external_function + body: (_) @function.inside) @function.around + +(function + body: (_) @function.inside) @function.around + +; class.inside & around +; --------------------- + +(struct + body: (_) @class.inside) @class.around + +(message + body: (_) @class.inside) @class.around + +(contract + body: (_) @class.inside) @class.around + +; NOTE: Marked as @definition.interface in tags, as it's semantically correct +(trait + body: (_) @class.inside) @class.around + +; parameter.inside & around +; ------------------------- + +(parameter_list + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + +(argument_list + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + +(instance_argument_list + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + +; comment.inside +; -------------- + +(comment) @comment.inside + +; comment.around +; -------------- + +(comment)+ @comment.around \ No newline at end of file From 75d61d8149d1e55d25f0bc34d95f01d6a3f1f526 Mon Sep 17 00:00:00 2001 From: Jaakko Paju Date: Sun, 4 Feb 2024 03:09:42 +0200 Subject: [PATCH 115/796] Improve tree-sitter queries for Scala (#9475) - Simplify function highlighting - Highlight extension methods - Textobject query (mia/maa) for class/trait constructor parameters/arguments - Textobject query (mif/maf) for Scala 3 braceless lambdas --- runtime/queries/scala/highlights.scm | 27 ++++++--------------------- runtime/queries/scala/textobjects.scm | 10 ++++++++-- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/runtime/queries/scala/highlights.scm b/runtime/queries/scala/highlights.scm index 4f90bfda6..e21a3909d 100644 --- a/runtime/queries/scala/highlights.scm +++ b/runtime/queries/scala/highlights.scm @@ -53,28 +53,13 @@ (var_declaration name: (identifier) @variable) -; method definition +; function definitions/declarations -(class_definition - body: (template_body - [ - (function_definition - name: (identifier) @function.method) - (function_declaration - name: (identifier) @function.method) - ])) -(trait_definition - body: (template_body - [ - (function_definition - name: (identifier) @function.method) - (function_declaration - name: (identifier) @function.method) - ])) -(object_definition - body: (template_body - (function_definition - name: (identifier) @function.method))) +(function_declaration + name: (identifier) @function.method) + +(function_definition + name: (identifier) @function.method) ; imports/exports diff --git a/runtime/queries/scala/textobjects.scm b/runtime/queries/scala/textobjects.scm index 6e551c417..21286b3ef 100644 --- a/runtime/queries/scala/textobjects.scm +++ b/runtime/queries/scala/textobjects.scm @@ -1,12 +1,15 @@ ; Function queries (function_definition - body: (_) @function.inside) @function.around + body: (_) @function.inside) @function.around ; Does not include end marker -; Does not match block lambdas or Scala 3 braceless lambdas (lambda_expression (_) @function.inside) @function.around +; Scala 3 braceless lambda +(colon_argument + (_) @function.inside) @function.around + ; Class queries @@ -32,6 +35,9 @@ (parameters ((_) @parameter.inside . ","? @parameter.around) @parameter.around) +(class_parameters + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + (parameter_types ((_) @parameter.inside . ","? @parameter.around) @parameter.around) From 6e3ed7f0fa91a3adc01f33e182999f606b48ce6a Mon Sep 17 00:00:00 2001 From: zetashift Date: Sun, 4 Feb 2024 02:10:20 +0100 Subject: [PATCH 116/796] Update Unison tree-sitter grammar for type changes and add indent queries (#9505) * Update Unison tree-sitter grammar for type changes * Add indent queries for Unison * Improve Unison indent queries --- book/src/generated/lang-support.md | 2 +- languages.toml | 2 +- runtime/queries/unison/highlights.scm | 4 +--- runtime/queries/unison/indents.scm | 15 +++++++++++++++ 4 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 runtime/queries/unison/indents.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index e7d29cc73..ddd480536 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -174,7 +174,7 @@ | typescript | ✓ | ✓ | ✓ | `typescript-language-server` | | typst | ✓ | | | `typst-lsp` | | ungrammar | ✓ | | | | -| unison | ✓ | | | | +| unison | ✓ | | ✓ | | | uxntal | ✓ | | | | | v | ✓ | ✓ | ✓ | `v-analyzer` | | vala | ✓ | | | `vala-language-server` | diff --git a/languages.toml b/languages.toml index e601c20d9..11afea0ca 100644 --- a/languages.toml +++ b/languages.toml @@ -2940,7 +2940,7 @@ indent = { tab-width = 4, unit = " " } [[grammar]] name = "unison" -source = { git = "https://github.com/kylegoetz/tree-sitter-unison", rev = "aaec316774c8b50d367ec7cf26523aac5ef0cfc5" } +source = { git = "https://github.com/kylegoetz/tree-sitter-unison", rev = "1f505e2447fa876a87aee47ff3d70b9e141c744f" } [[language]] name = "todotxt" diff --git a/runtime/queries/unison/highlights.scm b/runtime/queries/unison/highlights.scm index d58285ed8..711779295 100644 --- a/runtime/queries/unison/highlights.scm +++ b/runtime/queries/unison/highlights.scm @@ -9,8 +9,6 @@ ;; Keywords [ (kw_forall) - (unique_kw) - (structural_kw) (type_kw) (kw_equals) (do) @@ -51,7 +49,7 @@ (blank_pattern) @variable.builtin ;; Types -(record_field name: (wordy_id) @variable.other.member type: (wordy_id) @type) +(record_field name: (wordy_id) @variable.other.member type: (_) @type) (type_constructor (type_name (wordy_id) @constructor)) (ability_declaration type_name: (wordy_id) @type type_arg: (wordy_id) @variable.parameter) (effect (wordy_id) @special) ;; NOTE: an effect is just like a type, but in signature we special case it diff --git a/runtime/queries/unison/indents.scm b/runtime/queries/unison/indents.scm new file mode 100644 index 000000000..6cb15517c --- /dev/null +++ b/runtime/queries/unison/indents.scm @@ -0,0 +1,15 @@ +[ + (term_definition) + (type_declaration) + (pattern) + (tuple_or_parenthesized) + (literal_list) + (tuple_pattern) + (function_application) + (exp_if) + (constructor) + (delay_block) + (type_signature) +] @indent + +[(kw_then) (kw_else) (cases)] @indent.always @extend From 5c567f31e236134c0e27dc689c8ad07ef5f9b5d1 Mon Sep 17 00:00:00 2001 From: Doug Kelkhoff <18220321+dgkf@users.noreply.github.com> Date: Sat, 3 Feb 2024 20:11:20 -0500 Subject: [PATCH 117/796] Adding two themes using only colors from 16-color terminal themes (#9477) * adding 16-color terminal themes * minor consistency update * minor consistency update * rename to be more consistent with other helix theme name conventions * fixing improper theme inherits name --- runtime/themes/term16_dark.toml | 80 ++++++++++++++++++++++++++++++ runtime/themes/term16_light.toml | 85 ++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 runtime/themes/term16_dark.toml create mode 100644 runtime/themes/term16_light.toml diff --git a/runtime/themes/term16_dark.toml b/runtime/themes/term16_dark.toml new file mode 100644 index 000000000..b34a4b4e2 --- /dev/null +++ b/runtime/themes/term16_dark.toml @@ -0,0 +1,80 @@ +# Author: dgkf + +"ui.background" = { } +"ui.background.separator" = { fg = "red" } +"ui.cursor" = { fg = "light-gray", modifiers = ["reversed"] } +"ui.cursor.match" = { fg = "light-yellow", modifiers = ["reversed"] } +"ui.cursor.primary" = { fg = "light-gray", modifiers = ["reversed"] } +"ui.cursor.secondary" = { fg = "gray", modifiers = ["reversed"] } +"ui.cursorline.primary" = { bg = "black" } +"ui.gutter" = { } +"ui.gutter.selected" = { bg = "black" } +"ui.help" = { fg = "white", bg = "black" } +"ui.linenr" = { fg = "gray", modifiers = ["bold"] } +"ui.linenr.selected" = { fg = "white", modifiers = ["bold"] } +"ui.menu" = { fg = "light-gray", bg = "gray" } +"ui.menu.selected" = { modifiers = ["reversed"] } +"ui.menu.scroll" = { fg = "light-blue" } +"ui.popup" = { bg = "black" } +"ui.selection" = { bg = "gray" } +"ui.statusline" = { fg = "light-gray", bg = "gray" } +"ui.statusline.inactive" = { bg = "black" } +"ui.virtual" = { bg = "black" } +"ui.virtual.indent-guide" = { fg = "gray" } +"ui.virtual.whitespace" = {} +"ui.virtual.wrap" = { fg = "gray" } +"ui.virtual.inlay-hint" = { fg = "light-gray", modifiers = ["dim", "italic"] } +"ui.virtual.inlay-hint.parameter" = { fg = "yellow", modifiers = ["dim", "italic"] } +"ui.virtual.inlay-hint.type" = { fg = "blue", modifiers = ["dim", "italic"] } +"ui.window" = { fg = "gray", modifiers = ["dim"] } + +"comment" = { fg = "light-gray", modifiers = ["italic", "dim"] } + +"attribute" = "light-yellow" +"constant" = { fg = "light-yellow", modifiers = ["bold", "dim"] } +"constant.numeric" = "light-yellow" +"constant.character.escape" = "light-cyan" +"constructor" = "light-blue" +"function" = "light-blue" +"function.macro" = "light-red" +"function.builtin" = { fg = "light-blue", modifiers = ["bold"] } +"tag" = { fg = "light-magenta", modifiers = ["dim"] } +"type" = "blue" +"type.builtin" = { fg = "blue", modifiers = ["bold"] } +"type.enum.variant" = { fg = "light-magenta", modifiers = ["dim"] } +"string" = "light-green" +"special" = "light-red" +"variable" = "white" +"variable.parameter" = { fg = "light-yellow", modifiers = ["italic"] } +"variable.other.member" = "light-green" +"keyword" = "light-magenta" +"keyword.control.exception" = "light-red" +"keyword.directive" = { fg = "light-yellow", modifiers = ["bold"] } +"keyword.operator" = { fg = "light-blue", modifiers = ["bold"] } +"label" = "light-green" +"namespace" = { fg = "blue", modifiers = ["dim"] } + +"markup.heading" = "light-blue" +"markup.list" = "light-red" +"markup.bold" = { fg = "light-cyan", modifiers = ["bold"] } +"markup.italic" = { fg = "light-blue", modifiers = ["italic"] } +"markup.strikethrough" = { modifiers = ["crossed_out"] } +"markup.link.url" = { fg = "magenta", modifiers = ["dim"] } +"markup.link.text" = "light-magenta" +"markup.quote" = "light-cyan" +"markup.raw" = "light-green" + +"diff.plus" = "light-green" +"diff.delta" = "light-yellow" +"diff.minus" = "light-red" + +"diagnostic.hint" = { underline = { color = "gray", style = "curl" } } +"diagnostic.info" = { underline = { color = "light-cyan", style = "curl" } } +"diagnostic.warning" = { underline = { color = "light-yellow", style = "curl" } } +"diagnostic.error" = { underline = { color = "light-red", style = "curl" } } + +"info" = "light-cyan" +"hint" = { fg = "light-gray", modifiers = ["dim"] } +"debug" = "white" +"warning" = "yellow" +"error" = "light-red" diff --git a/runtime/themes/term16_light.toml b/runtime/themes/term16_light.toml new file mode 100644 index 000000000..a02784b25 --- /dev/null +++ b/runtime/themes/term16_light.toml @@ -0,0 +1,85 @@ +# Author: dgkf +# Modified from base16_terminal, Author: NNB + +inherits = "term16_dark" + +"ui.background.separator" = "light-gray" +"ui.cursor" = { fg = "gray", modifiers = ["reversed"] } +"ui.cursor.match" = { fg = "yellow", modifiers = ["reversed"] } +"ui.cursor.primary" = { fg = "black", modifiers = ["reversed"] } +"ui.cursor.secondary" = { fg = "gray", modifiers = ["reversed"] } +"ui.cursorline.primary" = { bg = "white" } +"ui.cursorline.secondary" = { bg = "white" } +"ui.cursorcolumn.primary" = { bg = "white" } +"ui.cursorcolumn.secondary" = { bg = "white" } +"ui.gutter" = { } +"ui.gutter.selected" = { bg = "white" } +"ui.linenr" = { fg = "gray", modifiers = ["dim"] } +"ui.linenr.selected" = { fg = "black", modifiers = ["bold"] } +"ui.menu" = { bg = "light-gray" } +"ui.menu.selected" = { fg = "white", bg = "gray", modifiers = ["bold"] } +"ui.menu.scroll" = { fg = "light-blue" } +"ui.help" = { } +"ui.text" = { } +"ui.text.focus" = { } +"ui.popup" = { bg = "white" } +"ui.selection" = { bg = "light-gray" } +"ui.statusline" = { bg = "white" } +"ui.statusline.inactive" = { fg = "gray", modifiers = ["underlined"] } +"ui.statusline.insert" = { fg = "white", bg = "blue" } +"ui.statusline.select" = { fg = "white", bg = "magenta" } +"ui.virtual" = { fg = "light-gray" } +"ui.virtual.indent-guide" = { fg = "light-gray", modifiers = ["dim"] } +"ui.virtual.ruler" = { bg = "white" } +"ui.virtual.wrap" = { fg = "light-gray" } +"ui.window" = { fg = "gray", modifiers = ["dim"] } + +"comment" = { fg = "gray", modifiers = ["italic", "dim"] } + +"attribute" = "yellow" +"constant" = { fg = "yellow", modifiers = ["bold"] } +"constant.numeric" = { fg = "yellow", modifiers = ["bold"] } +"constant.character.escape" = "blue" +"constructor" = "blue" +"function" = "blue" +"function.builtin" = { fg = "blue", modifiers = ["bold"] } +"tag" = { fg = "magenta", modifiers = ["dim"] } +"type" = "blue" +"type.builtin" = { fg = "blue", modifiers = ["bold"] } +"type.enum.variant" = { fg = "magenta", modifiers = ["dim"] } +"string" = "green" +"special" = "red" +"variable" = { fg = "black", modifiers = ["dim"] } +"variable.parameter" = { fg = "red", modifiers = ["italic", "dim"] } +"variable.other.member" = "green" +"keyword" = "magenta" +"keyword.control.exception" = "red" +"keyword.directive" = { fg = "yellow", modifiers = ["bold"] } +"keyword.operator" = { fg = "blue", modifiers = ["bold"] } +"label" = "red" +"namespace" = { fg = "blue", modifiers = ["dim"] } + +"markup.heading" = { fg = "blue", modifiers = ["bold"] } +"markup.list" = "red" +"markup.bold" = { fg = "cyan", modifiers = ["bold"] } +"markup.italic" = { fg = "blue", modifiers = ["italic"] } +"markup.strikethrough" = { modifiers = ["crossed_out"] } +"markup.link.url" = { fg = "magenta", modifiers = ["dim"] } +"markup.link.text" = { fg = "magenta", modifiers = ["bold"] } +"markup.quote" = "cyan" +"markup.raw" = "blue" + +"diff.plus" = "green" +"diff.delta" = "yellow" +"diff.minus" = "red" + +"diagnostic.hint" = { underline = { color = "cyan", style = "curl" } } +"diagnostic.info" = { underline = { color = "blue", style = "curl" } } +"diagnostic.warning" = { underline = { color = "yellow", style = "curl" } } +"diagnostic.error" = { underline = { color = "red", style = "curl" } } + +"hint" = "cyan" +"info" = "blue" +"debug" = "light-yellow" +"warning" = "yellow" +"error" = "red" From 0975d9c5e7e4caf08d1c1204f09ab6d2f718105d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Dzivjak?= Date: Tue, 6 Feb 2024 00:55:56 +0000 Subject: [PATCH 118/796] feat(languages): golang comments and numeric types (#9525) --- runtime/queries/go/highlights.scm | 32 ++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/runtime/queries/go/highlights.scm b/runtime/queries/go/highlights.scm index fba2df99e..8eed12afb 100644 --- a/runtime/queries/go/highlights.scm +++ b/runtime/queries/go/highlights.scm @@ -183,9 +183,12 @@ [ (int_literal) +] @constant.numeric.integer + +[ (float_literal) (imaginary_literal) -] @constant.numeric.integer +] @constant.numeric.float [ (true) @@ -197,4 +200,31 @@ (iota) ] @constant.builtin +; Comments + (comment) @comment + +; Doc Comments +(source_file + . + (comment)+ @comment.block.documentation) + +(source_file + (comment)+ @comment.block.documentation + . + (const_declaration)) + +(source_file + (comment)+ @comment.block.documentation + . + (function_declaration)) + +(source_file + (comment)+ @comment.block.documentation + . + (type_declaration)) + +(source_file + (comment)+ @comment.block.documentation + . + (var_declaration)) From 1d87c6a999d389011cf8a5849a6096d981c93e33 Mon Sep 17 00:00:00 2001 From: eh Date: Tue, 6 Feb 2024 01:13:20 -0500 Subject: [PATCH 119/796] Update colors used for zed themes (#9544) Official colors used is now publically available: https://github.com/zed-industries/zed/blob/main/assets/themes/one/one.json Modified the theme to more accurately reflect the actual colors being used. Co-authored-by: e4 --- runtime/themes/zed_onedark.toml | 83 ++++++++++++++++---------------- runtime/themes/zed_onelight.toml | 39 +++++++++------ 2 files changed, 64 insertions(+), 58 deletions(-) diff --git a/runtime/themes/zed_onedark.toml b/runtime/themes/zed_onedark.toml index 5fda576f3..300b3c9c5 100644 --- a/runtime/themes/zed_onedark.toml +++ b/runtime/themes/zed_onedark.toml @@ -4,22 +4,20 @@ "comment" = { fg = "light-gray", modifiers = ["italic"] } "constant" = { fg = "yellow" } "constant.numeric" = { fg = "orange" } -"constant.builtin" = { fg = "orange" } +"constant.builtin" = { fg = "yellow" } "constant.builtin.boolean" = { fg = "yellow" } -"constant.character.escape" = { fg = "orange" } +"constant.character.escape" = { fg = "yellow" } "constructor" = { fg = "blue" } "function" = { fg = "blue" } "function.builtin" = { fg = "blue" } -"function.macro" = { fg = "purple" } +"function.method" = { fg = "blue" } +"function.macro" = { fg = "blue" } "keyword" = { fg = "purple" } -"keyword.control" = { fg = "purple" } -"keyword.control.import" = { fg = "purple" } -"keyword.directive" = { fg = "purple" } "label" = { fg = "ui-text" } "namespace" = { fg = "ui-text" } "operator" = { fg = "ui-text" } -"keyword.operator" = { fg = "purple" } -"special" = { fg = "blue" } +"puncuation" = { fg = "ui-text" } +"special" = { fg = "ui-text" } "string" = { fg = "green" } "type" = { fg = "cyan" } "variable.builtin" = { fg = "orange" } @@ -28,41 +26,43 @@ "markup.heading" = { fg = "red" } "markup.raw.inline" = { fg = "green" } -"markup.bold" = { fg = "orange", modifiers = ["bold"] } +"markup.bold" = { fg = "yellow", modifiers = ["bold"] } "markup.italic" = { fg = "purple", modifiers = ["italic"] } "markup.strikethrough" = { modifiers = ["crossed_out"] } "markup.list" = { fg = "red" } "markup.quote" = { fg = "yellow" } -"markup.link.url" = { fg = "cyan", modifiers = ["underlined"]} +"markup.link.url" = { fg = "cyan", modifiers = ["underlined"] } "markup.link.text" = { fg = "purple" } "diff.plus" = "green" -"diff.delta" = "orange" +"diff.delta" = "yellow" "diff.minus" = "red" -"diagnostic.info".underline = { color = "blue", style = "curl" } -"diagnostic.hint".underline = { color = "green", style = "curl" } -"diagnostic.warning".underline = { color = "yellow", style = "curl" } -"diagnostic.error".underline = { color = "red", style = "curl" } +"diagnostic.info".underline = { color = "blue", style = "curl" } +"diagnostic.hint".underline = { color = "green", style = "curl" } +"diagnostic.warning".underline = { color = "yellow", style = "curl" } +"diagnostic.error".underline = { color = "red", style = "curl" } "info" = { fg = "blue", modifiers = ["bold"] } "hint" = { fg = "green", modifiers = ["bold"] } "warning" = { fg = "yellow", modifiers = ["bold"] } "error" = { fg = "red", modifiers = ["bold"] } "ui.background" = { bg = "ui-text-reversed" } +"ui.gutter" = { bg = "gray" } "ui.virtual" = { fg = "faint-gray" } -"ui.virtual.indent-guide" = { fg = "faint-gray" } +"ui.virtual.indent-guidje" = { fg = "faint-gray" } "ui.virtual.whitespace" = { fg = "light-gray" } "ui.virtual.ruler" = { bg = "gray" } -"ui.virtual.inlay-hint" = { fg = "light-gray" } +"ui.virtual.inlay-hint" = { fg = "blue-gray", modifiers = ["bold"] } "ui.cursor" = { fg = "white", modifiers = ["reversed"] } "ui.cursor.primary" = { fg = "white", modifiers = ["reversed"] } -"ui.cursor.match" = { fg = "blue", modifiers = ["underlined"]} +"ui.cursor.match" = { fg = "blue", modifiers = ["underlined"] } +"ui.cursor.insert" = { fg = "dark-blue" } "ui.selection" = { bg = "faint-gray" } -"ui.selection.primary" = { bg = "gray" } -"ui.cursorline.primary" = { bg = "light-black" } +"ui.selection.primary" = { bg = "#293b5bff" } +"ui.cursorline.primary" = { bg = "black" } "ui.highlight" = { bg = "gray" } "ui.highlight.frameline" = { bg = "#97202a" } @@ -70,14 +70,14 @@ "ui.linenr" = { fg = "linenr" } "ui.linenr.selected" = { fg = "ui-text" } -"ui.statusline" = { fg = "white", bg = "light-black" } -"ui.statusline.inactive" = { fg = "light-gray", bg = "light-black" } -"ui.statusline.normal" = { fg = "light-black", bg = "blue" } -"ui.statusline.insert" = { fg = "light-black", bg = "green" } -"ui.statusline.select" = { fg = "light-black", bg = "purple" } +"ui.statusline" = { fg = "white", bg = "gray" } +"ui.statusline.inactive" = { fg = "light-gray", bg = "black" } +"ui.statusline.normal" = { fg = "black", bg = "blue" } +"ui.statusline.insert" = { fg = "black", bg = "green" } +"ui.statusline.select" = { fg = "black", bg = "purple" } "ui.text" = { fg = "ui-text" } -"ui.text.focus" = { fg = "white", bg = "light-black", modifiers = ["bold"] } +"ui.text.focus" = { fg = "white", bg = "black", modifiers = ["bold"] } "ui.help" = { fg = "white", bg = "gray" } "ui.popup" = { bg = "gray" } @@ -89,22 +89,21 @@ "ui.debug" = { fg = "red" } [palette] - -yellow = "#dac18c" -blue = "#7ca8dd" -red = "#bd7476" -purple = "#9d74b9" -green = "#a0b783" -orange = "#b4926e" -cyan = "#7eb2be" -light-black = "#2e323a" -gray = "#363f4c" -light-gray = "#5c606b" +yellow = "#dfc184ff" +orange = "#bf956aff" +blue = "#73ade9ff" +blue-gray = "#5a6f89ff" +red = "#d07277ff" +purple = "#b477cfff" +green = "#a1c181ff" +cyan = "#6eb4bfff" +gray = "#2f343ebf" +light-gray = "#5d636fff" faint-gray = "#3B4048" -linenr = "#4B5263" +linenr = "#5d636fff" -white = "#a8adb7" -black = "#292c33" +white = "#c8ccd4ff" +black = "#282c33ff" # black and white are used for a lot of the UI text -ui-text = "#a8adb7" #white -ui-text-reversed = "#292c33" #black +ui-text = "#c8ccd4ff" #white +ui-text-reversed = "#282c33ff" #black diff --git a/runtime/themes/zed_onelight.toml b/runtime/themes/zed_onelight.toml index 2b54cd5be..f3da11676 100644 --- a/runtime/themes/zed_onelight.toml +++ b/runtime/themes/zed_onelight.toml @@ -16,9 +16,13 @@ inherits = "zed_onedark" "ui.cursor" = { fg = "dark-blue", modifiers = ["reversed"] } "ui.cursor.primary" = { fg = "dark-blue", modifiers = ["reversed"] } +"ui.cursor.insert" = { fg = "dark-blue" } +"ui.selection.primary" = { bg = "blue-gray" } "ui.cursorline.primary" = { bg = "faint-gray" } +"ui.virtual.inlay-hint" = { fg = "violet", modifiers = ["bold"] } + "ui.statusline" = { fg = "black", bg = "gray" } "ui.statusline.inactive" = { fg = "white", bg = "light-black" } "ui.statusline.normal" = { fg = "white", bg = "blue" } @@ -33,23 +37,26 @@ inherits = "zed_onedark" [palette] -yellow = "#dac18c" -blue = "#5185b5" -red = "#bd7476" -dark-blue = "#607bdb" -orange = "#ca7667" -purple = "#a160ac" -green = "#739d60" -gold = "#a8763c" -cyan = "#4b80b2" +yellow = "#dabb7e" +red = "#d36151ff" +orange = "#d3604fff" +blue = "#5b79e3ff" +dark-blue = "#4a62db" +purple = "#a449abff" +violet = "#9294beff" +green = "#649f57ff" +gold = "#ad6e25ff" +cyan = "#3882b7ff" light-black = "#2e323a" -gray = "#dcdcdd" +# gray = "#dcdcdd" +gray = "#eaeaed" dark-gray = "#ebebec" -light-gray = "#a6a6aa" +light-gray = "#a2a3a7ff" +blue-gray = "#d9dcea" faint-gray = "#efefef" -linenr = "#4B5263" +linenr = "#b0b1b3" -black = "#404248" -white = "#fafafa" -ui-text = "#404248" -ui-text-reversed = "#fafafa" +black = "#383a41ff" +white = "#fafafaff" +ui-text = "#383a41ff" +ui-text-reversed = "#fafafaff" From a37af2dcbf57b6b800946115b4156fefc199ce7a Mon Sep 17 00:00:00 2001 From: Diego Date: Tue, 6 Feb 2024 14:45:57 +0100 Subject: [PATCH 120/796] fix division by zero when prompt completion area is too small (#9524) --- helix-term/src/ui/prompt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs index 702a6e671..3764bba60 100644 --- a/helix-term/src/ui/prompt.rs +++ b/helix-term/src/ui/prompt.rs @@ -393,7 +393,7 @@ impl Prompt { height, ); - if !self.completion.is_empty() { + if completion_area.height > 0 && !self.completion.is_empty() { let area = completion_area; let background = theme.get("ui.menu"); From 28a39e6efc79b9aac53d49ac8325e8e32fb127e2 Mon Sep 17 00:00:00 2001 From: eh Date: Tue, 6 Feb 2024 11:47:13 -0500 Subject: [PATCH 121/796] Fix cursorline Zed OneDark (#9549) Co-authored-by: e4 --- runtime/themes/zed_onedark.toml | 6 +++--- runtime/themes/zed_onelight.toml | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/runtime/themes/zed_onedark.toml b/runtime/themes/zed_onedark.toml index 300b3c9c5..7ac1e73cc 100644 --- a/runtime/themes/zed_onedark.toml +++ b/runtime/themes/zed_onedark.toml @@ -50,7 +50,7 @@ "ui.background" = { bg = "ui-text-reversed" } "ui.gutter" = { bg = "gray" } "ui.virtual" = { fg = "faint-gray" } -"ui.virtual.indent-guidje" = { fg = "faint-gray" } +"ui.virtual.indent-guide" = { fg = "faint-gray" } "ui.virtual.whitespace" = { fg = "light-gray" } "ui.virtual.ruler" = { bg = "gray" } "ui.virtual.inlay-hint" = { fg = "blue-gray", modifiers = ["bold"] } @@ -62,7 +62,7 @@ "ui.selection" = { bg = "faint-gray" } "ui.selection.primary" = { bg = "#293b5bff" } -"ui.cursorline.primary" = { bg = "black" } +"ui.cursorline.primary" = { bg = "gray" } "ui.highlight" = { bg = "gray" } "ui.highlight.frameline" = { bg = "#97202a" } @@ -77,7 +77,7 @@ "ui.statusline.select" = { fg = "black", bg = "purple" } "ui.text" = { fg = "ui-text" } -"ui.text.focus" = { fg = "white", bg = "black", modifiers = ["bold"] } +"ui.text.focus" = { fg = "white", bg = "gray", modifiers = ["bold"] } "ui.help" = { fg = "white", bg = "gray" } "ui.popup" = { bg = "gray" } diff --git a/runtime/themes/zed_onelight.toml b/runtime/themes/zed_onelight.toml index f3da11676..086fce34b 100644 --- a/runtime/themes/zed_onelight.toml +++ b/runtime/themes/zed_onelight.toml @@ -36,7 +36,6 @@ inherits = "zed_onedark" "ui.window" = { fg = "dark-gray" } [palette] - yellow = "#dabb7e" red = "#d36151ff" orange = "#d3604fff" From c64a0e615b3b8e182c02efdb7c67162ec269619f Mon Sep 17 00:00:00 2001 From: Bogdan Agica Date: Tue, 6 Feb 2024 23:50:14 -0800 Subject: [PATCH 122/796] Revert "build(deps): bump cc from 1.0.83 to 1.0.84 (#8809)" (#9548) --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cb815ee3e..3871768f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -145,9 +145,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "cc" -version = "1.0.84" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f8e7c90afad890484a21653d08b6e209ae34770fb5ee298f9c699fcc1e5c856" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "libc", ] From 72c508de248e363d8a3481ad9d028320361df1fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 17:34:49 +0900 Subject: [PATCH 123/796] build(deps): bump libc from 0.2.152 to 0.2.153 (#9541) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- helix-term/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3871768f8..d25bd373c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1596,9 +1596,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.152" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 9a7162ac1..295782976 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -72,7 +72,7 @@ grep-searcher = "0.1.13" [target.'cfg(not(windows))'.dependencies] # https://github.com/vorner/signal-hook/issues/100 signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] } -libc = "0.2.152" +libc = "0.2.153" [target.'cfg(target_os = "macos")'.dependencies] crossterm = { version = "0.27", features = ["event-stream", "use-dev-tty"] } From 630d91168a7aa2a3da72a55266dee663565f6edf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 23:56:26 +0000 Subject: [PATCH 124/796] build(deps): bump pulldown-cmark from 0.9.6 to 0.10.0 Bumps [pulldown-cmark](https://github.com/raphlinus/pulldown-cmark) from 0.9.6 to 0.10.0. - [Release notes](https://github.com/raphlinus/pulldown-cmark/releases) - [Commits](https://github.com/raphlinus/pulldown-cmark/compare/v0.9.6...v0.10.0) --- updated-dependencies: - dependency-name: pulldown-cmark dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- helix-term/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d25bd373c..73e548ae5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1839,9 +1839,9 @@ checksum = "744a264d26b88a6a7e37cbad97953fa233b94d585236310bcbc88474b4092d79" [[package]] name = "pulldown-cmark" -version = "0.9.6" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" +checksum = "dce76ce678ffc8e5675b22aa1405de0b7037e2fdf8913fea40d1926c6fe1e6e7" dependencies = [ "bitflags 2.4.2", "memchr", diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 295782976..a0d6754d0 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -52,7 +52,7 @@ log = "0.4" nucleo.workspace = true ignore = "0.4" # markdown doc rendering -pulldown-cmark = { version = "0.9", default-features = false } +pulldown-cmark = { version = "0.10", default-features = false } # file type detection content_inspector = "0.2.4" From bbcc89241fd63b460ea7b1661a550f5b9eacba4c Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Mon, 5 Feb 2024 19:32:25 -0500 Subject: [PATCH 125/796] Fix pulldown_cmark breaking changes to tag types * Tags and TagEnd are now separate enums since . * The `Tag::Heading` member has been changed from a tuple variant to a struct variant. --- helix-term/src/ui/markdown.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/helix-term/src/ui/markdown.rs b/helix-term/src/ui/markdown.rs index 4d0c0d4a5..5cf530ad8 100644 --- a/helix-term/src/ui/markdown.rs +++ b/helix-term/src/ui/markdown.rs @@ -6,7 +6,7 @@ use tui::{ use std::sync::Arc; -use pulldown_cmark::{CodeBlockKind, Event, HeadingLevel, Options, Parser, Tag}; +use pulldown_cmark::{CodeBlockKind, Event, HeadingLevel, Options, Parser, Tag, TagEnd}; use helix_core::{ syntax::{self, HighlightEvent, InjectionLanguageMarker, Syntax}, @@ -209,7 +209,7 @@ impl Markdown { list_stack.push(list); } - Event::End(Tag::List(_)) => { + Event::End(TagEnd::List(_)) => { list_stack.pop(); // whenever top-level list closes, empty line @@ -249,7 +249,10 @@ impl Markdown { Event::End(tag) => { tags.pop(); match tag { - Tag::Heading(_, _, _) | Tag::Paragraph | Tag::CodeBlock(_) | Tag::Item => { + TagEnd::Heading(_) + | TagEnd::Paragraph + | TagEnd::CodeBlock + | TagEnd::Item => { push_line(&mut spans, &mut lines); } _ => (), @@ -257,7 +260,7 @@ impl Markdown { // whenever heading, code block or paragraph closes, empty line match tag { - Tag::Heading(_, _, _) | Tag::Paragraph | Tag::CodeBlock(_) => { + TagEnd::Heading(_) | TagEnd::Paragraph | TagEnd::CodeBlock => { lines.push(Spans::default()); } _ => (), @@ -279,7 +282,7 @@ impl Markdown { lines.extend(tui_text.lines.into_iter()); } else { let style = match tags.last() { - Some(Tag::Heading(level, ..)) => match level { + Some(Tag::Heading { level, .. }) => match level { HeadingLevel::H1 => heading_styles[0], HeadingLevel::H2 => heading_styles[1], HeadingLevel::H3 => heading_styles[2], From a1272bdb17a63361342a318982e46129d558743c Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 7 Feb 2024 19:36:29 +0100 Subject: [PATCH 126/796] slint: Update treesitter parser and queries (#9551) * slint: Update treesitter parser and queries * slint: Port over suggestions from nvim review --- book/src/generated/lang-support.md | 2 +- languages.toml | 2 +- runtime/queries/rust/injections.scm | 20 +- runtime/queries/slint/highlights.scm | 339 ++++++++++++++++---------- runtime/queries/slint/indents.scm | 17 +- runtime/queries/slint/locals.scm | 9 +- runtime/queries/slint/textobjects.scm | 35 +++ 7 files changed, 278 insertions(+), 146 deletions(-) create mode 100644 runtime/queries/slint/textobjects.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index ddd480536..279acc4fb 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -148,7 +148,7 @@ | scala | ✓ | ✓ | ✓ | `metals` | | scheme | ✓ | | ✓ | | | scss | ✓ | | | `vscode-css-language-server` | -| slint | ✓ | | ✓ | `slint-lsp` | +| slint | ✓ | ✓ | ✓ | `slint-lsp` | | smali | ✓ | | ✓ | | | smithy | ✓ | | | `cs` | | sml | ✓ | | | | diff --git a/languages.toml b/languages.toml index 11afea0ca..bad221299 100644 --- a/languages.toml +++ b/languages.toml @@ -2139,7 +2139,7 @@ language-servers = [ "slint-lsp" ] [[grammar]] name = "slint" -source = { git = "https://github.com/jrmoulton/tree-sitter-slint", rev = "00c8a2d3645766f68c0d0460086c0a994e5b0d85" } +source = { git = "https://github.com/slint-ui/tree-sitter-slint", rev = "15618215b79b9db08f824a5c97a12d073dcc1c00" } [[language]] name = "task" diff --git a/runtime/queries/rust/injections.scm b/runtime/queries/rust/injections.scm index ae9e587fd..b05b9d975 100644 --- a/runtime/queries/rust/injections.scm +++ b/runtime/queries/rust/injections.scm @@ -2,11 +2,29 @@ (#set! injection.language "comment")) ((macro_invocation - macro: (identifier) @_html (#eq? @_html "html") + macro: + [ + (scoped_identifier + name: (_) @_macro_name) + (identifier) @_macro_name + ] (token_tree) @injection.content) + (#eq? @_macro_name "html") (#set! injection.language "html") (#set! injection.include-children)) +((macro_invocation + macro: + [ + (scoped_identifier + name: (_) @_macro_name) + (identifier) @_macro_name + ] + (token_tree) @injection.content) + (#eq? @_macro_name "slint") + (#set! injection.language "slint") + (#set! injection.include-children)) + ((macro_invocation (token_tree) @injection.content) (#set! injection.language "rust") diff --git a/runtime/queries/slint/highlights.scm b/runtime/queries/slint/highlights.scm index c0ef3dd88..06d82a413 100644 --- a/runtime/queries/slint/highlights.scm +++ b/runtime/queries/slint/highlights.scm @@ -1,122 +1,109 @@ +(comment) @comment + +; Different types: +(string_value) @string +(bool_value) @constant.builtin.boolean + +; Constants + +(escape_sequence) @constant.character.escape + +(color_value) @constant -(identifier) @variable [ - (type_identifier) - (units) -]@type + (children_identifier) + (easing_kind_identifier) +] @constant.builtin -(array_literal - (identifier) @type) +[ + (int_value) + (physical_length_value) +] @constant.numeric.integer -(function_identifier) @function [ - (image_macro) - (children_macro) - (radial_grad_macro) - (linear_grad_macro) -] @function.macro + (float_value) + (percent_value) + (length_value) + (duration_value) + (angle_value) + (relative_font_size_value) +] @constant.numeric.float -(call_expression - function: (identifier) @function) -(call_expression - function: (field_expression - field: (identifier) @function)) +(purity) @keyword.storage.modifier -(vis) @keyword.control.import +(function_visibility) @keyword.storage.modifier -(transition_statement state: (identifier) @variable.other.member) -(state_expression state: (identifier) @variable.other.member) -(struct_block_definition field: (identifier) @variable.other.member) -(assign_property (identifier) @attribute) +(property_visibility) @keyword.storage.modifier -(comment) @comment +(builtin_type_identifier) @type.builtin -(string_literal) @string -(int_literal) @constant.numeric.integer -(float_literal) @constant.numeric.float +(reference_identifier) @variable.builtin -[ - "in" - "in-out" - "for" -] @keyword.control.repeat +(type + [ + (type_list) + (user_type_identifier) + (anon_struct_block) + ]) @type -[ - "import" - "export" - "from" -] @keyword.control.import +(user_type_identifier) @type -[ - "if" - "else" - "when" -] @keyword.control.conditional +; Functions and callbacks +(argument) @variable.parameter -[ - "struct" - "property" -] @keyword.storage.type +(function_call + name: (_) @function.call) -[ - "global" -] @keyword.storage.modifier +; definitions +(callback + name: (_) @function) +(callback_alias + name: (_) @function) -[ - "root" - "parent" - "duration" - "easing" -] @variable.builtin +(callback_event + name: (simple_identifier) @function.call) +(enum_definition + name: (_) @type.enum) -[ - "callback" - "animate" - "states" - "out" - "transitions" - "component" - "inherits" -] @keyword +(function_definition + name: (_) @function) -[ - "black" - "transparent" - "blue" - "ease" - "ease_in" - "ease-in" - "ease_in_out" - "ease-in-out" - "ease_out" - "ease-out" - "end" - "green" - "red" - "start" - "yellow" - "white" - "gray" - ] @constant.builtin +(struct_definition + name: (_) @type) + +(typed_identifier + type: (_) @type) + +; Operators +(binary_expression + op: (_) @operator) + +(unary_expression + op: (_) @operator) [ - "true" - "false" -] @constant.builtin.boolean + (comparison_operator) + (mult_prec_operator) + (add_prec_operator) + (unary_prec_operator) + (assignment_prec_operator) +] @operator -"@" @keyword +[ + ":=" + "=>" + "->" + "<=>" +] @operator -; ; Punctuation [ - "," - "." ";" - ":" + "." + "," ] @punctuation.delimiter -; ; Brackets [ "(" ")" @@ -126,46 +113,136 @@ "}" ] @punctuation.bracket -(define_property ["<" ">"] @punctuation.bracket) +(property + [ + "<" + ">" + ] @punctuation.bracket) -[ - "angle" - "bool" - "brush" - "color" - "duration" - "easing" - "float" - "image" - "int" - "length" - "percent" - "physical-length" - "physical_length" - "string" -] @type.builtin +; Properties, constants and variables +(component + id: (simple_identifier) @constant) + +(property + name: (simple_identifier) @variable) + +(binding_alias + name: (simple_identifier) @variable) + +(binding + name: (simple_identifier) @variable) + +(struct_block + (simple_identifier) @variable.other.member) + +(anon_struct_block + (simple_identifier) @variable.other.member) + +(property_assignment + property: (simple_identifier) @variable) + +(states_definition + name: (simple_identifier) @variable) + +(callback + name: (simple_identifier) @variable) + +(typed_identifier + name: (_) @variable) + +(simple_indexed_identifier + (simple_identifier) @variable) + +(expression + (simple_identifier) @variable) +; Attributes [ - ":=" - "<=>" - "!" - "-" - "+" - "*" - "/" - "&&" - "||" - ">" - "<" - ">=" - "<=" - "=" - ":" - "+=" - "-=" - "*=" - "/=" - "?" - "=>" ] @operator - -(ternary_expression [":" "?"] @keyword.control.conditional) \ No newline at end of file + (linear_gradient_identifier) + (radial_gradient_identifier) + (radial_gradient_kind) +] @attribute + +(image_call + "@image-url" @attribute) + +(tr + "@tr" @attribute) + +; Keywords +(animate_option_identifier) @keyword + +(export) @keyword.control.import + +(if_statement + "if" @keyword.control.conditional) + +(if_expr + [ + "if" + "else" + ] @keyword.control.conditional) + +(ternary_expression + [ + "?" + ":" + ] @keyword.control.conditional) + +(animate_statement + "animate" @keyword) + +(callback + "callback" @keyword.function) + +(component_definition + [ + "component" + "inherits" + ] @keyword.storage.type) + +(enum_definition + "enum" @keyword.storage.type) + +(for_loop + [ + "for" + "in" + ] @keyword.control.repeat) + +(function_definition + "function" @keyword.function) + +(global_definition + "global" @keyword.storage.type) + +(imperative_block + "return" @keyword.control.return) + +(import_statement + [ + "import" + "from" + ] @keyword.control.import) + +(import_type + "as" @keyword.control.import) + +(property + "property" @keyword.storage.type) + +(states_definition + [ + "states" + "when" + ] @keyword) + +(struct_definition + "struct" @keyword.storage.type) + +(transitions_definition + [ + "transitions" + "in" + "out" + ] @keyword) diff --git a/runtime/queries/slint/indents.scm b/runtime/queries/slint/indents.scm index 4b5ce41b8..189f8a0e5 100644 --- a/runtime/queries/slint/indents.scm +++ b/runtime/queries/slint/indents.scm @@ -1,12 +1,11 @@ [ - (comp_body) - (state_statement) - (transition_statement) - (handler_body) - (consequence_body) - (global_single) + (anon_struct_block) + (assignment_block) + (block) + (enum_block) + (global_block) + (imperative_block) + (struct_block) ] @indent -[ - "}" -] @outdent +"}" @outdent diff --git a/runtime/queries/slint/locals.scm b/runtime/queries/slint/locals.scm index a115f0c69..06601b05d 100644 --- a/runtime/queries/slint/locals.scm +++ b/runtime/queries/slint/locals.scm @@ -1,3 +1,6 @@ -; locals.scm - -(component_item) @local.scope +[ + (component) + (component_definition) + (function_definition) + (imperative_block) +] @local.scope diff --git a/runtime/queries/slint/textobjects.scm b/runtime/queries/slint/textobjects.scm new file mode 100644 index 000000000..7e2f36096 --- /dev/null +++ b/runtime/queries/slint/textobjects.scm @@ -0,0 +1,35 @@ +(function_definition + (imperative_block) @funtion.inside) @function.around + +(callback_event + (imperative_block) @function.inside) @function.around + +(property + (imperative_block) @function.inside) @function.around + +(struct_definition + (struct_block) @class.inside) @class.around + +(enum_definition + (enum_block) @class.inside) @class.around + +(global_definition + (global_block) @class.inside) @class.around + +(component_definition + (block) @class.inside) @class.around + +(component_definition + (block) @class.inside) @class.around + +(comment) @comment.around + +(typed_identifier + name: (_) @parameter.inside) @parameter.around + +(callback + arguments: (_) @parameter.inside) + +(string_value + "\"" . (_) @text.inside . "\"") @text.around + From f8e2d822ba3716aa986acee003f3093268344160 Mon Sep 17 00:00:00 2001 From: ath3 <45574139+ath3@users.noreply.github.com> Date: Fri, 9 Feb 2024 03:24:45 +0100 Subject: [PATCH 127/796] Fix scroll track (#9508) --- helix-term/src/ui/menu.rs | 1 + helix-term/src/ui/popup.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/helix-term/src/ui/menu.rs b/helix-term/src/ui/menu.rs index 64127e3af..c0e60b33e 100644 --- a/helix-term/src/ui/menu.rs +++ b/helix-term/src/ui/menu.rs @@ -427,6 +427,7 @@ impl Component for Menu { cell.set_fg(scroll_style.fg.unwrap_or(helix_view::theme::Color::Reset)); } else if !render_borders { // Draw scroll track + cell.set_symbol(half_block); cell.set_fg(scroll_style.bg.unwrap_or(helix_view::theme::Color::Reset)); } } diff --git a/helix-term/src/ui/popup.rs b/helix-term/src/ui/popup.rs index 7a6ffe9dd..b38b8b6e3 100644 --- a/helix-term/src/ui/popup.rs +++ b/helix-term/src/ui/popup.rs @@ -303,6 +303,7 @@ impl Component for Popup { cell.set_fg(scroll_style.fg.unwrap_or(helix_view::theme::Color::Reset)); } else if !render_borders { // Draw scroll track + cell.set_symbol(half_block); cell.set_fg(scroll_style.bg.unwrap_or(helix_view::theme::Color::Reset)); } } From d137a08231515b0a1694a3841081331e529ce953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matou=C5=A1=20Dzivjak?= Date: Fri, 9 Feb 2024 10:44:46 +0000 Subject: [PATCH 128/796] feat(languages): pkl (#9515) * feat(languages): pkl Add [pkl](https://github.com/apple/pkl) language. Official documentation: https://pkl-lang.org/ * remove branch indent --- book/src/generated/lang-support.md | 1 + languages.toml | 11 ++ runtime/queries/pkl/highlights.scm | 179 +++++++++++++++++++++++++++++ runtime/queries/pkl/indents.scm | 23 ++++ runtime/queries/pkl/injections.scm | 30 +++++ 5 files changed, 244 insertions(+) create mode 100644 runtime/queries/pkl/highlights.scm create mode 100644 runtime/queries/pkl/indents.scm create mode 100644 runtime/queries/pkl/injections.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 279acc4fb..c91b9ae11 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -123,6 +123,7 @@ | pem | ✓ | | | | | perl | ✓ | ✓ | ✓ | `perlnavigator` | | php | ✓ | ✓ | ✓ | `intelephense` | +| pkl | ✓ | | ✓ | | | po | ✓ | ✓ | | | | pod | ✓ | | | | | ponylang | ✓ | ✓ | ✓ | | diff --git a/languages.toml b/languages.toml index bad221299..30b411571 100644 --- a/languages.toml +++ b/languages.toml @@ -3061,3 +3061,14 @@ indent = { tab-width = 4, unit = " " } name = "tact" source = { git = "https://github.com/tact-lang/tree-sitter-tact", rev = "ec57ab29c86d632639726631fb2bb178d23e1c91" } +[[language]] +name = "pkl" +scope = "source.pkl" +injection-regex = "pkl" +file-types = ["pkl", "pcf"] +comment-token = "//" +indent = { tab-width = 2, unit = " " } + +[[grammar]] +name = "pkl" +source = { git = "https://github.com/apple/tree-sitter-pkl", rev = "c03f04a313b712f8ab00a2d862c10b37318699ae" } diff --git a/runtime/queries/pkl/highlights.scm b/runtime/queries/pkl/highlights.scm new file mode 100644 index 000000000..501c94859 --- /dev/null +++ b/runtime/queries/pkl/highlights.scm @@ -0,0 +1,179 @@ +; Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; https://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +; this definition is imprecise in that +; * any qualified or unqualified call to a method named "Regex" is considered a regex +; * string delimiters are considered part of the regex + +; Operators + +[ + "??" + "@" + "=" + "<" + ">" + "!" + "==" + "!=" + "<=" + ">=" + "&&" + "||" + "+" + "-" + "**" + "*" + "/" + "~/" + "%" + "|>" +] @keyword.operator + +[ + "?" + "|" + "->" +] @operator.type + +[ + "," + ":" + "." + "?." +] @punctuation.delimiter + +[ + "(" + ")" + "]" + "{" + "}" + ; "[" @punctuation.bracket TODO: FIGURE OUT HOW TO REFER TO CUSTOM TOKENS +] @punctuation.bracket + +; Keywords + +[ + "abstract" + "amends" + "as" + "class" + "extends" + "external" + "function" + "hidden" + "import" + "import*" + "in" + "let" + "local" + "module" + "new" + "open" + "out" + "typealias" + "when" +] @keyword + +[ + "if" + "is" + "else" +] @keyword.control.conditional + +[ + "for" +] @keyword.control.repeat + +(importExpr "import" @keyword.control.import) +(importGlobExpr "import*" @keyword.control.import) + +"read" @function.builtin +"read?" @function.builtin +"read*" @function.builtin +"throw" @function.builtin +"trace" @function.builtin + +(moduleExpr "module" @type.builtin) +"nothing" @type.builtin +"unknown" @type.builtin + +(outerExpr) @variable.builtin +"super" @variable.builtin +(thisExpr) @variable.builtin + +[ + (falseLiteral) + (nullLiteral) + (trueLiteral) +] @constant.builtin + +; Literals + +(stringConstant) @string +(slStringLiteral) @string +(mlStringLiteral) @string + +(escapeSequence) @constent.character.escape + +(intLiteral) @constant.numeric.integer +(floatLiteral) @constant.numeric.float + +(interpolationExpr + "\\(" @punctuation.special + ")" @punctuation.special) @embedded + +(interpolationExpr + "\\#(" @punctuation.special + ")" @punctuation.special) @embedded + +(interpolationExpr + "\\##(" @punctuation.special + ")" @punctuation.special) @embedded + +(lineComment) @comment +(blockComment) @comment +(docComment) @comment + +; Identifiers + +(classProperty (identifier) @variable.other.member) +(objectProperty (identifier) @variable.other.member) + +(parameterList (typedIdentifier (identifier) @variable.parameter)) +(objectBodyParameters (typedIdentifier (identifier) @variable.parameter)) + +(identifier) @variable + +; Method definitions + +(classMethod (methodHeader (identifier)) @function.method) +(objectMethod (methodHeader (identifier)) @function.method) + +; Method calls + +(methodCallExpr + (identifier) @function.method) + +; Types + +(clazz (identifier) @type) +(typeAlias (identifier) @type) +((identifier) @type + (match? @type "^[A-Z]")) + +(typeArgumentList + "<" @punctuation.bracket + ">" @punctuation.bracket) diff --git a/runtime/queries/pkl/indents.scm b/runtime/queries/pkl/indents.scm new file mode 100644 index 000000000..d2a9be1ab --- /dev/null +++ b/runtime/queries/pkl/indents.scm @@ -0,0 +1,23 @@ +; Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; https://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +; this definition is imprecise in that +; * any qualified or unqualified call to a method named "Regex" is considered a regex +; * string delimiters are considered part of the regex +[ + (objectBody) + (classBody) + (ifExpr) + (mlStringLiteral) ; This isn't perfect; newlines are too indented but it's better than if omitted. +] @indent diff --git a/runtime/queries/pkl/injections.scm b/runtime/queries/pkl/injections.scm new file mode 100644 index 000000000..15867f35e --- /dev/null +++ b/runtime/queries/pkl/injections.scm @@ -0,0 +1,30 @@ +; Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; https://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +; this definition is imprecise in that +; * any qualified or unqualified call to a method named "Regex" is considered a regex +; * string delimiters are considered part of the regex +( + ((methodCallExpr (identifier) @methodName (argumentList (slStringLiteral) @injection.content)) + (#set! injection.language "regex")) + (eq? @methodName "Regex")) + +((lineComment) @injection.content + (#set! injection.language "comment")) + +((blockComment) @injection.content + (#set! injection.language "comment")) + +((docComment) @injection.content + (#set! injection.language "markdown")) From d570c29ce37ffbb46a9c49708c31dfd81daa27cf Mon Sep 17 00:00:00 2001 From: Android789515 Date: Fri, 9 Feb 2024 14:29:11 +0000 Subject: [PATCH 129/796] expand upon the arch linux install instructions (#9574) Signed-off-by: Android789515 --- book/src/install.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/book/src/install.md b/book/src/install.md index 1f200e2ed..07865e698 100644 --- a/book/src/install.md +++ b/book/src/install.md @@ -76,6 +76,15 @@ Releases are available in the `extra` repository: ```sh sudo pacman -S helix ``` + +> 💡 When installed from the `extra` repository, run Helix with `helix` instead of `hx`. +> +> For example: +> ```sh +> helix --health +> ``` +> to check health + Additionally, a [helix-git](https://aur.archlinux.org/packages/helix-git/) package is available in the AUR, which builds the master branch. From 581a1ebf5d327c1128fe6c283578e8f36a4b5fb5 Mon Sep 17 00:00:00 2001 From: Galen Abell Date: Sun, 11 Feb 2024 18:24:20 +0100 Subject: [PATCH 130/796] Add glob file type support (#8006) * Replace FileType::Suffix with FileType::Glob Suffix is rather limited and cannot be used to match files which have semantic meaning based on location + file type (for example, Github Action workflow files). This patch adds support for a Glob FileType to replace Suffix, which encompasses the existing behavior & adds additional file matching functionality. Globs are standard Unix-style path globs, which are matched against the absolute path of the file. If the configured glob for a language is a relative glob (that is, it isn't an absolute path or already starts with a glob pattern), a glob pattern will be prepended to allow matching relative paths from any directory. The order of file type matching is also updated to first match on globs and then on extension. This is necessary as most cases where glob-matching is useful will have already been matched by an extension if glob matching is done last. * Convert file-types suffixes to globs * Use globs for filename matching Trying to match the file-type raw strings against both filename and extension leads to files with the same name as the extension having the incorrect syntax. * Match dockerfiles with suffixes It's common practice to add a suffix to dockerfiles based on their context, e.g. `Dockerfile.dev`, `Dockerfile.prod`, etc. * Make env filetype matching more generic Match on `.env` or any `.env.*` files. * Update docs * Use GlobSet to match all file type globs at once * Update todo.txt glob patterns * Consolidate language Configuration and Loader creation This is a refactor that improves the error handling for creating the `helix_core::syntax::Loader` from the default and user language configuration. * Fix integration tests * Add additional starlark file-type glob --------- Co-authored-by: Michael Davis --- Cargo.lock | 1 + book/src/languages.md | 28 ++-- helix-core/Cargo.toml | 1 + helix-core/src/config.rs | 45 ++++++- helix-core/src/syntax.rs | 150 +++++++++++++-------- helix-core/tests/indent.rs | 2 +- helix-term/src/application.rs | 14 +- helix-term/src/health.rs | 10 +- helix-term/src/main.rs | 10 +- helix-term/tests/test/commands/write.rs | 2 +- helix-term/tests/test/helpers.rs | 18 +-- languages.toml | 168 ++++++++++++------------ 12 files changed, 262 insertions(+), 187 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 73e548ae5..a7ef8eb05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1240,6 +1240,7 @@ dependencies = [ "dunce", "encoding_rs", "etcetera", + "globset", "hashbrown 0.14.3", "helix-loader", "helix-stdx", diff --git a/book/src/languages.md b/book/src/languages.md index 944ebf097..7e49a6036 100644 --- a/book/src/languages.md +++ b/book/src/languages.md @@ -78,24 +78,26 @@ from the above section. `file-types` is a list of strings or tables, for example: ```toml -file-types = ["Makefile", "toml", { suffix = ".git/config" }] +file-types = ["toml", { glob = "Makefile" }, { glob = ".git/config" }, { glob = ".github/workflows/*.yaml" } ] ``` When determining a language configuration to use, Helix searches the file-types with the following priorities: -1. Exact match: if the filename of a file is an exact match of a string in a - `file-types` list, that language wins. In the example above, `"Makefile"` - will match against `Makefile` files. -2. Extension: if there are no exact matches, any `file-types` string that - matches the file extension of a given file wins. In the example above, the - `"toml"` matches files like `Cargo.toml` or `languages.toml`. -3. Suffix: if there are still no matches, any values in `suffix` tables - are checked against the full path of the given file. In the example above, - the `{ suffix = ".git/config" }` would match against any `config` files - in `.git` directories. Note: `/` is used as the directory separator but is - replaced at runtime with the appropriate path separator for the operating - system, so this rule would match against `.git\config` files on Windows. +1. Glob: values in `glob` tables are checked against the full path of the given + file. Globs are standard Unix-style path globs (e.g. the kind you use in Shell) + and can be used to match paths for a specific prefix, suffix, directory, etc. + In the above example, the `{ glob = "Makefile" }` config would match files + with the name `Makefile`, the `{ glob = ".git/config" }` config would match + `config` files in `.git` directories, and the `{ glob = ".github/workflows/*.yaml" }` + config would match any `yaml` files in `.github/workflow` directories. Note + that globs should always use the Unix path separator `/` even on Windows systems; + the matcher will automatically take the machine-specific separators into account. + If the glob isn't an absolute path or doesn't already start with a glob prefix, + `*/` will automatically be added to ensure it matches for any subdirectory. +2. Extension: if there are no glob matches, any `file-types` string that matches + the file extension of a given file wins. In the example above, the `"toml"` + config matches files like `Cargo.toml` or `languages.toml`. ## Language Server configuration diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index 8c63af8ef..bdc879caa 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -49,6 +49,7 @@ chrono = { version = "0.4", default-features = false, features = ["alloc", "std" etcetera = "0.8" textwrap = "0.16.0" +globset = "0.4.14" nucleo.workspace = true parking_lot = "0.12" diff --git a/helix-core/src/config.rs b/helix-core/src/config.rs index 2076fc224..27cd4e297 100644 --- a/helix-core/src/config.rs +++ b/helix-core/src/config.rs @@ -1,10 +1,45 @@ -/// Syntax configuration loader based on built-in languages.toml. -pub fn default_syntax_loader() -> crate::syntax::Configuration { +use crate::syntax::{Configuration, Loader, LoaderError}; + +/// Language configuration based on built-in languages.toml. +pub fn default_lang_config() -> Configuration { helix_loader::config::default_lang_config() .try_into() - .expect("Could not serialize built-in languages.toml") + .expect("Could not deserialize built-in languages.toml") } -/// Syntax configuration loader based on user configured languages.toml. -pub fn user_syntax_loader() -> Result { + +/// Language configuration loader based on built-in languages.toml. +pub fn default_lang_loader() -> Loader { + Loader::new(default_lang_config()).expect("Could not compile loader for default config") +} + +#[derive(Debug)] +pub enum LanguageLoaderError { + DeserializeError(toml::de::Error), + LoaderError(LoaderError), +} + +impl std::fmt::Display for LanguageLoaderError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::DeserializeError(err) => write!(f, "Failed to parse language config: {err}"), + Self::LoaderError(err) => write!(f, "Failed to compile language config: {err}"), + } + } +} + +impl std::error::Error for LanguageLoaderError {} + +/// Language configuration based on user configured languages.toml. +pub fn user_lang_config() -> Result { helix_loader::config::user_lang_config()?.try_into() } + +/// Language configuration loader based on user configured languages.toml. +pub fn user_lang_loader() -> Result { + let config: Configuration = helix_loader::config::user_lang_config() + .map_err(LanguageLoaderError::DeserializeError)? + .try_into() + .map_err(LanguageLoaderError::DeserializeError)?; + + Loader::new(config).map_err(LanguageLoaderError::LoaderError) +} diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 24de1a338..99b5a3d10 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -82,12 +82,6 @@ pub struct Configuration { pub language_server: HashMap, } -impl Default for Configuration { - fn default() -> Self { - crate::config::default_syntax_loader() - } -} - // largely based on tree-sitter/cli/src/loader.rs #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "kebab-case", deny_unknown_fields)] @@ -164,9 +158,11 @@ pub enum FileType { /// The extension of the file, either the `Path::extension` or the full /// filename if the file does not have an extension. Extension(String), - /// The suffix of a file. This is compared to a given file's absolute - /// path, so it can be used to detect files based on their directories. - Suffix(String), + /// A Unix-style path glob. This is compared to the file's absolute path, so + /// it can be used to detect files based on their directories. If the glob + /// is not an absolute path and does not already start with a glob pattern, + /// a glob pattern will be prepended to it. + Glob(globset::Glob), } impl Serialize for FileType { @@ -178,9 +174,9 @@ impl Serialize for FileType { match self { FileType::Extension(extension) => serializer.serialize_str(extension), - FileType::Suffix(suffix) => { + FileType::Glob(glob) => { let mut map = serializer.serialize_map(Some(1))?; - map.serialize_entry("suffix", &suffix.replace(std::path::MAIN_SEPARATOR, "/"))?; + map.serialize_entry("glob", glob.glob())?; map.end() } } @@ -213,9 +209,20 @@ impl<'de> Deserialize<'de> for FileType { M: serde::de::MapAccess<'de>, { match map.next_entry::()? { - Some((key, suffix)) if key == "suffix" => Ok(FileType::Suffix({ - suffix.replace('/', std::path::MAIN_SEPARATOR_STR) - })), + Some((key, mut glob)) if key == "glob" => { + // If the glob isn't an absolute path or already starts + // with a glob pattern, add a leading glob so we + // properly match relative paths. + if !glob.starts_with('/') && !glob.starts_with("*/") { + glob.insert_str(0, "*/"); + } + + globset::Glob::new(glob.as_str()) + .map(FileType::Glob) + .map_err(|err| { + serde::de::Error::custom(format!("invalid `glob` pattern: {}", err)) + }) + } Some((key, _value)) => Err(serde::de::Error::custom(format!( "unknown key in `file-types` list: {}", key @@ -752,6 +759,47 @@ pub struct SoftWrap { pub wrap_at_text_width: Option, } +#[derive(Debug)] +struct FileTypeGlob { + glob: globset::Glob, + language_id: usize, +} + +impl FileTypeGlob { + fn new(glob: globset::Glob, language_id: usize) -> Self { + Self { glob, language_id } + } +} + +#[derive(Debug)] +struct FileTypeGlobMatcher { + matcher: globset::GlobSet, + file_types: Vec, +} + +impl FileTypeGlobMatcher { + fn new(file_types: Vec) -> Result { + let mut builder = globset::GlobSetBuilder::new(); + for file_type in &file_types { + builder.add(file_type.glob.clone()); + } + + Ok(Self { + matcher: builder.build()?, + file_types, + }) + } + + fn language_id_for_path(&self, path: &Path) -> Option<&usize> { + self.matcher + .matches(path) + .iter() + .filter_map(|idx| self.file_types.get(*idx)) + .max_by_key(|file_type| file_type.glob.glob().len()) + .map(|file_type| &file_type.language_id) + } +} + // Expose loader as Lazy<> global since it's always static? #[derive(Debug)] @@ -759,7 +807,7 @@ pub struct Loader { // highlight_names ? language_configs: Vec>, language_config_ids_by_extension: HashMap, // Vec - language_config_ids_by_suffix: HashMap, + language_config_ids_glob_matcher: FileTypeGlobMatcher, language_config_ids_by_shebang: HashMap, language_server_configs: HashMap, @@ -767,66 +815,57 @@ pub struct Loader { scopes: ArcSwap>, } +pub type LoaderError = globset::Error; + impl Loader { - pub fn new(config: Configuration) -> Self { - let mut loader = Self { - language_configs: Vec::new(), - language_server_configs: config.language_server, - language_config_ids_by_extension: HashMap::new(), - language_config_ids_by_suffix: HashMap::new(), - language_config_ids_by_shebang: HashMap::new(), - scopes: ArcSwap::from_pointee(Vec::new()), - }; + pub fn new(config: Configuration) -> Result { + let mut language_configs = Vec::new(); + let mut language_config_ids_by_extension = HashMap::new(); + let mut language_config_ids_by_shebang = HashMap::new(); + let mut file_type_globs = Vec::new(); for config in config.language { // get the next id - let language_id = loader.language_configs.len(); + let language_id = language_configs.len(); for file_type in &config.file_types { // entry().or_insert(Vec::new).push(language_id); match file_type { - FileType::Extension(extension) => loader - .language_config_ids_by_extension - .insert(extension.clone(), language_id), - FileType::Suffix(suffix) => loader - .language_config_ids_by_suffix - .insert(suffix.clone(), language_id), + FileType::Extension(extension) => { + language_config_ids_by_extension.insert(extension.clone(), language_id); + } + FileType::Glob(glob) => { + file_type_globs.push(FileTypeGlob::new(glob.to_owned(), language_id)); + } }; } for shebang in &config.shebangs { - loader - .language_config_ids_by_shebang - .insert(shebang.clone(), language_id); + language_config_ids_by_shebang.insert(shebang.clone(), language_id); } - loader.language_configs.push(Arc::new(config)); + language_configs.push(Arc::new(config)); } - loader + Ok(Self { + language_configs, + language_config_ids_by_extension, + language_config_ids_glob_matcher: FileTypeGlobMatcher::new(file_type_globs)?, + language_config_ids_by_shebang, + language_server_configs: config.language_server, + scopes: ArcSwap::from_pointee(Vec::new()), + }) } pub fn language_config_for_file_name(&self, path: &Path) -> Option> { // Find all the language configurations that match this file name // or a suffix of the file name. - let configuration_id = path - .file_name() - .and_then(|n| n.to_str()) - .and_then(|file_name| self.language_config_ids_by_extension.get(file_name)) + let configuration_id = self + .language_config_ids_glob_matcher + .language_id_for_path(path) .or_else(|| { path.extension() .and_then(|extension| extension.to_str()) .and_then(|extension| self.language_config_ids_by_extension.get(extension)) - }) - .or_else(|| { - self.language_config_ids_by_suffix - .iter() - .find_map(|(file_type, id)| { - if path.to_str()?.ends_with(file_type) { - Some(id) - } else { - None - } - }) }); configuration_id.and_then(|&id| self.language_configs.get(id).cloned()) @@ -2592,7 +2631,8 @@ mod test { let loader = Loader::new(Configuration { language: vec![], language_server: HashMap::new(), - }); + }) + .unwrap(); let language = get_language("rust").unwrap(); let query = Query::new(language, query_str).unwrap(); @@ -2654,7 +2694,8 @@ mod test { let loader = Loader::new(Configuration { language: vec![], language_server: HashMap::new(), - }); + }) + .unwrap(); let language = get_language("rust").unwrap(); let config = HighlightConfiguration::new( @@ -2760,7 +2801,8 @@ mod test { let loader = Loader::new(Configuration { language: vec![], language_server: HashMap::new(), - }); + }) + .unwrap(); let language = get_language(language_name).unwrap(); let config = HighlightConfiguration::new(language, "", "", "").unwrap(); diff --git a/helix-core/tests/indent.rs b/helix-core/tests/indent.rs index faf845c07..de1434f72 100644 --- a/helix-core/tests/indent.rs +++ b/helix-core/tests/indent.rs @@ -186,7 +186,7 @@ fn test_treesitter_indent( lang_scope: &str, ignored_lines: Vec>, ) { - let loader = Loader::new(indent_tests_config()); + let loader = Loader::new(indent_tests_config()).unwrap(); // set runtime path so we can find the queries let mut runtime = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index b5150a13a..b844b5f05 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -96,11 +96,7 @@ fn setup_integration_logging() { } impl Application { - pub fn new( - args: Args, - config: Config, - syn_loader_conf: syntax::Configuration, - ) -> Result { + pub fn new(args: Args, config: Config, lang_loader: syntax::Loader) -> Result { #[cfg(feature = "integration")] setup_integration_logging(); @@ -126,7 +122,7 @@ impl Application { }) .unwrap_or_else(|| theme_loader.default_theme(true_color)); - let syn_loader = std::sync::Arc::new(syntax::Loader::new(syn_loader_conf)); + let syn_loader = std::sync::Arc::new(lang_loader); #[cfg(not(feature = "integration"))] let backend = CrosstermBackend::new(stdout(), &config.editor); @@ -394,10 +390,8 @@ impl Application { /// refresh language config after config change fn refresh_language_config(&mut self) -> Result<(), Error> { - let syntax_config = helix_core::config::user_syntax_loader() - .map_err(|err| anyhow::anyhow!("Failed to load language config: {}", err))?; - - self.syn_loader = std::sync::Arc::new(syntax::Loader::new(syntax_config)); + let lang_loader = helix_core::config::user_lang_loader()?; + self.syn_loader = std::sync::Arc::new(lang_loader); self.editor.syn_loader = self.syn_loader.clone(); for document in self.editor.documents.values_mut() { document.detect_language(self.syn_loader.clone()); diff --git a/helix-term/src/health.rs b/helix-term/src/health.rs index 5f2019265..0bbb5735c 100644 --- a/helix-term/src/health.rs +++ b/helix-term/src/health.rs @@ -2,7 +2,7 @@ use crossterm::{ style::{Color, Print, Stylize}, tty::IsTty, }; -use helix_core::config::{default_syntax_loader, user_syntax_loader}; +use helix_core::config::{default_lang_config, user_lang_config}; use helix_loader::grammar::load_runtime_file; use helix_view::clipboard::get_clipboard_provider; use std::io::Write; @@ -128,7 +128,7 @@ pub fn languages_all() -> std::io::Result<()> { let stdout = std::io::stdout(); let mut stdout = stdout.lock(); - let mut syn_loader_conf = match user_syntax_loader() { + let mut syn_loader_conf = match user_lang_config() { Ok(conf) => conf, Err(err) => { let stderr = std::io::stderr(); @@ -141,7 +141,7 @@ pub fn languages_all() -> std::io::Result<()> { err )?; writeln!(stderr, "{}", "Using default language config".yellow())?; - default_syntax_loader() + default_lang_config() } }; @@ -234,7 +234,7 @@ pub fn language(lang_str: String) -> std::io::Result<()> { let stdout = std::io::stdout(); let mut stdout = stdout.lock(); - let syn_loader_conf = match user_syntax_loader() { + let syn_loader_conf = match user_lang_config() { Ok(conf) => conf, Err(err) => { let stderr = std::io::stderr(); @@ -247,7 +247,7 @@ pub fn language(lang_str: String) -> std::io::Result<()> { err )?; writeln!(stderr, "{}", "Using default language config".yellow())?; - default_syntax_loader() + default_lang_config() } }; diff --git a/helix-term/src/main.rs b/helix-term/src/main.rs index 132ee796f..fbe1a8460 100644 --- a/helix-term/src/main.rs +++ b/helix-term/src/main.rs @@ -145,18 +145,18 @@ FLAGS: } }; - let syn_loader_conf = helix_core::config::user_syntax_loader().unwrap_or_else(|err| { - eprintln!("Bad language config: {}", err); + let lang_loader = helix_core::config::user_lang_loader().unwrap_or_else(|err| { + eprintln!("{}", err); eprintln!("Press to continue with default language config"); use std::io::Read; // This waits for an enter press. let _ = std::io::stdin().read(&mut []); - helix_core::config::default_syntax_loader() + helix_core::config::default_lang_loader() }); // TODO: use the thread local executor to spawn the application task separately from the work pool - let mut app = Application::new(args, config, syn_loader_conf) - .context("unable to create new application")?; + let mut app = + Application::new(args, config, lang_loader).context("unable to create new application")?; let exit_code = app.run(&mut EventStream::new()).await?; diff --git a/helix-term/tests/test/commands/write.rs b/helix-term/tests/test/commands/write.rs index adc721c5f..f65352c7e 100644 --- a/helix-term/tests/test/commands/write.rs +++ b/helix-term/tests/test/commands/write.rs @@ -315,7 +315,7 @@ async fn test_write_auto_format_fails_still_writes() -> anyhow::Result<()> { let mut app = helpers::AppBuilder::new() .with_file(file.path(), None) .with_input_text("#[l|]#et foo = 0;\n") - .with_lang_config(helpers::test_syntax_conf(Some(lang_conf.into()))) + .with_lang_loader(helpers::test_syntax_loader(Some(lang_conf.into()))) .build()?; test_key_sequences(&mut app, vec![(Some(":w"), None)], false).await?; diff --git a/helix-term/tests/test/helpers.rs b/helix-term/tests/test/helpers.rs index 112b5e358..a978f386e 100644 --- a/helix-term/tests/test/helpers.rs +++ b/helix-term/tests/test/helpers.rs @@ -139,7 +139,7 @@ pub async fn test_key_sequence_with_input_text>( let test_case = test_case.into(); let mut app = match app { Some(app) => app, - None => Application::new(Args::default(), test_config(), test_syntax_conf(None))?, + None => Application::new(Args::default(), test_config(), test_syntax_loader(None))?, }; let (view, doc) = helix_view::current!(app.editor); @@ -162,9 +162,9 @@ pub async fn test_key_sequence_with_input_text>( .await } -/// Generates language configs that merge in overrides, like a user language +/// Generates language config loader that merge in overrides, like a user language /// config. The argument string must be a raw TOML document. -pub fn test_syntax_conf(overrides: Option) -> helix_core::syntax::Configuration { +pub fn test_syntax_loader(overrides: Option) -> helix_core::syntax::Loader { let mut lang = helix_loader::config::default_lang_config(); if let Some(overrides) = overrides { @@ -172,7 +172,7 @@ pub fn test_syntax_conf(overrides: Option) -> helix_core::syntax::Config lang = helix_loader::merge_toml_values(lang, override_toml, 3); } - lang.try_into().unwrap() + helix_core::syntax::Loader::new(lang.try_into().unwrap()).unwrap() } /// Use this for very simple test cases where there is one input @@ -271,7 +271,7 @@ pub fn new_readonly_tempfile() -> anyhow::Result { pub struct AppBuilder { args: Args, config: Config, - syn_conf: helix_core::syntax::Configuration, + syn_loader: helix_core::syntax::Loader, input: Option<(String, Selection)>, } @@ -280,7 +280,7 @@ impl Default for AppBuilder { Self { args: Args::default(), config: test_config(), - syn_conf: test_syntax_conf(None), + syn_loader: test_syntax_loader(None), input: None, } } @@ -314,8 +314,8 @@ impl AppBuilder { self } - pub fn with_lang_config(mut self, syn_conf: helix_core::syntax::Configuration) -> Self { - self.syn_conf = syn_conf; + pub fn with_lang_loader(mut self, syn_loader: helix_core::syntax::Loader) -> Self { + self.syn_loader = syn_loader; self } @@ -328,7 +328,7 @@ impl AppBuilder { bail!("Having the directory {path:?} in args.files[0] is not yet supported for integration tests"); } - let mut app = Application::new(self.args, self.config, self.syn_conf)?; + let mut app = Application::new(self.args, self.config, self.syn_loader)?; if let Some((text, selection)) = self.input { let (view, doc) = helix_view::current!(app.editor); diff --git a/languages.toml b/languages.toml index 30b411571..084d4932c 100644 --- a/languages.toml +++ b/languages.toml @@ -253,7 +253,7 @@ source = { git = "https://github.com/FuelLabs/tree-sitter-sway", rev = "e491a005 name = "toml" scope = "source.toml" injection-regex = "toml" -file-types = ["toml", "poetry.lock", "Cargo.lock"] +file-types = ["toml", { glob = "poetry.lock" }, { glob = "Cargo.lock" }] comment-token = "#" language-servers = [ "taplo" ] indent = { tab-width = 2, unit = " " } @@ -292,7 +292,7 @@ source = { git = "https://github.com/yusdacra/tree-sitter-protobuf", rev = "19c2 name = "elixir" scope = "source.elixir" injection-regex = "(elixir|ex)" -file-types = ["ex", "exs", "mix.lock"] +file-types = ["ex", "exs", { glob = "mix.lock" }] shebangs = ["elixir"] roots = ["mix.exs", "mix.lock"] comment-token = "#" @@ -361,20 +361,20 @@ file-types = [ "geojson", "gltf", "webmanifest", - "flake.lock", - ".babelrc", - ".bowerrc", - ".jscrc", + { glob = "flake.lock" }, + { glob = ".babelrc" }, + { glob = ".bowerrc" }, + { glob = ".jscrc" }, "js.map", "ts.map", "css.map", - ".jslintrc", + { glob = ".jslintrc" }, "jsonld", - ".vuerc", - "composer.lock", - ".watchmanconfig", + { glob = ".vuerc" }, + { glob = "composer.lock" }, + { glob = ".watchmanconfig" }, "avsc", - ".prettierrc" + { glob = ".prettierrc" }, ] language-servers = [ "vscode-json-language-server" ] auto-format = true @@ -439,7 +439,7 @@ source = { git = "https://github.com/tree-sitter/tree-sitter-c", rev = "7175a6dd name = "cpp" scope = "source.cpp" injection-regex = "cpp" -file-types = ["cc", "hh", "c++", "cpp", "hpp", "h", "ipp", "tpp", "cxx", "hxx", "ixx", "txx", "ino", "C", "H", "cu", "cuh", "cppm", "h++", "ii", "inl", { suffix = ".hpp.in" }, { suffix = ".h.in" }] +file-types = ["cc", "hh", "c++", "cpp", "hpp", "h", "ipp", "tpp", "cxx", "hxx", "ixx", "txx", "ino", "C", "H", "cu", "cuh", "cppm", "h++", "ii", "inl", { glob = ".hpp.in" }, { glob = ".h.in" }] comment-token = "//" language-servers = [ "clangd" ] indent = { tab-width = 2, unit = " " } @@ -571,7 +571,7 @@ source = { git = "https://github.com/tree-sitter/tree-sitter-go", rev = "64457ea name = "gomod" scope = "source.gomod" injection-regex = "gomod" -file-types = ["go.mod"] +file-types = [{ glob = "go.mod" }] auto-format = true comment-token = "//" language-servers = [ "gopls" ] @@ -598,7 +598,7 @@ source = { git = "https://github.com/dannylongeuay/tree-sitter-go-template", rev name = "gowork" scope = "source.gowork" injection-regex = "gowork" -file-types = ["go.work"] +file-types = [{ glob = "go.work" }] auto-format = true comment-token = "//" language-servers = [ "gopls" ] @@ -613,7 +613,7 @@ name = "javascript" scope = "source.js" injection-regex = "(js|javascript)" language-id = "javascript" -file-types = ["js", "mjs", "cjs", "rules", "es6", "pac", "jakefile"] +file-types = ["js", "mjs", "cjs", "rules", "es6", "pac", { glob = "jakefile" }] shebangs = ["node"] comment-token = "//" language-servers = [ "typescript-language-server" ] @@ -716,7 +716,7 @@ source = { git = "https://github.com/tree-sitter/tree-sitter-html", rev = "29f53 name = "python" scope = "source.python" injection-regex = "python" -file-types = ["py","pyi","py3","pyw","ptl",".pythonstartup",".pythonrc","SConstruct", "rpy", "cpy", "ipy", "pyt", "SConscript"] +file-types = ["py", "pyi", "py3", "pyw", "ptl", "rpy", "cpy", "ipy", "pyt", { glob = ".pythonstartup" }, { glob = ".pythonrc" }, { glob = "SConstruct" }, { glob = "SConscript" }] shebangs = ["python"] roots = ["pyproject.toml", "setup.py", "poetry.lock", "pyrightconfig.json"] comment-token = "#" @@ -769,38 +769,38 @@ injection-regex = "ruby" file-types = [ "rb", "rake", - "rakefile", "irb", - "gemfile", "gemspec", - "Rakefile", - "Gemfile", "rabl", "jbuilder", "jb", - "Podfile", "podspec", - "Vagrantfile", - "Brewfile", "rjs", "rbi", - "Guardfile", - "Capfile", - "Cheffile", - "Hobofile", - "Appraisals", - "Rantfile", - "Berksfile", - "Berksfile.lock", - "Thorfile", - "Puppetfile", - "Fastfile", - "Appfile", - "Deliverfile", - "Matchfile", - "Scanfile", - "Snapfile", - "Gymfile" + { glob = "rakefile" }, + { glob = "gemfile" }, + { glob = "Rakefile" }, + { glob = "Gemfile" }, + { glob = "Podfile" }, + { glob = "Vagrantfile" }, + { glob = "Brewfile" }, + { glob = "Guardfile" }, + { glob = "Capfile" }, + { glob = "Cheffile" }, + { glob = "Hobofile" }, + { glob = "Appraisals" }, + { glob = "Rantfile" }, + { glob = "Berksfile" }, + { glob = "Berksfile.lock" }, + { glob = "Thorfile" }, + { glob = "Puppetfile" }, + { glob = "Fastfile" }, + { glob = "Appfile" }, + { glob = "Deliverfile" }, + { glob = "Matchfile" }, + { glob = "Scanfile" }, + { glob = "Snapfile" }, + { glob = "Gymfile" }, ] shebangs = ["ruby"] comment-token = "#" @@ -819,43 +819,43 @@ file-types = [ "sh", "bash", "zsh", - ".bash_history", - ".bash_login", - ".bash_logout", - ".bash_profile", - ".bashrc", - ".profile", - ".zshenv", "zshenv", - ".zlogin", "zlogin", - ".zlogout", "zlogout", - ".zprofile", "zprofile", - ".zshrc", "zshrc", - ".zimrc", - "APKBUILD", - "PKGBUILD", "eclass", "ebuild", "bazelrc", - ".bash_aliases", "Renviron", - ".Renviron", - ".xprofile", - ".xsession", - ".xsessionrc", "zsh-theme", "ksh", "cshrc", "tcshrc", - ".yashrc", - ".yash_profile", - ".hushlogin", "bashrc_Apple_Terminal", - "zshrc_Apple_Terminal" + "zshrc_Apple_Terminal", + { glob = ".bash_history" }, + { glob = ".bash_login" }, + { glob = ".bash_logout" }, + { glob = ".bash_profile" }, + { glob = ".bashrc" }, + { glob = ".profile" }, + { glob = ".zshenv" }, + { glob = ".zlogin" }, + { glob = ".zlogout" }, + { glob = ".zprofile" }, + { glob = ".zshrc" }, + { glob = ".zimrc" }, + { glob = "APKBUILD" }, + { glob = "PKGBUILD" }, + { glob = ".bash_aliases" }, + { glob = ".Renviron" }, + { glob = ".xprofile" }, + { glob = ".xsession" }, + { glob = ".xsessionrc" }, + { glob = ".yashrc" }, + { glob = ".yash_profile" }, + { glob = ".hushlogin" }, ] shebangs = ["sh", "bash", "dash", "zsh"] comment-token = "#" @@ -1216,7 +1216,7 @@ source = { git = "https://github.com/the-mikedavis/tree-sitter-tsq", rev = "48b5 [[language]] name = "cmake" scope = "source.cmake" -file-types = ["cmake", "CMakeLists.txt"] +file-types = ["cmake", { glob = "CMakeLists.txt" }] comment-token = "#" indent = { tab-width = 2, unit = " " } language-servers = [ "cmake-language-server" ] @@ -1229,7 +1229,7 @@ source = { git = "https://github.com/uyha/tree-sitter-cmake", rev = "6e51463ef30 [[language]] name = "make" scope = "source.make" -file-types = ["Makefile", "makefile", "make", "mk", "mak", "GNUmakefile", "OCamlMakefile"] +file-types = [{ glob = "Makefile" }, { glob = "makefile" }, "make", "mk", "mak", {glob = "GNUmakefile" }, { glob = "OCamlMakefile" }] shebangs = ["make", "gmake"] injection-regex = "(make|makefile|Makefile|mk)" comment-token = "#" @@ -1372,7 +1372,7 @@ source = { git = "https://github.com/Flakebi/tree-sitter-tablegen", rev = "568dd name = "markdown" scope = "source.md" injection-regex = "md|markdown" -file-types = ["md", "markdown", "PULLREQ_EDITMSG", "mkd", "mdwn", "mdown", "markdn", "mdtxt", "mdtext", "workbook"] +file-types = ["md", "markdown", "mkd", "mdwn", "mdown", "markdn", "mdtxt", "mdtext", "workbook", { glob = "PULLREQ_EDITMSG" }] roots = [".marksman.toml"] language-servers = [ "marksman" ] indent = { tab-width = 2, unit = " " } @@ -1424,7 +1424,7 @@ name = "dockerfile" scope = "source.dockerfile" injection-regex = "docker|dockerfile" roots = ["Dockerfile", "Containerfile"] -file-types = ["Dockerfile", "dockerfile", "Containerfile", "containerfile"] +file-types = [{ glob = "Dockerfile*" }, { glob = "dockerfile*" }, { glob = "Containerfile*" }, { glob = "containerfile*" }] comment-token = "#" indent = { tab-width = 2, unit = " " } language-servers = [ "docker-langserver" ] @@ -1436,7 +1436,7 @@ source = { git = "https://github.com/camdencheek/tree-sitter-dockerfile", rev = [[language]] name = "git-commit" scope = "git.commitmsg" -file-types = ["COMMIT_EDITMSG"] +file-types = [{ glob = "COMMIT_EDITMSG" }] comment-token = "#" indent = { tab-width = 2, unit = " " } rulers = [51, 73] @@ -1461,7 +1461,7 @@ source = { git = "https://github.com/the-mikedavis/tree-sitter-diff", rev = "fd7 [[language]] name = "git-rebase" scope = "source.gitrebase" -file-types = ["git-rebase-todo"] +file-types = [{ glob = "git-rebase-todo" }] injection-regex = "git-rebase" comment-token = "#" indent = { tab-width = 2, unit = "y" } @@ -1474,7 +1474,7 @@ source = { git = "https://github.com/the-mikedavis/tree-sitter-git-rebase", rev name = "regex" scope = "source.regex" injection-regex = "regex" -file-types = ["regex", ".Rbuildignore"] +file-types = ["regex", { glob = ".Rbuildignore" }] [[grammar]] name = "regex" @@ -1483,7 +1483,7 @@ source = { git = "https://github.com/tree-sitter/tree-sitter-regex", rev = "e1cf [[language]] name = "git-config" scope = "source.gitconfig" -file-types = [".gitmodules", ".gitconfig", { suffix = ".git/config" }, { suffix = ".config/git/config" }] +file-types = [{ glob = ".gitmodules" }, { glob = ".gitconfig" }, { glob = ".git/config" }, { glob = ".config/git/config" }] injection-regex = "git-config" comment-token = "#" indent = { tab-width = 4, unit = "\t" } @@ -1495,7 +1495,7 @@ source = { git = "https://github.com/the-mikedavis/tree-sitter-git-config", rev [[language]] name = "git-attributes" scope = "source.gitattributes" -file-types = [".gitattributes"] +file-types = [{ glob = ".gitattributes" }] injection-regex = "git-attributes" comment-token = "#" grammar = "gitattributes" @@ -1507,7 +1507,7 @@ source = { git = "https://github.com/mtoohey31/tree-sitter-gitattributes", rev = [[language]] name = "git-ignore" scope = "source.gitignore" -file-types = [".gitignore", ".gitignore_global", ".ignore", ".prettierignore", ".eslintignore", ".npmignore", "CODEOWNERS", { suffix = ".config/helix/ignore" }, { suffix = ".helix/ignore" }] +file-types = [{ glob = ".gitignore" }, { glob = ".gitignore_global" }, { glob = ".ignore" }, { glob = ".prettierignore" }, { glob = ".eslintignore" }, { glob = ".npmignore"}, { glob = "CODEOWNERS" }, { glob = ".config/helix/ignore" }, { glob = ".helix/ignore" }] injection-regex = "git-ignore" comment-token = "#" grammar = "gitignore" @@ -1572,7 +1572,7 @@ source = { git = "https://github.com/jaredramirez/tree-sitter-rescript", rev = " name = "erlang" scope = "source.erlang" injection-regex = "erl(ang)?" -file-types = ["erl", "hrl", "app", "rebar.config", "rebar.lock"] +file-types = ["erl", "hrl", "app", { glob = "rebar.config" }, { glob = "rebar.lock" }] roots = ["rebar.config"] shebangs = ["escript"] comment-token = "%%" @@ -1698,7 +1698,7 @@ source = { git = "https://github.com/Hubro/tree-sitter-robot", rev = "322e4cc657 name = "r" scope = "source.r" injection-regex = "(r|R)" -file-types = ["r", "R", ".Rprofile", "Rprofile.site", ".RHistory"] +file-types = ["r", "R", { glob = ".Rprofile" }, { glob = "Rprofile.site" }, { glob = ".RHistory" }] shebangs = ["r", "R"] comment-token = "#" indent = { tab-width = 2, unit = " " } @@ -1913,7 +1913,7 @@ source = { git = "https://github.com/ap29600/tree-sitter-odin", rev = "b219207e4 name = "meson" scope = "source.meson" injection-regex = "meson" -file-types = ["meson.build", "meson_options.txt"] +file-types = [{ glob = "meson.build" }, { glob = "meson_options.txt" }] comment-token = "#" indent = { tab-width = 2, unit = " " } @@ -1924,7 +1924,7 @@ source = { git = "https://github.com/staysail/tree-sitter-meson", rev = "32a83e8 [[language]] name = "sshclientconfig" scope = "source.sshclientconfig" -file-types = [{ suffix = ".ssh/config" }, { suffix = "/etc/ssh/ssh_config" }] +file-types = [{ glob = ".ssh/config" }, { glob = "/etc/ssh/ssh_config" }] comment-token = "#" [[grammar]] @@ -2045,7 +2045,7 @@ source = { git = "https://github.com/sogaiu/tree-sitter-clojure", rev = "e57c569 name = "starlark" scope = "source.starlark" injection-regex = "(starlark|bzl|bazel)" -file-types = ["bzl", "bazel", "BUILD", "star"] +file-types = ["bzl", "bazel", "star", { glob = "BUILD" }, { glob = "BUILD.*" }] comment-token = "#" indent = { tab-width = 4, unit = " " } grammar = "python" @@ -2413,7 +2413,7 @@ source = { git = "https://github.com/hh9527/tree-sitter-wit", rev = "c917790ab9a [[language]] name = "env" scope = "source.env" -file-types = [".env", ".env.local", ".env.development", ".env.production", ".env.dist", ".envrc", ".envrc.local", ".envrc.private"] +file-types = [{ glob = ".env" }, { glob = ".env.*" }, { glob = ".envrc" }, { glob = ".envrc.*" }] injection-regex = "env" comment-token = "#" indent = { tab-width = 4, unit = "\t" } @@ -2441,7 +2441,7 @@ file-types = [ "volume", "kube", "network", - ".editorconfig", + { glob = ".editorconfig" }, "properties", "cfg", "directory" @@ -2569,7 +2569,7 @@ source = { git = "https://github.com/mtoohey31/tree-sitter-pem", rev = "be67a433 [[language]] name = "passwd" scope = "source.passwd" -file-types = ["passwd"] +file-types = [{ glob = "passwd" }] [[grammar]] name = "passwd" @@ -2578,7 +2578,7 @@ source = { git = "https://github.com/ath3/tree-sitter-passwd", rev = "20239395ea [[language]] name = "hosts" scope = "source.hosts" -file-types = ["hosts"] +file-types = [{ glob = "hosts" }] comment-token = "#" [[grammar]] @@ -2786,7 +2786,7 @@ source = { git = "https://github.com/lefp/tree-sitter-opencl", rev = "8e1d24a570 [[language]] name = "just" scope = "source.just" -file-types = ["justfile", "Justfile", ".justfile", ".Justfile"] +file-types = [{ glob = "justfile" }, { glob = "Justfile" }, { glob = ".justfile" }, { glob = ".Justfile" }] injection-regex = "just" comment-token = "#" indent = { tab-width = 4, unit = "\t" } @@ -2945,7 +2945,7 @@ source = { git = "https://github.com/kylegoetz/tree-sitter-unison", rev = "1f505 [[language]] name = "todotxt" scope = "text.todotxt" -file-types = [{ suffix = ".todo.txt" }, "todotxt"] +file-types = [{ glob = "todo.txt" }, { glob = "*.todo.txt" }, "todotxt"] formatter = { command = "sort" } auto-format = true From 786b5c533e32cfe88dbf8742fdbc18829fb46334 Mon Sep 17 00:00:00 2001 From: Kirawi <67773714+kirawi@users.noreply.github.com> Date: Sun, 11 Feb 2024 12:38:09 -0500 Subject: [PATCH 131/796] follow neovim's truecolor detection (#9577) --- Cargo.lock | 1 + helix-term/Cargo.toml | 1 + helix-term/src/lib.rs | 25 +++++++++++++++++++------ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a7ef8eb05..a0f5645dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1387,6 +1387,7 @@ dependencies = [ "signal-hook-tokio", "smallvec", "tempfile", + "termini", "tokio", "tokio-stream", "toml", diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index a0d6754d0..f3343b7a4 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -42,6 +42,7 @@ signal-hook = "0.3" tokio-stream = "0.1" futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false } arc-swap = { version = "1.6.0" } +termini = "1" # Logging fern = "0.6" diff --git a/helix-term/src/lib.rs b/helix-term/src/lib.rs index b1413ed0d..cdde86ec5 100644 --- a/helix-term/src/lib.rs +++ b/helix-term/src/lib.rs @@ -22,17 +22,30 @@ use url::Url; pub use keymap::macros::*; -#[cfg(not(windows))] -fn true_color() -> bool { - std::env::var("COLORTERM") - .map(|v| matches!(v.as_str(), "truecolor" | "24bit")) - .unwrap_or(false) -} #[cfg(windows)] fn true_color() -> bool { true } +#[cfg(not(windows))] +fn true_color() -> bool { + if matches!( + std::env::var("COLORTERM").map(|v| matches!(v.as_str(), "truecolor" | "24bit")), + Ok(true) + ) { + return true; + } + + match termini::TermInfo::from_env() { + Ok(t) => { + t.extended_cap("RGB").is_some() + || t.extended_cap("Tc").is_some() + || (t.extended_cap("setrgbf").is_some() && t.extended_cap("setrgbb").is_some()) + } + Err(_) => false, + } +} + /// Function used for filtering dir entries in the various file pickers. fn filter_picker_entry(entry: &DirEntry, root: &Path, dedup_symlinks: bool) -> bool { // We always want to ignore the .git directory, otherwise if From 13b9885084ed516276db17b730cb1d818fe95716 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 02:17:02 +0100 Subject: [PATCH 132/796] build(deps): bump tokio from 1.35.1 to 1.36.0 (#9540) Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.35.1 to 1.36.0. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.35.1...tokio-1.36.0) --- updated-dependencies: - dependency-name: tokio dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- helix-lsp/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a0f5645dd..8827dfdb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2318,9 +2318,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", diff --git a/helix-lsp/Cargo.toml b/helix-lsp/Cargo.toml index 8e9e3407c..2bdd5cfce 100644 --- a/helix-lsp/Cargo.toml +++ b/helix-lsp/Cargo.toml @@ -27,6 +27,6 @@ lsp-types = { version = "0.95" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" thiserror = "1.0" -tokio = { version = "1.35", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] } +tokio = { version = "1.36", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] } tokio-stream = "0.1.14" parking_lot = "0.12.1" From 204c3707b070c0933366b7366d48744b1426abaa Mon Sep 17 00:00:00 2001 From: 7ombie <80077603+7ombie@users.noreply.github.com> Date: Mon, 12 Feb 2024 01:17:44 +0000 Subject: [PATCH 133/796] Updated Swift grammar, adding 'any' and 'await' keywords. (#9586) --- languages.toml | 2 +- runtime/queries/swift/highlights.scm | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/languages.toml b/languages.toml index 084d4932c..c2432c278 100644 --- a/languages.toml +++ b/languages.toml @@ -1730,7 +1730,7 @@ language-servers = [ "sourcekit-lsp" ] [[grammar]] name = "swift" -source = { git = "https://github.com/alex-pinkus/tree-sitter-swift", rev = "77c6312c8438f4dbaa0350cec92b3d6dd3d74a66" } +source = { git = "https://github.com/alex-pinkus/tree-sitter-swift", rev = "b1b66955d420d5cf5ff268ae552f0d6e43ff66e1" } [[language]] name = "erb" diff --git a/runtime/queries/swift/highlights.scm b/runtime/queries/swift/highlights.scm index 5560010b0..e7610e38d 100644 --- a/runtime/queries/swift/highlights.scm +++ b/runtime/queries/swift/highlights.scm @@ -1,10 +1,10 @@ -; Upstream: https://github.com/alex-pinkus/tree-sitter-swift/blob/8d2fd80e3322df51e3f70952e60d57f5d4077eb8/queries/highlights.scm +; Upstream: https://github.com/alex-pinkus/tree-sitter-swift/blob/1c586339fb00014b23d6933f2cc32b588a226f3b/queries/highlights.scm (line_string_literal ["\\(" ")"] @punctuation.special) ["." ";" ":" "," ] @punctuation.delimiter -["(" ")" "[" "]" "{" "}"] @punctuation.bracket ; TODO: "\\(" ")" in interpolations should be @punctuation.special +["(" ")" "[" "]" "{" "}"] @punctuation.bracket ; Identifiers (attribute) @variable @@ -26,6 +26,7 @@ (function_declaration "init" @constructor) (throws) @keyword "async" @keyword +"await" @keyword (where_keyword) @keyword (parameter external_name: (simple_identifier) @variable.parameter) (parameter name: (simple_identifier) @variable.parameter) @@ -48,6 +49,7 @@ "convenience" "required" "some" + "any" ] @keyword [ From 7d8ce1a4000939bb1d1e0a67277f7733735607c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 02:23:24 +0100 Subject: [PATCH 134/796] build(deps): bump tempfile from 3.9.0 to 3.10.0 (#9538) Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.9.0 to 3.10.0. - [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md) - [Commits](https://github.com/Stebalien/tempfile/compare/v3.9.0...v3.10.0) --- updated-dependencies: - dependency-name: tempfile dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 13 ++++++------- helix-loader/Cargo.toml | 2 +- helix-stdx/Cargo.toml | 2 +- helix-term/Cargo.toml | 2 +- helix-vcs/Cargo.toml | 2 +- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8827dfdb3..24c689336 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -424,9 +424,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fern" @@ -1973,9 +1973,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ "bitflags 2.4.2", "errno", @@ -2203,13 +2203,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.9.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.4.1", "rustix", "windows-sys 0.52.0", ] diff --git a/helix-loader/Cargo.toml b/helix-loader/Cargo.toml index 469bedc10..6c3617dd1 100644 --- a/helix-loader/Cargo.toml +++ b/helix-loader/Cargo.toml @@ -30,7 +30,7 @@ log = "0.4" # cloning/compiling tree-sitter grammars cc = { version = "1" } threadpool = { version = "1.0" } -tempfile = "3.9.0" +tempfile = "3.10.0" dunce = "1.0.4" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/helix-stdx/Cargo.toml b/helix-stdx/Cargo.toml index e77f8b91f..540a1b99a 100644 --- a/helix-stdx/Cargo.toml +++ b/helix-stdx/Cargo.toml @@ -18,4 +18,4 @@ ropey = { version = "1.6.1", default-features = false } which = "6.0" [dev-dependencies] -tempfile = "3.9" +tempfile = "3.10" diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index f3343b7a4..09f02c67c 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -84,4 +84,4 @@ helix-loader = { path = "../helix-loader" } [dev-dependencies] smallvec = "1.13" indoc = "2.0.4" -tempfile = "3.9.0" +tempfile = "3.10.0" diff --git a/helix-vcs/Cargo.toml b/helix-vcs/Cargo.toml index 6aa50dcf7..32aca4f09 100644 --- a/helix-vcs/Cargo.toml +++ b/helix-vcs/Cargo.toml @@ -29,4 +29,4 @@ log = "0.4" git = ["gix"] [dev-dependencies] -tempfile = "3.9" +tempfile = "3.10" From d9f7aaacaf65272dfce092954ec49a78961e8112 Mon Sep 17 00:00:00 2001 From: Jimmy Zelinskie Date: Sun, 11 Feb 2024 20:28:52 -0500 Subject: [PATCH 135/796] languages: add CEL, SpiceDB schema language (#9296) * languages: add CEL language and grammar * languages: add spicedb schema language * chore: docgen * runtime/queries: refine spicedb & cel highlights Co-authored-by: Michael Davis * languages: update spicedb --------- Co-authored-by: Michael Davis --- book/src/generated/lang-support.md | 2 + languages.toml | 24 ++++++++++ runtime/queries/cel/highlights.scm | 66 ++++++++++++++++++++++++++ runtime/queries/spicedb/highlights.scm | 47 ++++++++++++++++++ runtime/queries/spicedb/injections.scm | 5 ++ runtime/queries/spicedb/tags.scm | 4 ++ 6 files changed, 148 insertions(+) create mode 100644 runtime/queries/cel/highlights.scm create mode 100644 runtime/queries/spicedb/highlights.scm create mode 100644 runtime/queries/spicedb/injections.scm create mode 100644 runtime/queries/spicedb/tags.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index c91b9ae11..9131ff580 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -14,6 +14,7 @@ | cabal | | | | `haskell-language-server-wrapper` | | cairo | ✓ | ✓ | ✓ | `cairo-language-server` | | capnp | ✓ | | ✓ | | +| cel | ✓ | | | | | clojure | ✓ | | | `clojure-lsp` | | cmake | ✓ | ✓ | ✓ | `cmake-language-server` | | comment | ✓ | | | | @@ -154,6 +155,7 @@ | smithy | ✓ | | | `cs` | | sml | ✓ | | | | | solidity | ✓ | | | `solc` | +| spicedb | ✓ | | | | | sql | ✓ | | | | | sshclientconfig | ✓ | | | | | starlark | ✓ | ✓ | | | diff --git a/languages.toml b/languages.toml index c2432c278..6fc811814 100644 --- a/languages.toml +++ b/languages.toml @@ -514,6 +514,30 @@ args = { processId = "{0}" } name = "c-sharp" source = { git = "https://github.com/tree-sitter/tree-sitter-c-sharp", rev = "5b60f99545fea00a33bbfae5be956f684c4c69e2" } +[[language]] +name = "cel" +scope = "source.cel" +injection-regex = "cel" +file-types = ["cel"] +comment-token = "//" +indent = { tab-width = 2, unit = " " } + +[[grammar]] +name = "cel" +source = { git = "https://github.com/bufbuild/tree-sitter-cel", rev = "9f2b65da14c216df53933748e489db0f11121464" } + +[[language]] +name = "spicedb" +scope = "source.zed" +injection-regex = "spicedb" +file-types = ["zed"] +comment-token = "//" +indent = { tab-width = 2, unit = " " } + +[[grammar]] +name = "spicedb" +source = { git = "https://github.com/jzelinskie/tree-sitter-spicedb", rev = "a4e4645651f86d6684c15dfa9931b7841dc52a66" } + [[language]] name = "go" scope = "source.go" diff --git a/runtime/queries/cel/highlights.scm b/runtime/queries/cel/highlights.scm new file mode 100644 index 000000000..ab3bae5a5 --- /dev/null +++ b/runtime/queries/cel/highlights.scm @@ -0,0 +1,66 @@ +; Operators + +[ + "-" + "!" + "*" + "/" + "&&" + "%" + "+" + "<" + "<=" + "==" + ">" + ">=" + "||" +] @operator + +; Keywords + +[ +"in" +] @keyword + +; Function calls + +(call_expression + function: (identifier) @function) + +(member_call_expression + function: (identifier) @function) + +; Identifiers + +(select_expression + operand: (identifier) @type) + +(select_expression + operand: (select_expression + member: (identifier) @type)) + +(identifier) @variable.other.member + +; Literals + +[ + (double_quote_string_literal) + (single_quoted_string_literal) + (triple_double_quote_string_literal) + (triple_single_quoted_string_literal) +] @string + +[ + (int_literal) + (uint_literal) +] @constant.numeric.integer +(float_literal) @constant.numeric.float + +[ + (true) + (false) +] @constant.builtin.boolean + +(null) @constant.builtin + +(comment) @comment diff --git a/runtime/queries/spicedb/highlights.scm b/runtime/queries/spicedb/highlights.scm new file mode 100644 index 000000000..63c939551 --- /dev/null +++ b/runtime/queries/spicedb/highlights.scm @@ -0,0 +1,47 @@ +; highlights.scm + +[ + "definition" + "caveat" + "permission" + "relation" + "nil" +] @keyword + +[ + "," + ":" +] @punctuation.delimiter + +[ + "(" + ")" + "{" + "}" +] @punctuation.bracket + +[ + "|" + "+" + "-" + "&" + "#" + "->" + "=" +] @operator +("with") @keyword.operator + +[ + "nil" + "*" +] @constant.builtin + +(comment) @comment +(type_identifier) @type +(cel_type_identifier) @type +(cel_variable_identifier) @variable.parameter +(field_identifier) @variable.other.member +[ + (func_identifier) + (method_identifier) +] @function.method diff --git a/runtime/queries/spicedb/injections.scm b/runtime/queries/spicedb/injections.scm new file mode 100644 index 000000000..f8cafc9d1 --- /dev/null +++ b/runtime/queries/spicedb/injections.scm @@ -0,0 +1,5 @@ +((comment) @injection.content + (#set! injection.language "comment")) + +((caveat_expr) @injection.content + (#set! injection.language "cel")) diff --git a/runtime/queries/spicedb/tags.scm b/runtime/queries/spicedb/tags.scm new file mode 100644 index 000000000..a8fe932b6 --- /dev/null +++ b/runtime/queries/spicedb/tags.scm @@ -0,0 +1,4 @@ +(object_definition + name: (type_identifier) @name) @definition.type + +(type_identifier) @name @reference.type From ac8d1f62a126781fbac087aa374aad540dd659b1 Mon Sep 17 00:00:00 2001 From: HumanEntity <102693471+HumanEntity@users.noreply.github.com> Date: Mon, 12 Feb 2024 02:29:49 +0100 Subject: [PATCH 136/796] sonokai: Add color modes support and change contrast between ruler and bg (#9376) * Added `editor.color-modes` option support * Less contrast between bg and ruler --- runtime/themes/sonokai.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runtime/themes/sonokai.toml b/runtime/themes/sonokai.toml index c7c9adc06..f586be28d 100644 --- a/runtime/themes/sonokai.toml +++ b/runtime/themes/sonokai.toml @@ -63,6 +63,9 @@ "ui.cursorline.primary" = { bg = "bg1" } "ui.statusline" = { fg = "fg", bg = "bg3" } "ui.statusline.inactive" = { fg = "grey", bg = "bg1" } +"ui.statusline.normal" = { fg = "bg0", bg = "blue", modifiers = ["bold"] } +"ui.statusline.insert" = { fg = "bg0", bg = "green", modifiers = ["bold"] } +"ui.statusline.select" = { fg = "bg0", bg = "purple", modifiers = ["bold"] } "ui.popup" = { fg = "grey", bg = "bg2" } "ui.window" = { fg = "grey", bg = "bg0" } "ui.help" = { fg = "fg", bg = "bg1" } @@ -71,7 +74,7 @@ "ui.menu" = { fg = "fg", bg = "bg2" } "ui.menu.selected" = { fg = "bg0", bg = "green" } "ui.virtual.whitespace" = { fg = "grey_dim" } -"ui.virtual.ruler" = { bg = "grey_dim" } +"ui.virtual.ruler" = { bg = "bg3" } "ui.virtual.inlay-hint" = { fg = "grey_dim" } info = { fg = 'green', bg = 'bg2' } From 6a90166d0a3d8fd0e2e96e4ac8e196b2b2989760 Mon Sep 17 00:00:00 2001 From: ontley <67148677+ontley@users.noreply.github.com> Date: Mon, 12 Feb 2024 02:35:25 +0100 Subject: [PATCH 137/796] Add required-root-patterns for situational lsp activation (#8696) * Added required-root-patterns for situational lsp activation using globbing * Replaced filter_map with flatten * updated book to include required-root-patterns option * fixed wrong function name for path * Added globset to helix-core. Moved globset building to config parsing. * Normalize implements AsRef * cargo fmt * Revert "cargo fmt" This reverts commit ca8ce123e8d77d2ae8ed84d5273a9b554101b0db. --- book/src/languages.md | 15 +++++----- helix-core/Cargo.toml | 1 + helix-core/src/syntax.rs | 23 +++++++++++++++ helix-lsp/src/client.rs | 21 ++------------ helix-lsp/src/lib.rs | 63 ++++++++++++++++++++++++++++++---------- 5 files changed, 81 insertions(+), 42 deletions(-) diff --git a/book/src/languages.md b/book/src/languages.md index 7e49a6036..e3900dca9 100644 --- a/book/src/languages.md +++ b/book/src/languages.md @@ -122,13 +122,14 @@ languages = { typescript = [ { formatCommand ="prettier --stdin-filepath ${INPUT These are the available options for a language server. -| Key | Description | -| ---- | ----------- | -| `command` | The name or path of the language server binary to execute. Binaries must be in `$PATH` | -| `args` | A list of arguments to pass to the language server binary | -| `config` | LSP initialization options | -| `timeout` | The maximum time a request to the language server may take, in seconds. Defaults to `20` | -| `environment` | Any environment variables that will be used when starting the language server `{ "KEY1" = "Value1", "KEY2" = "Value2" }` | +| Key | Description | +| ---- | ----------- | +| `command` | The name or path of the language server binary to execute. Binaries must be in `$PATH` | +| `args` | A list of arguments to pass to the language server binary | +| `config` | LSP initialization options | +| `timeout` | The maximum time a request to the language server may take, in seconds. Defaults to `20` | +| `environment` | Any environment variables that will be used when starting the language server `{ "KEY1" = "Value1", "KEY2" = "Value2" }` | +| `required-root-patterns` | A list of `glob` patterns to look for in the working directory. The language server is started if at least one of them is found. | A `format` sub-table within `config` can be used to pass extra formatting options to [Document Formatting Requests](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_formatting). diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index bdc879caa..e0eb6401c 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -53,6 +53,7 @@ globset = "0.4.14" nucleo.workspace = true parking_lot = "0.12" +globset = "0.4.14" [dev-dependencies] quickcheck = { version = "1", default-features = false } diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 99b5a3d10..5d45deaf4 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -10,6 +10,7 @@ use crate::{ use ahash::RandomState; use arc_swap::{ArcSwap, Guard}; use bitflags::bitflags; +use globset::GlobSet; use hashbrown::raw::RawTable; use slotmap::{DefaultKey as LayerId, HopSlotMap}; @@ -365,6 +366,22 @@ where serializer.end() } +fn deserialize_required_root_patterns<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + let patterns = Vec::::deserialize(deserializer)?; + if patterns.is_empty() { + return Ok(None); + } + let mut builder = globset::GlobSetBuilder::new(); + for pattern in patterns { + let glob = globset::Glob::new(&pattern).map_err(serde::de::Error::custom)?; + builder.add(glob); + } + builder.build().map(Some).map_err(serde::de::Error::custom) +} + #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] pub struct LanguageServerConfiguration { @@ -378,6 +395,12 @@ pub struct LanguageServerConfiguration { pub config: Option, #[serde(default = "default_timeout")] pub timeout: u64, + #[serde( + default, + skip_serializing, + deserialize_with = "deserialize_required_root_patterns" + )] + pub required_root_patterns: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 94bad6faf..0d3a2a56e 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -177,12 +177,11 @@ impl Client { args: &[String], config: Option, server_environment: HashMap, - root_markers: &[String], - manual_roots: &[PathBuf], + root_path: PathBuf, + root_uri: Option, id: usize, name: String, req_timeout: u64, - doc_path: Option<&std::path::PathBuf>, ) -> Result<(Self, UnboundedReceiver<(usize, Call)>, Arc)> { // Resolve path to the binary let cmd = helix_stdx::env::which(cmd)?; @@ -206,22 +205,6 @@ impl Client { let (server_rx, server_tx, initialize_notify) = Transport::start(reader, writer, stderr, id, name.clone()); - let (workspace, workspace_is_cwd) = find_workspace(); - let workspace = path::normalize(workspace); - let root = find_lsp_workspace( - doc_path - .and_then(|x| x.parent().and_then(|x| x.to_str())) - .unwrap_or("."), - root_markers, - manual_roots, - &workspace, - workspace_is_cwd, - ); - - // `root_uri` and `workspace_folder` can be empty in case there is no workspace - // `root_url` can not, use `workspace` as a fallback - let root_path = root.clone().unwrap_or_else(|| workspace.clone()); - let root_uri = root.and_then(|root| lsp::Url::from_file_path(root).ok()); let workspace_folders = root_uri .clone() diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index 4ce445aee..05764418f 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -680,7 +680,7 @@ impl Registry { doc_path: Option<&std::path::PathBuf>, root_dirs: &[PathBuf], enable_snippets: bool, - ) -> Result> { + ) -> Result>> { let config = self .syn_loader .language_server_configs() @@ -688,7 +688,7 @@ impl Registry { .ok_or_else(|| anyhow::anyhow!("Language server '{name}' not defined"))?; let id = self.counter; self.counter += 1; - let NewClient(client, incoming) = start_client( + if let Some(NewClient(client, incoming)) = start_client( id, name, ls_config, @@ -696,9 +696,12 @@ impl Registry { doc_path, root_dirs, enable_snippets, - )?; - self.incoming.push(UnboundedReceiverStream::new(incoming)); - Ok(client) + )? { + self.incoming.push(UnboundedReceiverStream::new(incoming)); + Ok(Some(client)) + } else { + Ok(None) + } } /// If this method is called, all documents that have a reference to language servers used by the language config have to refresh their language servers, @@ -723,8 +726,8 @@ impl Registry { root_dirs, enable_snippets, ) { - Ok(client) => client, - error => return Some(error), + Ok(client) => client?, + Err(error) => return Some(Err(error)), }; let old_clients = self .inner @@ -764,13 +767,13 @@ impl Registry { root_dirs: &'a [PathBuf], enable_snippets: bool, ) -> impl Iterator>)> + 'a { - language_config.language_servers.iter().map( + language_config.language_servers.iter().filter_map( move |LanguageServerFeatures { name, .. }| { if let Some(clients) = self.inner.get(name) { if let Some((_, client)) = clients.iter().enumerate().find(|(i, client)| { client.try_add_doc(&language_config.roots, root_dirs, doc_path, *i == 0) }) { - return (name.to_owned(), Ok(client.clone())); + return Some((name.to_owned(), Ok(client.clone()))); } } match self.start_client( @@ -781,13 +784,14 @@ impl Registry { enable_snippets, ) { Ok(client) => { + let client = client?; self.inner .entry(name.to_owned()) .or_default() .push(client.clone()); - (name.clone(), Ok(client)) + Some((name.clone(), Ok(client))) } - Err(err) => (name.to_owned(), Err(err)), + Err(err) => Some((name.to_owned(), Err(err))), } }, ) @@ -888,18 +892,45 @@ fn start_client( doc_path: Option<&std::path::PathBuf>, root_dirs: &[PathBuf], enable_snippets: bool, -) -> Result { +) -> Result> { + let (workspace, workspace_is_cwd) = helix_loader::find_workspace(); + let workspace = path::normalize(workspace); + let root = find_lsp_workspace( + doc_path + .and_then(|x| x.parent().and_then(|x| x.to_str())) + .unwrap_or("."), + &config.roots, + config.workspace_lsp_roots.as_deref().unwrap_or(root_dirs), + &workspace, + workspace_is_cwd, + ); + + // `root_uri` and `workspace_folder` can be empty in case there is no workspace + // `root_url` can not, use `workspace` as a fallback + let root_path = root.clone().unwrap_or_else(|| workspace.clone()); + let root_uri = root.and_then(|root| lsp::Url::from_file_path(root).ok()); + + if let Some(globset) = &ls_config.required_root_patterns { + if !root_path + .read_dir()? + .flatten() + .map(|entry| entry.file_name()) + .any(|entry| globset.is_match(entry)) + { + return Ok(None); + } + } + let (client, incoming, initialize_notify) = Client::start( &ls_config.command, &ls_config.args, ls_config.config.clone(), ls_config.environment.clone(), - &config.roots, - config.workspace_lsp_roots.as_deref().unwrap_or(root_dirs), + root_path, + root_uri, id, name, ls_config.timeout, - doc_path, )?; let client = Arc::new(client); @@ -938,7 +969,7 @@ fn start_client( initialize_notify.notify_one(); }); - Ok(NewClient(client, incoming)) + Ok(Some(NewClient(client, incoming))) } /// Find an LSP workspace of a file using the following mechanism: From ad7b7bc8047d3c0930bbc6201d8f69132b396b9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Mon, 12 Feb 2024 12:01:17 +0900 Subject: [PATCH 138/796] minor: Fix compilation --- helix-core/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index e0eb6401c..fb68ccc08 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -49,7 +49,6 @@ chrono = { version = "0.4", default-features = false, features = ["alloc", "std" etcetera = "0.8" textwrap = "0.16.0" -globset = "0.4.14" nucleo.workspace = true parking_lot = "0.12" From d7c7589fd5d4dc307f37e23bdac1e5fec4ded4fc Mon Sep 17 00:00:00 2001 From: iko Date: Mon, 12 Feb 2024 15:36:14 +0000 Subject: [PATCH 139/796] Add Hoon (#9190) * Added Hoon * Added highlights.scm * Updated docs * Update runtime/queries/hoon/highlights.scm Co-authored-by: Michael Davis --------- Co-authored-by: Michael Davis --- book/src/generated/lang-support.md | 1 + languages.toml | 12 +++++++++++ runtime/queries/hoon/highlights.scm | 32 +++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 runtime/queries/hoon/highlights.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 9131ff580..bfe6d6b1e 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -70,6 +70,7 @@ | hcl | ✓ | | ✓ | `terraform-ls` | | heex | ✓ | ✓ | | `elixir-ls` | | hocon | ✓ | | ✓ | | +| hoon | ✓ | | | | | hosts | ✓ | | | | | html | ✓ | | | `vscode-html-language-server` | | hurl | ✓ | | ✓ | | diff --git a/languages.toml b/languages.toml index 6fc811814..d7b8e0d94 100644 --- a/languages.toml +++ b/languages.toml @@ -3055,6 +3055,18 @@ file-types = ["log"] name = "log" source = { git = "https://github.com/Tudyx/tree-sitter-log", rev = "62cfe307e942af3417171243b599cc7deac5eab9" } +[[language]] +name = "hoon" +scope = "source.hoon" +injection-regex = "hoon" +file-types = ["hoon"] +comment-token = "::" +indent = {tab-width = 2, unit = " "} + +[[grammar]] +name = "hoon" +source = { git = "https://github.com/urbit-pilled/tree-sitter-hoon", rev = "1d5df35af3e0afe592832a67b9fb3feeeba1f7b6" } + [[language]] name = "hocon" scope = "source.conf" diff --git a/runtime/queries/hoon/highlights.scm b/runtime/queries/hoon/highlights.scm new file mode 100644 index 000000000..91e6b8542 --- /dev/null +++ b/runtime/queries/hoon/highlights.scm @@ -0,0 +1,32 @@ +(number) @constant.numeric + +(string) @string + +[ + "(" + ")" + "[" + "]" +] @punctuation.bracket + +[ + (coreTerminator) + (seriesTerminator) +] @punctuation.delimiter + + +(rune) @keyword + +(term) @constant + +(aura) @constant.builtin + +(Gap) @comment + +(boolean) @constant.builtin + +(date) @constant.builtin +(mold) @constant.builtin +(specialIndex) @constant.builtin +(lark) @operator +(fullContext) @constant.builtin From 45ee568b98dc8c045d1a1bdaea833457b8b7d926 Mon Sep 17 00:00:00 2001 From: Chirikumbrah <78883260+Chirikumbrah@users.noreply.github.com> Date: Mon, 12 Feb 2024 19:47:08 +0300 Subject: [PATCH 140/796] Dracula whitespace color changes (#9588) * added color to render whitespace characters * made some alphabet sort --- runtime/themes/dracula.toml | 150 ++++++++++++++++++++---------------- 1 file changed, 84 insertions(+), 66 deletions(-) diff --git a/runtime/themes/dracula.toml b/runtime/themes/dracula.toml index 1473c1a83..32d9c8f5d 100644 --- a/runtime/themes/dracula.toml +++ b/runtime/themes/dracula.toml @@ -2,135 +2,153 @@ # Author : Chirikumbrah "annotation" = { fg = "foreground" } + "attribute" = { fg = "green", modifiers = ["italic"] } + "comment" = { fg = "comment" } -"comment.block.documentation" = { fg = "comment" } "comment.block" = { fg = "comment" } +"comment.block.documentation" = { fg = "comment" } "comment.line" = { fg = "comment" } + "constant" = { fg = "purple" } -"constant.numeric" = { fg = "purple" } "constant.builtin" = { fg = "purple" } "constant.builtin.boolean" = { fg = "purple" } "constant.character" = { fg = "cyan" } "constant.character.escape" = { fg = "pink" } "constant.macro" = { fg = "purple" } +"constant.numeric" = { fg = "purple" } "constructor" = { fg = "purple" } + +"definition" = { underline = { color = "cyan" } } + +"diagnostic" = { underline = { color = "orange", style = "curl" } } +"diagnostic.hint" = { underline = { color = "purple", style = "curl" } } +"diagnostic.warning" = { underline = { color = "yellow", style = "curl" } } +"diagnostic.error" = { underline = { color = "red", style = "curl" } } +"diagnostic.info" = { underline = { color = "cyan", style = "curl" } } + +"error" = { fg = "red" } +"hint" = { fg = "purple" } +"info" = { fg = "cyan" } +"warning" = { fg = "yellow" } + +"diff.delta" = { fg = "orange" } +"diff.minus" = { fg = "red" } +"diff.plus" = { fg = "green" } + +# d "function" = { fg = "green" } "function.builtin" = { fg = "green" } -"function.method" = { fg = "green" } -"function.macro" = { fg = "purple" } "function.call" = { fg = "green" } +"function.macro" = { fg = "purple" } +"function.method" = { fg = "green" } + "keyword" = { fg = "pink" } -"keyword.operator" = { fg = "pink" } -"keyword.function" = { fg = "pink" } -"keyword.return" = { fg = "pink" } -"keyword.control.import" = { fg = "pink" } -"keyword.directive" = { fg = "green" } -"keyword.control.repeat" = { fg = "pink" } "keyword.control.conditional" = { fg = "pink" } "keyword.control.exception" = { fg = "purple" } +"keyword.control.import" = { fg = "pink" } +"keyword.control.repeat" = { fg = "pink" } +"keyword.directive" = { fg = "green" } +"keyword.function" = { fg = "pink" } +"keyword.operator" = { fg = "pink" } +"keyword.return" = { fg = "pink" } "keyword.storage" = { fg = "pink" } -"keyword.storage.type" = { fg = "cyan", modifiers = ["italic"] } "keyword.storage.modifier" = { fg = "pink" } -"tag" = { fg = "pink" } -"tag.attribute" = { fg = "purple" } -"tag.delimiter" = { fg = "foreground" } +"keyword.storage.type" = { fg = "cyan", modifiers = ["italic"] } + "label" = { fg = "cyan" } + +"markup.bold" = { fg = "orange", modifiers = ["bold"] } +"markup.heading" = { fg = "purple", modifiers = ["bold"] } +"markup.italic" = { fg = "yellow", modifiers = ["italic"] } +"markup.link.text" = { fg = "pink" } +"markup.link.url" = { fg = "cyan" } +"markup.list" = { fg = "cyan" } +"markup.quote" = { fg = "yellow", modifiers = ["italic"] } +"markup.raw" = { fg = "foreground" } +"markup.strikethrough" = { modifiers = ["crossed_out"] } + "punctuation" = { fg = "foreground" } "punctuation.bracket" = { fg = "foreground" } "punctuation.delimiter" = { fg = "foreground" } "punctuation.special" = { fg = "pink" } + "special" = { fg = "pink" } + "string" = { fg = "yellow" } +"string.regexp" = { fg = "red" } "string.special" = { fg = "orange" } "string.symbol" = { fg = "yellow" } -"string.regexp" = { fg = "red" } -"type.builtin" = { fg = "cyan" } + +"tag" = { fg = "pink" } +"tag.attribute" = { fg = "purple" } +"tag.delimiter" = { fg = "foreground" } + "type" = { fg = "cyan", modifiers = ["italic"] } +"type.builtin" = { fg = "cyan" } "type.enum.variant" = { fg = "foreground", modifiers = ["italic"] } -"variable" = { fg = "foreground" } -"variable.builtin" = { fg = "purple", modifiers = ["italic"] } -"variable.parameter" = { fg = "orange", modifiers = ["italic"] } -"variable.other" = { fg = "foreground" } -"variable.other.member" = { fg = "foreground" } - -"diff.plus" = { fg = "green" } -"diff.delta" = { fg = "orange" } -"diff.minus" = { fg = "red" } "ui.background" = { fg = "foreground", bg = "background" } -"ui.cursor.match" = { fg = "foreground", bg = "grey" } "ui.cursor" = { fg = "background", bg = "purple", modifiers = ["dim"] } -"ui.cursor.normal" = { fg = "background", bg = "purple", modifiers = ["dim"] } "ui.cursor.insert" = { fg = "background", bg = "green", modifiers = ["dim"] } -"ui.cursor.select" = { fg = "background", bg = "cyan", modifiers = ["dim"] } -"ui.cursor.primary.normal" = { fg = "background", bg = "purple" } +"ui.cursor.match" = { fg = "foreground", bg = "grey" } +"ui.cursor.normal" = { fg = "background", bg = "purple", modifiers = ["dim"] } "ui.cursor.primary.insert" = { fg = "background", bg = "green" } +"ui.cursor.primary.normal" = { fg = "background", bg = "purple" } "ui.cursor.primary.select" = { fg = "background", bg = "cyan" } +"ui.cursor.select" = { fg = "background", bg = "cyan", modifiers = ["dim"] } "ui.cursorline.primary" = { bg = "cursorline" } -"ui.help" = { fg = "foreground", bg = "black" } "ui.debug" = { fg = "red" } +"ui.help" = { fg = "foreground", bg = "black" } "ui.highlight.frameline" = { fg = "background", bg = "red" } "ui.linenr" = { fg = "comment" } "ui.linenr.selected" = { fg = "foreground" } "ui.menu" = { fg = "foreground", bg = "current_line" } -"ui.menu.selected" = { fg = "current_line", bg = "purple", modifiers = ["dim"] } "ui.menu.scroll" = { fg = "foreground", bg = "current_line" } +"ui.menu.selected" = { fg = "current_line", bg = "purple", modifiers = ["dim"] } "ui.popup" = { fg = "foreground", bg = "black" } -"ui.selection.primary" = { bg = "current_line" } "ui.selection" = { bg = "selection" } +"ui.selection.primary" = { bg = "current_line" } "ui.statusline" = { fg = "foreground", bg = "darker" } "ui.statusline.inactive" = { fg = "comment", bg = "darker" } -"ui.statusline.normal" = { fg = "black", bg = "purple", modifiers = ["bold"] } "ui.statusline.insert" = { fg = "black", bg = "green", modifiers = ["bold"] } +"ui.statusline.normal" = { fg = "black", bg = "purple", modifiers = ["bold"] } "ui.statusline.select" = { fg = "black", bg = "cyan", modifiers = ["bold"] } "ui.text" = { fg = "foreground" } "ui.text.focus" = { fg = "cyan" } -"ui.window" = { fg = "foreground" } -"ui.virtual.whitespace" = { fg = "current_line" } -"ui.virtual.wrap" = { fg = "current_line" } -"ui.virtual.ruler" = { bg = "black" } "ui.virtual.indent-guide" = { fg = "indent" } "ui.virtual.inlay-hint" = { fg = "cyan" } "ui.virtual.inlay-hint.parameter" = { fg = "cyan", modifiers = ["italic", "dim"] } "ui.virtual.inlay-hint.type" = { fg = "cyan", modifiers = ["italic", "dim"] } -"hint" = { fg = "purple" } -"error" = { fg = "red" } -"warning" = { fg = "yellow" } -"info" = { fg = "cyan" } -"markup.heading" = { fg = "purple", modifiers = ["bold"] } -"markup.list" = { fg = "cyan" } -"markup.bold" = { fg = "orange", modifiers = ["bold"] } -"markup.italic" = { fg = "yellow", modifiers = ["italic"] } -"markup.strikethrough" = { modifiers = ["crossed_out"] } -"markup.link.url" = { fg = "cyan" } -"markup.link.text" = { fg = "pink" } -"markup.quote" = { fg = "yellow", modifiers = ["italic"] } -"markup.raw" = { fg = "foreground" } -"diagnostic" = { underline = { color = "orange", style = "curl" } } -"diagnostic.hint" = { underline = { color = "purple", style = "curl" } } -"diagnostic.warning" = { underline = { color = "yellow", style = "curl" } } -"diagnostic.error" = { underline = { color = "red", style = "curl" } } -"diagnostic.info" = { underline = { color = "cyan", style = "curl" } } -"definition" = { underline = { color = "cyan" } } +"ui.virtual.ruler" = { bg = "black" } +"ui.virtual.whitespace" = { fg = "whitespace" } +"ui.virtual.wrap" = { fg = "current_line" } +"ui.window" = { fg = "foreground" } + +"variable" = { fg = "foreground" } +"variable.builtin" = { fg = "purple", modifiers = ["italic"] } +"variable.other" = { fg = "foreground" } +"variable.other.member" = { fg = "foreground" } +"variable.parameter" = { fg = "orange", modifiers = ["italic"] } [palette] -foreground = "#f8f8f2" background = "#282A36" -cursorline = "#2d303e" -darker = "#222430" black = "#191A21" -grey = "#666771" comment = "#6272A4" current_line = "#44475a" +cursorline = "#2d303e" +cyan = "#8be9fd" +darker = "#222430" +foreground = "#f8f8f2" +green = "#50fa7b" +grey = "#666771" indent = "#56596a" -selection = "#363848" -red = "#ff5555" orange = "#ffb86c" -yellow = "#f1fa8c" -green = "#50fa7b" -purple = "#BD93F9" -cyan = "#8be9fd" pink = "#ff79c6" +purple = "#BD93F9" +red = "#ff5555" +selection = "#363848" +whitespace = "#586693" +yellow = "#f1fa8c" From 0eec518ff68072071d595b4a297080add11738e3 Mon Sep 17 00:00:00 2001 From: kpbaks Date: Fri, 9 Feb 2024 21:39:50 +0100 Subject: [PATCH 141/796] feat(languages): add formatter for just --- languages.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/languages.toml b/languages.toml index d7b8e0d94..f99e1a63d 100644 --- a/languages.toml +++ b/languages.toml @@ -2814,6 +2814,8 @@ file-types = [{ glob = "justfile" }, { glob = "Justfile" }, { glob = ".justfile" injection-regex = "just" comment-token = "#" indent = { tab-width = 4, unit = "\t" } +auto-format = true +formatter = { command = "just", args = ["--dump"] } [[grammar]] name = "just" From 78ed3aded3622e0036a0476aa9daf5cbeaa24431 Mon Sep 17 00:00:00 2001 From: kpbaks Date: Fri, 9 Feb 2024 22:00:33 +0100 Subject: [PATCH 142/796] feat(languages): use `fish_indent` as the formatter for fish --- languages.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/languages.toml b/languages.toml index f99e1a63d..4d5be2824 100644 --- a/languages.toml +++ b/languages.toml @@ -311,6 +311,8 @@ file-types = ["fish"] shebangs = ["fish"] comment-token = "#" indent = { tab-width = 4, unit = " " } +auto-format = true +formatter = { command = "fish_indent" } [[grammar]] name = "fish" From bb1e2ddcd8c01256d81ae712090c4ca1e2d09561 Mon Sep 17 00:00:00 2001 From: Tom Manner Date: Mon, 12 Feb 2024 11:47:52 -0500 Subject: [PATCH 143/796] Added "zon" file type to zig language section. (#9582) `build.zig.zon` is what 0.11.0 uses for external dependencies. The syntax is a subset of zig and can be highlighted and formatted like normal zig code. --- languages.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index 4d5be2824..2cf1750be 100644 --- a/languages.toml +++ b/languages.toml @@ -1184,7 +1184,7 @@ source = { git = "https://github.com/postsolar/tree-sitter-purescript", rev = "5 name = "zig" scope = "source.zig" injection-regex = "zig" -file-types = ["zig"] +file-types = ["zig", "zon"] roots = ["build.zig"] auto-format = true comment-token = "//" From 0516337abb7096c7d909624a7e1307ee4b837835 Mon Sep 17 00:00:00 2001 From: Mark Stosberg Date: Mon, 12 Feb 2024 11:48:10 -0500 Subject: [PATCH 144/796] docs: Document that what the `diff` gutter symbol does (#9587) Before there was no document about what the `diff` gutter is displaying or what the colors mean. These docs clarify it's a `git` diff and makes it easier to cross-reference the theme if you aren't sure what the colors mean or want to change them. --- book/src/configuration.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/book/src/configuration.md b/book/src/configuration.md index a43ede76a..de33c1ade 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -340,7 +340,12 @@ Currently unused #### `[editor.gutters.diff]` Section -Currently unused +The `diff` gutter option displays colored bars indicating whether a `git` diff represents that a line was added, removed or changed. +These colors are controlled by the theme attributes `diff.plus`, `diff.minus` and `diff.delta`. + +Other diff providers will eventually be supported by a future plugin system. + +There are currently no options for this section. #### `[editor.gutters.spacer]` Section From d232e7a98579c3f964ade7ed93a1e6f944f1c89a Mon Sep 17 00:00:00 2001 From: Gagan Janjua Date: Mon, 12 Feb 2024 11:48:18 -0500 Subject: [PATCH 145/796] Adding Curzon theme (#9553) * adding Curzon theme * Adding cursive strings --- runtime/themes/curzon.toml | 118 +++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 runtime/themes/curzon.toml diff --git a/runtime/themes/curzon.toml b/runtime/themes/curzon.toml new file mode 100644 index 000000000..1303519fb --- /dev/null +++ b/runtime/themes/curzon.toml @@ -0,0 +1,118 @@ +attribute_color = "attribute_color" +keyword = "keyword_foreground_color" +"keyword.directive" = "light_blue" +namespace = "light_blue" +punctuation = "punctuation_color" +"punctuation.delimiter" = "punctuation_color" +operator = "operator_color" +special = "label" +"variable.other.member" = "white" +variable = "variable" +"variable.parameter" = { fg = "variable" } +"variable.builtin" = {fg = "built_in", modifiers=["bold","italic"]} +type = "white" +"type.builtin" = "white" +constructor = "light_blue" +function = "white" +"function.macro" = {fg ="light_blue" } +"function.builtin" = "white" +tag = "tag" +comment = { fg = "comment_color", modifiers = ["italic"] } +constant = {fg ="white"} +"constant.builtin" = "white" +string = {fg="string", modifiers=["italic"]} +"constant.numeric" = "constant_numeric_foreground_color" +"constant.character.escape" = "label" +# used for lifetimes +label = "label" + +"markup.heading" = "light_blue" +"markup.bold" = { modifiers = ["bold"] } +"markup.italic" = { modifiers = ["italic"] } +"markup.strikethrough" = { modifiers = ["crossed_out"] } +"markup.link.url" = { fg = "link_url_foreground_color", modifiers = ["underlined"] } +"markup.link.text" = "markup_link_foreground_color" +"markup.raw" = "markup_raw_foreground_color" + +"diff.plus" = "#35bf86" +"diff.minus" = "#f22c86" +"diff.delta" = "info" + +"ui.background" = { bg = "black" } +"ui.background.separator" = { fg = "window_color" } +"ui.linenr" = { fg = "window_color" } +"ui.linenr.selected" = { fg = "light_blue" } +"ui.statusline" = { fg = "statusline_foreground_color", bg = "black" } +"ui.statusline.inactive" = { fg = "statusline_inactive_foreground_color", bg = "black" } +"ui.virtual.ruler" = { bg = "dark"} + +"ui.popup" = { fg = "menu_normal_text_color", bg = "menu_background_color" } +"ui.window" = { fg = "dark"} +"ui.help" = { fg = "menu_normal_text_color", bg = "menu_background_color" } + +"ui.text" = { fg = "text" } +"ui.text.focus" = { fg = "white" } +"ui.text.inactive" = "comment_color" +"ui.virtual" = { fg = "#008DFF" } + +"ui.virtual.indent-guide" = { fg = "window_color" } + +"ui.selection" = { bg = "#4f46e5" } +"ui.selection.primary" = { bg = "#4f46e5" } +"ui.cursor.select" = { bg = "cursor_normal_bg_color" } +"ui.cursor.primary.insert" = { bg = "#f43f5e", fg = "white" } +"ui.cursor.match" = { fg = "#212121", bg = "#6C6999" } +"ui.cursorline.primary" = { bg = "dark"} +"ui.highlight" = { bg = "dark" } +"ui.highlight.frameline" = { bg = "#634450" } +"ui.debug" = { fg = "#634450" } +"ui.debug.breakpoint" = { fg = "debug_breakpoint" } +"ui.menu" = { fg = "menu_normal_text_color", bg = "menu_background_color" } +"ui.menu.selected" = { fg = "menu_background_color", bg = "white" } +"ui.menu.scroll" = { fg = "menu_scroll", bg = "window_color" } + +"diagnostic.hint" = { underline = { color = "hint", style = "curl" } } +"diagnostic.info" = { underline = { color = "info", style = "curl" } } +"diagnostic.warning" = { underline = { color = "warning", style = "curl" } } +"diagnostic.error" = { underline = { color = "error", style = "curl" } } + +warning = "warning" +error = "#f43f5e" +info = "info" +hint = "#38bdf8" + +[palette] +label = "#efba5d" +constant_numeric_foreground_color = "#E8DCA0" +tag = "#eccdba" +markup_link_foreground_color = "#eccdba" +markup_raw_foreground_color = "#eccdba" +keyword_foreground_color="#eccdba" # alternative color "#ecc1ba" +comment_color = "#697C81" +link_url_foreground_color="#b8b8b8" +debug_breakpoint = "#f47868" +window_color = "#484a4d" +light_blue = "#bee0ec" #change name +text="#bfdbfe" +black = "#000000" +white = "#ffffff" +dark= "#111111" +punctuation_color = "#a4a0e8" +string="#6ee7b7" +attribute_color="#dbbfef" +operator_color="#bee0ec" +menu_background_color="#1e3a8a" +menu_normal_text_color="#93c5fd" +statusline_active_background_color="#111111" +statusline_inactive_background_color="#0e0e0e" +statusline_inactive_foreground_color="#b8b8b8" +popup_background_color="#1e3a8a" +cursor_normal_bg_color="#6366f1" +warning="#ffcd1c" +error = "#f43f5e" +hint = "#38bdf8" +info = "#6366f1" +variable="#c7d2fe" +menu_scroll="#93c5fd" +built_in="#10b981" +statusline_foreground_color="#6366f1" From c111e28a6f4ab81bf1f5b60173f6e7bf3e63aa15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Feb 2024 01:48:45 +0900 Subject: [PATCH 146/796] build(deps): bump toml from 0.7.6 to 0.8.10 (#9539) Bumps [toml](https://github.com/toml-rs/toml) from 0.7.6 to 0.8.10. - [Commits](https://github.com/toml-rs/toml/compare/toml-v0.7.6...toml-v0.8.10) --- updated-dependencies: - dependency-name: toml dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 35 +++++++++++++---------------------- helix-core/Cargo.toml | 2 +- helix-loader/Cargo.toml | 2 +- helix-term/Cargo.toml | 2 +- helix-view/Cargo.toml | 2 +- xtask/Cargo.toml | 2 +- 6 files changed, 18 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 24c689336..7d8329b3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -591,7 +591,7 @@ dependencies = [ "gix-date", "itoa", "thiserror", - "winnow 0.5.28", + "winnow", ] [[package]] @@ -673,7 +673,7 @@ dependencies = [ "smallvec", "thiserror", "unicode-bom", - "winnow 0.5.28", + "winnow", ] [[package]] @@ -888,7 +888,7 @@ dependencies = [ "itoa", "smallvec", "thiserror", - "winnow 0.5.28", + "winnow", ] [[package]] @@ -1001,7 +1001,7 @@ dependencies = [ "gix-validate", "memmap2", "thiserror", - "winnow 0.5.28", + "winnow", ] [[package]] @@ -2055,9 +2055,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] @@ -2358,9 +2358,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.6" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" dependencies = [ "serde", "serde_spanned", @@ -2370,24 +2370,24 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.12" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" +checksum = "0c9ffdf896f8daaabf9b66ba8e77ea1ed5ed0f72821b398aba62352e95062951" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow 0.4.6", + "winnow", ] [[package]] @@ -2798,15 +2798,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" -[[package]] -name = "winnow" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" -dependencies = [ - "memchr", -] - [[package]] name = "winnow" version = "0.5.28" diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index fb68ccc08..40230e1ec 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -39,7 +39,7 @@ dunce = "1.0" log = "0.4" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -toml = "0.7" +toml = "0.8" imara-diff = "0.1.0" diff --git a/helix-loader/Cargo.toml b/helix-loader/Cargo.toml index 6c3617dd1..25b559696 100644 --- a/helix-loader/Cargo.toml +++ b/helix-loader/Cargo.toml @@ -19,7 +19,7 @@ helix-stdx = { path = "../helix-stdx" } anyhow = "1" serde = { version = "1.0", features = ["derive"] } -toml = "0.7" +toml = "0.8" etcetera = "0.8" tree-sitter.workspace = true once_cell = "1.19" diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 09f02c67c..1e21ec161 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -62,7 +62,7 @@ open = "5.0.1" url = "2.5.0" # config -toml = "0.7" +toml = "0.8" serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index 2e6893414..5a5525454 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -43,7 +43,7 @@ chardetng = "0.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -toml = "0.7" +toml = "0.8" log = "~0.4" parking_lot = "0.12.1" diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 0a61536fc..25d8955e5 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -16,4 +16,4 @@ helix-term = { path = "../helix-term" } helix-core = { path = "../helix-core" } helix-view = { path = "../helix-view" } helix-loader = { path = "../helix-loader" } -toml = "0.7" +toml = "0.8" From 661e12315279ac9032632e51bf32d4e36025f5af Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 12 Feb 2024 16:49:04 +0000 Subject: [PATCH 147/796] Add support for Odin's formatter (#9537) --- languages.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index 2cf1750be..e0f3961e0 100644 --- a/languages.toml +++ b/languages.toml @@ -1923,13 +1923,14 @@ source = { git = "https://github.com/fvacek/tree-sitter-cpon", rev = "0d01fcdae5 [[language]] name = "odin" -auto-format = false +auto-format = true scope = "source.odin" file-types = ["odin"] roots = ["ols.json"] language-servers = [ "ols" ] comment-token = "//" indent = { tab-width = 4, unit = "\t" } +formatter = { command = "odinfmt", args = [ "-stdin", "true" ] } [[grammar]] name = "odin" From fd7b722dfb15b1e2211e1bcce5b2631113d1741e Mon Sep 17 00:00:00 2001 From: ves <28784043+vesdev@users.noreply.github.com> Date: Mon, 12 Feb 2024 18:49:42 +0200 Subject: [PATCH 148/796] theme: horizon-dark fix constructor color (#9493) * theme: add horizon-dark * fix whitespace color and gutter selected * taplo fmt * markup and color tweaks markup colors diff colors better comment and menu colors * horizon-dark: fix constructor color makes Some and None to look better and more like the vscode theme --- runtime/themes/horizon-dark.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/themes/horizon-dark.toml b/runtime/themes/horizon-dark.toml index aec113dfa..2ab10e911 100644 --- a/runtime/themes/horizon-dark.toml +++ b/runtime/themes/horizon-dark.toml @@ -11,6 +11,7 @@ keyword = "purple" function = "blue" label = "orange" type = "orange" +constructor = "orange" namespace = "orange" # User Interface From caad87e9996b176c4e39441abf201f25a887c879 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Feb 2024 09:57:12 +0900 Subject: [PATCH 149/796] build(deps): bump thiserror from 1.0.56 to 1.0.57 (#9615) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d8329b3a..3f5b3e566 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2244,18 +2244,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", From c55bf72d25e37ae0e87a53f106e29d265e892c5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Feb 2024 09:57:22 +0900 Subject: [PATCH 150/796] build(deps): bump clipboard-win from 5.0.0 to 5.1.0 (#9616) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- helix-view/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3f5b3e566..5485125ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -183,9 +183,9 @@ dependencies = [ [[package]] name = "clipboard-win" -version = "5.0.0" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c57002a5d9be777c1ef967e33674dac9ebd310d8893e4e3437b14d5f0f6372cc" +checksum = "3ec832972fefb8cf9313b45a0d1945e29c9c251f1d4c6eafc5fe2124c02d2e81" dependencies = [ "error-code", ] diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index 5a5525454..b1b444f90 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -50,7 +50,7 @@ parking_lot = "0.12.1" [target.'cfg(windows)'.dependencies] -clipboard-win = { version = "5.0", features = ["std"] } +clipboard-win = { version = "5.1", features = ["std"] } [target.'cfg(unix)'.dependencies] libc = "0.2" From 36ad70e4c79e08f0a200162a66b894237e6e16f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Feb 2024 09:57:37 +0900 Subject: [PATCH 151/796] build(deps): bump unicode-segmentation from 1.10.1 to 1.11.0 (#9617) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- helix-core/Cargo.toml | 2 +- helix-tui/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5485125ae..8cdf1e0c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2449,9 +2449,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index 40230e1ec..ca2f505c6 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -22,7 +22,7 @@ helix-loader = { path = "../helix-loader" } ropey = { version = "1.6.1", default-features = false, features = ["simd"] } smallvec = "1.13" smartstring = "1.0.1" -unicode-segmentation = "1.10" +unicode-segmentation = "1.11" unicode-width = "0.1" unicode-general-category = "0.6" # slab = "0.4.2" diff --git a/helix-tui/Cargo.toml b/helix-tui/Cargo.toml index abb6122af..f880ca6e3 100644 --- a/helix-tui/Cargo.toml +++ b/helix-tui/Cargo.toml @@ -20,7 +20,7 @@ helix-core = { path = "../helix-core" } bitflags = "2.4" cassowary = "0.3" -unicode-segmentation = "1.10" +unicode-segmentation = "1.11" crossterm = { version = "0.27", optional = true } termini = "1.0" serde = { version = "1", "optional" = true, features = ["derive"]} From 7934ac77143e69068420556b043dde035255340b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Feb 2024 09:58:23 +0900 Subject: [PATCH 152/796] build(deps): bump cc from 1.0.83 to 1.0.85 (#9618) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8cdf1e0c3..6131871d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -145,12 +145,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "9b918671670962b48bc23753aef0c51d072dca6f52f01f800854ada6ddb7f7d3" [[package]] name = "cfg-if" From fe869e5dc7a8cd6c2c2e3945816bd890956eef3a Mon Sep 17 00:00:00 2001 From: kyfanc Date: Tue, 13 Feb 2024 18:58:53 +0800 Subject: [PATCH 153/796] fix lsp config reload (#9415) `syn_loader` was replaced rather than interior value being replace, old value was still being referenced and not updated after `:config-refresh`. By using `ArcSwap` like for `config`, each `.load()` call will return the most updated value. Co-authored-by: kyfan --- Cargo.lock | 1 + helix-core/src/syntax.rs | 30 +++++++++++++++++++++++------- helix-core/tests/indent.rs | 10 ++++++++-- helix-lsp/Cargo.toml | 1 + helix-lsp/src/lib.rs | 9 +++++---- helix-term/src/application.rs | 7 ++++--- helix-term/src/ui/lsp.rs | 9 +++++++-- helix-term/src/ui/markdown.rs | 8 +++++--- helix-term/src/ui/mod.rs | 4 ++-- helix-term/src/ui/picker.rs | 11 +++++++---- helix-term/src/ui/prompt.rs | 9 +++++++-- helix-view/src/document.rs | 20 ++++++++++++-------- helix-view/src/editor.rs | 11 +++++++---- 13 files changed, 89 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6131871d7..6f38f0034 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1316,6 +1316,7 @@ name = "helix-lsp" version = "23.10.0" dependencies = [ "anyhow", + "arc-swap", "futures-executor", "futures-util", "globset", diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 5d45deaf4..a9344448f 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -1000,7 +1000,7 @@ thread_local! { pub struct Syntax { layers: HopSlotMap, root: LayerId, - loader: Arc, + loader: Arc>, } fn byte_range_to_str(range: std::ops::Range, source: RopeSlice) -> Cow { @@ -1011,7 +1011,7 @@ impl Syntax { pub fn new( source: RopeSlice, config: Arc, - loader: Arc, + loader: Arc>, ) -> Option { let root_layer = LanguageLayer { tree: None, @@ -1055,9 +1055,10 @@ impl Syntax { let mut queue = VecDeque::new(); queue.push_back(self.root); - let scopes = self.loader.scopes.load(); + let loader = self.loader.load(); + let scopes = loader.scopes.load(); let injection_callback = |language: &InjectionLanguageMarker| { - self.loader + loader .language_configuration_for_injection_string(language) .and_then(|language_config| language_config.highlight_config(&scopes)) }; @@ -2663,7 +2664,12 @@ mod test { let mut cursor = QueryCursor::new(); let config = HighlightConfiguration::new(language, "", "", "").unwrap(); - let syntax = Syntax::new(source.slice(..), Arc::new(config), Arc::new(loader)).unwrap(); + let syntax = Syntax::new( + source.slice(..), + Arc::new(config), + Arc::new(ArcSwap::from_pointee(loader)), + ) + .unwrap(); let root = syntax.tree().root_node(); let mut test = |capture, range| { @@ -2738,7 +2744,12 @@ mod test { fn main() {} ", ); - let syntax = Syntax::new(source.slice(..), Arc::new(config), Arc::new(loader)).unwrap(); + let syntax = Syntax::new( + source.slice(..), + Arc::new(config), + Arc::new(ArcSwap::from_pointee(loader)), + ) + .unwrap(); let tree = syntax.tree(); let root = tree.root_node(); assert_eq!(root.kind(), "source_file"); @@ -2829,7 +2840,12 @@ mod test { let language = get_language(language_name).unwrap(); let config = HighlightConfiguration::new(language, "", "", "").unwrap(); - let syntax = Syntax::new(source.slice(..), Arc::new(config), Arc::new(loader)).unwrap(); + let syntax = Syntax::new( + source.slice(..), + Arc::new(config), + Arc::new(ArcSwap::from_pointee(loader)), + ) + .unwrap(); let root = syntax .tree() diff --git a/helix-core/tests/indent.rs b/helix-core/tests/indent.rs index de1434f72..53265e0b1 100644 --- a/helix-core/tests/indent.rs +++ b/helix-core/tests/indent.rs @@ -1,10 +1,11 @@ +use arc_swap::ArcSwap; use helix_core::{ indent::{indent_level_for_line, treesitter_indent_for_pos, IndentStyle}, syntax::{Configuration, Loader}, Syntax, }; use ropey::Rope; -use std::{ops::Range, path::PathBuf, process::Command}; +use std::{ops::Range, path::PathBuf, process::Command, sync::Arc}; #[test] fn test_treesitter_indent_rust() { @@ -197,7 +198,12 @@ fn test_treesitter_indent( let indent_style = IndentStyle::from_str(&language_config.indent.as_ref().unwrap().unit); let highlight_config = language_config.highlight_config(&[]).unwrap(); let text = doc.slice(..); - let syntax = Syntax::new(text, highlight_config, std::sync::Arc::new(loader)).unwrap(); + let syntax = Syntax::new( + text, + highlight_config, + Arc::new(ArcSwap::from_pointee(loader)), + ) + .unwrap(); let indent_query = language_config.indent_query().unwrap(); for i in 0..doc.len_lines() { diff --git a/helix-lsp/Cargo.toml b/helix-lsp/Cargo.toml index 2bdd5cfce..4284b0520 100644 --- a/helix-lsp/Cargo.toml +++ b/helix-lsp/Cargo.toml @@ -30,3 +30,4 @@ thiserror = "1.0" tokio = { version = "1.36", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] } tokio-stream = "0.1.14" parking_lot = "0.12.1" +arc-swap = "1" diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index 05764418f..c58d967b6 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -5,6 +5,7 @@ pub mod jsonrpc; pub mod snippet; mod transport; +use arc_swap::ArcSwap; pub use client::Client; pub use futures_executor::block_on; pub use jsonrpc::Call; @@ -640,14 +641,14 @@ impl Notification { #[derive(Debug)] pub struct Registry { inner: HashMap>>, - syn_loader: Arc, + syn_loader: Arc>, counter: usize, pub incoming: SelectAll>, pub file_event_handler: file_event::Handler, } impl Registry { - pub fn new(syn_loader: Arc) -> Self { + pub fn new(syn_loader: Arc>) -> Self { Self { inner: HashMap::new(), syn_loader, @@ -681,8 +682,8 @@ impl Registry { root_dirs: &[PathBuf], enable_snippets: bool, ) -> Result>> { - let config = self - .syn_loader + let syn_loader = self.syn_loader.load(); + let config = syn_loader .language_server_configs() .get(&name) .ok_or_else(|| anyhow::anyhow!("Language server '{name}' not defined"))?; diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index b844b5f05..30df3981c 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -66,7 +66,7 @@ pub struct Application { #[allow(dead_code)] theme_loader: Arc, #[allow(dead_code)] - syn_loader: Arc, + syn_loader: Arc>, signals: Signals, jobs: Jobs, @@ -122,7 +122,7 @@ impl Application { }) .unwrap_or_else(|| theme_loader.default_theme(true_color)); - let syn_loader = std::sync::Arc::new(lang_loader); + let syn_loader = Arc::new(ArcSwap::from_pointee(lang_loader)); #[cfg(not(feature = "integration"))] let backend = CrosstermBackend::new(stdout(), &config.editor); @@ -391,7 +391,8 @@ impl Application { /// refresh language config after config change fn refresh_language_config(&mut self) -> Result<(), Error> { let lang_loader = helix_core::config::user_lang_loader()?; - self.syn_loader = std::sync::Arc::new(lang_loader); + + self.syn_loader.store(Arc::new(lang_loader)); self.editor.syn_loader = self.syn_loader.clone(); for document in self.editor.documents.values_mut() { document.detect_language(self.syn_loader.clone()); diff --git a/helix-term/src/ui/lsp.rs b/helix-term/src/ui/lsp.rs index 7037b1552..879f963e7 100644 --- a/helix-term/src/ui/lsp.rs +++ b/helix-term/src/ui/lsp.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use arc_swap::ArcSwap; use helix_core::syntax; use helix_view::graphics::{Margin, Rect, Style}; use tui::buffer::Buffer; @@ -18,13 +19,17 @@ pub struct SignatureHelp { active_param_range: Option<(usize, usize)>, language: String, - config_loader: Arc, + config_loader: Arc>, } impl SignatureHelp { pub const ID: &'static str = "signature-help"; - pub fn new(signature: String, language: String, config_loader: Arc) -> Self { + pub fn new( + signature: String, + language: String, + config_loader: Arc>, + ) -> Self { Self { signature, signature_doc: None, diff --git a/helix-term/src/ui/markdown.rs b/helix-term/src/ui/markdown.rs index 5cf530ad8..749d58508 100644 --- a/helix-term/src/ui/markdown.rs +++ b/helix-term/src/ui/markdown.rs @@ -1,4 +1,5 @@ use crate::compositor::{Component, Context}; +use arc_swap::ArcSwap; use tui::{ buffer::Buffer as Surface, text::{Span, Spans, Text}, @@ -31,7 +32,7 @@ pub fn highlighted_code_block<'a>( text: &str, language: &str, theme: Option<&Theme>, - config_loader: Arc, + config_loader: Arc>, additional_highlight_spans: Option)>>, ) -> Text<'a> { let mut spans = Vec::new(); @@ -48,6 +49,7 @@ pub fn highlighted_code_block<'a>( let ropeslice = RopeSlice::from(text); let syntax = config_loader + .load() .language_configuration_for_injection_string(&InjectionLanguageMarker::Name( language.into(), )) @@ -121,7 +123,7 @@ pub fn highlighted_code_block<'a>( pub struct Markdown { contents: String, - config_loader: Arc, + config_loader: Arc>, } // TODO: pre-render and self reference via Pin @@ -140,7 +142,7 @@ impl Markdown { ]; const INDENT: &'static str = " "; - pub fn new(contents: String, config_loader: Arc) -> Self { + pub fn new(contents: String, config_loader: Arc>) -> Self { Self { contents, config_loader, diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index efa2473e0..d27e83553 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -336,8 +336,8 @@ pub mod completers { pub fn language(editor: &Editor, input: &str) -> Vec { let text: String = "text".into(); - let language_ids = editor - .syn_loader + let loader = editor.syn_loader.load(); + let language_ids = loader .language_configs() .map(|config| &config.language_id) .chain(std::iter::once(&text)); diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 4be5a11ef..c2728888a 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -461,14 +461,17 @@ impl Picker { // Then attempt to highlight it if it has no language set if doc.language_config().is_none() { - if let Some(language_config) = doc.detect_language_config(&cx.editor.syn_loader) { + if let Some(language_config) = doc.detect_language_config(&cx.editor.syn_loader.load()) + { doc.language = Some(language_config.clone()); let text = doc.text().clone(); let loader = cx.editor.syn_loader.clone(); let job = tokio::task::spawn_blocking(move || { - let syntax = language_config.highlight_config(&loader.scopes()).and_then( - |highlight_config| Syntax::new(text.slice(..), highlight_config, loader), - ); + let syntax = language_config + .highlight_config(&loader.load().scopes()) + .and_then(|highlight_config| { + Syntax::new(text.slice(..), highlight_config, loader) + }); let callback = move |editor: &mut Editor, compositor: &mut Compositor| { let Some(syntax) = syntax else { log::info!("highlighting picker item failed"); diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs index 3764bba60..a6ee7f05d 100644 --- a/helix-term/src/ui/prompt.rs +++ b/helix-term/src/ui/prompt.rs @@ -1,5 +1,6 @@ use crate::compositor::{Component, Compositor, Context, Event, EventResult}; use crate::{alt, ctrl, key, shift, ui}; +use arc_swap::ArcSwap; use helix_core::syntax; use helix_view::input::KeyEvent; use helix_view::keyboard::KeyCode; @@ -34,7 +35,7 @@ pub struct Prompt { callback_fn: CallbackFn, pub doc_fn: DocFn, next_char_handler: Option, - language: Option<(&'static str, Arc)>, + language: Option<(&'static str, Arc>)>, } #[derive(Clone, Copy, PartialEq, Eq)] @@ -98,7 +99,11 @@ impl Prompt { self } - pub fn with_language(mut self, language: &'static str, loader: Arc) -> Self { + pub fn with_language( + mut self, + language: &'static str, + loader: Arc>, + ) -> Self { self.language = Some((language, loader)); self } diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index 33137c6c9..4e7b1de9f 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -681,7 +681,7 @@ impl Document { pub fn open( path: &Path, encoding: Option<&'static Encoding>, - config_loader: Option>, + config_loader: Option>>, config: Arc>, ) -> Result { // Open the file if it exists, otherwise assume it is a new file (and thus empty). @@ -922,10 +922,11 @@ impl Document { } /// Detect the programming language based on the file type. - pub fn detect_language(&mut self, config_loader: Arc) { + pub fn detect_language(&mut self, config_loader: Arc>) { + let loader = config_loader.load(); self.set_language( - self.detect_language_config(&config_loader), - Some(config_loader), + self.detect_language_config(&loader), + Some(Arc::clone(&config_loader)), ); } @@ -1059,10 +1060,12 @@ impl Document { pub fn set_language( &mut self, language_config: Option>, - loader: Option>, + loader: Option>>, ) { if let (Some(language_config), Some(loader)) = (language_config, loader) { - if let Some(highlight_config) = language_config.highlight_config(&loader.scopes()) { + if let Some(highlight_config) = + language_config.highlight_config(&(*loader).load().scopes()) + { self.syntax = Syntax::new(self.text.slice(..), highlight_config, loader); } @@ -1078,9 +1081,10 @@ impl Document { pub fn set_language_by_language_id( &mut self, language_id: &str, - config_loader: Arc, + config_loader: Arc>, ) -> anyhow::Result<()> { - let language_config = config_loader + let language_config = (*config_loader) + .load() .language_config_for_language_id(language_id) .ok_or_else(|| anyhow!("invalid language id: {}", language_id))?; self.set_language(Some(language_config), Some(config_loader)); diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 0fa6d67c9..68b74cf00 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -50,7 +50,10 @@ use helix_stdx::path::canonicalize; use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer}; -use arc_swap::access::{DynAccess, DynGuard}; +use arc_swap::{ + access::{DynAccess, DynGuard}, + ArcSwap, +}; fn deserialize_duration_millis<'de, D>(deserializer: D) -> Result where @@ -918,7 +921,7 @@ pub struct Editor { pub debugger_events: SelectAll>, pub breakpoints: HashMap>, - pub syn_loader: Arc, + pub syn_loader: Arc>, pub theme_loader: Arc, /// last_theme is used for theme previews. We store the current theme here, /// and if previewing is cancelled, we can return to it. @@ -1029,7 +1032,7 @@ impl Editor { pub fn new( mut area: Rect, theme_loader: Arc, - syn_loader: Arc, + syn_loader: Arc>, config: Arc>, handlers: Handlers, ) -> Self { @@ -1190,7 +1193,7 @@ impl Editor { } let scopes = theme.scopes(); - self.syn_loader.set_scopes(scopes.to_vec()); + (*self.syn_loader).load().set_scopes(scopes.to_vec()); match preview { ThemeAction::Preview => { From c59f29921da98e23525c9ff4ffa82e330310e6fe Mon Sep 17 00:00:00 2001 From: Mihir Gadgil Date: Tue, 13 Feb 2024 09:16:23 -0700 Subject: [PATCH 154/796] Add changes to history when paste-replacing (#9625) --- helix-term/src/commands.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index d44f477b7..e46109c0f 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4084,6 +4084,7 @@ fn replace_with_yanked_impl(editor: &mut Editor, register: char, count: usize) { return; }; let values: Vec<_> = values.map(|value| value.to_string()).collect(); + let scrolloff = editor.config().scrolloff; let (view, doc) = current!(editor); let repeat = std::iter::repeat( @@ -4106,6 +4107,8 @@ fn replace_with_yanked_impl(editor: &mut Editor, register: char, count: usize) { }); doc.apply(&transaction, view.id); + doc.append_changes_to_history(view); + view.ensure_cursor_in_view(doc, scrolloff); } fn replace_selections_with_clipboard(cx: &mut Context) { From 4df08ddbe02f1e8d84c6aa4be810a91f83c73441 Mon Sep 17 00:00:00 2001 From: Mehdi Abedi <30735843+areux@users.noreply.github.com> Date: Wed, 14 Feb 2024 10:53:15 +0000 Subject: [PATCH 155/796] Allow numbers as second input event (#8471) * Make sure pending key list is empty when count handling This will allow using numbers as second key event. * count handling; add an exception for 'g' * Lookup the key event before considering a number as count * Avoid the allocation of another vec for the pending keys --------- Co-authored-by: x --- helix-term/src/keymap.rs | 9 +++++++++ helix-term/src/ui/editor.rs | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index d9297e08d..975274ed1 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -303,6 +303,15 @@ impl Keymaps { self.sticky.as_ref() } + pub fn contains_key(&self, mode: Mode, key: KeyEvent) -> bool { + let keymaps = &*self.map(); + let keymap = &keymaps[&mode]; + keymap + .search(self.pending()) + .and_then(KeyTrie::node) + .is_some_and(|node| node.contains_key(&key)) + } + /// Lookup `key` in the keymap to try and find a command to execute. Escape /// key cancels pending keystrokes. If there are no pending keystrokes but a /// sticky node is in use, it will be cleared. diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index fef62a292..bb749d2e7 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -903,7 +903,9 @@ impl EditorView { fn command_mode(&mut self, mode: Mode, cxt: &mut commands::Context, event: KeyEvent) { match (event, cxt.editor.count) { // count handling - (key!(i @ '0'), Some(_)) | (key!(i @ '1'..='9'), _) => { + (key!(i @ '0'), Some(_)) | (key!(i @ '1'..='9'), _) + if !self.keymaps.contains_key(mode, event) => + { let i = i.to_digit(10).unwrap() as usize; cxt.editor.count = std::num::NonZeroUsize::new(cxt.editor.count.map_or(i, |c| c.get() * 10 + i)); From 59369d99e2ed891206bd63c9b83b034da474920a Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Wed, 14 Feb 2024 11:55:55 -0500 Subject: [PATCH 156/796] Bump tree-sitter-erlang, add `*.app.src` file-type (#9627) --- languages.toml | 4 ++-- runtime/queries/erlang/highlights.scm | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/languages.toml b/languages.toml index e0f3961e0..bfa33bf58 100644 --- a/languages.toml +++ b/languages.toml @@ -1598,7 +1598,7 @@ source = { git = "https://github.com/jaredramirez/tree-sitter-rescript", rev = " name = "erlang" scope = "source.erlang" injection-regex = "erl(ang)?" -file-types = ["erl", "hrl", "app", { glob = "rebar.config" }, { glob = "rebar.lock" }] +file-types = ["erl", "hrl", "app", { glob = "rebar.config" }, { glob = "rebar.lock" }, { glob = "*.app.src" }] roots = ["rebar.config"] shebangs = ["escript"] comment-token = "%%" @@ -1615,7 +1615,7 @@ language-servers = [ "erlang-ls" ] [[grammar]] name = "erlang" -source = { git = "https://github.com/the-mikedavis/tree-sitter-erlang", rev = "ce0ed253d72c199ab93caba7542b6f62075339c4" } +source = { git = "https://github.com/the-mikedavis/tree-sitter-erlang", rev = "731e50555a51f0d8635992b0e60dc98cc47a58d7" } [[language]] name = "kotlin" diff --git a/runtime/queries/erlang/highlights.scm b/runtime/queries/erlang/highlights.scm index 741f5e544..6d0ec36d7 100644 --- a/runtime/queries/erlang/highlights.scm +++ b/runtime/queries/erlang/highlights.scm @@ -145,8 +145,9 @@ ((atom) @constant.builtin.boolean (#match? @constant.builtin.boolean "^(true|false)$")) (atom) @string.special.symbol -(string) @string +[(string) (sigil)] @string (character) @constant.character +(escape_sequence) @constant.character.escape (integer) @constant.numeric.integer (float) @constant.numeric.float From 76e512f9445b2a26655248b46cf13413f9a6bbba Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 16 Feb 2024 15:43:14 +0100 Subject: [PATCH 157/796] Remove unwrap on line option, preventing DAP crash (#9632) * Remove unwrap on line option, preventing DAP crash, ref #4683 * Update to fall back to existing values for option fields --- helix-view/src/handlers/dap.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/helix-view/src/handlers/dap.rs b/helix-view/src/handlers/dap.rs index e1437bef7..a5fa0c29c 100644 --- a/helix-view/src/handlers/dap.rs +++ b/helix-view/src/handlers/dap.rs @@ -226,10 +226,15 @@ impl Editor { breakpoints.iter().position(|b| b.id == breakpoint.id) { breakpoints[i].verified = breakpoint.verified; - breakpoints[i].message = breakpoint.message.clone(); - breakpoints[i].line = - breakpoint.line.unwrap().saturating_sub(1); // TODO: no unwrap - breakpoints[i].column = breakpoint.column; + breakpoints[i].message = breakpoint + .message + .clone() + .or_else(|| breakpoints[i].message.take()); + breakpoints[i].line = breakpoint + .line + .map_or(breakpoints[i].line, |line| line.saturating_sub(1)); + breakpoints[i].column = + breakpoint.column.or(breakpoints[i].column); } } } From 6ffe09e873821c7232d1da23ea4642214e42a1e7 Mon Sep 17 00:00:00 2001 From: Nick Condron Date: Sat, 17 Feb 2024 11:08:18 -0500 Subject: [PATCH 158/796] Fix Sonokai theme to better match original (#5379) * Make sonokai palette perfectfully faithful * Amend theme to better match original sonokai Changes based on the following references: (1) https://www.sainnhe.dev/post/contributing-guide/#sonokai (2) https://github.com/sainnhe/sonokai/blob/master/colors/sonokai.vim * Make constants white (1) * Make builtin variables purple (1) * Make members orange (1) * Make labels red (2) * Make operators red (1) * Make all punctuation grey (2) * Make builtin functions and macros green (2) * Make diff delta blue (2) * Make cursor match bg4 (2) * Make visible whitespace bg4 (2) * Make Sonokai special punctuation yellow --- runtime/themes/sonokai.toml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/runtime/themes/sonokai.toml b/runtime/themes/sonokai.toml index f586be28d..4bbdb4dbe 100644 --- a/runtime/themes/sonokai.toml +++ b/runtime/themes/sonokai.toml @@ -6,24 +6,23 @@ # License: MIT License "type" = "blue" -"constant" = "purple" +"constant" = "fg" "constant.numeric" = "purple" "constant.character.escape" = "orange" "string" = "yellow" "comment" = "grey" "variable" = "fg" -"variable.builtin" = "orange" +"variable.builtin" = "purple" "variable.parameter" = "fg" -"variable.other.member" = "fg" -"label" = "orange" +"variable.other.member" = "orange" +"label" = "red" "punctuation" = "grey" -"punctuation.delimiter" = "grey" -"punctuation.bracket" = "fg" +"punctuation.special" = "yellow" "keyword" = "red" -"operator" = "orange" +"operator" = "red" "function" = "green" -"function.builtin" = "blue" -"function.macro" = "purple" +"function.builtin" = "green" +"function.macro" = "green" "tag" = "yellow" "namespace" = "blue" "attribute" = "purple" @@ -48,12 +47,12 @@ "markup.raw" = "green" "diff.plus" = "green" -"diff.delta" = "orange" +"diff.delta" = "blue" "diff.minus" = "red" "ui.background" = { bg = "bg0" } "ui.cursor" = { modifiers = ['reversed'] } -"ui.cursor.match" = { fg = "orange", bg = "diff_yellow" } +"ui.cursor.match" = { bg = "bg4" } "ui.cursor.insert" = { fg = "black", bg = "grey" } "ui.cursor.select" = { fg = "bg0", bg = "blue" } "ui.selection" = { bg = "bg5" } @@ -73,7 +72,7 @@ "ui.text.focus" = "green" "ui.menu" = { fg = "fg", bg = "bg2" } "ui.menu.selected" = { fg = "bg0", bg = "green" } -"ui.virtual.whitespace" = { fg = "grey_dim" } +"ui.virtual.whitespace" = "bg4" "ui.virtual.ruler" = { bg = "bg3" } "ui.virtual.inlay-hint" = { fg = "grey_dim" } @@ -92,11 +91,12 @@ error = { fg = 'red', bg = 'bg2', modifiers = ['bold'] } [palette] black = "#181819" +bg_dim = "#222327" bg0 = "#2c2e34" bg1 = "#33353f" bg2 = "#363944" bg3 = "#3b3e48" -bg4 = "#5C606A" +bg4 = "#414550" bg5 = "#444852" bg_red = "#ff6077" diff_red = "#55393d" From 3e963b3c1b5eb4b5cd7f33b8ef6d6642de210a9b Mon Sep 17 00:00:00 2001 From: Vivek Kethineni <35619837+uek-1@users.noreply.github.com> Date: Sat, 17 Feb 2024 10:08:44 -0600 Subject: [PATCH 159/796] Add Rust fields as argument textobject (#9637) * added field_declaration_list and field_initializer_list as parameter textobjects * removed field_declaration_listt from textobjects.scm --- runtime/queries/rust/textobjects.scm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/runtime/queries/rust/textobjects.scm b/runtime/queries/rust/textobjects.scm index 837f981e7..df26331d8 100644 --- a/runtime/queries/rust/textobjects.scm +++ b/runtime/queries/rust/textobjects.scm @@ -34,6 +34,9 @@ (arguments ((_) @parameter.inside . ","? @parameter.around) @parameter.around) +(field_initializer_list + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + [ (line_comment) (block_comment) From 78c34194b5c83beb26ca04f12bf9d53fd5aba801 Mon Sep 17 00:00:00 2001 From: melted-brownie <66597133+melted-brownie@users.noreply.github.com> Date: Sat, 17 Feb 2024 16:09:21 +0000 Subject: [PATCH 160/796] Improve textobjects for parameter/argument for Dart (#9644) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sébastien Blondiau --- runtime/queries/dart/textobjects.scm | 29 ++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/runtime/queries/dart/textobjects.scm b/runtime/queries/dart/textobjects.scm index 028276156..b88b97bc9 100644 --- a/runtime/queries/dart/textobjects.scm +++ b/runtime/queries/dart/textobjects.scm @@ -56,9 +56,34 @@ (documentation_comment)+ @comment.around -(formal_parameter) @parameter.inside +(formal_parameter_list + ( + (formal_parameter) @parameter.inside . ","? @parameter.around + ) @parameter.around +) + +(optional_formal_parameters + ( + (formal_parameter) @parameter.inside . ","? @parameter.around + ) @parameter.around +) + +(arguments + ( + [ + (argument) @parameter.inside + (named_argument (label) . (_)* @parameter.inside) + ] + . ","? @parameter.around + ) @parameter.around +) -(formal_parameter_list) @parameter.around +(type_arguments + ( + ((_) . ("." . (_) @parameter.inside @parameter.around)?) @parameter.inside + . ","? @parameter.around + ) @parameter.around +) (expression_statement ((identifier) @_name (#any-of? @_name "test" "testWidgets")) From 64326698225999016300423c4b90f8a8c7b8f38b Mon Sep 17 00:00:00 2001 From: Vince Varga Date: Sun, 18 Feb 2024 15:55:21 +0100 Subject: [PATCH 161/796] Add tmux.conf as a bash file type (#9653) * Add conf as a bash file type Tmux and tmux.conf is used widely in software developer circles. Having the tmux.conf file not have any syntax highlighting by default is (IMO) not ideal for an editor that otherwise "just works". * Use tmux.conf glob instead of simply conf for tmux Co-authored-by: Michael Davis --------- Co-authored-by: Michael Davis --- languages.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/languages.toml b/languages.toml index bfa33bf58..78078ca58 100644 --- a/languages.toml +++ b/languages.toml @@ -860,6 +860,7 @@ file-types = [ "tcshrc", "bashrc_Apple_Terminal", "zshrc_Apple_Terminal", + { glob = "tmux.conf" }, { glob = ".bash_history" }, { glob = ".bash_login" }, { glob = ".bash_logout" }, From 9ab3f9d01a8ea4967f5a7e64a3f6c8f350674c18 Mon Sep 17 00:00:00 2001 From: AlexanderDickie <75994927+AlexanderDickie@users.noreply.github.com> Date: Sun, 18 Feb 2024 23:13:04 +0000 Subject: [PATCH 162/796] Scroll cursor and page together (neovim-like scrolling) (#8015) * neovim like scroll function * clear line annotations outside of move_vertically/_visual * add nvim scroll function to commands * assign nvim-scroll to C-d and C-u (half page scrolls) * dont remove backspace and space mapping * move non-softwrap logic to seperate function, call this in nvim-scroll fn * Revert "move non-softwrap logic to seperate function, call this in nvim-scroll fn" This reverts commit e4905729c338a2260e6981f1d8fac022897b4191. * Revert "clear line annotations outside of move_vertically/_visual" This reverts commit 1df3fefe55afc840d1ab5094b2116d1127fc363f. * add TODO for when inline diagnostics gets merged * move nvim-scroll logic into scroll(), dont respect scrolloff * run cargo fmt * run cargo clippy * update documenation for Ctrl-d and Ctrl-u remap --- book/src/keymap.md | 28 ++++++------- helix-term/src/commands.rs | 68 ++++++++++++++++++++++++++++---- helix-term/src/keymap/default.rs | 12 +++--- helix-term/src/ui/editor.rs | 2 +- 4 files changed, 81 insertions(+), 29 deletions(-) diff --git a/book/src/keymap.md b/book/src/keymap.md index a3e41666f..ac84147cd 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -53,8 +53,8 @@ Normal mode is the default mode when you launch helix. You can return to it from | `End` | Move to the end of the line | `goto_line_end` | | `Ctrl-b`, `PageUp` | Move page up | `page_up` | | `Ctrl-f`, `PageDown` | Move page down | `page_down` | -| `Ctrl-u` | Move half page up | `half_page_up` | -| `Ctrl-d` | Move half page down | `half_page_down` | +| `Ctrl-u` | Move cursor and page half page up | `page_cursor_half_up` | +| `Ctrl-d` | Move cursor and page half page down | `page_cursor_half_down` | | `Ctrl-i` | Jump forward on the jumplist | `jump_forward` | | `Ctrl-o` | Jump backward on the jumplist | `jump_backward` | | `Ctrl-s` | Save the current selection to the jumplist | `save_selection` | @@ -182,18 +182,18 @@ normal mode) is persistent and can be exited using the escape key. This is useful when you're simply looking over text and not actively editing it. -| Key | Description | Command | -| ----- | ----------- | ------- | -| `z`, `c` | Vertically center the line | `align_view_center` | -| `t` | Align the line to the top of the screen | `align_view_top` | -| `b` | Align the line to the bottom of the screen | `align_view_bottom` | -| `m` | Align the line to the middle of the screen (horizontally) | `align_view_middle` | -| `j`, `down` | Scroll the view downwards | `scroll_down` | -| `k`, `up` | Scroll the view upwards | `scroll_up` | -| `Ctrl-f`, `PageDown` | Move page down | `page_down` | -| `Ctrl-b`, `PageUp` | Move page up | `page_up` | -| `Ctrl-d` | Move half page down | `half_page_down` | -| `Ctrl-u` | Move half page up | `half_page_up` | +| Key | Description | Command | +| ----- | ----------- | ------- | +| `z`, `c` | Vertically center the line | `align_view_center` | +| `t` | Align the line to the top of the screen | `align_view_top` | +| `b` | Align the line to the bottom of the screen | `align_view_bottom` | +| `m` | Align the line to the middle of the screen (horizontally) | `align_view_middle` | +| `j`, `down` | Scroll the view downwards | `scroll_down` | +| `k`, `up` | Scroll the view upwards | `scroll_up` | +| `Ctrl-f`, `PageDown` | Move page down | `page_down` | +| `Ctrl-b`, `PageUp` | Move page up | `page_up` | +| `Ctrl-u` | Move cursor and page half page up | `page_cursor_half_up` | +| `Ctrl-d` | Move cursor and page half page down | `page_cursor_half_down` | #### Goto mode diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index e46109c0f..51a1ede9b 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -277,6 +277,10 @@ impl MappableCommand { page_down, "Move page down", half_page_up, "Move half page up", half_page_down, "Move half page down", + page_cursor_up, "Move page and cursor up", + page_cursor_down, "Move page and cursor down", + page_cursor_half_up, "Move page and cursor half up", + page_cursor_half_down, "Move page and cursor half down", select_all, "Select whole document", select_regex, "Select all regex matches inside selections", split_selection, "Split selections on regex matches", @@ -1608,7 +1612,7 @@ fn switch_to_lowercase(cx: &mut Context) { }); } -pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) { +pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor: bool) { use Direction::*; let config = cx.editor.config(); let (view, doc) = current!(cx.editor); @@ -1628,7 +1632,7 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) { let doc_text = doc.text().slice(..); let viewport = view.inner_area(doc); let text_fmt = doc.text_format(viewport.width, None); - let annotations = view.text_annotations(doc, None); + let mut annotations = view.text_annotations(doc, None); (view.offset.anchor, view.offset.vertical_offset) = char_idx_at_visual_offset( doc_text, view.offset.anchor, @@ -1638,6 +1642,30 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) { &annotations, ); + if sync_cursor { + let movement = match cx.editor.mode { + Mode::Select => Movement::Extend, + _ => Movement::Move, + }; + // TODO: When inline diagnostics gets merged- 1. move_vertically_visual removes + // line annotations/diagnostics so the cursor may jump further than the view. + // 2. If the cursor lands on a complete line of virtual text, the cursor will + // jump a different distance than the view. + let selection = doc.selection(view.id).clone().transform(|range| { + move_vertically_visual( + doc_text, + range, + direction, + offset.unsigned_abs(), + movement, + &text_fmt, + &mut annotations, + ) + }); + doc.set_selection(view.id, selection); + return; + } + let mut head; match direction { Forward => { @@ -1688,25 +1716,49 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) { fn page_up(cx: &mut Context) { let view = view!(cx.editor); let offset = view.inner_height(); - scroll(cx, offset, Direction::Backward); + scroll(cx, offset, Direction::Backward, false); } fn page_down(cx: &mut Context) { let view = view!(cx.editor); let offset = view.inner_height(); - scroll(cx, offset, Direction::Forward); + scroll(cx, offset, Direction::Forward, false); } fn half_page_up(cx: &mut Context) { let view = view!(cx.editor); let offset = view.inner_height() / 2; - scroll(cx, offset, Direction::Backward); + scroll(cx, offset, Direction::Backward, false); } fn half_page_down(cx: &mut Context) { let view = view!(cx.editor); let offset = view.inner_height() / 2; - scroll(cx, offset, Direction::Forward); + scroll(cx, offset, Direction::Forward, false); +} + +fn page_cursor_up(cx: &mut Context) { + let view = view!(cx.editor); + let offset = view.inner_height(); + scroll(cx, offset, Direction::Backward, true); +} + +fn page_cursor_down(cx: &mut Context) { + let view = view!(cx.editor); + let offset = view.inner_height(); + scroll(cx, offset, Direction::Forward, true); +} + +fn page_cursor_half_up(cx: &mut Context) { + let view = view!(cx.editor); + let offset = view.inner_height() / 2; + scroll(cx, offset, Direction::Backward, true); +} + +fn page_cursor_half_down(cx: &mut Context) { + let view = view!(cx.editor); + let offset = view.inner_height() / 2; + scroll(cx, offset, Direction::Forward, true); } #[allow(deprecated)] @@ -4856,11 +4908,11 @@ fn align_view_middle(cx: &mut Context) { } fn scroll_up(cx: &mut Context) { - scroll(cx, cx.count(), Direction::Backward); + scroll(cx, cx.count(), Direction::Backward, false); } fn scroll_down(cx: &mut Context) { - scroll(cx, cx.count(), Direction::Forward); + scroll(cx, cx.count(), Direction::Forward, false); } fn goto_ts_object_impl(cx: &mut Context, object: &'static str, direction: Direction) { diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index 763ed4ae7..92d6b5906 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -178,8 +178,8 @@ pub fn default() -> HashMap { "esc" => normal_mode, "C-b" | "pageup" => page_up, "C-f" | "pagedown" => page_down, - "C-u" => half_page_up, - "C-d" => half_page_down, + "C-u" => page_cursor_half_up, + "C-d" => page_cursor_half_down, "C-w" => { "Window" "C-w" | "w" => rotate_view, @@ -287,8 +287,8 @@ pub fn default() -> HashMap { "j" | "down" => scroll_down, "C-b" | "pageup" => page_up, "C-f" | "pagedown" => page_down, - "C-u" | "backspace" => half_page_up, - "C-d" | "space" => half_page_down, + "C-u" | "backspace" => page_cursor_half_up, + "C-d" | "space" => page_cursor_half_down, "/" => search, "?" => rsearch, @@ -304,8 +304,8 @@ pub fn default() -> HashMap { "j" | "down" => scroll_down, "C-b" | "pageup" => page_up, "C-f" | "pagedown" => page_down, - "C-u" | "backspace" => half_page_up, - "C-d" | "space" => half_page_down, + "C-u" | "backspace" => page_cursor_half_up, + "C-d" | "space" => page_cursor_half_down, "/" => search, "?" => rsearch, diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index bb749d2e7..a87e6cbca 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -1156,7 +1156,7 @@ impl EditorView { } let offset = config.scroll_lines.unsigned_abs(); - commands::scroll(cxt, offset, direction); + commands::scroll(cxt, offset, direction, false); cxt.editor.tree.focus = current_view; cxt.editor.ensure_cursor_in_view(current_view); From b950dea003750120f6e764afd5afd7b738aa6410 Mon Sep 17 00:00:00 2001 From: Jimmy Zelinskie Date: Sun, 18 Feb 2024 18:19:26 -0500 Subject: [PATCH 163/796] add monokai soda theme (#9651) --- runtime/themes/monokai_soda.toml | 120 +++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 runtime/themes/monokai_soda.toml diff --git a/runtime/themes/monokai_soda.toml b/runtime/themes/monokai_soda.toml new file mode 100644 index 000000000..7e43247e3 --- /dev/null +++ b/runtime/themes/monokai_soda.toml @@ -0,0 +1,120 @@ +# Monokai Soda port for Helix (https://helix-editor.com) +# Author : Jimmy Zelinskie + +# Syntax + +## Constants +"constant" = "white" +"constant.builtin" = "pink" +"constant.character.escape" = "blue" +"constant.numeric" = "purple" + +## Diagnostics +"diagnostic" = { modifiers = ["underlined"] } +"diagnostic.error" = { underline = { style = "curl", color = "pink" } } +"diagnostic.warning" = { underline = { style = "curl", color = "orange" } } +"diagnostic.info" = { underline = { style = "curl", color = "white" } } + +## Diffs +"diff.plus" = "green" +"diff.delta" = "orange" +"diff.minus" = "pink" +"diff.delta.moved" = "orange" + +## Functions +"function" = "green" +"function.macro" = "blue" +"function.builtin" = "pink" +"constructor" = "blue" + +## Keywords +"keyword" = "pink" +"keyword.directive" = "blue" + +## Punctuation +"punctuation" = "gray" + +## Strings +"string" = "yellow" + +## Types +"type" = "blue" +"type.builtin" = "pink" + +## Variables +"variable" = "white" +"variable.builtin" = "pink" +"variable.other.member" = "white" +"variable.parameter" = "softorange" + +## Markup +"markup.heading" = "green" +"markup.bold" = { fg = "orange", modifiers = ["bold"] } +"markup.italic" = { fg = "orange", modifiers = ["italic"] } +"markup.link.url" = { fg = "orange", modifiers = ["underlined"] } +"markup.link.text" = "yellow" +"markup.quote" = "green" + +## Misc +"attribute" = "blue" +"comment" = { fg = "gray", modifiers = ["italic"] } +"error" = "pink" +"hint" = "white" +"info" = "white" +"label" = "yellow" +"module" = "softorange" +"namespace" = "pink" +"operator" = "pink" +"special" = "softorange" +"warning" = "orange" + +# Editor UI + +## Main +"ui.background" = { bg = "background" } +"ui.text" = "white" +"ui.window" = { bg = "darkgray" } + +## Debug (TODO) + +## Menus +"ui.menu" = { fg = "white", bg = "darkgray" } +"ui.menu.selected" = { modifiers = ["reversed"] } +"ui.popup" = { bg = "darkgray" } +"ui.help" = { fg = "white", bg = "darkgray" } + +## Gutter +"ui.linenr" = "darkgray" +"ui.linenr.selected" = "orange" + +## Cursor +"ui.cursor.primary" = { fg = "white", modifiers = ["reversed"] } +"ui.cursor.match" = { fg = "white", modifiers = ["reversed"] } +"ui.selection" = { bg = "darkgray" } + +## Statusline +"ui.statusline" = { bg = "darkgray" } +"ui.statusline.inactive" = { fg = "white", bg = "darkgray" } +"ui.statusline.normal" = { fg = "white", bg = "blue" } +"ui.statusline.insert" = { fg = "white", bg = "green" } +"ui.statusline.select" = { fg = "white", bg = "purple" } + +"ui.text.focus" = { fg = "yellow", modifiers = ["bold"] } +"ui.virtual" = "darkgray" +"ui.virtual.ruler" = { bg = "darkgray" } + +# Palette + +[palette] +"purple" = "#AE81FF" +"yellow" = "#E6DB74" +"pink" = "#f92a72" +"white" = "#cfcfc2" +"gray" = "#75715e" +"darkgray" = "#444444" +"black" = "#222222" +"blue" = "#66d9ef" +"green" = "#a6e22e" +"softorange" = "#f59762" +"orange" = "#fd971f" +"background" = "#191919" From c72426cc874c70bfb5660b2866e634927e5fb4fd Mon Sep 17 00:00:00 2001 From: Malpha Date: Sun, 18 Feb 2024 23:19:44 +0000 Subject: [PATCH 164/796] Add docker-compose language (#9661) * languages: add docker-compose language it uses docker-compose-langserver as lsp And yaml for syntax highlighting, indents and injections * languages: add luajit as a shebang of lua This helps to provide syntax highlighting and other lua goodies when writing luajit * book(update): run cargo xtask docgen * since #8006 full filenames uses glob --- book/src/generated/lang-support.md | 1 + languages.toml | 13 ++++++++++++- runtime/queries/docker-compose/highlights.scm | 1 + runtime/queries/docker-compose/indents.scm | 1 + runtime/queries/docker-compose/injections.scm | 1 + 5 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 runtime/queries/docker-compose/highlights.scm create mode 100644 runtime/queries/docker-compose/indents.scm create mode 100644 runtime/queries/docker-compose/injections.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index bfe6d6b1e..a37e165f2 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -30,6 +30,7 @@ | devicetree | ✓ | | | | | dhall | ✓ | ✓ | | `dhall-lsp-server` | | diff | ✓ | | | | +| docker-compose | ✓ | | ✓ | `docker-compose-langserver` | | dockerfile | ✓ | | | `docker-langserver` | | dot | ✓ | | | `dot-language-server` | | dtd | ✓ | | | | diff --git a/languages.toml b/languages.toml index 78078ca58..e52dcabbc 100644 --- a/languages.toml +++ b/languages.toml @@ -23,6 +23,7 @@ cuelsp = { command = "cuelsp" } dart = { command = "dart", args = ["language-server", "--client-id=helix"] } dhall-lsp-server = { command = "dhall-lsp-server" } docker-langserver = { command = "docker-langserver", args = ["--stdio"] } +docker-compose-langserver = { command = "docker-compose-langserver", args = ["--stdio"]} dot-language-server = { command = "dot-language-server", args = ["--stdio"] } elixir-ls = { command = "elixir-ls", config = { elixirLS.dialyzerEnabled = false } } elm-language-server = { command = "elm-language-server" } @@ -1092,7 +1093,7 @@ name = "lua" injection-regex = "lua" scope = "source.lua" file-types = ["lua"] -shebangs = ["lua"] +shebangs = ["lua", "luajit"] roots = [".luarc.json", ".luacheckrc", ".stylua.toml", "selene.toml", ".git"] comment-token = "--" indent = { tab-width = 2, unit = " " } @@ -1460,6 +1461,16 @@ language-servers = [ "docker-langserver" ] name = "dockerfile" source = { git = "https://github.com/camdencheek/tree-sitter-dockerfile", rev = "8ee3a0f7587b2bd8c45c8cb7d28bd414604aec62" } +[[language]] +name = "docker-compose" +scope = "source.yaml.docker-compose" +roots = ["docker-compose.yaml", "docker-compose.yml"] +language-servers = [ "docker-compose-langserver" ] +file-types = [{ glob = "docker-compose.yaml" }, { glob = "docker-compose.yml" }] +comment-token = "#" +indent = { tab-width = 2, unit = " " } +grammar = "yaml" + [[language]] name = "git-commit" scope = "git.commitmsg" diff --git a/runtime/queries/docker-compose/highlights.scm b/runtime/queries/docker-compose/highlights.scm new file mode 100644 index 000000000..4ba254e82 --- /dev/null +++ b/runtime/queries/docker-compose/highlights.scm @@ -0,0 +1 @@ +; inherits: yaml diff --git a/runtime/queries/docker-compose/indents.scm b/runtime/queries/docker-compose/indents.scm new file mode 100644 index 000000000..4ba254e82 --- /dev/null +++ b/runtime/queries/docker-compose/indents.scm @@ -0,0 +1 @@ +; inherits: yaml diff --git a/runtime/queries/docker-compose/injections.scm b/runtime/queries/docker-compose/injections.scm new file mode 100644 index 000000000..4ba254e82 --- /dev/null +++ b/runtime/queries/docker-compose/injections.scm @@ -0,0 +1 @@ +; inherits: yaml From 2dc9ce68ec84fd26e1489e3bac76fc5114d0023e Mon Sep 17 00:00:00 2001 From: Jaakko Paju Date: Mon, 19 Feb 2024 01:46:13 +0200 Subject: [PATCH 165/796] Add textobject queries for Nix (#9659) * Add textobject queries for Nix * Add to lang-support.md --- book/src/generated/lang-support.md | 2 +- runtime/queries/nix/textobjects.scm | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 runtime/queries/nix/textobjects.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index a37e165f2..09ed4dac5 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -112,7 +112,7 @@ | nasm | ✓ | ✓ | | | | nickel | ✓ | | ✓ | `nls` | | nim | ✓ | ✓ | ✓ | `nimlangserver` | -| nix | ✓ | | | `nil` | +| nix | ✓ | ✓ | | `nil` | | nu | ✓ | | | `nu` | | nunjucks | ✓ | | | | | ocaml | ✓ | | ✓ | `ocamllsp` | diff --git a/runtime/queries/nix/textobjects.scm b/runtime/queries/nix/textobjects.scm new file mode 100644 index 000000000..1508d4c2b --- /dev/null +++ b/runtime/queries/nix/textobjects.scm @@ -0,0 +1,9 @@ +(comment) @comment.inside +(comment)+ @comment.around + +(formals + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + +(function_expression + body: (_) @function.inside) @function.around + From ebf155d6351a84dec010447af88b8246103537da Mon Sep 17 00:00:00 2001 From: Jaakko Paju Date: Mon, 19 Feb 2024 01:46:32 +0200 Subject: [PATCH 166/796] Add textobject queries for HCL (#9658) * Add textobject queries for HCL * Add to lang-support.md --- book/src/generated/lang-support.md | 2 +- runtime/queries/hcl/textobjects.scm | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 runtime/queries/hcl/textobjects.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 09ed4dac5..f46c9f5e2 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -68,7 +68,7 @@ | hare | ✓ | | | | | haskell | ✓ | ✓ | | `haskell-language-server-wrapper` | | haskell-persistent | ✓ | | | | -| hcl | ✓ | | ✓ | `terraform-ls` | +| hcl | ✓ | ✓ | ✓ | `terraform-ls` | | heex | ✓ | ✓ | | `elixir-ls` | | hocon | ✓ | | ✓ | | | hoon | ✓ | | | | diff --git a/runtime/queries/hcl/textobjects.scm b/runtime/queries/hcl/textobjects.scm new file mode 100644 index 000000000..1e6505876 --- /dev/null +++ b/runtime/queries/hcl/textobjects.scm @@ -0,0 +1,6 @@ +(comment) @comment.inside +(comment)+ @comment.around + +(function_arguments + ((_) @parameter.inside . ","? @parameter.around) @parameter.around) + From 787cc36092a5d1a575697287d1d6ba08336a8a96 Mon Sep 17 00:00:00 2001 From: nkitsaini <74284503+nkitsaini@users.noreply.github.com> Date: Mon, 19 Feb 2024 18:28:17 +0530 Subject: [PATCH 167/796] fix LSP ComplitionTriggerKind value for `TriggerKind::Auto` (#9660) --- helix-term/src/handlers/completion.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/helix-term/src/handlers/completion.rs b/helix-term/src/handlers/completion.rs index d71fd24fc..491ca5638 100644 --- a/helix-term/src/handlers/completion.rs +++ b/helix-term/src/handlers/completion.rs @@ -221,9 +221,17 @@ fn request_completion( .iter() .find(|&trigger| trigger_text.ends_with(trigger)) }); - lsp::CompletionContext { - trigger_kind: lsp::CompletionTriggerKind::TRIGGER_CHARACTER, - trigger_character: trigger_char.cloned(), + + if trigger_char.is_some() { + lsp::CompletionContext { + trigger_kind: lsp::CompletionTriggerKind::TRIGGER_CHARACTER, + trigger_character: trigger_char.cloned(), + } + } else { + lsp::CompletionContext { + trigger_kind: lsp::CompletionTriggerKind::INVOKED, + trigger_character: None, + } } }; From cdef4f8a701f921c29fdfe66f104a2edac7fe05c Mon Sep 17 00:00:00 2001 From: Jonathan LEI Date: Mon, 19 Feb 2024 22:08:26 +0900 Subject: [PATCH 168/796] Make mouse click extend selection in select mode (#5436) * Make mouse click extend selection in select mode * chore: better readability with `Option::take()` --- helix-term/src/ui/editor.rs | 40 ++++++++++++++++++++++++++----------- helix-view/src/editor.rs | 7 +++++-- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index a87e6cbca..66f290a22 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -1088,6 +1088,15 @@ impl EditorView { if modifiers == KeyModifiers::ALT { let selection = doc.selection(view_id).clone(); doc.set_selection(view_id, selection.push(Range::point(pos))); + } else if editor.mode == Mode::Select { + // Discards non-primary selections for consistent UX with normal mode + let primary = doc.selection(view_id).primary().put_cursor( + doc.text().slice(..), + pos, + true, + ); + editor.mouse_down_range = Some(primary); + doc.set_selection(view_id, Selection::single(primary.anchor, primary.head)); } else { doc.set_selection(view_id, Selection::point(pos)); } @@ -1171,19 +1180,26 @@ impl EditorView { let (view, doc) = current!(cxt.editor); - if doc - .selection(view.id) - .primary() - .slice(doc.text().slice(..)) - .len_chars() - <= 1 - { - return EventResult::Ignored(None); - } - - commands::MappableCommand::yank_main_selection_to_primary_clipboard.execute(cxt); + let should_yank = match cxt.editor.mouse_down_range.take() { + Some(down_range) => doc.selection(view.id).primary() != down_range, + None => { + // This should not happen under normal cases. We fall back to the original + // behavior of yanking on non-single-char selections. + doc.selection(view.id) + .primary() + .slice(doc.text().slice(..)) + .len_chars() + > 1 + } + }; - EventResult::Consumed(None) + if should_yank { + commands::MappableCommand::yank_main_selection_to_primary_clipboard + .execute(cxt); + EventResult::Consumed(None) + } else { + EventResult::Ignored(None) + } } MouseEventKind::Up(MouseButton::Right) => { diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 68b74cf00..fffbe6207 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -42,7 +42,7 @@ pub use helix_core::diagnostic::Severity; use helix_core::{ auto_pairs::AutoPairs, syntax::{self, AutoPairConfig, IndentationHeuristic, LanguageServerFeature, SoftWrap}, - Change, LineEnding, Position, Selection, NATIVE_LINE_ENDING, + Change, LineEnding, Position, Range, Selection, NATIVE_LINE_ENDING, }; use helix_dap as dap; use helix_lsp::lsp; @@ -964,6 +964,8 @@ pub struct Editor { /// times during rendering and should not be set by other functions. pub cursor_cache: Cell>>, pub handlers: Handlers, + + pub mouse_down_range: Option, } pub type Motion = Box; @@ -1080,6 +1082,7 @@ impl Editor { needs_redraw: false, cursor_cache: Cell::new(None), handlers, + mouse_down_range: None, } } @@ -1978,7 +1981,7 @@ impl Editor { /// Switches the editor into normal mode. pub fn enter_normal_mode(&mut self) { - use helix_core::{graphemes, Range}; + use helix_core::graphemes; if self.mode == Mode::Normal { return; From 990378a46be2138b2f74799d1af8955390360c4e Mon Sep 17 00:00:00 2001 From: Volodymyr Chernetskyi Date: Mon, 19 Feb 2024 19:37:02 +0200 Subject: [PATCH 169/796] Add Groovy grammar (#9350) * Add Groovy grammar * Rewrite Neovim captures into Helix for Groovy * Simplify Groovy injections Co-authored-by: Michael Davis * Remove Neovim's spell from Groovy highlights Co-authored-by: Michael Davis * Apply suggestions to languages.toml * Escape backslash in groovy highlights.scm --------- Co-authored-by: Michael Davis --- book/src/generated/lang-support.md | 1 + languages.toml | 13 ++++ runtime/queries/groovy/highlights.scm | 96 +++++++++++++++++++++++++++ runtime/queries/groovy/injections.scm | 2 + 4 files changed, 112 insertions(+) create mode 100644 runtime/queries/groovy/highlights.scm create mode 100644 runtime/queries/groovy/injections.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index f46c9f5e2..7aec37778 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -65,6 +65,7 @@ | gotmpl | ✓ | | | `gopls` | | gowork | ✓ | | | `gopls` | | graphql | ✓ | | | `graphql-lsp` | +| groovy | ✓ | | | | | hare | ✓ | | | | | haskell | ✓ | ✓ | | `haskell-language-server-wrapper` | | haskell-persistent | ✓ | | | | diff --git a/languages.toml b/languages.toml index e52dcabbc..1c4c61267 100644 --- a/languages.toml +++ b/languages.toml @@ -3125,3 +3125,16 @@ indent = { tab-width = 2, unit = " " } [[grammar]] name = "pkl" source = { git = "https://github.com/apple/tree-sitter-pkl", rev = "c03f04a313b712f8ab00a2d862c10b37318699ae" } + +[[language]] +name = "groovy" +language-id = "groovy" +scope = "source.groovy" +file-types = ["groovy", "jenkinsfile", { glob = "Jenkinsfile" }, { glob = "Jenkinsfile.*" }] +shebangs = ["groovy"] +comment-token = "//" +indent = { tab-width = 2, unit = " " } + +[[grammar]] +name = "groovy" +source = { git = "https://github.com/Decodetalkers/tree-sitter-groovy", rev = "7e023227f46fee428b16a0288eeb0f65ee2523ec" } diff --git a/runtime/queries/groovy/highlights.scm b/runtime/queries/groovy/highlights.scm new file mode 100644 index 000000000..4e94ccd3a --- /dev/null +++ b/runtime/queries/groovy/highlights.scm @@ -0,0 +1,96 @@ +(unit + (identifier) @variable) + +(string + (identifier) @variable) + +(escape_sequence) @constant.character.escape + +(block + (unit + (identifier) @namespace)) + +(func + (identifier) @function) + +(number) @constant.numeric + +((identifier) @constant.builtin.boolean + (#any-of? @constant.builtin.boolean "true" "false")) + +((identifier) @constant + (#match? @constant "^[A-Z][A-Z\\d_]*$")) + +((identifier) @constant.builtin + (#eq? @constant.builtin "null")) + +((identifier) @type + (#any-of? @type + "String" + "Map" + "Object" + "Boolean" + "Integer" + "List")) + +((identifier) @function.builtin + (#any-of? @function.builtin + "void" + "id" + "version" + "apply" + "implementation" + "testImplementation" + "androidTestImplementation" + "debugImplementation")) + +((identifier) @keyword.storage.modifier + (#eq? @keyword.storage.modifier "static")) + +((identifier) @keyword.storage.type + (#any-of? @keyword.storage.type "class" "def" "interface")) + +((identifier) @keyword + (#any-of? @keyword + "assert" + "new" + "extends" + "implements" + "instanceof")) + +((identifier) @keyword.control.import + (#any-of? @keyword.control.import "import" "package")) + +((identifier) @keyword.storage.modifier + (#any-of? @keyword.storage.modifier + "abstract" + "protected" + "private" + "public")) + +((identifier) @keyword.control.exception + (#any-of? @keyword.control.exception + "throw" + "finally" + "try" + "catch")) + +(string) @string + +[ + (line_comment) + (block_comment) +] @comment + +((block_comment) @comment.block.documentation + (#match? @comment.block.documentation "^/[*][*][^*](?s:.)*[*]/$")) + +((line_comment) @comment.block.documentation + (#match? @comment.block.documentation "^///[^/]*.*$")) + +[ + (operators) + (leading_key) +] @operator + +["(" ")" "[" "]" "{" "}"] @punctuation.bracket diff --git a/runtime/queries/groovy/injections.scm b/runtime/queries/groovy/injections.scm new file mode 100644 index 000000000..e4509a5fd --- /dev/null +++ b/runtime/queries/groovy/injections.scm @@ -0,0 +1,2 @@ +([(line_comment) (block_comment)] @injection.content + (#set! injection.language "comment")) From 27335476edc22c2da652c4636d9b1bae751fe22d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Feb 2024 17:06:40 +0100 Subject: [PATCH 170/796] build(deps): bump chrono from 0.4.33 to 0.4.34 (#9673) Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.33 to 0.4.34. - [Release notes](https://github.com/chronotope/chrono/releases) - [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md) - [Commits](https://github.com/chronotope/chrono/compare/v0.4.33...v0.4.34) --- updated-dependencies: - dependency-name: chrono dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f38f0034..2f8b1f97d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -168,9 +168,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", From d2aacb3e36d59fb6cef42cdc5bd317a3d15d1a0a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Feb 2024 17:06:58 +0100 Subject: [PATCH 171/796] build(deps): bump anyhow from 1.0.79 to 1.0.80 (#9675) Bumps [anyhow](https://github.com/dtolnay/anyhow) from 1.0.79 to 1.0.80. - [Release notes](https://github.com/dtolnay/anyhow/releases) - [Commits](https://github.com/dtolnay/anyhow/compare/1.0.79...1.0.80) --- updated-dependencies: - dependency-name: anyhow dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f8b1f97d..32b5ad84a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,9 +62,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" [[package]] name = "arc-swap" From cad0209e202a7513105639daa36c82578db4032d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Feb 2024 17:07:16 +0100 Subject: [PATCH 172/796] build(deps): bump textwrap from 0.16.0 to 0.16.1 (#9674) Bumps [textwrap](https://github.com/mgeisler/textwrap) from 0.16.0 to 0.16.1. - [Release notes](https://github.com/mgeisler/textwrap/releases) - [Changelog](https://github.com/mgeisler/textwrap/blob/master/CHANGELOG.md) - [Commits](https://github.com/mgeisler/textwrap/compare/0.16.0...0.16.1) --- updated-dependencies: - dependency-name: textwrap dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- helix-core/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 32b5ad84a..2b8a25c85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2231,9 +2231,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" dependencies = [ "smawk", "unicode-linebreak", diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index ca2f505c6..0b0dd7452 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -48,7 +48,7 @@ encoding_rs = "0.8" chrono = { version = "0.4", default-features = false, features = ["alloc", "std"] } etcetera = "0.8" -textwrap = "0.16.0" +textwrap = "0.16.1" nucleo.workspace = true parking_lot = "0.12" From eca537615a554e3b1e379ba53d9f3e0654e67c70 Mon Sep 17 00:00:00 2001 From: Benedikt Ritter Date: Wed, 21 Feb 2024 15:46:31 +0100 Subject: [PATCH 173/796] Use groovy support when editing Gradle files (#9681) The Gradle build tool provides two DSLs for configuring builds. On is based on Groovy and Gradle build files written in Gradle Groovy DSL use *.gradle file ending. This change adds `gradle` to the supported file types of the groovy language configuration. --- languages.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index 1c4c61267..b158606b0 100644 --- a/languages.toml +++ b/languages.toml @@ -3130,7 +3130,7 @@ source = { git = "https://github.com/apple/tree-sitter-pkl", rev = "c03f04a313b7 name = "groovy" language-id = "groovy" scope = "source.groovy" -file-types = ["groovy", "jenkinsfile", { glob = "Jenkinsfile" }, { glob = "Jenkinsfile.*" }] +file-types = ["gradle", "groovy", "jenkinsfile", { glob = "Jenkinsfile" }, { glob = "Jenkinsfile.*" }] shebangs = ["groovy"] comment-token = "//" indent = { tab-width = 2, unit = " " } From 98ebeeebd8c7462409f82d34ff4ac0a7ae9116c7 Mon Sep 17 00:00:00 2001 From: Abderrahmane TAHRI JOUTI <302837+atahrijouti@users.noreply.github.com> Date: Wed, 21 Feb 2024 15:47:14 +0100 Subject: [PATCH 174/796] Cyan Light theme : Add License and Author (#9688) * Cyan Light theme : Add License and Author * Add License Copy license from https://github.com/OlyaB/CyanTheme/blob/master/LICENSE * better credits to original author --- runtime/themes/cyan_light.toml | 10 +++++++--- runtime/themes/licenses/cyan_light.LICENSE | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 runtime/themes/licenses/cyan_light.LICENSE diff --git a/runtime/themes/cyan_light.toml b/runtime/themes/cyan_light.toml index 45cb6539d..a35ad5847 100644 --- a/runtime/themes/cyan_light.toml +++ b/runtime/themes/cyan_light.toml @@ -1,6 +1,10 @@ -# An approximation/port of the Cyan Light Theme from Jetbrains -# -# Original Color Scheme here https://plugins.jetbrains.com/plugin/12102-cyan-light-theme +# Cyan Light +# Adapted from JetBrains' Cyan Light Theme https://plugins.jetbrains.com/plugin/12102-cyan-light-theme +# Author: Abderrahmane Tahri Jouti + +# Original Author : Olga Berdnikova +# LICENSE : MIT +# Source: https://github.com/OlyaB/CyanTheme "attribute" = "blue" "type" = "shade07" diff --git a/runtime/themes/licenses/cyan_light.LICENSE b/runtime/themes/licenses/cyan_light.LICENSE new file mode 100644 index 000000000..3a4a2fb87 --- /dev/null +++ b/runtime/themes/licenses/cyan_light.LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 CloudCannon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From b7b6f300841bb61d8833fee1c58d0e3670849b61 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Mon, 19 Feb 2024 10:07:03 -0500 Subject: [PATCH 175/796] Use a hook for resolving completion items Previously we used the IdleTimeout event to trigger LSP `completion/resolveItem` requests. We can now refactor this to use an event system hook instead and lower the timeout. --- helix-lsp/src/client.rs | 5 +- helix-term/src/ui/completion.rs | 156 ++++++++++++++++++++------------ helix-term/src/ui/editor.rs | 8 -- 3 files changed, 100 insertions(+), 69 deletions(-) diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 0d3a2a56e..8d03d7992 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -1017,7 +1017,7 @@ impl Client { pub fn resolve_completion_item( &self, completion_item: lsp::CompletionItem, - ) -> Option>> { + ) -> Option>> { let capabilities = self.capabilities.get().unwrap(); // Return early if the server does not support resolving completion items. @@ -1029,7 +1029,8 @@ impl Client { _ => return None, } - Some(self.call::(completion_item)) + let res = self.call::(completion_item); + Some(async move { Ok(serde_json::from_value(res.await?)?) }) } pub fn resolve_code_action( diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index 48d97fbd8..6cbb5b109 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -1,7 +1,9 @@ use crate::{ compositor::{Component, Context, Event, EventResult}, handlers::trigger_auto_completion, + job, }; +use helix_event::AsyncHook; use helix_view::{ document::SavePoint, editor::CompleteAction, @@ -10,14 +12,14 @@ use helix_view::{ theme::{Modifier, Style}, ViewId, }; +use tokio::time::Instant; use tui::{buffer::Buffer as Surface, text::Span}; -use std::{borrow::Cow, sync::Arc}; +use std::{borrow::Cow, sync::Arc, time::Duration}; use helix_core::{chars, Change, Transaction}; use helix_view::{graphics::Rect, Document, Editor}; -use crate::commands; use crate::ui::{menu, Markdown, Menu, Popup, PromptEvent}; use helix_lsp::{lsp, util, OffsetEncoding}; @@ -102,6 +104,7 @@ pub struct Completion { #[allow(dead_code)] trigger_offset: usize, filter: String, + resolve_handler: tokio::sync::mpsc::Sender, } impl Completion { @@ -368,6 +371,7 @@ impl Completion { // TODO: expand nucleo api to allow moving straight to a Utf32String here // and avoid allocation during matching filter: String::from(fragment), + resolve_handler: ResolveHandler::default().spawn(), }; // need to recompute immediately in case start_offset != trigger_offset @@ -379,6 +383,8 @@ impl Completion { completion } + /// Synchronously resolve the given completion item. This is used when + /// accepting a completion. fn resolve_completion_item( language_server: &helix_lsp::Client, completion_item: lsp::CompletionItem, @@ -386,7 +392,7 @@ impl Completion { let future = language_server.resolve_completion_item(completion_item)?; let response = helix_lsp::block_on(future); match response { - Ok(value) => serde_json::from_value(value).ok(), + Ok(item) => Some(item), Err(err) => { log::error!("Failed to resolve completion item: {}", err); None @@ -420,62 +426,6 @@ impl Completion { self.popup.contents_mut().replace_option(old_item, new_item); } - /// Asynchronously requests that the currently selection completion item is - /// resolved through LSP `completionItem/resolve`. - pub fn ensure_item_resolved(&mut self, cx: &mut commands::Context) -> bool { - // > If computing full completion items is expensive, servers can additionally provide a - // > handler for the completion item resolve request. ... - // > A typical use case is for example: the `textDocument/completion` request doesn't fill - // > in the `documentation` property for returned completion items since it is expensive - // > to compute. When the item is selected in the user interface then a - // > 'completionItem/resolve' request is sent with the selected completion item as a parameter. - // > The returned completion item should have the documentation property filled in. - // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion - let current_item = match self.popup.contents().selection() { - Some(item) if !item.resolved => item.clone(), - _ => return false, - }; - - let Some(language_server) = cx - .editor - .language_server_by_id(current_item.language_server_id) - else { - return false; - }; - - // This method should not block the compositor so we handle the response asynchronously. - let Some(future) = language_server.resolve_completion_item(current_item.item.clone()) - else { - return false; - }; - - cx.callback( - future, - move |_editor, compositor, response: Option| { - let resolved_item = match response { - Some(item) => item, - None => return, - }; - - if let Some(completion) = &mut compositor - .find::() - .unwrap() - .completion - { - let resolved_item = CompletionItem { - item: resolved_item, - language_server_id: current_item.language_server_id, - resolved: true, - }; - - completion.replace_item(current_item, resolved_item); - } - }, - ); - - true - } - pub fn area(&mut self, viewport: Rect, editor: &Editor) -> Rect { self.popup.area(viewport, editor) } @@ -498,6 +448,9 @@ impl Component for Completion { Some(option) => option, None => return, }; + if !option.resolved { + helix_event::send_blocking(&self.resolve_handler, option.clone()); + } // need to render: // option.detail // --- @@ -599,3 +552,88 @@ impl Component for Completion { markdown_doc.render(doc_area, surface, cx); } } + +/// A hook for resolving incomplete completion items. +/// +/// From the [LSP spec](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion): +/// +/// > If computing full completion items is expensive, servers can additionally provide a +/// > handler for the completion item resolve request. ... +/// > A typical use case is for example: the `textDocument/completion` request doesn't fill +/// > in the `documentation` property for returned completion items since it is expensive +/// > to compute. When the item is selected in the user interface then a +/// > 'completionItem/resolve' request is sent with the selected completion item as a parameter. +/// > The returned completion item should have the documentation property filled in. +#[derive(Debug, Default)] +struct ResolveHandler { + trigger: Option, + request: Option, +} + +impl AsyncHook for ResolveHandler { + type Event = CompletionItem; + + fn handle_event( + &mut self, + item: Self::Event, + timeout: Option, + ) -> Option { + if self + .trigger + .as_ref() + .is_some_and(|trigger| trigger == &item) + { + timeout + } else { + self.trigger = Some(item); + self.request = None; + Some(Instant::now() + Duration::from_millis(150)) + } + } + + fn finish_debounce(&mut self) { + let Some(item) = self.trigger.take() else { return }; + let (tx, rx) = helix_event::cancelation(); + self.request = Some(tx); + job::dispatch_blocking(move |editor, _| resolve_completion_item(editor, item, rx)) + } +} + +fn resolve_completion_item( + editor: &mut Editor, + item: CompletionItem, + cancel: helix_event::CancelRx, +) { + let Some(language_server) = editor.language_server_by_id(item.language_server_id) else { + return; + }; + + let Some(future) = language_server.resolve_completion_item(item.item.clone()) else { + return; + }; + + tokio::spawn(async move { + match helix_event::cancelable_future(future, cancel).await { + Some(Ok(resolved_item)) => { + job::dispatch(move |_, compositor| { + if let Some(completion) = &mut compositor + .find::() + .unwrap() + .completion + { + let resolved_item = CompletionItem { + item: resolved_item, + language_server_id: item.language_server_id, + resolved: true, + }; + + completion.replace_item(item, resolved_item); + }; + }) + .await + } + Some(Err(err)) => log::error!("completion resolve request failed: {err}"), + None => (), + } + }); +} diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 66f290a22..15a7262a8 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -1027,14 +1027,6 @@ impl EditorView { pub fn handle_idle_timeout(&mut self, cx: &mut commands::Context) -> EventResult { commands::compute_inlay_hints_for_all_views(cx.editor, cx.jobs); - if let Some(completion) = &mut self.completion { - return if completion.ensure_item_resolved(cx) { - EventResult::Consumed(None) - } else { - EventResult::Ignored(None) - }; - } - EventResult::Ignored(None) } } From 7100ed4efc2207a9d7a45ce6e9550564e091f508 Mon Sep 17 00:00:00 2001 From: wr7 Date: Fri, 23 Feb 2024 09:46:41 -0600 Subject: [PATCH 176/796] Properly handle spaces in filenames in bash autocomplete (#9702) --- contrib/completion/hx.bash | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/contrib/completion/hx.bash b/contrib/completion/hx.bash index 01b42deb6..6ef0329e4 100644 --- a/contrib/completion/hx.bash +++ b/contrib/completion/hx.bash @@ -5,19 +5,20 @@ _hx() { # $1 command name # $2 word being completed # $3 word preceding - COMPREPLY=() case "$3" in -g | --grammar) - COMPREPLY=($(compgen -W "fetch build" -- $2)) + COMPREPLY="$(compgen -W "fetch build" -- $2)" ;; --health) local languages=$(hx --health |tail -n '+7' |awk '{print $1}' |sed 's/\x1b\[[0-9;]*m//g') - COMPREPLY=($(compgen -W "$languages" -- $2)) + COMPREPLY="$(compgen -W "$languages" -- $2)" ;; *) - COMPREPLY=($(compgen -fd -W "-h --help --tutor -V --version -v -vv -vvv --health -g --grammar --vsplit --hsplit -c --config --log" -- $2)) + COMPREPLY="$(compgen -fd -W "-h --help --tutor -V --version -v -vv -vvv --health -g --grammar --vsplit --hsplit -c --config --log" -- $2)" ;; esac -} && complete -o filenames -F _hx hx + local IFS=$'\n' + COMPREPLY=($COMPREPLY) +} && complete -o filenames -F _hx hx From 03623f2f407d592c38d21e085ae1fb2e1aa1ea22 Mon Sep 17 00:00:00 2001 From: Jake Low Date: Fri, 23 Feb 2024 18:53:16 -0800 Subject: [PATCH 177/796] Add osm and osc extensions to xml language filetypes (#9697) --- languages.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/languages.toml b/languages.toml index b158606b0..ad2e66589 100644 --- a/languages.toml +++ b/languages.toml @@ -2371,6 +2371,8 @@ file-types = [ "menu", "mxml", "nuspec", + "osc", + "osm", "pt", "publishsettings", "pubxml", From 38484f33e5298b2764eb9afd3bf6a60f1facc260 Mon Sep 17 00:00:00 2001 From: wr7 Date: Fri, 23 Feb 2024 20:54:40 -0600 Subject: [PATCH 178/796] Completely fix bash autocomplete handling of filenames with spaces (#9708) --- contrib/completion/hx.bash | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/completion/hx.bash b/contrib/completion/hx.bash index 6ef0329e4..62ca029bf 100644 --- a/contrib/completion/hx.bash +++ b/contrib/completion/hx.bash @@ -8,14 +8,14 @@ _hx() { case "$3" in -g | --grammar) - COMPREPLY="$(compgen -W "fetch build" -- $2)" + COMPREPLY="$(compgen -W 'fetch build' -- $2)" ;; --health) local languages=$(hx --health |tail -n '+7' |awk '{print $1}' |sed 's/\x1b\[[0-9;]*m//g') - COMPREPLY="$(compgen -W "$languages" -- $2)" + COMPREPLY="$(compgen -W """$languages""" -- $2)" ;; *) - COMPREPLY="$(compgen -fd -W "-h --help --tutor -V --version -v -vv -vvv --health -g --grammar --vsplit --hsplit -c --config --log" -- $2)" + COMPREPLY="$(compgen -fd -W "-h --help --tutor -V --version -v -vv -vvv --health -g --grammar --vsplit --hsplit -c --config --log" -- """$2""")" ;; esac From ec9efdef3b2f613a86098058f5705e7863e375e2 Mon Sep 17 00:00:00 2001 From: DS/Charlie <82801887+ds-cbo@users.noreply.github.com> Date: Sat, 24 Feb 2024 04:28:25 +0100 Subject: [PATCH 179/796] Bump tree-sitter-sql (#9634) --- languages.toml | 2 +- runtime/queries/sql/highlights.scm | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/languages.toml b/languages.toml index ad2e66589..112333ea9 100644 --- a/languages.toml +++ b/languages.toml @@ -1825,7 +1825,7 @@ injection-regex = "sql" [[grammar]] name = "sql" -source = { git = "https://github.com/DerekStride/tree-sitter-sql", rev = "25be0b8f17e9189ad9e1b875869d025c5aec1286" } +source = { git = "https://github.com/DerekStride/tree-sitter-sql", rev = "da2d1eff425b146d3c8cab7be8dfa98b11d896dc" } [[language]] name = "gdscript" diff --git a/runtime/queries/sql/highlights.scm b/runtime/queries/sql/highlights.scm index 09b07489e..e575debc5 100644 --- a/runtime/queries/sql/highlights.scm +++ b/runtime/queries/sql/highlights.scm @@ -24,20 +24,20 @@ (term alias: (identifier) @variable.parameter) -(term +((term value: (cast name: (keyword_cast) @function.builtin - parameter: [(literal)]?)) + parameter: [(literal)]?))) (literal) @string (comment) @comment.line (marginalia) @comment.block ((literal) @constant.numeric.integer - (#match? @constant.numeric.integer "^-?\\d+$")) + (#match? @constant.numeric.integer "^[-+]?\\d+$")) ((literal) @constant.numeric.float - (#match? @constant.numeric.float "^-?\\d*\\.\\d*$")) + (#match? @constant.numeric.float "^[-+]?\\d*\\.\\d*$")) (parameter) @variable.parameter From 6db666fce1fb4627c06d147554b8e1eb9970619e Mon Sep 17 00:00:00 2001 From: Mo <76752051+mo8it@users.noreply.github.com> Date: Sat, 24 Feb 2024 16:59:11 +0100 Subject: [PATCH 180/796] Optimization of tilde expansion (#9709) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use next and avoid a redundant prefix strip * Avoid allocations Especially when `expand_tilde` is claled on a path that doesn't contain a tilde. * Add a test * Use Into> * Put the expand_tilde test at the end of the file * Remove unused importsw --- helix-loader/src/lib.rs | 2 +- helix-stdx/src/path.rs | 59 +++++++++++++++++++++++++------- helix-term/src/commands/typed.rs | 14 ++++---- helix-term/src/ui/mod.rs | 4 +-- 4 files changed, 57 insertions(+), 22 deletions(-) diff --git a/helix-loader/src/lib.rs b/helix-loader/src/lib.rs index f8fac6703..93488e452 100644 --- a/helix-loader/src/lib.rs +++ b/helix-loader/src/lib.rs @@ -53,7 +53,7 @@ fn prioritize_runtime_dirs() -> Vec { rt_dirs.push(conf_rt_dir); if let Ok(dir) = std::env::var("HELIX_RUNTIME") { - let dir = path::expand_tilde(dir); + let dir = path::expand_tilde(Path::new(&dir)); rt_dirs.push(path::normalize(dir)); } diff --git a/helix-stdx/src/path.rs b/helix-stdx/src/path.rs index 5746657c3..1dc4d0b24 100644 --- a/helix-stdx/src/path.rs +++ b/helix-stdx/src/path.rs @@ -1,6 +1,9 @@ pub use etcetera::home_dir; -use std::path::{Component, Path, PathBuf}; +use std::{ + borrow::Cow, + path::{Component, Path, PathBuf}, +}; use crate::env::current_working_dir; @@ -19,19 +22,22 @@ pub fn fold_home_dir(path: &Path) -> PathBuf { /// Expands tilde `~` into users home directory if available, otherwise returns the path /// unchanged. The tilde will only be expanded when present as the first component of the path /// and only slash follows it. -pub fn expand_tilde(path: impl AsRef) -> PathBuf { - let path = path.as_ref(); - let mut components = path.components().peekable(); - if let Some(Component::Normal(c)) = components.peek() { - if c == &"~" { - if let Ok(home) = home_dir() { - // it's ok to unwrap, the path starts with `~` - return home.join(path.strip_prefix("~").unwrap()); +pub fn expand_tilde<'a, P>(path: P) -> Cow<'a, Path> +where + P: Into>, +{ + let path = path.into(); + let mut components = path.components(); + if let Some(Component::Normal(c)) = components.next() { + if c == "~" { + if let Ok(mut buf) = home_dir() { + buf.push(components); + return Cow::Owned(buf); } } } - path.to_path_buf() + path } /// Normalize a path without resolving symlinks. @@ -109,9 +115,9 @@ pub fn normalize(path: impl AsRef) -> PathBuf { /// This function is used instead of [`std::fs::canonicalize`] because we don't want to verify /// here if the path exists, just normalize it's components. pub fn canonicalize(path: impl AsRef) -> PathBuf { - let path = expand_tilde(path); + let path = expand_tilde(path.as_ref()); let path = if path.is_relative() { - current_working_dir().join(path) + Cow::Owned(current_working_dir().join(path)) } else { path }; @@ -183,3 +189,32 @@ pub fn get_truncated_path(path: impl AsRef) -> PathBuf { ret.push(file); ret } + +#[cfg(test)] +mod tests { + use std::{ + ffi::OsStr, + path::{Component, Path}, + }; + + use crate::path; + + #[test] + fn expand_tilde() { + for path in ["~", "~/foo"] { + let expanded = path::expand_tilde(Path::new(path)); + + let tilde = Component::Normal(OsStr::new("~")); + + let mut component_count = 0; + for component in expanded.components() { + // No tilde left. + assert_ne!(component, tilde); + component_count += 1; + } + + // The path was at least expanded to something. + assert_ne!(component_count, 0); + } + } +} diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index b7ceeba59..3d7ea3fc8 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -110,14 +110,14 @@ fn open(cx: &mut compositor::Context, args: &[Cow], event: PromptEvent) -> ensure!(!args.is_empty(), "wrong argument count"); for arg in args { let (path, pos) = args::parse_file(arg); - let path = helix_stdx::path::expand_tilde(&path); + let path = helix_stdx::path::expand_tilde(path); // If the path is a directory, open a file picker on that directory and update the status // message if let Ok(true) = std::fs::canonicalize(&path).map(|p| p.is_dir()) { let callback = async move { let call: job::Callback = job::Callback::EditorCompositor(Box::new( move |editor: &mut Editor, compositor: &mut Compositor| { - let picker = ui::file_picker(path, &editor.config()); + let picker = ui::file_picker(path.into_owned(), &editor.config()); compositor.push(Box::new(overlaid(picker))); }, )); @@ -1078,11 +1078,11 @@ fn change_current_directory( return Ok(()); } - let dir = helix_stdx::path::expand_tilde( - args.first() - .context("target directory not provided")? - .as_ref(), - ); + let dir = args + .first() + .context("target directory not provided")? + .as_ref(); + let dir = helix_stdx::path::expand_tilde(Path::new(dir)); helix_stdx::env::set_current_working_dir(dir)?; diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index d27e83553..0873116cb 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -428,9 +428,9 @@ pub mod completers { path } else { match path.parent() { - Some(path) if !path.as_os_str().is_empty() => path.to_path_buf(), + Some(path) if !path.as_os_str().is_empty() => Cow::Borrowed(path), // Path::new("h")'s parent is Some("")... - _ => helix_stdx::env::current_working_dir(), + _ => Cow::Owned(helix_stdx::env::current_working_dir()), } }; From dfa5382c51978c6a582d4586c65aa0f677be2ee8 Mon Sep 17 00:00:00 2001 From: Ryan Mehri <52933714+rmehri01@users.noreply.github.com> Date: Sun, 25 Feb 2024 02:37:54 -0800 Subject: [PATCH 181/796] Don't run scheduled builds on forks (#9718) --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3d47c2088..7ba46ce56 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,6 +12,7 @@ jobs: check: name: Check (msrv) runs-on: ubuntu-latest + if: github.repository == 'helix-editor/helix' || github.event_name != 'schedule' steps: - name: Checkout sources uses: actions/checkout@v4 @@ -31,6 +32,7 @@ jobs: test: name: Test Suite runs-on: ${{ matrix.os }} + if: github.repository == 'helix-editor/helix' || github.event_name != 'schedule' env: RUST_BACKTRACE: 1 HELIX_LOG_LEVEL: info @@ -65,6 +67,7 @@ jobs: lints: name: Lints runs-on: ubuntu-latest + if: github.repository == 'helix-editor/helix' || github.event_name != 'schedule' steps: - name: Checkout sources uses: actions/checkout@v4 @@ -92,6 +95,7 @@ jobs: docs: name: Docs runs-on: ubuntu-latest + if: github.repository == 'helix-editor/helix' || github.event_name != 'schedule' steps: - name: Checkout sources uses: actions/checkout@v4 From 8141a4a1ab78084df94c19e6225fc3c64a05b88f Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Sun, 27 Aug 2023 13:32:17 -0500 Subject: [PATCH 182/796] LSP: Key diagnostics off file path instead of URI URIs need to be normalized to be comparable. For example a language server could send a URI for a path containing '+' as '%2B' but we might encode this in something like 'Document::url' as just '+'. We can normalize the URI straight into a PathBuf though since this is the only value we compare these diagnostics URIs against. This also covers edge-cases like windows drive letter capitalization. --- helix-term/src/application.rs | 6 +-- helix-term/src/commands/lsp.rs | 72 ++++++++++++++++------------------ helix-view/src/editor.rs | 9 ++--- 3 files changed, 39 insertions(+), 48 deletions(-) diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 30df3981c..0ef200c2f 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -753,9 +753,7 @@ impl Application { let lang_conf = doc.language.clone(); if let Some(lang_conf) = &lang_conf { - if let Some(old_diagnostics) = - self.editor.diagnostics.get(¶ms.uri) - { + if let Some(old_diagnostics) = self.editor.diagnostics.get(&path) { if !lang_conf.persistent_diagnostic_sources.is_empty() { // Sort diagnostics first by severity and then by line numbers. // Note: The `lsp::DiagnosticSeverity` enum is already defined in decreasing order @@ -788,7 +786,7 @@ impl Application { // Insert the original lsp::Diagnostics here because we may have no open document // for diagnosic message and so we can't calculate the exact position. // When using them later in the diagnostics picker, we calculate them on-demand. - let diagnostics = match self.editor.diagnostics.entry(params.uri) { + let diagnostics = match self.editor.diagnostics.entry(path) { Entry::Occupied(o) => { let current_diagnostics = o.into_mut(); // there may entries of other language servers, which is why we can't overwrite the whole entry diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index a1f7bf17d..a3168dc2d 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -38,7 +38,7 @@ use std::{ collections::{BTreeMap, HashSet}, fmt::Write, future::Future, - path::PathBuf, + path::{Path, PathBuf}, }; /// Gets the first language server that is attached to a document which supports a specific feature. @@ -134,7 +134,7 @@ struct DiagnosticStyles { } struct PickerDiagnostic { - url: lsp::Url, + path: PathBuf, diag: lsp::Diagnostic, offset_encoding: OffsetEncoding, } @@ -167,8 +167,7 @@ impl ui::menu::Item for PickerDiagnostic { let path = match format { DiagnosticsFormat::HideSourcePath => String::new(), DiagnosticsFormat::ShowSourcePath => { - let file_path = self.url.to_file_path().unwrap(); - let path = path::get_truncated_path(file_path); + let path = path::get_truncated_path(&self.path); format!("{}: ", path.to_string_lossy()) } }; @@ -208,24 +207,33 @@ fn jump_to_location( return; } }; + jump_to_position(editor, &path, location.range, offset_encoding, action); +} - let doc = match editor.open(&path, action) { +fn jump_to_position( + editor: &mut Editor, + path: &Path, + range: lsp::Range, + offset_encoding: OffsetEncoding, + action: Action, +) { + let doc = match editor.open(path, action) { Ok(id) => doc_mut!(editor, &id), Err(err) => { - let err = format!("failed to open path: {:?}: {:?}", location.uri, err); + let err = format!("failed to open path: {:?}: {:?}", path, err); editor.set_error(err); return; } }; let view = view_mut!(editor); // TODO: convert inside server - let new_range = - if let Some(new_range) = lsp_range_to_range(doc.text(), location.range, offset_encoding) { - new_range - } else { - log::warn!("lsp position out of bounds - {:?}", location.range); - return; - }; + let new_range = if let Some(new_range) = lsp_range_to_range(doc.text(), range, offset_encoding) + { + new_range + } else { + log::warn!("lsp position out of bounds - {:?}", range); + return; + }; // we flip the range so that the cursor sits on the start of the symbol // (for example start of the function). doc.set_selection(view.id, Selection::single(new_range.head, new_range.anchor)); @@ -258,21 +266,20 @@ enum DiagnosticsFormat { fn diag_picker( cx: &Context, - diagnostics: BTreeMap>, - _current_path: Option, + diagnostics: BTreeMap>, format: DiagnosticsFormat, ) -> Picker { // TODO: drop current_path comparison and instead use workspace: bool flag? // flatten the map to a vec of (url, diag) pairs let mut flat_diag = Vec::new(); - for (url, diags) in diagnostics { + for (path, diags) in diagnostics { flat_diag.reserve(diags.len()); for (diag, ls) in diags { if let Some(ls) = cx.editor.language_server_by_id(ls) { flat_diag.push(PickerDiagnostic { - url: url.clone(), + path: path.clone(), diag, offset_encoding: ls.offset_encoding(), }); @@ -292,22 +299,17 @@ fn diag_picker( (styles, format), move |cx, PickerDiagnostic { - url, + path, diag, offset_encoding, }, action| { - jump_to_location( - cx.editor, - &lsp::Location::new(url.clone(), diag.range), - *offset_encoding, - action, - ) + jump_to_position(cx.editor, path, diag.range, *offset_encoding, action) }, ) - .with_preview(move |_editor, PickerDiagnostic { url, diag, .. }| { - let location = lsp::Location::new(url.clone(), diag.range); - Some(location_to_file_location(&location)) + .with_preview(move |_editor, PickerDiagnostic { path, diag, .. }| { + let line = Some((diag.range.start.line as usize, diag.range.end.line as usize)); + Some((path.clone().into(), line)) }) .truncate_start(false) } @@ -470,17 +472,16 @@ pub fn workspace_symbol_picker(cx: &mut Context) { pub fn diagnostics_picker(cx: &mut Context) { let doc = doc!(cx.editor); - if let Some(current_url) = doc.url() { + if let Some(current_path) = doc.path() { let diagnostics = cx .editor .diagnostics - .get(¤t_url) + .get(current_path) .cloned() .unwrap_or_default(); let picker = diag_picker( cx, - [(current_url.clone(), diagnostics)].into(), - Some(current_url), + [(current_path.clone(), diagnostics)].into(), DiagnosticsFormat::HideSourcePath, ); cx.push_layer(Box::new(overlaid(picker))); @@ -488,16 +489,9 @@ pub fn diagnostics_picker(cx: &mut Context) { } pub fn workspace_diagnostics_picker(cx: &mut Context) { - let doc = doc!(cx.editor); - let current_url = doc.url(); // TODO not yet filtered by LanguageServerFeature, need to do something similar as Document::shown_diagnostics here for all open documents let diagnostics = cx.editor.diagnostics.clone(); - let picker = diag_picker( - cx, - diagnostics, - current_url, - DiagnosticsFormat::ShowSourcePath, - ); + let picker = diag_picker(cx, diagnostics, DiagnosticsFormat::ShowSourcePath); cx.push_layer(Box::new(overlaid(picker))); } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index fffbe6207..f46a0d6a6 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -914,7 +914,7 @@ pub struct Editor { pub macro_recording: Option<(char, Vec)>, pub macro_replaying: Vec, pub language_servers: helix_lsp::Registry, - pub diagnostics: BTreeMap>, + pub diagnostics: BTreeMap>, pub diff_providers: DiffProviderRegistry, pub debugger: Option, @@ -1815,7 +1815,7 @@ impl Editor { /// Returns all supported diagnostics for the document pub fn doc_diagnostics<'a>( language_servers: &'a helix_lsp::Registry, - diagnostics: &'a BTreeMap>, + diagnostics: &'a BTreeMap>, document: &Document, ) -> impl Iterator + 'a { Editor::doc_diagnostics_with_filter(language_servers, diagnostics, document, |_, _| true) @@ -1825,7 +1825,7 @@ impl Editor { /// filtered by `filter` which is invocated with the raw `lsp::Diagnostic` and the language server id it came from pub fn doc_diagnostics_with_filter<'a>( language_servers: &'a helix_lsp::Registry, - diagnostics: &'a BTreeMap>, + diagnostics: &'a BTreeMap>, document: &Document, filter: impl Fn(&lsp::Diagnostic, usize) -> bool + 'a, @@ -1834,8 +1834,7 @@ impl Editor { let language_config = document.language.clone(); document .path() - .and_then(|path| url::Url::from_file_path(path).ok()) // TODO log error? - .and_then(|uri| diagnostics.get(&uri)) + .and_then(|path| diagnostics.get(path)) .map(|diags| { diags.iter().filter_map(move |(diagnostic, lsp_id)| { let ls = language_servers.get_by_id(*lsp_id)?; From 928bf80d9a1d6206f864e9b375f67662a49a6265 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Sat, 17 Jun 2023 17:01:36 -0500 Subject: [PATCH 183/796] LSP: Normalize diagnostic file paths --- helix-term/src/application.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 0ef200c2f..809393c7f 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -724,7 +724,7 @@ impl Application { } Notification::PublishDiagnostics(mut params) => { let path = match params.uri.to_file_path() { - Ok(path) => path, + Ok(path) => helix_stdx::path::normalize(&path), Err(_) => { log::error!("Unsupported file URI: {}", params.uri); return; From a87614858571ed7f9ab4a3145187cf598ec0faeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carsten=20F=C3=BChrmann?= Date: Mon, 26 Feb 2024 02:53:59 +0100 Subject: [PATCH 184/796] Fix colors of tokyonight diagnostic undercurls (#9724) --- runtime/themes/tokyonight.toml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/runtime/themes/tokyonight.toml b/runtime/themes/tokyonight.toml index 95ebd4087..fbd8f2ed5 100644 --- a/runtime/themes/tokyonight.toml +++ b/runtime/themes/tokyonight.toml @@ -58,13 +58,13 @@ variable = { fg = "fg" } "diff.plus" = { fg = "add" } error = { fg = "error" } -hint = { fg = "hint" } -info = { fg = "info" } warning = { fg = "yellow" } -"diagnostic.error" = { underline = { style = "curl" } } -"diagnostic.warning" = { underline = { style = "curl" } } -"diagnostic.info" = { underline = { style = "curl" } } -"diagnostic.hint" = { underline = { style = "curl" } } +info = { fg = "info" } +hint = { fg = "hint" } +"diagnostic.error" = { underline = { style = "curl", color = "error" } } +"diagnostic.warning" = { underline = { style = "curl", color = "yellow"} } +"diagnostic.info" = { underline = { style = "curl", color = "info"} } +"diagnostic.hint" = { underline = { style = "curl", color = "hint" } } "ui.background" = { bg = "bg", fg = "fg" } "ui.cursor" = { modifiers = ["reversed"] } @@ -114,8 +114,8 @@ change = "#6183bb" delete = "#914c54" error = "#db4b4b" -hint = "#1abc9c" info = "#0db9d7" +hint = "#1abc9c" fg = "#c0caf5" fg-dark = "#a9b1d6" From c68ec92c5e1bd3a2bf402fb583de23693f59b722 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Mon, 26 Feb 2024 08:08:31 +0100 Subject: [PATCH 185/796] slint: Update SHA of tree-sitter parser (#9698) --- languages.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index 112333ea9..750ec9088 100644 --- a/languages.toml +++ b/languages.toml @@ -2178,7 +2178,7 @@ language-servers = [ "slint-lsp" ] [[grammar]] name = "slint" -source = { git = "https://github.com/slint-ui/tree-sitter-slint", rev = "15618215b79b9db08f824a5c97a12d073dcc1c00" } +source = { git = "https://github.com/slint-ui/tree-sitter-slint", rev = "3c82235f41b63f35a01ae3888206e93585cbb84a" } [[language]] name = "task" From cd02976fa3a55c2c1f01b95c40d178061968f797 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Mon, 26 Feb 2024 08:45:20 +0100 Subject: [PATCH 186/796] switch to regex-cursor (#9422) --- Cargo.lock | 18 ++++++- helix-core/src/selection.rs | 96 +++++++++++++++++++++++-------------- helix-core/src/syntax.rs | 12 +++-- helix-stdx/Cargo.toml | 1 + helix-stdx/src/rope.rs | 45 ++++++++++++++++- helix-term/src/commands.rs | 56 ++++++++-------------- helix-term/src/ui/mod.rs | 33 +++++++++---- 7 files changed, 175 insertions(+), 86 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2b8a25c85..b8d375c51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1344,6 +1344,7 @@ version = "23.10.0" dependencies = [ "dunce", "etcetera", + "regex-cursor", "ropey", "tempfile", "which", @@ -1938,15 +1939,28 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] +[[package]] +name = "regex-cursor" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a43718aa0040434d45728c43f56bd53bda75a91c46954cdf0f2ff4dbc8aabbe7" +dependencies = [ + "log", + "memchr", + "regex-automata", + "regex-syntax", + "ropey", +] + [[package]] name = "regex-syntax" version = "0.8.2" diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs index c44685eea..91f1d0de5 100644 --- a/helix-core/src/selection.rs +++ b/helix-core/src/selection.rs @@ -7,9 +7,11 @@ use crate::{ ensure_grapheme_boundary_next, ensure_grapheme_boundary_prev, next_grapheme_boundary, prev_grapheme_boundary, }, + line_ending::get_line_ending, movement::Direction, Assoc, ChangeSet, RopeGraphemes, RopeSlice, }; +use helix_stdx::rope::{self, RopeSliceExt}; use smallvec::{smallvec, SmallVec}; use std::borrow::Cow; @@ -708,12 +710,12 @@ impl IntoIterator for Selection { pub fn keep_or_remove_matches( text: RopeSlice, selection: &Selection, - regex: &crate::regex::Regex, + regex: &rope::Regex, remove: bool, ) -> Option { let result: SmallVec<_> = selection .iter() - .filter(|range| regex.is_match(&range.fragment(text)) ^ remove) + .filter(|range| regex.is_match(text.regex_input_at(range.from()..range.to())) ^ remove) .copied() .collect(); @@ -724,25 +726,20 @@ pub fn keep_or_remove_matches( None } +// TODO: support to split on capture #N instead of whole match pub fn select_on_matches( text: RopeSlice, selection: &Selection, - regex: &crate::regex::Regex, + regex: &rope::Regex, ) -> Option { let mut result = SmallVec::with_capacity(selection.len()); for sel in selection { - // TODO: can't avoid occasional allocations since Regex can't operate on chunks yet - let fragment = sel.fragment(text); - - let sel_start = sel.from(); - let start_byte = text.char_to_byte(sel_start); - - for mat in regex.find_iter(&fragment) { + for mat in regex.find_iter(text.regex_input_at(sel.from()..sel.to())) { // TODO: retain range direction - let start = text.byte_to_char(start_byte + mat.start()); - let end = text.byte_to_char(start_byte + mat.end()); + let start = text.byte_to_char(mat.start()); + let end = text.byte_to_char(mat.end()); let range = Range::new(start, end); // Make sure the match is not right outside of the selection. @@ -761,12 +758,7 @@ pub fn select_on_matches( None } -// TODO: support to split on capture #N instead of whole match -pub fn split_on_matches( - text: RopeSlice, - selection: &Selection, - regex: &crate::regex::Regex, -) -> Selection { +pub fn split_on_newline(text: RopeSlice, selection: &Selection) -> Selection { let mut result = SmallVec::with_capacity(selection.len()); for sel in selection { @@ -776,21 +768,47 @@ pub fn split_on_matches( continue; } - // TODO: can't avoid occasional allocations since Regex can't operate on chunks yet - let fragment = sel.fragment(text); - let sel_start = sel.from(); let sel_end = sel.to(); - let start_byte = text.char_to_byte(sel_start); + let mut start = sel_start; + for mat in sel.slice(text).lines() { + let len = mat.len_chars(); + let line_end_len = get_line_ending(&mat).map(|le| le.len_chars()).unwrap_or(0); + // TODO: retain range direction + result.push(Range::new(start, start + len - line_end_len)); + start += len; + } + + if start < sel_end { + result.push(Range::new(start, sel_end)); + } + } + + // TODO: figure out a new primary index + Selection::new(result, 0) +} + +pub fn split_on_matches(text: RopeSlice, selection: &Selection, regex: &rope::Regex) -> Selection { + let mut result = SmallVec::with_capacity(selection.len()); + + for sel in selection { + // Special case: zero-width selection. + if sel.from() == sel.to() { + result.push(*sel); + continue; + } + + let sel_start = sel.from(); + let sel_end = sel.to(); let mut start = sel_start; - for mat in regex.find_iter(&fragment) { + for mat in regex.find_iter(text.regex_input_at(sel_start..sel_end)) { // TODO: retain range direction - let end = text.byte_to_char(start_byte + mat.start()); + let end = text.byte_to_char(mat.start()); result.push(Range::new(start, end)); - start = text.byte_to_char(start_byte + mat.end()); + start = text.byte_to_char(mat.end()); } if start < sel_end { @@ -1021,14 +1039,12 @@ mod test { #[test] fn test_select_on_matches() { - use crate::regex::{Regex, RegexBuilder}; - let r = Rope::from_str("Nobody expects the Spanish inquisition"); let s = r.slice(..); let selection = Selection::single(0, r.len_chars()); assert_eq!( - select_on_matches(s, &selection, &Regex::new(r"[A-Z][a-z]*").unwrap()), + select_on_matches(s, &selection, &rope::Regex::new(r"[A-Z][a-z]*").unwrap()), Some(Selection::new( smallvec![Range::new(0, 6), Range::new(19, 26)], 0 @@ -1038,8 +1054,14 @@ mod test { let r = Rope::from_str("This\nString\n\ncontains multiple\nlines"); let s = r.slice(..); - let start_of_line = RegexBuilder::new(r"^").multi_line(true).build().unwrap(); - let end_of_line = RegexBuilder::new(r"$").multi_line(true).build().unwrap(); + let start_of_line = rope::RegexBuilder::new() + .syntax(rope::Config::new().multi_line(true)) + .build(r"^") + .unwrap(); + let end_of_line = rope::RegexBuilder::new() + .syntax(rope::Config::new().multi_line(true)) + .build(r"$") + .unwrap(); // line without ending assert_eq!( @@ -1077,9 +1099,9 @@ mod test { select_on_matches( s, &Selection::single(0, s.len_chars()), - &RegexBuilder::new(r"^[a-z ]*$") - .multi_line(true) - .build() + &rope::RegexBuilder::new() + .syntax(rope::Config::new().multi_line(true)) + .build(r"^[a-z ]*$") .unwrap() ), Some(Selection::new( @@ -1171,13 +1193,15 @@ mod test { #[test] fn test_split_on_matches() { - use crate::regex::Regex; - let text = Rope::from(" abcd efg wrs xyz 123 456"); let selection = Selection::new(smallvec![Range::new(0, 9), Range::new(11, 20),], 0); - let result = split_on_matches(text.slice(..), &selection, &Regex::new(r"\s+").unwrap()); + let result = split_on_matches( + text.slice(..), + &selection, + &rope::Regex::new(r"\s+").unwrap(), + ); assert_eq!( result.ranges(), diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index a9344448f..0d8559ca9 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -12,6 +12,7 @@ use arc_swap::{ArcSwap, Guard}; use bitflags::bitflags; use globset::GlobSet; use hashbrown::raw::RawTable; +use helix_stdx::rope::{self, RopeSliceExt}; use slotmap::{DefaultKey as LayerId, HopSlotMap}; use std::{ @@ -1961,11 +1962,16 @@ impl HighlightConfiguration { node_slice }; - static SHEBANG_REGEX: Lazy = Lazy::new(|| Regex::new(SHEBANG).unwrap()); + static SHEBANG_REGEX: Lazy = + Lazy::new(|| rope::Regex::new(SHEBANG).unwrap()); injection_capture = SHEBANG_REGEX - .captures(&Cow::from(lines)) - .map(|cap| InjectionLanguageMarker::Shebang(cap[1].to_owned())) + .captures_iter(lines.regex_input()) + .map(|cap| { + let cap = lines.byte_slice(cap.get_group(1).unwrap().range()); + InjectionLanguageMarker::Shebang(cap.into()) + }) + .next() } else if index == self.injection_content_capture_index { content_node = Some(capture.node); } diff --git a/helix-stdx/Cargo.toml b/helix-stdx/Cargo.toml index 540a1b99a..5ac7c011f 100644 --- a/helix-stdx/Cargo.toml +++ b/helix-stdx/Cargo.toml @@ -16,6 +16,7 @@ dunce = "1.0" etcetera = "0.8" ropey = { version = "1.6.1", default-features = false } which = "6.0" +regex-cursor = "0.1.3" [dev-dependencies] tempfile = "3.10" diff --git a/helix-stdx/src/rope.rs b/helix-stdx/src/rope.rs index 4ee39d4a8..7b4edda4f 100644 --- a/helix-stdx/src/rope.rs +++ b/helix-stdx/src/rope.rs @@ -1,11 +1,22 @@ +use std::ops::{Bound, RangeBounds}; + +pub use regex_cursor::engines::meta::{Builder as RegexBuilder, Regex}; +pub use regex_cursor::regex_automata::util::syntax::Config; +use regex_cursor::{Input as RegexInput, RopeyCursor}; use ropey::RopeSlice; -pub trait RopeSliceExt: Sized { +pub trait RopeSliceExt<'a>: Sized { fn ends_with(self, text: &str) -> bool; fn starts_with(self, text: &str) -> bool; + fn regex_input(self) -> RegexInput>; + fn regex_input_at_bytes>( + self, + byte_range: R, + ) -> RegexInput>; + fn regex_input_at>(self, char_range: R) -> RegexInput>; } -impl RopeSliceExt for RopeSlice<'_> { +impl<'a> RopeSliceExt<'a> for RopeSlice<'a> { fn ends_with(self, text: &str) -> bool { let len = self.len_bytes(); if len < text.len() { @@ -23,4 +34,34 @@ impl RopeSliceExt for RopeSlice<'_> { self.get_byte_slice(..len - text.len()) .map_or(false, |start| start == text) } + + fn regex_input(self) -> RegexInput> { + RegexInput::new(self) + } + + fn regex_input_at>(self, char_range: R) -> RegexInput> { + let start_bound = match char_range.start_bound() { + Bound::Included(&val) => Bound::Included(self.char_to_byte(val)), + Bound::Excluded(&val) => Bound::Excluded(self.char_to_byte(val)), + Bound::Unbounded => Bound::Unbounded, + }; + let end_bound = match char_range.end_bound() { + Bound::Included(&val) => Bound::Included(self.char_to_byte(val)), + Bound::Excluded(&val) => Bound::Excluded(self.char_to_byte(val)), + Bound::Unbounded => Bound::Unbounded, + }; + self.regex_input_at_bytes((start_bound, end_bound)) + } + fn regex_input_at_bytes>( + self, + byte_range: R, + ) -> RegexInput> { + let input = match byte_range.start_bound() { + Bound::Included(&pos) | Bound::Excluded(&pos) => { + RegexInput::new(RopeyCursor::at(self, pos)) + } + Bound::Unbounded => RegexInput::new(self), + }; + input.range(byte_range) + } } diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 51a1ede9b..fdad31a81 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -3,6 +3,7 @@ pub(crate) mod lsp; pub(crate) mod typed; pub use dap::*; +use helix_stdx::rope::{self, RopeSliceExt}; use helix_vcs::Hunk; pub use lsp::*; use tui::widgets::Row; @@ -19,7 +20,7 @@ use helix_core::{ match_brackets, movement::{self, move_vertically_visual, Direction}, object, pos_at_coords, - regex::{self, Regex, RegexBuilder}, + regex::{self, Regex}, search::{self, CharMatcher}, selection, shellwords, surround, syntax::LanguageServerFeature, @@ -1907,11 +1908,7 @@ fn split_selection(cx: &mut Context) { fn split_selection_on_newline(cx: &mut Context) { let (view, doc) = current!(cx.editor); let text = doc.text().slice(..); - // only compile the regex once - #[allow(clippy::trivial_regex)] - static REGEX: Lazy = - Lazy::new(|| Regex::new(r"\r\n|[\n\r\u{000B}\u{000C}\u{0085}\u{2028}\u{2029}]").unwrap()); - let selection = selection::split_on_matches(text, doc.selection(view.id), ®EX); + let selection = selection::split_on_newline(text, doc.selection(view.id)); doc.set_selection(view.id, selection); } @@ -1930,8 +1927,7 @@ fn merge_consecutive_selections(cx: &mut Context) { #[allow(clippy::too_many_arguments)] fn search_impl( editor: &mut Editor, - contents: &str, - regex: &Regex, + regex: &rope::Regex, movement: Movement, direction: Direction, scrolloff: usize, @@ -1959,23 +1955,20 @@ fn search_impl( // do a reverse search and wraparound to the end, we don't need to search // the text before the current cursor position for matches, but by slicing // it out, we need to add it back to the position of the selection. - let mut offset = 0; + let doc = doc!(editor).text().slice(..); // use find_at to find the next match after the cursor, loop around the end // Careful, `Regex` uses `bytes` as offsets, not character indices! let mut mat = match direction { - Direction::Forward => regex.find_at(contents, start), - Direction::Backward => regex.find_iter(&contents[..start]).last(), + Direction::Forward => regex.find(doc.regex_input_at_bytes(start..)), + Direction::Backward => regex.find_iter(doc.regex_input_at_bytes(..start)).last(), }; if mat.is_none() { if wrap_around { mat = match direction { - Direction::Forward => regex.find(contents), - Direction::Backward => { - offset = start; - regex.find_iter(&contents[start..]).last() - } + Direction::Forward => regex.find(doc.regex_input()), + Direction::Backward => regex.find_iter(doc.regex_input_at_bytes(start..)).last(), }; } if show_warnings { @@ -1992,8 +1985,8 @@ fn search_impl( let selection = doc.selection(view.id); if let Some(mat) = mat { - let start = text.byte_to_char(mat.start() + offset); - let end = text.byte_to_char(mat.end() + offset); + let start = text.byte_to_char(mat.start()); + let end = text.byte_to_char(mat.end()); if end == 0 { // skip empty matches that don't make sense @@ -2037,13 +2030,7 @@ fn searcher(cx: &mut Context, direction: Direction) { let scrolloff = config.scrolloff; let wrap_around = config.search.wrap_around; - let doc = doc!(cx.editor); - // TODO: could probably share with select_on_matches? - - // HAXX: sadly we can't avoid allocating a single string for the whole buffer since we can't - // feed chunks into the regex yet - let contents = doc.text().slice(..).to_string(); let completions = search_completions(cx, Some(reg)); ui::regex_prompt( @@ -2065,7 +2052,6 @@ fn searcher(cx: &mut Context, direction: Direction) { } search_impl( cx.editor, - &contents, ®ex, Movement::Move, direction, @@ -2085,8 +2071,6 @@ fn search_next_or_prev_impl(cx: &mut Context, movement: Movement, direction: Dir let config = cx.editor.config(); let scrolloff = config.scrolloff; if let Some(query) = cx.editor.registers.first(register, cx.editor) { - let doc = doc!(cx.editor); - let contents = doc.text().slice(..).to_string(); let search_config = &config.search; let case_insensitive = if search_config.smart_case { !query.chars().any(char::is_uppercase) @@ -2094,15 +2078,17 @@ fn search_next_or_prev_impl(cx: &mut Context, movement: Movement, direction: Dir false }; let wrap_around = search_config.wrap_around; - if let Ok(regex) = RegexBuilder::new(&query) - .case_insensitive(case_insensitive) - .multi_line(true) - .build() + if let Ok(regex) = rope::RegexBuilder::new() + .syntax( + rope::Config::new() + .case_insensitive(case_insensitive) + .multi_line(true), + ) + .build(&query) { for _ in 0..count { search_impl( cx.editor, - &contents, ®ex, movement, direction, @@ -2239,7 +2225,7 @@ fn global_search(cx: &mut Context) { let reg = cx.register.unwrap_or('/'); let completions = search_completions(cx, Some(reg)); - ui::regex_prompt( + ui::raw_regex_prompt( cx, "global-search:".into(), Some(reg), @@ -2250,7 +2236,7 @@ fn global_search(cx: &mut Context) { .map(|comp| (0.., std::borrow::Cow::Owned(comp.clone()))) .collect() }, - move |cx, regex, event| { + move |cx, _, input, event| { if event != PromptEvent::Validate { return; } @@ -2265,7 +2251,7 @@ fn global_search(cx: &mut Context) { if let Ok(matcher) = RegexMatcherBuilder::new() .case_smart(smart_case) - .build(regex.as_str()) + .build(input) { let search_root = helix_stdx::env::current_working_dir(); if !search_root.exists() { diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 0873116cb..a4b148af3 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -18,6 +18,7 @@ use crate::filter_picker_entry; use crate::job::{self, Callback}; pub use completion::{Completion, CompletionItem}; pub use editor::EditorView; +use helix_stdx::rope; pub use markdown::Markdown; pub use menu::Menu; pub use picker::{DynamicPicker, FileLocation, Picker}; @@ -26,8 +27,6 @@ pub use prompt::{Prompt, PromptEvent}; pub use spinner::{ProgressSpinners, Spinner}; pub use text::Text; -use helix_core::regex::Regex; -use helix_core::regex::RegexBuilder; use helix_view::Editor; use std::path::PathBuf; @@ -63,7 +62,22 @@ pub fn regex_prompt( prompt: std::borrow::Cow<'static, str>, history_register: Option, completion_fn: impl FnMut(&Editor, &str) -> Vec + 'static, - fun: impl Fn(&mut crate::compositor::Context, Regex, PromptEvent) + 'static, + fun: impl Fn(&mut crate::compositor::Context, rope::Regex, PromptEvent) + 'static, +) { + raw_regex_prompt( + cx, + prompt, + history_register, + completion_fn, + move |cx, regex, _, event| fun(cx, regex, event), + ); +} +pub fn raw_regex_prompt( + cx: &mut crate::commands::Context, + prompt: std::borrow::Cow<'static, str>, + history_register: Option, + completion_fn: impl FnMut(&Editor, &str) -> Vec + 'static, + fun: impl Fn(&mut crate::compositor::Context, rope::Regex, &str, PromptEvent) + 'static, ) { let (view, doc) = current!(cx.editor); let doc_id = view.doc; @@ -94,10 +108,13 @@ pub fn regex_prompt( false }; - match RegexBuilder::new(input) - .case_insensitive(case_insensitive) - .multi_line(true) - .build() + match rope::RegexBuilder::new() + .syntax( + rope::Config::new() + .case_insensitive(case_insensitive) + .multi_line(true), + ) + .build(input) { Ok(regex) => { let (view, doc) = current!(cx.editor); @@ -110,7 +127,7 @@ pub fn regex_prompt( view.jumps.push((doc_id, snapshot.clone())); } - fun(cx, regex, event); + fun(cx, regex, input, event); let (view, doc) = current!(cx.editor); view.ensure_cursor_in_view(doc, config.scrolloff); From b43d9aa306099ca1b85543bac8453cf7b67eab3e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 01:22:00 +0100 Subject: [PATCH 187/796] build(deps): bump ahash from 0.8.6 to 0.8.9 (#9737) Bumps [ahash](https://github.com/tkaitchuck/ahash) from 0.8.6 to 0.8.9. - [Release notes](https://github.com/tkaitchuck/ahash/releases) - [Commits](https://github.com/tkaitchuck/ahash/commits/v0.8.9) --- updated-dependencies: - dependency-name: ahash dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- helix-core/Cargo.toml | 2 +- helix-event/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b8d375c51..9430cce26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f" dependencies = [ "cfg-if", "getrandom", diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index 0b0dd7452..be5ea5eb8 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -32,7 +32,7 @@ once_cell = "1.19" arc-swap = "1" regex = "1" bitflags = "2.4" -ahash = "0.8.6" +ahash = "0.8.9" hashbrown = { version = "0.14.3", features = ["raw"] } dunce = "1.0" diff --git a/helix-event/Cargo.toml b/helix-event/Cargo.toml index a5c88e93d..8711568e8 100644 --- a/helix-event/Cargo.toml +++ b/helix-event/Cargo.toml @@ -12,7 +12,7 @@ homepage.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ahash = "0.8.3" +ahash = "0.8.9" hashbrown = "0.14.0" tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "parking_lot", "macros"] } # the event registry is essentially read only but must be an rwlock so we can From d0f8261141f22c7954d1665bcbc4e89bda9bd6cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 01:25:17 +0100 Subject: [PATCH 188/796] build(deps): bump tempfile from 3.10.0 to 3.10.1 (#9733) Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.10.0 to 3.10.1. - [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md) - [Commits](https://github.com/Stebalien/tempfile/compare/v3.10.0...v3.10.1) --- updated-dependencies: - dependency-name: tempfile dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- helix-loader/Cargo.toml | 2 +- helix-term/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9430cce26..d376538e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2215,9 +2215,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", diff --git a/helix-loader/Cargo.toml b/helix-loader/Cargo.toml index 25b559696..d15d87f95 100644 --- a/helix-loader/Cargo.toml +++ b/helix-loader/Cargo.toml @@ -30,7 +30,7 @@ log = "0.4" # cloning/compiling tree-sitter grammars cc = { version = "1" } threadpool = { version = "1.0" } -tempfile = "3.10.0" +tempfile = "3.10.1" dunce = "1.0.4" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 1e21ec161..8c6ae9f42 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -84,4 +84,4 @@ helix-loader = { path = "../helix-loader" } [dev-dependencies] smallvec = "1.13" indoc = "2.0.4" -tempfile = "3.10.0" +tempfile = "3.10.1" From ea95c687751ebee391088bde269f0b40267dfdf0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 01:25:52 +0100 Subject: [PATCH 189/796] build(deps): bump serde_json from 1.0.113 to 1.0.114 (#9735) Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.113 to 1.0.114. - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.113...v1.0.114) --- updated-dependencies: - dependency-name: serde_json dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d376538e4..6f4d9758e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2045,9 +2045,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.113" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", From 1a82aeeae91be33cb0923c9f652fa6db250efd7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 01:26:24 +0100 Subject: [PATCH 190/796] build(deps): bump serde from 1.0.196 to 1.0.197 (#9736) Bumps [serde](https://github.com/serde-rs/serde) from 1.0.196 to 1.0.197. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.196...v1.0.197) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f4d9758e..7f57b75d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2025,18 +2025,18 @@ checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" [[package]] name = "serde" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", From 358ac6bc1f512ca7303856dc904d4b4cdc1fe718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B7=A2=E9=B9=8F?= Date: Mon, 26 Feb 2024 19:41:50 -0500 Subject: [PATCH 191/796] add fidl support (#9713) --- book/src/generated/lang-support.md | 1 + languages.toml | 18 ++++++++ runtime/queries/fidl/folds.scm | 6 +++ runtime/queries/fidl/highlights.scm | 64 +++++++++++++++++++++++++++++ runtime/queries/fidl/injections.scm | 2 + 5 files changed, 91 insertions(+) create mode 100644 runtime/queries/fidl/folds.scm create mode 100644 runtime/queries/fidl/highlights.scm create mode 100644 runtime/queries/fidl/injections.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 7aec37778..1bc6b0817 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -44,6 +44,7 @@ | erb | ✓ | | | | | erlang | ✓ | ✓ | | `erlang_ls` | | esdl | ✓ | | | | +| fidl | ✓ | | | | | fish | ✓ | ✓ | ✓ | | | forth | ✓ | | | `forth-lsp` | | fortran | ✓ | | ✓ | `fortls` | diff --git a/languages.toml b/languages.toml index 750ec9088..313b3d95e 100644 --- a/languages.toml +++ b/languages.toml @@ -3140,3 +3140,21 @@ indent = { tab-width = 2, unit = " " } [[grammar]] name = "groovy" source = { git = "https://github.com/Decodetalkers/tree-sitter-groovy", rev = "7e023227f46fee428b16a0288eeb0f65ee2523ec" } + +[[language]] +name = "fidl" +scope = "source.fidl" +injection-regex = "fidl" +file-types = ["fidl"] +comment-token = "//" +indent = { tab-width = 4, unit = " " } + +[language.auto-pairs] +'"' = '"' +'{' = '}' +'(' = ')' +'<' = '>' + +[[grammar]] +name = "fidl" +source = { git = "https://github.com/google/tree-sitter-fidl", rev = "bdbb635a7f5035e424f6173f2f11b9cd79703f8d" } diff --git a/runtime/queries/fidl/folds.scm b/runtime/queries/fidl/folds.scm new file mode 100644 index 000000000..f524c455b --- /dev/null +++ b/runtime/queries/fidl/folds.scm @@ -0,0 +1,6 @@ +[ + (layout_declaration) + (protocol_declaration) + (resource_declaration) + (service_declaration) +] @fold diff --git a/runtime/queries/fidl/highlights.scm b/runtime/queries/fidl/highlights.scm new file mode 100644 index 000000000..c70d22198 --- /dev/null +++ b/runtime/queries/fidl/highlights.scm @@ -0,0 +1,64 @@ +[ + "ajar" + "alias" + "as" + "bits" + "closed" + "compose" + "const" + "enum" + "error" + "flexible" + "library" + "open" + ; "optional" we did not specify a node for optional yet + "overlay" + "protocol" + "reserved" + "resource" + "service" + "strict" + "struct" + "table" + "type" + "union" + "using" +] @keyword + +(primitives_type) @type.builtin + +(builtin_complex_type) @type.builtin + +(const_declaration + (identifier) @constant) + +[ + "=" + "|" + "&" + "->" +] @operator + +(attribute + "@" @attribute + (identifier) @attribute) + +(string_literal) @string + +(numeric_literal) @constant.numeric + +[ + (true) + (false) +] @constant.builtin.boolean + +(comment) @comment + +[ + "(" + ")" + "<" + ">" + "{" + "}" +] @punctuation.bracket diff --git a/runtime/queries/fidl/injections.scm b/runtime/queries/fidl/injections.scm new file mode 100644 index 000000000..2f0e58eb6 --- /dev/null +++ b/runtime/queries/fidl/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) From f46a09ab4f945273c7baf32e58438b501914fabb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 02:06:37 +0100 Subject: [PATCH 192/796] build(deps): bump cc from 1.0.85 to 1.0.88 (#9734) Bumps [cc](https://github.com/rust-lang/cc-rs) from 1.0.85 to 1.0.88. - [Release notes](https://github.com/rust-lang/cc-rs/releases) - [Commits](https://github.com/rust-lang/cc-rs/compare/1.0.85...1.0.88) --- updated-dependencies: - dependency-name: cc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7f57b75d3..08fa4789e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -145,9 +145,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "cc" -version = "1.0.85" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b918671670962b48bc23753aef0c51d072dca6f52f01f800854ada6ddb7f7d3" +checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" [[package]] name = "cfg-if" From 26b3dc29be886c5a2ed1a5caaaf09eb730829c3e Mon Sep 17 00:00:00 2001 From: Gabriel Dinner-David <82682503+gabydd@users.noreply.github.com> Date: Tue, 27 Feb 2024 08:36:25 -0500 Subject: [PATCH 193/796] toggling of block comments (#4718) --- book/src/keymap.md | 4 + book/src/languages.md | 5 +- helix-core/src/comment.rs | 270 ++++++++++++++++++++++++++++++- helix-core/src/indent.rs | 4 +- helix-core/src/lib.rs | 3 - helix-core/src/syntax.rs | 67 +++++++- helix-core/tests/indent.rs | 3 +- helix-stdx/src/rope.rs | 11 ++ helix-term/src/commands.rs | 136 ++++++++++++++-- helix-term/src/keymap/default.rs | 3 + languages.toml | 91 ++++++++++- 11 files changed, 568 insertions(+), 29 deletions(-) diff --git a/book/src/keymap.md b/book/src/keymap.md index ac84147cd..bb09b0319 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -12,6 +12,7 @@ - [Match mode](#match-mode) - [Window mode](#window-mode) - [Space mode](#space-mode) + - [Comment mode](#comment-mode) - [Popup](#popup) - [Unimpaired](#unimpaired) - [Insert mode](#insert-mode) @@ -289,6 +290,9 @@ This layer is a kludge of mappings, mostly pickers. | `h` | Select symbol references (**LSP**) | `select_references_to_symbol_under_cursor` | | `'` | Open last fuzzy picker | `last_picker` | | `w` | Enter [window mode](#window-mode) | N/A | +| `c` | Comment/uncomment selections | `toggle_comments` | +| `C` | Block comment/uncomment selections | `toggle_block_comments` | +| `Alt-c` | Line comment/uncomment selections | `toggle_line_comments` | | `p` | Paste system clipboard after selections | `paste_clipboard_after` | | `P` | Paste system clipboard before selections | `paste_clipboard_before` | | `y` | Yank selections to clipboard | `yank_to_clipboard` | diff --git a/book/src/languages.md b/book/src/languages.md index e3900dca9..dd93fec53 100644 --- a/book/src/languages.md +++ b/book/src/languages.md @@ -42,7 +42,7 @@ name = "mylang" scope = "source.mylang" injection-regex = "mylang" file-types = ["mylang", "myl"] -comment-token = "#" +comment-tokens = "#" indent = { tab-width = 2, unit = " " } formatter = { command = "mylang-formatter" , args = ["--stdin"] } language-servers = [ "mylang-lsp" ] @@ -61,7 +61,8 @@ These configuration keys are available: | `roots` | A set of marker files to look for when trying to find the workspace root. For example `Cargo.lock`, `yarn.lock` | | `auto-format` | Whether to autoformat this language when saving | | `diagnostic-severity` | Minimal severity of diagnostic for it to be displayed. (Allowed values: `Error`, `Warning`, `Info`, `Hint`) | -| `comment-token` | The token to use as a comment-token | +| `comment-tokens` | The tokens to use as a comment token, either a single token `"//"` or an array `["//", "///", "//!"]` (the first token will be used for commenting). Also configurable as `comment-token` for backwards compatibility| +| `block-comment-tokens`| The start and end tokens for a multiline comment either an array or single table of `{ start = "/*", end = "*/"}`. The first set of tokens will be used for commenting, any pairs in the array can be uncommented | | `indent` | The indent to use. Has sub keys `unit` (the text inserted into the document when indenting; usually set to N spaces or `"\t"` for tabs) and `tab-width` (the number of spaces rendered for a tab) | | `language-servers` | The Language Servers used for this language. See below for more information in the section [Configuring Language Servers for a language](#configuring-language-servers-for-a-language) | | `grammar` | The tree-sitter grammar to use (defaults to the value of `name`) | diff --git a/helix-core/src/comment.rs b/helix-core/src/comment.rs index 9c7e50f33..536b710ab 100644 --- a/helix-core/src/comment.rs +++ b/helix-core/src/comment.rs @@ -1,9 +1,12 @@ //! This module contains the functionality toggle comments on lines over the selection //! using the comment character defined in the user's `languages.toml` +use smallvec::SmallVec; + use crate::{ - find_first_non_whitespace_char, Change, Rope, RopeSlice, Selection, Tendril, Transaction, + syntax::BlockCommentToken, Change, Range, Rope, RopeSlice, Selection, Tendril, Transaction, }; +use helix_stdx::rope::RopeSliceExt; use std::borrow::Cow; /// Given text, a comment token, and a set of line indices, returns the following: @@ -22,12 +25,12 @@ fn find_line_comment( ) -> (bool, Vec, usize, usize) { let mut commented = true; let mut to_change = Vec::new(); - let mut min = usize::MAX; // minimum col for find_first_non_whitespace_char + let mut min = usize::MAX; // minimum col for first_non_whitespace_char let mut margin = 1; let token_len = token.chars().count(); for line in lines { let line_slice = text.line(line); - if let Some(pos) = find_first_non_whitespace_char(line_slice) { + if let Some(pos) = line_slice.first_non_whitespace_char() { let len = line_slice.len_chars(); if pos < min { @@ -94,6 +97,222 @@ pub fn toggle_line_comments(doc: &Rope, selection: &Selection, token: Option<&st Transaction::change(doc, changes.into_iter()) } +#[derive(Debug, PartialEq, Eq)] +pub enum CommentChange { + Commented { + range: Range, + start_pos: usize, + end_pos: usize, + start_margin: bool, + end_margin: bool, + start_token: String, + end_token: String, + }, + Uncommented { + range: Range, + start_pos: usize, + end_pos: usize, + start_token: String, + end_token: String, + }, + Whitespace { + range: Range, + }, +} + +pub fn find_block_comments( + tokens: &[BlockCommentToken], + text: RopeSlice, + selection: &Selection, +) -> (bool, Vec) { + let mut commented = true; + let mut only_whitespace = true; + let mut comment_changes = Vec::with_capacity(selection.len()); + let default_tokens = tokens.first().cloned().unwrap_or_default(); + // TODO: check if this can be removed on MSRV bump + #[allow(clippy::redundant_clone)] + let mut start_token = default_tokens.start.clone(); + #[allow(clippy::redundant_clone)] + let mut end_token = default_tokens.end.clone(); + + let mut tokens = tokens.to_vec(); + // sort the tokens by length, so longer tokens will match first + tokens.sort_by(|a, b| { + if a.start.len() == b.start.len() { + b.end.len().cmp(&a.end.len()) + } else { + b.start.len().cmp(&a.start.len()) + } + }); + for range in selection { + let selection_slice = range.slice(text); + if let (Some(start_pos), Some(end_pos)) = ( + selection_slice.first_non_whitespace_char(), + selection_slice.last_non_whitespace_char(), + ) { + let mut line_commented = false; + let mut after_start = 0; + let mut before_end = 0; + let len = (end_pos + 1) - start_pos; + + for BlockCommentToken { start, end } in &tokens { + let start_len = start.chars().count(); + let end_len = end.chars().count(); + after_start = start_pos + start_len; + before_end = end_pos.saturating_sub(end_len); + + if len >= start_len + end_len { + let start_fragment = selection_slice.slice(start_pos..after_start); + let end_fragment = selection_slice.slice(before_end + 1..end_pos + 1); + + // block commented with these tokens + if start_fragment == start.as_str() && end_fragment == end.as_str() { + start_token = start.to_string(); + end_token = end.to_string(); + line_commented = true; + break; + } + } + } + + if !line_commented { + comment_changes.push(CommentChange::Uncommented { + range: *range, + start_pos, + end_pos, + start_token: default_tokens.start.clone(), + end_token: default_tokens.end.clone(), + }); + commented = false; + } else { + comment_changes.push(CommentChange::Commented { + range: *range, + start_pos, + end_pos, + start_margin: selection_slice + .get_char(after_start) + .map_or(false, |c| c == ' '), + end_margin: after_start != before_end + && selection_slice + .get_char(before_end) + .map_or(false, |c| c == ' '), + start_token: start_token.to_string(), + end_token: end_token.to_string(), + }); + } + only_whitespace = false; + } else { + comment_changes.push(CommentChange::Whitespace { range: *range }); + } + } + if only_whitespace { + commented = false; + } + (commented, comment_changes) +} + +#[must_use] +pub fn create_block_comment_transaction( + doc: &Rope, + selection: &Selection, + commented: bool, + comment_changes: Vec, +) -> (Transaction, SmallVec<[Range; 1]>) { + let mut changes: Vec = Vec::with_capacity(selection.len() * 2); + let mut ranges: SmallVec<[Range; 1]> = SmallVec::with_capacity(selection.len()); + let mut offs = 0; + for change in comment_changes { + if commented { + if let CommentChange::Commented { + range, + start_pos, + end_pos, + start_token, + end_token, + start_margin, + end_margin, + } = change + { + let from = range.from(); + changes.push(( + from + start_pos, + from + start_pos + start_token.len() + start_margin as usize, + None, + )); + changes.push(( + from + end_pos - end_token.len() - end_margin as usize + 1, + from + end_pos + 1, + None, + )); + } + } else { + // uncommented so manually map ranges through changes + match change { + CommentChange::Uncommented { + range, + start_pos, + end_pos, + start_token, + end_token, + } => { + let from = range.from(); + changes.push(( + from + start_pos, + from + start_pos, + Some(Tendril::from(format!("{} ", start_token))), + )); + changes.push(( + from + end_pos + 1, + from + end_pos + 1, + Some(Tendril::from(format!(" {}", end_token))), + )); + + let offset = start_token.chars().count() + end_token.chars().count() + 2; + ranges.push( + Range::new(from + offs, from + offs + end_pos + 1 + offset) + .with_direction(range.direction()), + ); + offs += offset; + } + CommentChange::Commented { range, .. } | CommentChange::Whitespace { range } => { + ranges.push(Range::new(range.from() + offs, range.to() + offs)); + } + } + } + } + (Transaction::change(doc, changes.into_iter()), ranges) +} + +#[must_use] +pub fn toggle_block_comments( + doc: &Rope, + selection: &Selection, + tokens: &[BlockCommentToken], +) -> Transaction { + let text = doc.slice(..); + let (commented, comment_changes) = find_block_comments(tokens, text, selection); + let (mut transaction, ranges) = + create_block_comment_transaction(doc, selection, commented, comment_changes); + if !commented { + transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index())); + } + transaction +} + +pub fn split_lines_of_selection(text: RopeSlice, selection: &Selection) -> Selection { + let mut ranges = SmallVec::new(); + for range in selection.ranges() { + let (line_start, line_end) = range.line_range(text.slice(..)); + let mut pos = text.line_to_char(line_start); + for line in text.slice(pos..text.line_to_char(line_end + 1)).lines() { + let start = pos; + pos += line.len_chars(); + ranges.push(Range::new(start, pos)); + } + } + Selection::new(ranges, 0) +} + #[cfg(test)] mod test { use super::*; @@ -149,4 +368,49 @@ mod test { // TODO: account for uncommenting with uneven comment indentation } + + #[test] + fn test_find_block_comments() { + // three lines 5 characters. + let mut doc = Rope::from("1\n2\n3"); + // select whole document + let selection = Selection::single(0, doc.len_chars()); + + let text = doc.slice(..); + + let res = find_block_comments(&[BlockCommentToken::default()], text, &selection); + + assert_eq!( + res, + ( + false, + vec![CommentChange::Uncommented { + range: Range::new(0, 5), + start_pos: 0, + end_pos: 4, + start_token: "/*".to_string(), + end_token: "*/".to_string(), + }] + ) + ); + + // comment + let transaction = toggle_block_comments(&doc, &selection, &[BlockCommentToken::default()]); + transaction.apply(&mut doc); + + assert_eq!(doc, "/* 1\n2\n3 */"); + + // uncomment + let selection = Selection::single(0, doc.len_chars()); + let transaction = toggle_block_comments(&doc, &selection, &[BlockCommentToken::default()]); + transaction.apply(&mut doc); + assert_eq!(doc, "1\n2\n3"); + + // don't panic when there is just a space in comment + doc = Rope::from("/* */"); + let selection = Selection::single(0, doc.len_chars()); + let transaction = toggle_block_comments(&doc, &selection, &[BlockCommentToken::default()]); + transaction.apply(&mut doc); + assert_eq!(doc, ""); + } } diff --git a/helix-core/src/indent.rs b/helix-core/src/indent.rs index c29bb3a0b..2a0a3876c 100644 --- a/helix-core/src/indent.rs +++ b/helix-core/src/indent.rs @@ -1,10 +1,10 @@ use std::{borrow::Cow, collections::HashMap}; +use helix_stdx::rope::RopeSliceExt; use tree_sitter::{Query, QueryCursor, QueryPredicateArg}; use crate::{ chars::{char_is_line_ending, char_is_whitespace}, - find_first_non_whitespace_char, graphemes::{grapheme_width, tab_width_at}, syntax::{IndentationHeuristic, LanguageConfiguration, RopeProvider, Syntax}, tree_sitter::Node, @@ -970,7 +970,7 @@ pub fn indent_for_newline( let mut num_attempts = 0; for line_idx in (0..=line_before).rev() { let line = text.line(line_idx); - let first_non_whitespace_char = match find_first_non_whitespace_char(line) { + let first_non_whitespace_char = match line.first_non_whitespace_char() { Some(i) => i, None => { continue; diff --git a/helix-core/src/lib.rs b/helix-core/src/lib.rs index 94802eba9..1abd90d10 100644 --- a/helix-core/src/lib.rs +++ b/helix-core/src/lib.rs @@ -37,9 +37,6 @@ pub mod unicode { pub use helix_loader::find_workspace; -pub fn find_first_non_whitespace_char(line: RopeSlice) -> Option { - line.chars().position(|ch| !ch.is_whitespace()) -} mod rope_reader; pub use rope_reader::RopeReader; diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 0d8559ca9..3b224e1b2 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -99,7 +99,19 @@ pub struct LanguageConfiguration { pub shebangs: Vec, // interpreter(s) associated with language #[serde(default)] pub roots: Vec, // these indicate project roots <.git, Cargo.toml> - pub comment_token: Option, + #[serde( + default, + skip_serializing, + deserialize_with = "from_comment_tokens", + alias = "comment-token" + )] + pub comment_tokens: Option>, + #[serde( + default, + skip_serializing, + deserialize_with = "from_block_comment_tokens" + )] + pub block_comment_tokens: Option>, pub text_width: Option, pub soft_wrap: Option, @@ -240,6 +252,59 @@ impl<'de> Deserialize<'de> for FileType { } } +fn from_comment_tokens<'de, D>(deserializer: D) -> Result>, D::Error> +where + D: serde::Deserializer<'de>, +{ + #[derive(Deserialize)] + #[serde(untagged)] + enum CommentTokens { + Multiple(Vec), + Single(String), + } + Ok( + Option::::deserialize(deserializer)?.map(|tokens| match tokens { + CommentTokens::Single(val) => vec![val], + CommentTokens::Multiple(vals) => vals, + }), + ) +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct BlockCommentToken { + pub start: String, + pub end: String, +} + +impl Default for BlockCommentToken { + fn default() -> Self { + BlockCommentToken { + start: "/*".to_string(), + end: "*/".to_string(), + } + } +} + +fn from_block_comment_tokens<'de, D>( + deserializer: D, +) -> Result>, D::Error> +where + D: serde::Deserializer<'de>, +{ + #[derive(Deserialize)] + #[serde(untagged)] + enum BlockCommentTokens { + Multiple(Vec), + Single(BlockCommentToken), + } + Ok( + Option::::deserialize(deserializer)?.map(|tokens| match tokens { + BlockCommentTokens::Single(val) => vec![val], + BlockCommentTokens::Multiple(vals) => vals, + }), + ) +} + #[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] #[serde(rename_all = "kebab-case")] pub enum LanguageServerFeature { diff --git a/helix-core/tests/indent.rs b/helix-core/tests/indent.rs index 53265e0b1..31946c56e 100644 --- a/helix-core/tests/indent.rs +++ b/helix-core/tests/indent.rs @@ -4,6 +4,7 @@ use helix_core::{ syntax::{Configuration, Loader}, Syntax, }; +use helix_stdx::rope::RopeSliceExt; use ropey::Rope; use std::{ops::Range, path::PathBuf, process::Command, sync::Arc}; @@ -211,7 +212,7 @@ fn test_treesitter_indent( if ignored_lines.iter().any(|range| range.contains(&(i + 1))) { continue; } - if let Some(pos) = helix_core::find_first_non_whitespace_char(line) { + if let Some(pos) = line.first_non_whitespace_char() { let tab_width: usize = 4; let suggested_indent = treesitter_indent_for_pos( indent_query, diff --git a/helix-stdx/src/rope.rs b/helix-stdx/src/rope.rs index 7b4edda4f..7e2549f5a 100644 --- a/helix-stdx/src/rope.rs +++ b/helix-stdx/src/rope.rs @@ -14,6 +14,8 @@ pub trait RopeSliceExt<'a>: Sized { byte_range: R, ) -> RegexInput>; fn regex_input_at>(self, char_range: R) -> RegexInput>; + fn first_non_whitespace_char(self) -> Option; + fn last_non_whitespace_char(self) -> Option; } impl<'a> RopeSliceExt<'a> for RopeSlice<'a> { @@ -64,4 +66,13 @@ impl<'a> RopeSliceExt<'a> for RopeSlice<'a> { }; input.range(byte_range) } + fn first_non_whitespace_char(self) -> Option { + self.chars().position(|ch| !ch.is_whitespace()) + } + fn last_non_whitespace_char(self) -> Option { + self.chars_at(self.len_chars()) + .reversed() + .position(|ch| !ch.is_whitespace()) + .map(|pos| self.len_chars() - pos - 1) + } } diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index fdad31a81..bd0a60b7c 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -12,7 +12,7 @@ pub use typed::*; use helix_core::{ char_idx_at_visual_offset, comment, doc_formatter::TextFormat, - encoding, find_first_non_whitespace_char, find_workspace, graphemes, + encoding, find_workspace, graphemes, history::UndoKind, increment, indent, indent::IndentStyle, @@ -23,7 +23,7 @@ use helix_core::{ regex::{self, Regex}, search::{self, CharMatcher}, selection, shellwords, surround, - syntax::LanguageServerFeature, + syntax::{BlockCommentToken, LanguageServerFeature}, text_annotations::TextAnnotations, textobject, tree_sitter::Node, @@ -415,6 +415,8 @@ impl MappableCommand { completion, "Invoke completion popup", hover, "Show docs for item under cursor", toggle_comments, "Comment/uncomment selections", + toggle_line_comments, "Line comment/uncomment selections", + toggle_block_comments, "Block comment/uncomment selections", rotate_selections_forward, "Rotate selections forward", rotate_selections_backward, "Rotate selections backward", rotate_selection_contents_forward, "Rotate selection contents forward", @@ -822,7 +824,7 @@ fn kill_to_line_start(cx: &mut Context) { let head = if anchor == first_char && line != 0 { // select until previous line line_end_char_index(&text, line - 1) - } else if let Some(pos) = find_first_non_whitespace_char(text.line(line)) { + } else if let Some(pos) = text.line(line).first_non_whitespace_char() { if first_char + pos < anchor { // select until first non-blank in line if cursor is after it first_char + pos @@ -884,7 +886,7 @@ fn goto_first_nonwhitespace_impl(view: &mut View, doc: &mut Document, movement: let selection = doc.selection(view.id).clone().transform(|range| { let line = range.cursor_line(text); - if let Some(pos) = find_first_non_whitespace_char(text.line(line)) { + if let Some(pos) = text.line(line).first_non_whitespace_char() { let pos = pos + text.line_to_char(line); range.put_cursor(text, pos, movement == Movement::Extend) } else { @@ -3087,11 +3089,11 @@ fn insert_with_indent(cx: &mut Context, cursor_fallback: IndentFallbackPos) { } else { // move cursor to the fallback position let pos = match cursor_fallback { - IndentFallbackPos::LineStart => { - find_first_non_whitespace_char(text.line(cursor_line)) - .map(|ws_offset| ws_offset + cursor_line_start) - .unwrap_or(cursor_line_start) - } + IndentFallbackPos::LineStart => text + .line(cursor_line) + .first_non_whitespace_char() + .map(|ws_offset| ws_offset + cursor_line_start) + .unwrap_or(cursor_line_start), IndentFallbackPos::LineEnd => line_end_char_index(&text, cursor_line), }; @@ -4462,18 +4464,124 @@ pub fn completion(cx: &mut Context) { } // comments -fn toggle_comments(cx: &mut Context) { +type CommentTransactionFn = fn( + line_token: Option<&str>, + block_tokens: Option<&[BlockCommentToken]>, + doc: &Rope, + selection: &Selection, +) -> Transaction; + +fn toggle_comments_impl(cx: &mut Context, comment_transaction: CommentTransactionFn) { let (view, doc) = current!(cx.editor); - let token = doc + let line_token: Option<&str> = doc + .language_config() + .and_then(|lc| lc.comment_tokens.as_ref()) + .and_then(|tc| tc.first()) + .map(|tc| tc.as_str()); + let block_tokens: Option<&[BlockCommentToken]> = doc .language_config() - .and_then(|lc| lc.comment_token.as_ref()) - .map(|tc| tc.as_ref()); - let transaction = comment::toggle_line_comments(doc.text(), doc.selection(view.id), token); + .and_then(|lc| lc.block_comment_tokens.as_ref()) + .map(|tc| &tc[..]); + + let transaction = + comment_transaction(line_token, block_tokens, doc.text(), doc.selection(view.id)); doc.apply(&transaction, view.id); exit_select_mode(cx); } +/// commenting behavior: +/// 1. only line comment tokens -> line comment +/// 2. each line block commented -> uncomment all lines +/// 3. whole selection block commented -> uncomment selection +/// 4. all lines not commented and block tokens -> comment uncommented lines +/// 5. no comment tokens and not block commented -> line comment +fn toggle_comments(cx: &mut Context) { + toggle_comments_impl(cx, |line_token, block_tokens, doc, selection| { + let text = doc.slice(..); + + // only have line comment tokens + if line_token.is_some() && block_tokens.is_none() { + return comment::toggle_line_comments(doc, selection, line_token); + } + + let split_lines = comment::split_lines_of_selection(text, selection); + + let default_block_tokens = &[BlockCommentToken::default()]; + let block_comment_tokens = block_tokens.unwrap_or(default_block_tokens); + + let (line_commented, line_comment_changes) = + comment::find_block_comments(block_comment_tokens, text, &split_lines); + + // block commented by line would also be block commented so check this first + if line_commented { + return comment::create_block_comment_transaction( + doc, + &split_lines, + line_commented, + line_comment_changes, + ) + .0; + } + + let (block_commented, comment_changes) = + comment::find_block_comments(block_comment_tokens, text, selection); + + // check if selection has block comments + if block_commented { + return comment::create_block_comment_transaction( + doc, + selection, + block_commented, + comment_changes, + ) + .0; + } + + // not commented and only have block comment tokens + if line_token.is_none() && block_tokens.is_some() { + return comment::create_block_comment_transaction( + doc, + &split_lines, + line_commented, + line_comment_changes, + ) + .0; + } + + // not block commented at all and don't have any tokens + comment::toggle_line_comments(doc, selection, line_token) + }) +} + +fn toggle_line_comments(cx: &mut Context) { + toggle_comments_impl(cx, |line_token, block_tokens, doc, selection| { + if line_token.is_none() && block_tokens.is_some() { + let default_block_tokens = &[BlockCommentToken::default()]; + let block_comment_tokens = block_tokens.unwrap_or(default_block_tokens); + comment::toggle_block_comments( + doc, + &comment::split_lines_of_selection(doc.slice(..), selection), + block_comment_tokens, + ) + } else { + comment::toggle_line_comments(doc, selection, line_token) + } + }); +} + +fn toggle_block_comments(cx: &mut Context) { + toggle_comments_impl(cx, |line_token, block_tokens, doc, selection| { + if line_token.is_some() && block_tokens.is_none() { + comment::toggle_line_comments(doc, selection, line_token) + } else { + let default_block_tokens = &[BlockCommentToken::default()]; + let block_comment_tokens = block_tokens.unwrap_or(default_block_tokens); + comment::toggle_block_comments(doc, selection, block_comment_tokens) + } + }); +} + fn rotate_selections(cx: &mut Context, direction: Direction) { let count = cx.count(); let (view, doc) = current!(cx.editor); diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index 92d6b5906..bab662b04 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -276,6 +276,9 @@ pub fn default() -> HashMap { "k" => hover, "r" => rename_symbol, "h" => select_references_to_symbol_under_cursor, + "c" => toggle_comments, + "C" => toggle_block_comments, + "A-c" => toggle_line_comments, "?" => command_palette, }, "z" => { "View" diff --git a/languages.toml b/languages.toml index 313b3d95e..5cfc0c28d 100644 --- a/languages.toml +++ b/languages.toml @@ -191,7 +191,12 @@ injection-regex = "rust" file-types = ["rs"] roots = ["Cargo.toml", "Cargo.lock"] auto-format = true -comment-token = "//" +comment-tokens = ["//", "///", "//!"] +block-comment-tokens = [ + { start = "/*", end = "*/" }, + { start = "/**", end = "*/" }, + { start = "/*!", end = "*/" }, +] language-servers = [ "rust-analyzer" ] indent = { tab-width = 4, unit = " " } persistent-diagnostic-sources = ["rustc", "clippy"] @@ -283,6 +288,7 @@ injection-regex = "protobuf" file-types = ["proto"] language-servers = [ "bufls", "pbkit" ] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 2, unit = " " } [[grammar]] @@ -326,6 +332,7 @@ injection-regex = "mint" file-types = ["mint"] shebangs = [] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "mint" ] indent = { tab-width = 2, unit = " " } @@ -408,6 +415,7 @@ scope = "source.c" injection-regex = "c" file-types = ["c"] # TODO: ["h"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "clangd" ] indent = { tab-width = 2, unit = " " } @@ -444,6 +452,7 @@ scope = "source.cpp" injection-regex = "cpp" file-types = ["cc", "hh", "c++", "cpp", "hpp", "h", "ipp", "tpp", "cxx", "hxx", "ixx", "txx", "ino", "C", "H", "cu", "cuh", "cppm", "h++", "ii", "inl", { glob = ".hpp.in" }, { glob = ".h.in" }] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "clangd" ] indent = { tab-width = 2, unit = " " } @@ -491,6 +500,7 @@ injection-regex = "c-?sharp" file-types = ["cs", "csx", "cake"] roots = ["sln", "csproj"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 4, unit = "\t" } language-servers = [ "omnisharp" ] @@ -549,6 +559,7 @@ file-types = ["go"] roots = ["go.work", "go.mod"] auto-format = true comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "gopls", "golangci-lint-lsp" ] # TODO: gopls needs utf-8 offsets? indent = { tab-width = 4, unit = "\t" } @@ -614,6 +625,7 @@ scope = "source.gotmpl" injection-regex = "gotmpl" file-types = ["gotmpl"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "gopls" ] indent = { tab-width = 2, unit = " " } @@ -643,6 +655,7 @@ language-id = "javascript" file-types = ["js", "mjs", "cjs", "rules", "es6", "pac", { glob = "jakefile" }] shebangs = ["node"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "typescript-language-server" ] indent = { tab-width = 2, unit = " " } @@ -669,6 +682,7 @@ injection-regex = "jsx" language-id = "javascriptreact" file-types = ["jsx"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "typescript-language-server" ] indent = { tab-width = 2, unit = " " } grammar = "javascript" @@ -680,6 +694,8 @@ injection-regex = "(ts|typescript)" file-types = ["ts", "mts", "cts"] language-id = "typescript" shebangs = ["deno", "ts-node"] +comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "typescript-language-server" ] indent = { tab-width = 2, unit = " " } @@ -693,6 +709,8 @@ scope = "source.tsx" injection-regex = "(tsx)" # |typescript language-id = "typescriptreact" file-types = ["tsx"] +comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "typescript-language-server" ] indent = { tab-width = 2, unit = " " } @@ -705,6 +723,7 @@ name = "css" scope = "source.css" injection-regex = "css" file-types = ["css", "scss"] +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "vscode-css-language-server" ] auto-format = true indent = { tab-width = 2, unit = " " } @@ -718,6 +737,7 @@ name = "scss" scope = "source.scss" injection-regex = "scss" file-types = ["scss"] +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "vscode-css-language-server" ] auto-format = true indent = { tab-width = 2, unit = " " } @@ -731,6 +751,7 @@ name = "html" scope = "text.html.basic" injection-regex = "html" file-types = ["html", "htm", "shtml", "xhtml", "xht", "jsp", "asp", "aspx", "jshtm", "volt", "rhtml"] +block-comment-tokens = { start = "" } language-servers = [ "vscode-html-language-server" ] auto-format = true indent = { tab-width = 2, unit = " " } @@ -901,6 +922,7 @@ injection-regex = "php" file-types = ["php", "inc", "php4", "php5", "phtml", "ctp"] shebangs = ["php"] roots = ["composer.json", "index.php"] +comment-token = "//" language-servers = [ "intelephense" ] indent = { tab-width = 4, unit = " " } @@ -913,6 +935,7 @@ name = "twig" scope = "source.twig" injection-regex = "twig" file-types = ["twig"] +block-comment-tokens = { start = "{#", end = "#}" } indent = { tab-width = 2, unit = " " } [[grammar]] @@ -966,6 +989,7 @@ injection-regex = "lean" file-types = ["lean"] roots = [ "lakefile.lean" ] comment-token = "--" +block-comment-tokens = { start = "/-", end = "-/" } language-servers = [ "lean" ] indent = { tab-width = 2, unit = " " } @@ -992,6 +1016,7 @@ file-types = ["jl"] shebangs = ["julia"] roots = ["Manifest.toml", "Project.toml"] comment-token = "#" +block-comment-tokens = { start = "#=", end = "=#" } language-servers = [ "julia" ] indent = { tab-width = 4, unit = " " } @@ -1055,6 +1080,7 @@ scope = "source.ocaml" injection-regex = "ocaml" file-types = ["ml"] shebangs = ["ocaml", "ocamlrun", "ocamlscript"] +block-comment-tokens = { start = "(*", end = "*)" } comment-token = "(**)" language-servers = [ "ocamllsp" ] indent = { tab-width = 2, unit = " " } @@ -1074,6 +1100,7 @@ name = "ocaml-interface" scope = "source.ocaml.interface" file-types = ["mli"] shebangs = [] +block-comment-tokens = { start = "(*", end = "*)" } comment-token = "(**)" language-servers = [ "ocamllsp" ] indent = { tab-width = 2, unit = " " } @@ -1096,6 +1123,7 @@ file-types = ["lua"] shebangs = ["lua", "luajit"] roots = [".luarc.json", ".luacheckrc", ".stylua.toml", "selene.toml", ".git"] comment-token = "--" +block-comment-tokens = { start = "--[[", end = "--]]" } indent = { tab-width = 2, unit = " " } language-servers = [ "lua-language-server" ] @@ -1121,6 +1149,7 @@ scope = "source.vue" injection-regex = "vue" file-types = ["vue"] roots = ["package.json"] +block-comment-tokens = { start = "" } indent = { tab-width = 2, unit = " " } language-servers = [ "vuels" ] @@ -1148,6 +1177,7 @@ injection-regex = "haskell" file-types = ["hs", "hs-boot"] roots = ["Setup.hs", "stack.yaml", "cabal.project"] comment-token = "--" +block-comment-tokens = { start = "{-", end = "-}" } language-servers = [ "haskell-language-server" ] indent = { tab-width = 2, unit = " " } @@ -1173,6 +1203,7 @@ injection-regex = "purescript" file-types = ["purs"] roots = ["spago.yaml", "spago.dhall", "bower.json"] comment-token = "--" +block-comment-tokens = { start = "{-", end = "-}" } language-servers = [ "purescript-language-server" ] indent = { tab-width = 2, unit = " " } auto-format = true @@ -1227,6 +1258,7 @@ scope = "source.prolog" file-types = ["pl", "prolog"] shebangs = ["swipl"] comment-token = "%" +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "swipl" ] [[language]] @@ -1246,6 +1278,7 @@ name = "cmake" scope = "source.cmake" file-types = ["cmake", { glob = "CMakeLists.txt" }] comment-token = "#" +block-comment-tokens = { start = "#[[", end = "]]" } indent = { tab-width = 2, unit = " " } language-servers = [ "cmake-language-server" ] injection-regex = "cmake" @@ -1272,6 +1305,7 @@ name = "glsl" scope = "source.glsl" file-types = ["glsl", "vert", "tesc", "tese", "geom", "frag", "comp" ] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 4, unit = " " } injection-regex = "glsl" @@ -1309,6 +1343,7 @@ file-types = ["rkt", "rktd", "rktl", "scrbl"] shebangs = ["racket"] comment-token = ";" indent = { tab-width = 2, unit = " " } +block-comment-tokens = { start = "#|", end = "|#" } language-servers = [ "racket" ] grammar = "scheme" @@ -1343,6 +1378,7 @@ name = "wgsl" scope = "source.wgsl" file-types = ["wgsl"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "wgsl_analyzer" ] indent = { tab-width = 4, unit = " " } @@ -1389,6 +1425,7 @@ name = "tablegen" scope = "source.tablegen" file-types = ["td"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 2, unit = " " } injection-regex = "tablegen" @@ -1404,6 +1441,7 @@ file-types = ["md", "markdown", "mkd", "mdwn", "mdown", "markdn", "mdtxt", "mdte roots = [".marksman.toml"] language-servers = [ "marksman" ] indent = { tab-width = 2, unit = " " } +block-comment-tokens = { start = "" } [[grammar]] name = "markdown" @@ -1427,6 +1465,7 @@ file-types = ["dart"] roots = ["pubspec.yaml"] auto-format = true comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "dart" ] indent = { tab-width = 2, unit = " " } @@ -1440,6 +1479,7 @@ scope = "source.scala" roots = ["build.sbt", "build.sc", "build.gradle", "build.gradle.kts", "pom.xml", ".scala-build"] file-types = ["scala", "sbt", "sc"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 2, unit = " " } language-servers = [ "metals" ] @@ -1560,6 +1600,8 @@ scope = "source.graphql" injection-regex = "graphql" file-types = ["gql", "graphql", "graphqls"] language-servers = [ "graphql-language-service" ] +comment-token = "#" +block-comment-tokens = { start = "\"\"\"", end = "\"\"\"" } indent = { tab-width = 2, unit = " " } [[grammar]] @@ -1574,6 +1616,7 @@ file-types = ["elm"] roots = ["elm.json"] auto-format = true comment-token = "--" +block-comment-tokens = { start = "{-", end = "-}" } language-servers = [ "elm-language-server" ] indent = { tab-width = 4, unit = " " } @@ -1586,6 +1629,7 @@ name = "iex" scope = "source.iex" injection-regex = "iex" file-types = ["iex"] +comment-token = "#" [[grammar]] name = "iex" @@ -1599,6 +1643,7 @@ file-types = ["res"] roots = ["bsconfig.json"] auto-format = true comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "rescript-language-server" ] indent = { tab-width = 2, unit = " " } @@ -1635,6 +1680,7 @@ scope = "source.kotlin" file-types = ["kt", "kts"] roots = ["settings.gradle", "settings.gradle.kts"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 4, unit = " " } language-servers = [ "kotlin-language-server" ] @@ -1649,6 +1695,7 @@ injection-regex = "(hcl|tf|nomad)" language-id = "terraform" file-types = ["hcl", "tf", "nomad"] comment-token = "#" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 2, unit = " " } language-servers = [ "terraform-ls" ] auto-format = true @@ -1663,6 +1710,7 @@ scope = "source.tfvars" language-id = "terraform-vars" file-types = ["tfvars"] comment-token = "#" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 2, unit = " " } language-servers = [ "terraform-ls" ] auto-format = true @@ -1685,6 +1733,7 @@ scope = "source.sol" injection-regex = "(sol|solidity)" file-types = ["sol"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 4, unit = " " } language-servers = [ "solc" ] @@ -1713,6 +1762,7 @@ scope = "source.ron" injection-regex = "ron" file-types = ["ron"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 4, unit = " " } [[grammar]] @@ -1754,6 +1804,7 @@ injection-regex = "(r|R)md" file-types = ["rmd", "Rmd"] indent = { tab-width = 2, unit = " " } grammar = "markdown" +block-comment-tokens = { start = "" } language-servers = [ "r" ] [[language]] @@ -1763,6 +1814,7 @@ injection-regex = "swift" file-types = ["swift"] roots = [ "Package.swift" ] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } auto-format = true language-servers = [ "sourcekit-lsp" ] @@ -1775,6 +1827,7 @@ name = "erb" scope = "text.html.erb" injection-regex = "erb" file-types = ["erb"] +block-comment-tokens = { start = "" } indent = { tab-width = 2, unit = " " } grammar = "embedded-template" @@ -1783,6 +1836,7 @@ name = "ejs" scope = "text.html.ejs" injection-regex = "ejs" file-types = ["ejs"] +block-comment-tokens = { start = "" } indent = { tab-width = 2, unit = " " } grammar = "embedded-template" @@ -1796,6 +1850,7 @@ scope = "source.eex" injection-regex = "eex" file-types = ["eex"] roots = ["mix.exs", "mix.lock"] +block-comment-tokens = { start = "" } indent = { tab-width = 2, unit = " " } [[grammar]] @@ -1808,6 +1863,7 @@ scope = "source.heex" injection-regex = "heex" file-types = ["heex"] roots = ["mix.exs", "mix.lock"] +block-comment-tokens = { start = "" } indent = { tab-width = 2, unit = " " } language-servers = [ "elixir-ls" ] @@ -1820,6 +1876,7 @@ name = "sql" scope = "source.sql" file-types = ["sql", "dsql"] comment-token = "--" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 4, unit = " " } injection-regex = "sql" @@ -1878,6 +1935,7 @@ scope = "source.vala" injection-regex = "vala" file-types = ["vala", "vapi"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 2, unit = " " } language-servers = [ "vala-language-server" ] @@ -1903,6 +1961,7 @@ scope = "source.devicetree" injection-regex = "(dtsi?|devicetree|fdt)" file-types = ["dts", "dtsi"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 4, unit = "\t" } [[grammar]] @@ -1941,6 +2000,7 @@ file-types = ["odin"] roots = ["ols.json"] language-servers = [ "ols" ] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 4, unit = "\t" } formatter = { command = "odinfmt", args = [ "-stdin", "true" ] } @@ -1998,6 +2058,7 @@ roots = ["v.mod"] language-servers = [ "vlang-language-server" ] auto-format = true comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 4, unit = "\t" } [[grammar]] @@ -2009,6 +2070,7 @@ name = "verilog" scope = "source.verilog" file-types = ["v", "vh", "sv", "svh"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "svlangserver" ] indent = { tab-width = 2, unit = " " } injection-regex = "verilog" @@ -2045,6 +2107,7 @@ scope = "source.openscad" injection-regex = "openscad" file-types = ["scad"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "openscad-lsp" ] indent = { tab-width = 2, unit = "\t" } @@ -2109,6 +2172,7 @@ injection-regex = "idr" file-types = ["idr"] shebangs = [] comment-token = "--" +block-comment-tokens = { start = "{-", end = "-}" } indent = { tab-width = 2, unit = " " } language-servers = [ "idris2-lsp" ] @@ -2144,6 +2208,7 @@ scope = "source.dot" injection-regex = "dot" file-types = ["dot"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 4, unit = " " } language-servers = [ "dot-language-server" ] @@ -2173,6 +2238,7 @@ scope = "source.slint" injection-regex = "slint" file-types = ["slint"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 4, unit = " " } language-servers = [ "slint-lsp" ] @@ -2222,6 +2288,7 @@ scope = "source.pascal" injection-regex = "pascal" file-types = ["pas", "pp", "inc", "lpr", "lfm"] comment-token = "//" +block-comment-tokens = { start = "{", end = "}" } indent = { tab-width = 2, unit = " " } language-servers = [ "pasls" ] @@ -2234,7 +2301,7 @@ name = "sml" scope = "source.sml" injection-regex = "sml" file-types = ["sml"] -comment-token = "(*" +block-comment-tokens = { start = "(*", end = "*)" } [[grammar]] name = "sml" @@ -2246,6 +2313,7 @@ scope = "source.jsonnet" file-types = ["libsonnet", "jsonnet"] roots = ["jsonnetfile.json"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 2, unit = " " } language-servers = [ "jsonnet-language-server" ] @@ -2258,6 +2326,7 @@ name = "astro" scope = "source.astro" injection-regex = "astro" file-types = ["astro"] +block-comment-tokens = { start = "" } indent = { tab-width = 2, unit = " " } [[grammar]] @@ -2281,6 +2350,7 @@ source = { git = "https://github.com/vito/tree-sitter-bass", rev = "501133e260d7 name = "wat" scope = "source.wat" comment-token = ";;" +block-comment-tokens = { start = "(;", end = ";)" } file-types = ["wat"] [[grammar]] @@ -2291,6 +2361,7 @@ source = { git = "https://github.com/wasm-lsp/tree-sitter-wasm", rev = "2ca28a9f name = "wast" scope = "source.wast" comment-token = ";;" +block-comment-tokens = { start = "(;", end = ";)" } file-types = ["wast"] [[grammar]] @@ -2302,6 +2373,7 @@ name = "d" scope = "source.d" file-types = [ "d", "dd" ] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } injection-regex = "d" indent = { tab-width = 4, unit = " "} language-servers = [ "serve-d" ] @@ -2328,6 +2400,7 @@ name = "kdl" scope = "source.kdl" file-types = ["kdl"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } injection-regex = "kdl" [[grammar]] @@ -2398,6 +2471,7 @@ file-types = [ "musicxml", "glif" ] +block-comment-tokens = { start = "" } indent = { tab-width = 2, unit = " " } [language.auto-pairs] @@ -2437,6 +2511,7 @@ scope = "source.wit" injection-regex = "wit" file-types = ["wit"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 2, unit = " " } [language.auto-pairs] @@ -2501,6 +2576,7 @@ scope = "source.bicep" file-types = ["bicep"] auto-format = true comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 2, unit = " "} language-servers = [ "bicep-langserver" ] @@ -2513,6 +2589,8 @@ name = "qml" scope = "source.qml" file-types = ["qml"] language-servers = [ "qmlls" ] +comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 4, unit = " " } grammar = "qmljs" @@ -2552,6 +2630,7 @@ injection-regex = "pony" roots = ["corral.json", "lock.json"] indent = { tab-width = 2, unit = " " } comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } [[grammar]] name = "ponylang" @@ -2563,6 +2642,7 @@ scope = "source.dhall" injection-regex = "dhall" file-types = ["dhall"] comment-token = "--" +block-comment-tokens = { start = "{-", end = "-}" } indent = { tab-width = 2, unit = " " } language-servers = [ "dhall-lsp-server" ] formatter = { command = "dhall" , args = ["format"] } @@ -2586,6 +2666,7 @@ scope = "source.msbuild" injection-regex = "msbuild" file-types = ["proj", "vbproj", "csproj", "fsproj", "targets", "props"] indent = { tab-width = 2, unit = " " } +block-comment-tokens = { start = "" } grammar = "xml" [language.auto-pairs] @@ -2632,7 +2713,7 @@ scope = "source.tal" injection-regex = "tal" file-types = ["tal"] auto-format = false -comment-token = "(" +block-comment-tokens = { start = "(", end = ")" } [[grammar]] name = "uxntal" @@ -2766,6 +2847,7 @@ injection-regex = "nim" file-types = ["nim", "nims", "nimble"] shebangs = [] comment-token = "#" +block-comment-tokens = { start = "#[", end = "]#" } indent = { tab-width = 2, unit = " " } language-servers = [ "nimlangserver" ] @@ -2805,6 +2887,7 @@ source = { git = "https://github.com/pfeiferj/tree-sitter-hurl", rev = "264c4206 [[language]] name = "markdoc" scope = "text.markdoc" +block-comment-tokens = { start = "" } file-types = ["mdoc"] language-servers = [ "markdoc-ls" ] @@ -2858,6 +2941,7 @@ scope = "source.blueprint" injection-regex = "blueprint" file-types = ["blp"] comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } language-servers = [ "blueprint-compiler" ] indent = { tab-width = 4, unit = " " } @@ -2910,6 +2994,7 @@ name = "webc" scope = "text.html.webc" injection-regex = "webc" file-types = ["webc"] +block-comment-tokens = { start = "" } indent = { tab-width = 2, unit = " " } grammar = "html" From 00653c772e7df6f68071d1cb1c92bfe9ca4876f9 Mon Sep 17 00:00:00 2001 From: Mo <76752051+mo8it@users.noreply.github.com> Date: Tue, 27 Feb 2024 18:24:05 +0100 Subject: [PATCH 194/796] Avoid cloning the whole paragraph content just for rendering (#9739) * Avoid cloning the whole paragraph content just for rendering * Fix tests --- helix-term/src/ui/editor.rs | 3 ++- helix-term/src/ui/info.rs | 3 ++- helix-term/src/ui/lsp.rs | 4 ++-- helix-term/src/ui/markdown.rs | 2 +- helix-term/src/ui/text.rs | 2 +- helix-tui/src/widgets/paragraph.rs | 19 ++++++++----------- helix-tui/tests/terminal.rs | 6 ++++-- helix-tui/tests/widgets_paragraph.rs | 18 +++++++++--------- 8 files changed, 29 insertions(+), 28 deletions(-) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index 15a7262a8..dffaeea03 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -716,7 +716,8 @@ impl EditorView { } } - let paragraph = Paragraph::new(lines) + let text = Text::from(lines); + let paragraph = Paragraph::new(&text) .alignment(Alignment::Right) .wrap(Wrap { trim: true }); let width = 100.min(viewport.width); diff --git a/helix-term/src/ui/info.rs b/helix-term/src/ui/info.rs index cc6b7483f..651e5ca93 100644 --- a/helix-term/src/ui/info.rs +++ b/helix-term/src/ui/info.rs @@ -2,6 +2,7 @@ use crate::compositor::{Component, Context}; use helix_view::graphics::{Margin, Rect}; use helix_view::info::Info; use tui::buffer::Buffer as Surface; +use tui::text::Text; use tui::widgets::{Block, Borders, Paragraph, Widget}; impl Component for Info { @@ -31,7 +32,7 @@ impl Component for Info { let inner = block.inner(area).inner(&margin); block.render(area, surface); - Paragraph::new(self.text.as_str()) + Paragraph::new(&Text::from(self.text.as_str())) .style(text_style) .render(inner, surface); } diff --git a/helix-term/src/ui/lsp.rs b/helix-term/src/ui/lsp.rs index 879f963e7..a3698e38d 100644 --- a/helix-term/src/ui/lsp.rs +++ b/helix-term/src/ui/lsp.rs @@ -77,7 +77,7 @@ impl Component for SignatureHelp { let (_, sig_text_height) = crate::ui::text::required_size(&sig_text, area.width); let sig_text_area = area.clip_top(1).with_height(sig_text_height); let sig_text_area = sig_text_area.inner(&margin).intersection(surface.area); - let sig_text_para = Paragraph::new(sig_text).wrap(Wrap { trim: false }); + let sig_text_para = Paragraph::new(&sig_text).wrap(Wrap { trim: false }); sig_text_para.render(sig_text_area, surface); if self.signature_doc.is_none() { @@ -100,7 +100,7 @@ impl Component for SignatureHelp { let sig_doc_area = area .clip_top(sig_text_area.height + 2) .clip_bottom(u16::from(cx.editor.popup_border())); - let sig_doc_para = Paragraph::new(sig_doc) + let sig_doc_para = Paragraph::new(&sig_doc) .wrap(Wrap { trim: false }) .scroll((cx.scroll.unwrap_or_default() as u16, 0)); sig_doc_para.render(sig_doc_area.inner(&margin), surface); diff --git a/helix-term/src/ui/markdown.rs b/helix-term/src/ui/markdown.rs index 749d58508..81499d039 100644 --- a/helix-term/src/ui/markdown.rs +++ b/helix-term/src/ui/markdown.rs @@ -346,7 +346,7 @@ impl Component for Markdown { let text = self.parse(Some(&cx.editor.theme)); - let par = Paragraph::new(text) + let par = Paragraph::new(&text) .wrap(Wrap { trim: false }) .scroll((cx.scroll.unwrap_or_default() as u16, 0)); diff --git a/helix-term/src/ui/text.rs b/helix-term/src/ui/text.rs index a379536f8..a9c995627 100644 --- a/helix-term/src/ui/text.rs +++ b/helix-term/src/ui/text.rs @@ -33,7 +33,7 @@ impl Component for Text { fn render(&mut self, area: Rect, surface: &mut Surface, _cx: &mut Context) { use tui::widgets::{Paragraph, Widget, Wrap}; - let par = Paragraph::new(self.contents.clone()).wrap(Wrap { trim: false }); + let par = Paragraph::new(&self.contents).wrap(Wrap { trim: false }); // .scroll(x, y) offsets par.render(area, surface); diff --git a/helix-tui/src/widgets/paragraph.rs b/helix-tui/src/widgets/paragraph.rs index 4e8391621..9c8ae127c 100644 --- a/helix-tui/src/widgets/paragraph.rs +++ b/helix-tui/src/widgets/paragraph.rs @@ -28,15 +28,15 @@ fn get_line_offset(line_width: u16, text_area_width: u16, alignment: Alignment) /// # use helix_tui::widgets::{Block, Borders, Paragraph, Wrap}; /// # use helix_tui::layout::{Alignment}; /// # use helix_view::graphics::{Style, Color, Modifier}; -/// let text = vec![ +/// let text = Text::from(vec![ /// Spans::from(vec![ /// Span::raw("First"), /// Span::styled("line",Style::default().add_modifier(Modifier::ITALIC)), /// Span::raw("."), /// ]), /// Spans::from(Span::styled("Second line", Style::default().fg(Color::Red))), -/// ]; -/// Paragraph::new(text) +/// ]); +/// Paragraph::new(&text) /// .block(Block::default().title("Paragraph").borders(Borders::ALL)) /// .style(Style::default().fg(Color::White).bg(Color::Black)) /// .alignment(Alignment::Center) @@ -51,7 +51,7 @@ pub struct Paragraph<'a> { /// How to wrap the text wrap: Option, /// The text to display - text: Text<'a>, + text: &'a Text<'a>, /// Scroll scroll: (u16, u16), /// Alignment of the text @@ -70,7 +70,7 @@ pub struct Paragraph<'a> { /// - Here is another point that is long enough to wrap"#); /// /// // With leading spaces trimmed (window width of 30 chars): -/// Paragraph::new(bullet_points.clone()).wrap(Wrap { trim: true }); +/// Paragraph::new(&bullet_points).wrap(Wrap { trim: true }); /// // Some indented points: /// // - First thing goes here and is /// // long so that it wraps @@ -78,7 +78,7 @@ pub struct Paragraph<'a> { /// // is long enough to wrap /// /// // But without trimming, indentation is preserved: -/// Paragraph::new(bullet_points).wrap(Wrap { trim: false }); +/// Paragraph::new(&bullet_points).wrap(Wrap { trim: false }); /// // Some indented points: /// // - First thing goes here /// // and is long so that it wraps @@ -92,15 +92,12 @@ pub struct Wrap { } impl<'a> Paragraph<'a> { - pub fn new(text: T) -> Paragraph<'a> - where - T: Into>, - { + pub fn new(text: &'a Text) -> Paragraph<'a> { Paragraph { block: None, style: Default::default(), wrap: None, - text: text.into(), + text, scroll: (0, 0), alignment: Alignment::Left, } diff --git a/helix-tui/tests/terminal.rs b/helix-tui/tests/terminal.rs index 2824c9f24..d2d8ca101 100644 --- a/helix-tui/tests/terminal.rs +++ b/helix-tui/tests/terminal.rs @@ -17,14 +17,16 @@ fn terminal_buffer_size_should_not_be_limited() { // let backend = TestBackend::new(10, 10); // let mut terminal = Terminal::new(backend)?; // let frame = terminal.draw(|f| { -// let paragraph = Paragraph::new("Test"); +// let text = Text::from("Test"); +// let paragraph = Paragraph::new(&text); // f.render_widget(paragraph, f.size()); // })?; // assert_eq!(frame.buffer.get(0, 0).symbol, "T"); // assert_eq!(frame.area, Rect::new(0, 0, 10, 10)); // terminal.backend_mut().resize(8, 8); // let frame = terminal.draw(|f| { -// let paragraph = Paragraph::new("test"); +// let text = Text::from("test"); +// let paragraph = Paragraph::new(&text); // f.render_widget(paragraph, f.size()); // })?; // assert_eq!(frame.buffer.get(0, 0).symbol, "t"); diff --git a/helix-tui/tests/widgets_paragraph.rs b/helix-tui/tests/widgets_paragraph.rs index a7c972eb5..3d2ac467b 100644 --- a/helix-tui/tests/widgets_paragraph.rs +++ b/helix-tui/tests/widgets_paragraph.rs @@ -21,8 +21,8 @@ // terminal // .draw(|f| { // let size = f.size(); -// let text = vec![Spans::from(SAMPLE_STRING)]; -// let paragraph = Paragraph::new(text) +// let text = Text::from(SAMPLE_STRING); +// let paragraph = Paragraph::new(&text) // .block(Block::default().borders(Borders::ALL)) // .alignment(alignment) // .wrap(Wrap { trim: true }); @@ -88,8 +88,8 @@ // terminal // .draw(|f| { // let size = f.size(); -// let text = vec![Spans::from(s)]; -// let paragraph = Paragraph::new(text) +// let text = Text::from(s); +// let paragraph = Paragraph::new(&text) // .block(Block::default().borders(Borders::ALL)) // .wrap(Wrap { trim: true }); // f.render_widget(paragraph, size); @@ -120,8 +120,8 @@ // terminal // .draw(|f| { // let size = f.size(); -// let text = vec![Spans::from(s)]; -// let paragraph = Paragraph::new(text) +// let text = Text::from(s); +// let paragraph = Paragraph::new(&text) // .block(Block::default().borders(Borders::ALL)) // .wrap(Wrap { trim: true }); // f.render_widget(paragraph, size); @@ -155,8 +155,8 @@ // terminal // .draw(|f| { // let size = f.size(); - -// let paragraph = Paragraph::new(line).block(Block::default().borders(Borders::ALL)); +// let text = Text::from(line); +// let paragraph = Paragraph::new(&text).block(Block::default().borders(Borders::ALL)); // f.render_widget(paragraph, size); // }) // .unwrap(); @@ -174,7 +174,7 @@ // let text = Text::from( // "段落现在可以水平滚动了!\nParagraph can scroll horizontally!\nShort line", // ); -// let paragraph = Paragraph::new(text) +// let paragraph = Paragraph::new(&text) // .block(Block::default().borders(Borders::ALL)) // .alignment(alignment) // .scroll(scroll); From 083a9e775d31678f4d984c526e7140c0d2bb2312 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 28 Feb 2024 12:18:00 +0000 Subject: [PATCH 195/796] Add support for pde files (#9741) --- languages.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index 5cfc0c28d..ea2339f8b 100644 --- a/languages.toml +++ b/languages.toml @@ -1028,7 +1028,7 @@ source = { git = "https://github.com/tree-sitter/tree-sitter-julia", rev = "8fb3 name = "java" scope = "source.java" injection-regex = "java" -file-types = ["java", "jav"] +file-types = ["java", "jav", "pde"] roots = ["pom.xml", "build.gradle", "build.gradle.kts"] language-servers = [ "jdtls" ] indent = { tab-width = 2, unit = " " } From f03b91d1b7907e78a4242c5b525e47c997f4457d Mon Sep 17 00:00:00 2001 From: Brian Dorsey Date: Wed, 28 Feb 2024 11:55:17 -0800 Subject: [PATCH 196/796] update languages.toml: tree-sitter-lua grammar (#9727) * update languages.toml: tree-sitter-lua grammar repo has moved, use new URL and the rev of the latest release (v0.0.19) * update highlight queries a novice attempt to port query updates from the source repo to Helix captures and ordering * Apply suggestions from code review Co-authored-by: Michael Davis --------- Co-authored-by: Michael Davis --- languages.toml | 2 +- runtime/queries/lua/highlights.scm | 81 +++++++++++++++++++++++++----- 2 files changed, 69 insertions(+), 14 deletions(-) diff --git a/languages.toml b/languages.toml index ea2339f8b..fb4cda225 100644 --- a/languages.toml +++ b/languages.toml @@ -1129,7 +1129,7 @@ language-servers = [ "lua-language-server" ] [[grammar]] name = "lua" -source = { git = "https://github.com/MunifTanjim/tree-sitter-lua", rev = "887dfd4e83c469300c279314ff1619b1d0b85b91" } +source = { git = "https://github.com/tree-sitter-grammars/tree-sitter-lua", rev = "88e446476a1e97a8724dff7a23e2d709855077f2" } [[language]] name = "svelte" diff --git a/runtime/queries/lua/highlights.scm b/runtime/queries/lua/highlights.scm index f48e607c5..2f3b3c05f 100644 --- a/runtime/queries/lua/highlights.scm +++ b/runtime/queries/lua/highlights.scm @@ -1,9 +1,5 @@ ;;; Highlighting for lua -;;; Builtins -((identifier) @variable.builtin - (#eq? @variable.builtin "self")) - ;; Keywords (if_statement @@ -130,16 +126,65 @@ ((identifier) @constant (#match? @constant "^[A-Z][A-Z_0-9]*$")) -;; Parameters -(parameters - (identifier) @variable.parameter) +;; Tables + +(field name: (identifier) @variable.other.member) + +(dot_index_expression field: (identifier) @variable.other.member) + +(table_constructor +[ + "{" + "}" +] @constructor) -; ;; Functions -(function_declaration name: (identifier) @function) -(function_call name: (identifier) @function.call) +;; Functions -(function_declaration name: (dot_index_expression field: (identifier) @function)) -(function_call name: (dot_index_expression field: (identifier) @function.call)) +(parameters (identifier) @variable.parameter) + +(function_call + (identifier) @function.builtin + (#any-of? @function.builtin + ;; built-in functions in Lua 5.1 + "assert" "collectgarbage" "dofile" "error" "getfenv" "getmetatable" "ipairs" + "load" "loadfile" "loadstring" "module" "next" "pairs" "pcall" "print" + "rawequal" "rawget" "rawset" "require" "select" "setfenv" "setmetatable" + "tonumber" "tostring" "type" "unpack" "xpcall")) + +(function_declaration + name: [ + (identifier) @function + (dot_index_expression + field: (identifier) @function) + ]) + +(function_declaration + name: (method_index_expression + method: (identifier) @function.method)) + +(assignment_statement + (variable_list . + name: [ + (identifier) @function + (dot_index_expression + field: (identifier) @function) + ]) + (expression_list . + value: (function_definition))) + +(table_constructor + (field + name: (identifier) @function + value: (function_definition))) + +(function_call + name: [ + (identifier) @function.call + (dot_index_expression + field: (identifier) @function.call) + (method_index_expression + method: (identifier) @function.method.call) + ]) ; TODO: incorrectly highlights variable N in `N, nop = 42, function() end` (assignment_statement @@ -153,6 +198,7 @@ ;; Nodes (comment) @comment (string) @string +(escape_sequence) @constant.character.escape (number) @constant.numeric.integer (label_statement) @label ; A bit of a tricky one, this will only match field names @@ -162,7 +208,16 @@ ;; Property (dot_index_expression field: (identifier) @variable.other.member) -;; Variable +;; Variables +((identifier) @variable.builtin + (#eq? @variable.builtin "self")) + +(variable_list + (attribute + "<" @punctuation.bracket + (identifier) @attribute + ">" @punctuation.bracket)) + (identifier) @variable ;; Error From 1143f4795414c26382a5647f2be8c20a7a62ada4 Mon Sep 17 00:00:00 2001 From: Pascal Kuthe Date: Thu, 29 Feb 2024 02:47:41 +0100 Subject: [PATCH 197/796] fix split_on_newline (#9756) --- helix-core/src/selection.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs index 91f1d0de5..bd252deb9 100644 --- a/helix-core/src/selection.rs +++ b/helix-core/src/selection.rs @@ -773,12 +773,12 @@ pub fn split_on_newline(text: RopeSlice, selection: &Selection) -> Selection { let mut start = sel_start; - for mat in sel.slice(text).lines() { - let len = mat.len_chars(); - let line_end_len = get_line_ending(&mat).map(|le| le.len_chars()).unwrap_or(0); + for line in sel.slice(text).lines() { + let Some(line_ending) = get_line_ending(&line) else { break }; + let line_end = start + line.len_chars(); // TODO: retain range direction - result.push(Range::new(start, start + len - line_end_len)); - start += len; + result.push(Range::new(start, line_end - line_ending.len_chars())); + start = line_end; } if start < sel_end { From e51a1e4e2ae99b8e1ad751e7dfa024a7d0e4ba8f Mon Sep 17 00:00:00 2001 From: JJ Date: Wed, 28 Feb 2024 17:49:10 -0800 Subject: [PATCH 198/796] Switch Nim tree-sitter queries to alaviss/tree-sitter-nim (#9722) --- languages.toml | 4 +- runtime/queries/nim/highlights.scm | 522 ++++++++++++++-------------- runtime/queries/nim/indents.scm | 83 +++-- runtime/queries/nim/textobjects.scm | 44 ++- 4 files changed, 348 insertions(+), 305 deletions(-) diff --git a/languages.toml b/languages.toml index fb4cda225..d2a183ded 100644 --- a/languages.toml +++ b/languages.toml @@ -50,6 +50,7 @@ metals = { command = "metals", config = { "isHttpEnabled" = true } } mint = { command = "mint", args = ["ls"] } nil = { command = "nil" } nimlangserver = { command = "nimlangserver" } +nimlsp = { command = "nimlsp" } nls = { command = "nls" } nu-lsp = { command = "nu", args = [ "--lsp" ] } ocamllsp = { command = "ocamllsp" } @@ -2858,10 +2859,9 @@ language-servers = [ "nimlangserver" ] "'" = "'" '{' = '}' -# Nim's tree-sitter grammar is in heavy development. [[grammar]] name = "nim" -source = { git = "https://github.com/aMOPel/tree-sitter-nim", rev = "240239b232550e431d67de250d1b5856209e7f06" } +source = { git = "https://github.com/alaviss/tree-sitter-nim", rev = "c5f0ce3b65222f5dbb1a12f9fe894524881ad590" } [[language]] name = "cabal" diff --git a/runtime/queries/nim/highlights.scm b/runtime/queries/nim/highlights.scm index 1d3256853..e02ba5165 100644 --- a/runtime/queries/nim/highlights.scm +++ b/runtime/queries/nim/highlights.scm @@ -1,33 +1,32 @@ ;; Constants, Comments, and Literals (comment) @comment.line -(multilineComment) @comment.block -(docComment) @comment.block.documentation -(multilineDocComment) @comment.block.documentation -; comments - -[(literal) (generalizedLit)] @constant -[(nil_lit)] @constant.builtin -[(bool_lit)] @constant.builtin.boolean -[(char_lit)] @constant.character -[(char_esc_seq) (str_esc_seq)] @constant.character.escape -[(custom_numeric_lit)] @constant.numeric -[(int_lit) (int_suffix)] @constant.numeric.integer -[(float_lit) (float_suffix)] @constant.numeric.float +(block_comment) @comment.block +[ + (documentation_comment) + (block_documentation_comment) +] @comment.block.documentation + +(nil_literal) @constant.builtin +((identifier) @constant.builtin.boolean + (#any-of? @constant.builtin.boolean "true" "false" "on" "off")) + +(char_literal) @constant.character +(escape_sequence) @constant.character.escape +(custom_numeric_literal) @constant.numeric +(integer_literal) @constant.numeric.integer +(float_literal) @constant.numeric.float ; literals -; note: somewhat irritatingly for testing, lits have the same syntax highlighting as types +; todo: literal? [ - (str_lit) - (triplestr_lit) - (rstr_lit) - (generalized_str_lit) - (generalized_triplestr_lit) - (interpolated_str_lit) - (interpolated_triplestr_lit) + (long_string_literal) + (raw_string_literal) + (generalized_string) + (interpreted_string_literal) ] @string +; (generalized_string (string_content) @none) ; todo: attempt to un-match string_content ; [] @string.regexp -; string literals [ "." @@ -44,272 +43,291 @@ "}" "{." ".}" - "#[" - "]#" ] @punctuation.bracket -(interpolated_str_lit "&" @punctuation.special) -(interpolated_str_lit "{" @punctuation.special) -(interpolated_str_lit "}" @punctuation.special) -; punctuation +; todo: interpolated_str_lit?? & { }? [ "and" "or" "xor" "not" - "in" - "notin" - "is" - "isnot" "div" "mod" "shl" "shr" + "from" + "as" + "of" + "in" + "notin" + "is" + "isnot" ] @keyword.operator -; operators: we list them explicitly to deliminate them from symbolic operators - -[(operator) (opr) "="] @operator -; all operators (must come after @keyword.operator) -(pragma) @attribute -; pragmas +[(operator) "="] @operator +(infix_expression operator: _ @operator) +(prefix_expression operator: _ @operator) +(pragma_list + (identifier)? @attribute + (colon_expression + (identifier) @attribute)?) ;; Imports and Exports -(importStmt - (keyw) @keyword.control.import - (expr (primary (symbol) @namespace))? - (expr (primary (arrayConstr (exprColonExprList (exprColonExpr (expr (primary (symbol) @namespace)))))))?) -(exportStmt - (keyw) @keyword.control.import - (expr (primary (symbol) @namespace))? - (expr (primary (arrayConstr (exprColonExprList (exprColonExpr (expr (primary (symbol) @namespace)))))))?) -(fromStmt - (keyw) @keyword.control.import - (expr (primary (symbol) @namespace))? - (expr (primary (arrayConstr (exprColonExprList (exprColonExpr (expr (primary (symbol) @namespace)))))))?) -(includeStmt - (keyw) @keyword.control.import - (expr (primary (symbol) @namespace))? - (expr (primary (arrayConstr (exprColonExprList (exprColonExpr (expr (primary (symbol) @namespace)))))))?) -(importExceptStmt - (keyw) @keyword.control.import - (expr (primary (symbol) @namespace))? - (expr (primary (arrayConstr (exprColonExprList (exprColonExpr (expr (primary (symbol) @namespace)))))))?) -; import statements -; yeah, this is a bit gross. - +[ + "import" + "export" + "include" + "from" +] @keyword.control.import + +(import_statement + [ + (identifier) @namespace + (expression_list (identifier) @namespace) + (except_clause + "except" @keyword.control.import + (expression_list (identifier) @namespace))]) +(import_from_statement + (identifier) @namespace + (expression_list (identifier) @namespace)) +(include_statement (expression_list (identifier) @namespace)) +(export_statement (expression_list (identifier) @namespace)) ;; Control Flow -(ifStmt (keyw) @keyword.control.conditional) -(whenStmt (keyw) @keyword.control.conditional) -(elifStmt (keyw) @keyword.control.conditional) -(elseStmt (keyw) @keyword.control.conditional) -(caseStmt (keyw) @keyword.control.conditional) -(ofBranch (keyw) @keyword.control.conditional) -(inlineIfStmt (keyw) @keyword.control.conditional) -(inlineWhenStmt (keyw) @keyword.control.conditional) +[ + "if" + "when" + "case" + "elif" + "else" +] @keyword.control.conditional +(of_branch "of" @keyword.control.conditional) ; conditional statements ; todo: do block -(forStmt - . (keyw) @keyword.control.repeat - . (symbol) @variable - . (keyw) @keyword.control.repeat) -(whileStmt (keyw) @keyword.control.repeat) -; loop statements - -(returnStmt (keyw) @keyword.control.repeat) -(yieldStmt (keyw) @keyword.control.repeat) -(discardStmt (keyw) @keyword.control.repeat) -(breakStmt (keyw) @keyword.control.repeat) -(continueStmt (keyw) @keyword.control.repeat) -; control flow statements - -(raiseStmt (keyw) @keyword.control.exception) -(tryStmt (keyw) @keyword.control.exception) -(tryExceptStmt (keyw) @keyword.control.exception) -(tryFinallyStmt (keyw) @keyword.control.exception) -(inlineTryStmt (keyw) @keyword.control.exception) -; (inlineTryExceptStmt (keyw) @keyword.control.exception) -; (inlineTryFinallyStmt (keyw) @keyword.control.exception) -; exception handling statements +"block" @keyword.control +(block label: (_) @label) -(staticStmt (keyw) @keyword) -(deferStmt (keyw) @keyword) -(asmStmt (keyw) @keyword) -(bindStmt (keyw) @keyword) -(mixinStmt (keyw) @keyword) -; miscellaneous blocks +[ + "for" + "while" + "continue" + "break" +] @keyword.control.repeat +(for "in" @keyword.control.repeat) -(blockStmt - (keyw) @keyword.control - (symbol) @label) -; block statements +[ + "return" + "yield" +] @keyword.control.return +; return statements +[ + "try" + "except" + "finally" + "raise" +] @keyword.control.exception +; exception handling statements -;; Types and Type Declarations +[ + "asm" + "bind" + "mixin" + "defer" + "static" +] @keyword +; miscellaneous keywords -(typeDef - (keyw) @keyword.storage.type - (symbol) @type) -; names of new types type declarations - -(exprColonEqExpr - . (expr (primary (symbol) @variable)) - . (expr (primary (symbol) @type))) -; variables in inline tuple declarations - -(primarySuffix - (indexSuffix - (exprColonEqExprList - (exprColonEqExpr - (expr - (primary - (symbol) @type)))))) -; nested types in brackets, i.e. seq[string] - -(primaryTypeDef (symbol) @type) -; primary types of type declarations (NOT nested types) - -(primaryTypeDef (primaryPrefix (keyw) @type)) -; for consistency - -(primaryTypeDesc (symbol) @type) -; type annotations, on declarations or in objects - -(primaryTypeDesc (primaryPrefix (keyw) @type)) -; var types etc - -(genericParamList (genericParam (symbol) @type)) -; types in generic blocks - -(enumDecl (keyw) @keyword.storage.type) -(enumElement (symbol) @type.enum.variant) -; enum declarations and elements - -(tupleDecl (keyw) @keyword.storage.type) -; tuple declarations - -(objectDecl (keyw) @keyword.storage.type) -(objectPart (symbol) @variable.other.member) -; object declarations and fields - -(objectCase - (keyw) @keyword.control.conditional - (symbol) @variable.other.member) -(objectBranch (keyw) @keyword.control.conditional) -(objectElif (keyw) @keyword.control.conditional) -(objectElse (keyw) @keyword.control.conditional) -(objectWhen (keyw) @keyword.control.conditional) -; variant objects - -(conceptDecl (keyw) @keyword.storage.type) -(conceptParam (keyw) @type) -(conceptParam (symbol) @variable) -; concept declarations, parameters, and qualifiers on those parameters - -((expr - (primary (symbol)) - (operator) @operator - (primary (symbol) @type)) - (#match? @operator "is")) -((exprStmt - (primary (symbol)) - (operator) @operator - (primary (symbol) @type)) - (#match? @operator "is")) -; symbols likely to be types: "x is t" means t is either a type or a type variable - -; distinct? +;; Types and Type Declarations +[ + "let" + "var" + "const" + "type" + "object" + "tuple" + "enum" + "concept" +] @keyword.storage.type + +(var_type "var" @keyword.storage.modifier) +(out_type "out" @keyword.storage.modifier) +(distinct_type "distinct" @keyword.storage.modifier) +(ref_type "ref" @keyword.storage.modifier) +(pointer_type "ptr" @keyword.storage.modifier) + +(var_parameter "var" @keyword.storage.modifier) +(type_parameter "type" @keyword.storage.modifier) +(static_parameter "static" @keyword.storage.modifier) +(ref_parameter "ref" @keyword.storage.modifier) +(pointer_parameter "ptr" @keyword.storage.modifier) +; (var_parameter (identifier) @variable.parameter) +; (type_parameter (identifier) @variable.parameter) +; (static_parameter (identifier) @variable.parameter) +; (ref_parameter (identifier) @variable.parameter) +; (pointer_parameter (identifier) @variable.parameter) +; todo: when are these used?? + +(type_section + (type_declaration + (type_symbol_declaration + name: (_) @type))) +; types in type declarations + +(enum_field_declaration + (symbol_declaration + name: (_) @type.enum.variant)) +; types as enum variants + +(variant_declaration + alternative: (of_branch + values: (expression_list (_) @type.enum.variant))) +; types as object variants + +(case + (of_branch + values: (expression_list (_) @constant))) +; case values are guaranteed to be constant + +(type_expression + [ + (identifier) @type + (bracket_expression + [ + (identifier) @type + (argument_list (identifier) @type)]) + (tuple_construction + [ + (identifier) @type + (bracket_expression + [ + (identifier) @type + (argument_list (identifier) @type)])])]) +; types in type expressions + +(call + function: (bracket_expression + right: (argument_list (identifier) @type))) +; types as generic parameters + +; (dot_generic_call +; generic_arguments: (_) @type) +; ??? + +(infix_expression + operator: + [ + "is" + "isnot" + ] + right: (_) @type) +; types in "is" comparisions + +(except_branch + values: (expression_list + [ + (identifier) @type + (infix_expression + left: (identifier) @type + operator: "as" + right: (_) @variable)])) +; types in exception branches ;; Functions -(routine - . (keyw) @keyword.function - . (symbol) @function) -; function declarations - -(routineExpr (keyw) @keyword.function) -; discarded function - -(routineExprTypeDesc (keyw) @keyword.function) -; function declarations as types - -(primary - . (symbol) @function.call - . (primarySuffix (functionCall))) -; regular function calls - -(primary - . (symbol) @function.call - . (primarySuffix (cmdCall))) -; function calls without parenthesis - -(primary - (primarySuffix (qualifiedSuffix (symbol) @function.call)) - . (primarySuffix (functionCall))) -; uniform function call syntax calls - -(primary - (primarySuffix (qualifiedSuffix (symbol) @function.call)) - . (primarySuffix (cmdCall))) -; just in case - -(primary - (symbol) @constructor - (primarySuffix (objectConstr))) -; object constructor - -; does not appear to be a way to distinguish these without verbatium matching -; [] @function.builtin -; [] @function.method -; [] @function.macro -; [] @function.special - +[ + "proc" + "func" + "method" + "converter" + "iterator" + "template" + "macro" +] @keyword.function + +(exported_symbol "*" @attribute) +(_ "=" @punctuation.delimiter [body: (_) value: (_)]) + +(proc_declaration name: (_) @function) +(func_declaration name: (_) @function) +(iterator_declaration name: (_) @function) +(converter_declaration name: (_) @function) +(method_declaration name: (_) @function.method) +(template_declaration name: (_) @function.macro) +(macro_declaration name: (_) @function.macro) +(symbol_declaration name: (_) @variable) + +(call + function: [ + (identifier) @function.call + (dot_expression + right: (identifier) @function.call) + (bracket_expression + left: [ + (identifier) @function.call + (dot_expression + right: (identifier) @function.call)])]) +(generalized_string + function: [ + (identifier) @function.call + (dot_expression + right: (identifier) @function.call) + (bracket_expression + left: [ + (identifier) @function.call + (dot_expression + right: (identifier) @function.call)])]) +(dot_generic_call function: (_) @function.call) ;; Variables -(paramList (paramColonEquals (symbol) @variable.parameter)) -; parameter identifiers - -(identColon (ident) @variable.other.member) -; named parts of tuples - -(symbolColonExpr (symbol) @variable) -; object constructor parameters - -(symbolEqExpr (symbol) @variable) -; named parameters +(parameter_declaration + (symbol_declaration_list + (symbol_declaration + name: (_) @variable.parameter))) +(argument_list + (equal_expression + left: (_) @variable.parameter)) +(concept_declaration + parameters: (parameter_list (identifier) @variable.parameter)) + +(field_declaration + (symbol_declaration_list + (symbol_declaration + name: (_) @variable.other.member))) +(call + (argument_list + (colon_expression + left: (_) @variable.other.member))) +(tuple_construction + (colon_expression + left: (_) @variable.other.member)) +(variant_declaration + (variant_discriminator_declaration + (symbol_declaration_list + (symbol_declaration + name: (_) @variable.other.member)))) + +;; Miscellaneous Matches -(variable - (keyw) @keyword.storage.type - (declColonEquals (symbol) @variable)) -; let, var, const expressions - -((primary (symbol) @variable.builtin) - (#match? @variable.builtin "result")) -; `result` is an implicit builtin variable inside function scopes - -((primary (symbol) @type) - (#match? @type "^[A-Z]")) -; assume PascalCase identifiers to be types - -((primary - (primarySuffix - (qualifiedSuffix - (symbol) @type))) - (#match? @type "^[A-Z]")) -; assume PascalCase member variables to be enum entries +[ + "cast" + "discard" + "do" +] @keyword +; also: addr end interface using -(primary (symbol) @variable) -; overzealous, matches variables +(blank_identifier) @variable.builtin +((identifier) @variable.builtin + (#eq? @variable.builtin "result")) -(primary (primarySuffix (qualifiedSuffix (symbol) @variable.other.member))) -; overzealous, matches member variables: i.e. x in foo.x +(dot_expression + left: (identifier) @variable + right: (identifier) @variable.other.member) -(keyw) @keyword -; more specific matches are done above whenever possible +(identifier) @variable diff --git a/runtime/queries/nim/indents.scm b/runtime/queries/nim/indents.scm index 677435407..3b3023868 100644 --- a/runtime/queries/nim/indents.scm +++ b/runtime/queries/nim/indents.scm @@ -1,48 +1,59 @@ [ - (typeDef) - (ifStmt) - (whenStmt) - (elifStmt) - (elseStmt) - (ofBranch) ; note: not caseStmt - (whileStmt) - (tryStmt) - (tryExceptStmt) - (tryFinallyStmt) - (forStmt) - (blockStmt) - (staticStmt) - (deferStmt) - (asmStmt) - ; exprStmt? + (if) + (when) + (elif_branch) + (else_branch) + (of_branch) ; note: not case_statement + (block) + (while) + (for) + (try) + (except_branch) + (finally_branch) + (defer) + (static_statement) + (proc_declaration) + (func_declaration) + (iterator_declaration) + (converter_declaration) + (method_declaration) + (template_declaration) + (macro_declaration) + (symbol_declaration) ] @indent ;; increase the indentation level [ - (ifStmt) - (whenStmt) - (elifStmt) - (elseStmt) - (ofBranch) ; note: not caseStmt - (whileStmt) - (tryStmt) - (tryExceptStmt) - (tryFinallyStmt) - (forStmt) - (blockStmt) - (staticStmt) - (deferStmt) - (asmStmt) - ; exprStmt? + (if) + (when) + (elif_branch) + (else_branch) + (of_branch) ; note: not case_statement + (block) + (while) + (for) + (try) + (except_branch) + (finally_branch) + (defer) + (static_statement) + (proc_declaration) + (func_declaration) + (iterator_declaration) + (converter_declaration) + (method_declaration) + (template_declaration) + (macro_declaration) + (symbol_declaration) ] @extend ;; ??? [ - (returnStmt) - (raiseStmt) - (yieldStmt) - (breakStmt) - (continueStmt) + (return_statement) + (raise_statement) + (yield_statement) + (break_statement) + (continue_statement) ] @extend.prevent-once ;; end a level of indentation while staying indented diff --git a/runtime/queries/nim/textobjects.scm b/runtime/queries/nim/textobjects.scm index 943aa7f08..eaa3e8e8c 100644 --- a/runtime/queries/nim/textobjects.scm +++ b/runtime/queries/nim/textobjects.scm @@ -1,19 +1,33 @@ -(routine - (block) @function.inside) @function.around +(proc_declaration + body: (_) @function.inside) @function.around +(func_declaration + body: (_) @function.inside) @function.around +(iterator_declaration + body: (_) @function.inside) @function.around +(converter_declaration + body: (_) @function.inside) @function.around +(method_declaration + body: (_) @function.inside) @function.around +(template_declaration + body: (_) @function.inside) @function.around +(macro_declaration + body: (_) @function.inside) @function.around -; @class.inside (types?) -; @class.around +(type_declaration (_) @class.inside) @class.around -; paramListSuffix is strange and i do not understand it -(paramList - (paramColonEquals) @parameter.inside) @parameter.around +(parameter_declaration + (symbol_declaration_list) @parameter.inside) @parameter.around -(comment) @comment.inside -(multilineComment) @comment.inside -(docComment) @comment.inside -(multilineDocComment) @comment.inside +[ + (comment) + (block_comment) + (documentation_comment) + (block_documentation_comment) +] @comment.inside -(comment)+ @comment.around -(multilineComment) @comment.around -(docComment)+ @comment.around -(multilineDocComment) @comment.around +[ + (comment)+ + (block_comment) + (documentation_comment)+ + (block_documentation_comment)+ +] @comment.around From d0bb77447138f5f70f96b174a8f29045a956c8c4 Mon Sep 17 00:00:00 2001 From: Keir Lawson Date: Thu, 29 Feb 2024 10:09:29 +0000 Subject: [PATCH 199/796] Mark GTK builder ui files as XML (#9754) --- languages.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index d2a183ded..a2c10d114 100644 --- a/languages.toml +++ b/languages.toml @@ -2470,7 +2470,8 @@ file-types = [ "xul", "xoml", "musicxml", - "glif" + "glif", + "ui" ] block-comment-tokens = { start = "" } indent = { tab-width = 2, unit = " " } From 44db25939c9361272660854878eb2fc18fcf08e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Ci=C4=99=C5=BCarkiewicz?= Date: Thu, 29 Feb 2024 17:57:31 -0800 Subject: [PATCH 200/796] Document embracing smart-tab navigation. (#9762) Re #4443 --- book/src/configuration.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/book/src/configuration.md b/book/src/configuration.md index de33c1ade..d87936457 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -375,8 +375,25 @@ wrap-indicator = "" # set wrap-indicator to "" to hide it ### `[editor.smart-tab]` Section +Options for navigating and editing using tab key. | Key | Description | Default | |------------|-------------|---------| | `enable` | If set to true, then when the cursor is in a position with non-whitespace to its left, instead of inserting a tab, it will run `move_parent_node_end`. If there is only whitespace to the left, then it inserts a tab as normal. With the default bindings, to explicitly insert a tab character, press Shift-tab. | `true` | | `supersede-menu` | Normally, when a menu is on screen, such as when auto complete is triggered, the tab key is bound to cycling through the items. This means when menus are on screen, one cannot use the tab key to trigger the `smart-tab` command. If this option is set to true, the `smart-tab` command always takes precedence, which means one cannot use the tab key to cycle through menu items. One of the other bindings must be used instead, such as arrow keys or `C-n`/`C-p`. | `false` | + + +Due to lack of support for S-tab in some terminals, the default keybindings don't fully embrace smart-tab editing experience. If you enjoy smart-tab navigation and a terminal that supports the [Enhanced Keyboard protocol](https://github.com/helix-editor/helix/wiki/Terminal-Support#enhanced-keyboard-protocol), consider setting extra keybindings: + +``` +[keys.normal] +tab = "move_parent_node_end" +S-tab = "move_parent_node_start" + +[keys.insert] +S-tab = "move_parent_node_start" + +[keys.select] +tab = "extend_parent_node_end" +S-tab = "extend_parent_node_start" +``` From 062fb819a21a3b17baf0cded3463a2d9f3e6b4a9 Mon Sep 17 00:00:00 2001 From: Felix Zeller Date: Fri, 1 Mar 2024 10:10:49 -0500 Subject: [PATCH 201/796] feat: Add markdown-oxide language server (#9758) --- book/src/generated/lang-support.md | 2 +- languages.toml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 1bc6b0817..c9668549e 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -104,7 +104,7 @@ | lua | ✓ | ✓ | ✓ | `lua-language-server` | | make | ✓ | | ✓ | | | markdoc | ✓ | | | `markdoc-ls` | -| markdown | ✓ | | | `marksman` | +| markdown | ✓ | | | `marksman`, `markdown-oxide` | | markdown.inline | ✓ | | | | | matlab | ✓ | ✓ | ✓ | | | mermaid | ✓ | | | | diff --git a/languages.toml b/languages.toml index a2c10d114..26f6509ce 100644 --- a/languages.toml +++ b/languages.toml @@ -45,6 +45,7 @@ kotlin-language-server = { command = "kotlin-language-server" } lean = { command = "lean", args = [ "--server" ] } ltex-ls = { command = "ltex-ls" } markdoc-ls = { command = "markdoc-ls", args = ["--stdio"] } +markdown-oxide = { command = "markdown-oxide" } marksman = { command = "marksman", args = ["server"] } metals = { command = "metals", config = { "isHttpEnabled" = true } } mint = { command = "mint", args = ["ls"] } @@ -1440,7 +1441,7 @@ scope = "source.md" injection-regex = "md|markdown" file-types = ["md", "markdown", "mkd", "mdwn", "mdown", "markdn", "mdtxt", "mdtext", "workbook", { glob = "PULLREQ_EDITMSG" }] roots = [".marksman.toml"] -language-servers = [ "marksman" ] +language-servers = [ "marksman", "markdown-oxide" ] indent = { tab-width = 2, unit = " " } block-comment-tokens = { start = "" } From 1d6db30acf91ec1041e014650bf263defdc3feee Mon Sep 17 00:00:00 2001 From: Marcin Drzymala <5504827+drzymalanet@users.noreply.github.com> Date: Sat, 2 Mar 2024 03:05:17 +0100 Subject: [PATCH 202/796] Fix bug 9703 by commenting out the wrong command (#9778) * Fix bug 9703 by commenting out the wrong command This fixes issue https://github.com/helix-editor/helix/issues/9703 by removing the wrong formatting command for justfiles. * Fix indentation width for justfile --- languages.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/languages.toml b/languages.toml index 26f6509ce..a4b6a5cd8 100644 --- a/languages.toml +++ b/languages.toml @@ -2915,9 +2915,9 @@ scope = "source.just" file-types = [{ glob = "justfile" }, { glob = "Justfile" }, { glob = ".justfile" }, { glob = ".Justfile" }] injection-regex = "just" comment-token = "#" -indent = { tab-width = 4, unit = "\t" } -auto-format = true -formatter = { command = "just", args = ["--dump"] } +indent = { tab-width = 4, unit = " " } +# auto-format = true +# formatter = { command = "just", args = ["--dump"] } # Please see: https://github.com/helix-editor/helix/issues/9703 [[grammar]] name = "just" From 5ca6a448e9b66f4f5b4caa7cd173252d0a78f92d Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Fri, 1 Mar 2024 23:37:11 -0500 Subject: [PATCH 203/796] Support LSP diagnostic tags (#9780) --- book/src/themes.md | 2 ++ helix-lsp/src/client.rs | 6 ++++++ helix-term/src/ui/editor.rs | 15 ++++++++++++++- theme.toml | 2 ++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/book/src/themes.md b/book/src/themes.md index f040dfb19..04d6a69b3 100644 --- a/book/src/themes.md +++ b/book/src/themes.md @@ -333,5 +333,7 @@ These scopes are used for theming the editor interface: | `diagnostic.info` | Diagnostics info (editing area) | | `diagnostic.warning` | Diagnostics warning (editing area) | | `diagnostic.error` | Diagnostics error (editing area) | +| `diagnostic.unnecessary` | Diagnostics with unnecessary tag (editing area) | +| `diagnostic.deprecated` | Diagnostics with deprecated tag (editing area) | [editor-section]: ./configuration.md#editor-section diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 8d03d7992..a7b3989dd 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -631,6 +631,12 @@ impl Client { }), publish_diagnostics: Some(lsp::PublishDiagnosticsClientCapabilities { version_support: Some(true), + tag_support: Some(lsp::TagSupport { + value_set: vec![ + lsp::DiagnosticTag::UNNECESSARY, + lsp::DiagnosticTag::DEPRECATED, + ], + }), ..Default::default() }), inlay_hint: Some(lsp::InlayHintClientCapabilities { diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index dffaeea03..f3bba5d1c 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -360,7 +360,7 @@ impl EditorView { doc: &Document, theme: &Theme, ) -> [Vec<(usize, std::ops::Range)>; 5] { - use helix_core::diagnostic::Severity; + use helix_core::diagnostic::{DiagnosticTag, Severity}; let get_scope_of = |scope| { theme .find_scope_index_exact(scope) @@ -380,6 +380,10 @@ impl EditorView { let error = get_scope_of("diagnostic.error"); let r#default = get_scope_of("diagnostic"); // this is a bit redundant but should be fine + // Diagnostic tags + let unnecessary = theme.find_scope_index_exact("diagnostic.unnecessary"); + let deprecated = theme.find_scope_index_exact("diagnostic.deprecated"); + let mut default_vec: Vec<(usize, std::ops::Range)> = Vec::new(); let mut info_vec = Vec::new(); let mut hint_vec = Vec::new(); @@ -396,6 +400,15 @@ impl EditorView { _ => (&mut default_vec, r#default), }; + let scope = diagnostic + .tags + .first() + .and_then(|tag| match tag { + DiagnosticTag::Unnecessary => unnecessary, + DiagnosticTag::Deprecated => deprecated, + }) + .unwrap_or(scope); + // If any diagnostic overlaps ranges with the prior diagnostic, // merge the two together. Otherwise push a new span. match vec.last_mut() { diff --git a/theme.toml b/theme.toml index dd1a5d889..8a5bfd72d 100644 --- a/theme.toml +++ b/theme.toml @@ -80,6 +80,8 @@ label = "honey" "diagnostic.info" = { underline = { color = "delta", style = "curl" } } "diagnostic.warning" = { underline = { color = "lightning", style = "curl" } } "diagnostic.error" = { underline = { color = "apricot", style = "curl" } } +"diagnostic.unnecessary" = { modifiers = ["dim"] } +"diagnostic.deprecated" = { modifiers = ["crossed_out"] } warning = "lightning" error = "apricot" From f04dafa2e23e30771db92fdf6f39fcd1f0f5d0d6 Mon Sep 17 00:00:00 2001 From: Malpha Date: Sat, 2 Mar 2024 07:47:10 +0000 Subject: [PATCH 204/796] languages.toml: add elvish shebang (#9779) --- languages.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/languages.toml b/languages.toml index a4b6a5cd8..7d859c2e4 100644 --- a/languages.toml +++ b/languages.toml @@ -2157,6 +2157,7 @@ grammar = "python" [[language]] name = "elvish" scope = "source.elvish" +shebangs = ["elvish"] file-types = ["elv"] comment-token = "#" indent = { tab-width = 2, unit = " " } From d769fadde085169c26a850966a6d5d8da7cc1c12 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Sat, 2 Mar 2024 02:47:24 -0500 Subject: [PATCH 205/796] Fix precedence of svelte typescript injection (#9777) --- runtime/queries/svelte/injections.scm | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/runtime/queries/svelte/injections.scm b/runtime/queries/svelte/injections.scm index 65a6e0e41..52d430c3f 100644 --- a/runtime/queries/svelte/injections.scm +++ b/runtime/queries/svelte/injections.scm @@ -19,13 +19,6 @@ (quoted_attribute_value (attribute_value) @css)) (#eq? @_attr "style")) -((script_element - (raw_text) @injection.content) - (#set! injection.language "javascript")) - -((raw_text_expr) @injection.content - (#set! injection.language "javascript")) - ( (script_element (start_tag @@ -36,5 +29,12 @@ (#set! injection.language "typescript") ) +((script_element + (raw_text) @injection.content) + (#set! injection.language "javascript")) + +((raw_text_expr) @injection.content + (#set! injection.language "javascript")) + ((comment) @injection.content (#set! injection.language "comment")) From 5bd007266a962a534bd722619821e998735b71e2 Mon Sep 17 00:00:00 2001 From: Mike Trinkala Date: Sat, 2 Mar 2024 06:05:58 -0800 Subject: [PATCH 206/796] Fix panic when using join_selections_space (#9783) Joining lines with Alt-J does not properly select the inserted spaces when the selection contains blank lines. In the worst case it panics with an out of bounds index. thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Char index out of bounds: char index 11, Rope/RopeSlice char length 10' Steps to reproduce: * Create a new document ``` a b c d e ``` * % (Select all) * Alt-J (join and select the spaces) --- helix-term/src/commands.rs | 23 ++++++--- helix-term/tests/test/commands.rs | 83 +++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 6 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index bd0a60b7c..0b2ea0b8a 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4372,16 +4372,27 @@ fn join_selections_impl(cx: &mut Context, select_space: bool) { // select inserted spaces let transaction = if select_space { + let mut offset: usize = 0; let ranges: SmallVec<_> = changes .iter() - .scan(0, |offset, change| { - let range = Range::point(change.0 - *offset); - *offset += change.1 - change.0 - 1; // -1 because cursor is 0-sized - Some(range) + .filter_map(|change| { + if change.2.is_some() { + let range = Range::point(change.0 - offset); + offset += change.1 - change.0 - 1; // -1 adjusts for the replacement of the range by a space + Some(range) + } else { + offset += change.1 - change.0; + None + } }) .collect(); - let selection = Selection::new(ranges, 0); - Transaction::change(text, changes.into_iter()).with_selection(selection) + let t = Transaction::change(text, changes.into_iter()); + if ranges.is_empty() { + t + } else { + let selection = Selection::new(ranges, 0); + t.with_selection(selection) + } } else { Transaction::change(text, changes.into_iter()) }; diff --git a/helix-term/tests/test/commands.rs b/helix-term/tests/test/commands.rs index e52b142c6..1172a7981 100644 --- a/helix-term/tests/test/commands.rs +++ b/helix-term/tests/test/commands.rs @@ -526,3 +526,86 @@ async fn test_join_selections() -> anyhow::Result<()> { Ok(()) } + +#[tokio::test(flavor = "multi_thread")] +async fn test_join_selections_space() -> anyhow::Result<()> { + // join with empty lines panic + test(( + platform_line(indoc! {"\ + #[a + + b + + c + + d + + e|]# + "}), + "", + platform_line(indoc! {"\ + a#[ |]#b#( |)#c#( |)#d#( |)#e + "}), + )) + .await?; + + // normal join + test(( + platform_line(indoc! {"\ + #[a|]#bc + def + "}), + "", + platform_line(indoc! {"\ + abc#[ |]#def + "}), + )) + .await?; + + // join with empty line + test(( + platform_line(indoc! {"\ + #[a|]#bc + + def + "}), + "", + platform_line(indoc! {"\ + #[a|]#bc + def + "}), + )) + .await?; + + // join with additional space in non-empty line + test(( + platform_line(indoc! {"\ + #[a|]#bc + + def + "}), + "", + platform_line(indoc! {"\ + abc#[ |]#def + "}), + )) + .await?; + + // join with retained trailing spaces + test(( + platform_line(indoc! {"\ + #[aaa + + bb + + c |]# + "}), + "", + platform_line(indoc! {"\ + aaa #[ |]#bb #( |)#c + "}), + )) + .await?; + + Ok(()) +} From 9267343830228490d379c90537ff1a6e4bba1260 Mon Sep 17 00:00:00 2001 From: Mike Trinkala Date: Sun, 3 Mar 2024 09:55:09 -0800 Subject: [PATCH 207/796] Fix panic when using surround_replace/delete (#9796) 1. Create a document containing `{A}` 1. C-w v # vsplit 1. gl # goto_line_end 1. b # move_prev_word_start 1. ` # switch_to_lowercase 1. mrm( # surround replace 1. C-w v # vsplit In the debug build surround_replace/delete will immedately assert with `assertion failed: last <= from', transaction.rs:597:13`. The splits and lowercase conversion are not needed to trigger the bug. In the release build the surround becomes `)a(` and the last vsplit causes the transaction to panic. `internal error: entered unreachable code: (Some(Retain(18446744073709551573)))', transaction.rs:185:46` Since the selection direction is backwards get_surround_pos returns the pairs reversed but the downstream code assumes they are in the forward direction. --- helix-core/src/surround.rs | 3 +- helix-term/tests/test/movement.rs | 54 +++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/helix-core/src/surround.rs b/helix-core/src/surround.rs index b96cce5a0..513f87493 100644 --- a/helix-core/src/surround.rs +++ b/helix-core/src/surround.rs @@ -260,7 +260,8 @@ pub fn get_surround_pos( if change_pos.contains(&open_pos) || change_pos.contains(&close_pos) { return Err(Error::CursorOverlap); } - change_pos.extend_from_slice(&[open_pos, close_pos]); + // ensure the positions are always paired in the forward direction + change_pos.extend_from_slice(&[open_pos.min(close_pos), close_pos.max(open_pos)]); } Ok(change_pos) } diff --git a/helix-term/tests/test/movement.rs b/helix-term/tests/test/movement.rs index e3c2668da..0873edbe5 100644 --- a/helix-term/tests/test/movement.rs +++ b/helix-term/tests/test/movement.rs @@ -552,3 +552,57 @@ async fn find_char_line_ending() -> anyhow::Result<()> { Ok(()) } + +#[tokio::test(flavor = "multi_thread")] +async fn test_surround_replace() -> anyhow::Result<()> { + test(( + platform_line(indoc! {"\ + (#[|a]#) + "}), + "mrm{", + platform_line(indoc! {"\ + {#[|a]#} + "}), + )) + .await?; + + test(( + platform_line(indoc! {"\ + (#[a|]#) + "}), + "mrm{", + platform_line(indoc! {"\ + {#[a|]#} + "}), + )) + .await?; + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_surround_delete() -> anyhow::Result<()> { + test(( + platform_line(indoc! {"\ + (#[|a]#) + "}), + "mdm", + platform_line(indoc! {"\ + #[|a]# + "}), + )) + .await?; + + test(( + platform_line(indoc! {"\ + (#[a|]#) + "}), + "mdm", + platform_line(indoc! {"\ + #[a|]# + "}), + )) + .await?; + + Ok(()) +} From cc43e3521ed94e9d6e77c719c14073d3e7217c97 Mon Sep 17 00:00:00 2001 From: RoloEdits Date: Sun, 3 Mar 2024 09:56:18 -0800 Subject: [PATCH 208/796] feat(languages): add support for `*.Dockerfile` `file-types` naming convention (#9772) Current `file-types` only supports up to a `Dockerfile.frontend` naming scheme. With these changes `frontend.Dockerfile` now gives proper highlights and lsp actions. --- languages.toml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index 7d859c2e4..49672a30b 100644 --- a/languages.toml +++ b/languages.toml @@ -1494,7 +1494,20 @@ name = "dockerfile" scope = "source.dockerfile" injection-regex = "docker|dockerfile" roots = ["Dockerfile", "Containerfile"] -file-types = [{ glob = "Dockerfile*" }, { glob = "dockerfile*" }, { glob = "Containerfile*" }, { glob = "containerfile*" }] +file-types = [ + "Dockerfile", + { glob = "Dockerfile" }, + { glob = "Dockerfile.*" }, + "dockerfile", + { glob = "dockerfile" }, + { glob = "dockerfile.*" }, + "Containerfile", + { glob = "Containerfile" }, + { glob = "Containerfile.*" }, + "containerfile", + { glob = "containerfile" }, + { glob = "containerfile.*" }, +] comment-token = "#" indent = { tab-width = 2, unit = " " } language-servers = [ "docker-langserver" ] From 3f98891e7952a748f814e6741f4375c9b7aa0983 Mon Sep 17 00:00:00 2001 From: varris1 <38386180+varris1@users.noreply.github.com> Date: Tue, 5 Mar 2024 16:00:34 +0100 Subject: [PATCH 209/796] flake.lock: Bump flake inputs to prevent a warning message (#9816) --- flake.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/flake.lock b/flake.lock index 9bb5dece1..48fb4a59f 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1701025348, - "narHash": "sha256-42GHmYH+GF7VjwGSt+fVT1CQuNpGanJbNgVHTAZppUM=", + "lastModified": 1709610799, + "narHash": "sha256-5jfLQx0U9hXbi2skYMGodDJkIgffrjIOgMRjZqms2QE=", "owner": "ipetkov", "repo": "crane", - "rev": "42afaeb1a0325194a7cdb526332d2cb92fddd07b", + "rev": "81c393c776d5379c030607866afef6406ca1be57", "type": "github" }, "original": { @@ -25,11 +25,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1694529238, - "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "lastModified": 1709126324, + "narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=", "owner": "numtide", "repo": "flake-utils", - "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "rev": "d465f4819400de7c8d874d50b982301f28a84605", "type": "github" }, "original": { @@ -40,11 +40,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1700794826, - "narHash": "sha256-RyJTnTNKhO0yqRpDISk03I/4A67/dp96YRxc86YOPgU=", + "lastModified": 1709479366, + "narHash": "sha256-n6F0n8UV6lnTZbYPl1A9q1BS0p4hduAv1mGAP17CVd0=", "owner": "nixos", "repo": "nixpkgs", - "rev": "5a09cb4b393d58f9ed0d9ca1555016a8543c2ac8", + "rev": "b8697e57f10292a6165a20f03d2f42920dfaf973", "type": "github" }, "original": { @@ -72,11 +72,11 @@ ] }, "locked": { - "lastModified": 1701137803, - "narHash": "sha256-0LcPAdql5IhQSUXJx3Zna0dYTgdIoYO7zUrsKgiBd04=", + "lastModified": 1709604635, + "narHash": "sha256-le4fwmWmjGRYWwkho0Gr7mnnZndOOe4XGbLw68OvF40=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "9dd940c967502f844eacea52a61e9596268d4f70", + "rev": "e86c0fb5d3a22a5f30d7f64ecad88643fe26449d", "type": "github" }, "original": { From 7d8c86e4039551ebe754d9e3753b9b99e6fa6419 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 11:08:07 +0900 Subject: [PATCH 210/796] build(deps): bump arc-swap from 1.6.0 to 1.7.0 (#9809) --- Cargo.lock | 4 ++-- helix-term/Cargo.toml | 2 +- helix-vcs/Cargo.toml | 2 +- helix-view/Cargo.toml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 08fa4789e..43213764d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,9 +68,9 @@ checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" [[package]] name = "arc-swap" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +checksum = "7b3d0060af21e8d11a926981cc00c6c1541aa91dd64b9f881985c3da1094425f" [[package]] name = "autocfg" diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 8c6ae9f42..accde567e 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -41,7 +41,7 @@ crossterm = { version = "0.27", features = ["event-stream"] } signal-hook = "0.3" tokio-stream = "0.1" futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false } -arc-swap = { version = "1.6.0" } +arc-swap = { version = "1.7.0" } termini = "1" # Logging diff --git a/helix-vcs/Cargo.toml b/helix-vcs/Cargo.toml index 32aca4f09..874b92d03 100644 --- a/helix-vcs/Cargo.toml +++ b/helix-vcs/Cargo.toml @@ -17,7 +17,7 @@ helix-event = { path = "../helix-event" } tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "parking_lot", "macros"] } parking_lot = "0.12" -arc-swap = { version = "1.6.0" } +arc-swap = { version = "1.7.0" } gix = { version = "0.58.0", features = ["attributes"], default-features = false, optional = true } imara-diff = "0.1.5" diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index b1b444f90..fc209b00f 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -31,7 +31,7 @@ crossterm = { version = "0.27", optional = true } once_cell = "1.19" url = "2.5.0" -arc-swap = { version = "1.6.0" } +arc-swap = { version = "1.7.0" } tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] } tokio-stream = "0.1" From ea0b2446441fd09b304e92581f644901018846d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 11:08:40 +0900 Subject: [PATCH 211/796] build(deps): bump libloading from 0.8.1 to 0.8.2 (#9810) --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 43213764d..fd498d9ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1603,12 +1603,12 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "2caa5afb8bf9f3a2652760ce7d4f62d21c4d5a423e68466fca30df82f2330164" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-targets 0.52.0", ] [[package]] From 7a473c74945bb7fd28abf9f23f9dfdd5562c7cd6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 11:09:13 +0900 Subject: [PATCH 212/796] build(deps): bump ahash from 0.8.9 to 0.8.11 (#9813) --- Cargo.lock | 4 ++-- helix-core/Cargo.toml | 2 +- helix-event/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd498d9ad..a330e5872 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "getrandom", diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index be5ea5eb8..5e2dbd977 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -32,7 +32,7 @@ once_cell = "1.19" arc-swap = "1" regex = "1" bitflags = "2.4" -ahash = "0.8.9" +ahash = "0.8.11" hashbrown = { version = "0.14.3", features = ["raw"] } dunce = "1.0" diff --git a/helix-event/Cargo.toml b/helix-event/Cargo.toml index 8711568e8..616c323dc 100644 --- a/helix-event/Cargo.toml +++ b/helix-event/Cargo.toml @@ -12,7 +12,7 @@ homepage.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ahash = "0.8.9" +ahash = "0.8.11" hashbrown = "0.14.0" tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "parking_lot", "macros"] } # the event registry is essentially read only but must be an rwlock so we can From 4e5f19df53cbe1f1b1e9ea590415c5bd58642b6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 11:10:24 +0900 Subject: [PATCH 213/796] build(deps): bump clipboard-win from 5.1.0 to 5.2.0 (#9811) --- Cargo.lock | 4 ++-- helix-view/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a330e5872..aa3700b93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -180,9 +180,9 @@ dependencies = [ [[package]] name = "clipboard-win" -version = "5.1.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ec832972fefb8cf9313b45a0d1945e29c9c251f1d4c6eafc5fe2124c02d2e81" +checksum = "12f9a0700e0127ba15d1d52dd742097f821cd9c65939303a44d970465040a297" dependencies = [ "error-code", ] diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index fc209b00f..335779bc2 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -50,7 +50,7 @@ parking_lot = "0.12.1" [target.'cfg(windows)'.dependencies] -clipboard-win = { version = "5.1", features = ["std"] } +clipboard-win = { version = "5.2", features = ["std"] } [target.'cfg(unix)'.dependencies] libc = "0.2" From b93fae9c8b955e11f427979134e3494294e8e2e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 11:10:59 +0900 Subject: [PATCH 214/796] build(deps): bump mio from 0.8.9 to 0.8.11 (#9808) --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa3700b93..fd74140a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1690,9 +1690,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", From cb01e52cd8b8021686ee98dd4d53dff8cdc826a9 Mon Sep 17 00:00:00 2001 From: Mike Trinkala Date: Thu, 7 Mar 2024 09:20:07 -0800 Subject: [PATCH 215/796] Fix panic in surround_replace/delete nested multi-cursor (#9815) Test Document ------------- ``` {{ } } ``` Steps To Reproduce ------------------ 1. 2j # move_visual_line_down 1. C # copy_selection_on_next_line 1. mdm # surround_delete Debug ----- `assertion failed: last <= from', transaction.rs:597:13` Release ------- `called `Result::unwrap()` on an `Err` value: Char range out of bounds: char range 18446744073709551614..18446744073709551615, Rope/RopeSlice char length 7', ropey-1.6.1/src/rope.rs:546:37` Description ----------- Processing the surrounding pairs in order violates the assertion the ranges are ordered. To handle nested surrounds all positions have to be sorted. Also surround_replace has to track the proper replacement character for each position. --- helix-term/src/commands.rs | 19 ++++++++++++++----- helix-term/tests/test/movement.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 0b2ea0b8a..4ac2496eb 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -5282,12 +5282,21 @@ fn surround_replace(cx: &mut Context) { None => return doc.set_selection(view.id, selection), }; let (open, close) = surround::get_pair(to); + + // the changeset has to be sorted to allow nested surrounds + let mut sorted_pos: Vec<(usize, char)> = Vec::new(); + for p in change_pos.chunks(2) { + sorted_pos.push((p[0], open)); + sorted_pos.push((p[1], close)); + } + sorted_pos.sort_unstable(); + let transaction = Transaction::change( doc.text(), - change_pos.iter().enumerate().map(|(i, &pos)| { + sorted_pos.iter().map(|&pos| { let mut t = Tendril::new(); - t.push(if i % 2 == 0 { open } else { close }); - (pos, pos + 1, Some(t)) + t.push(pos.1); + (pos.0, pos.0 + 1, Some(t)) }), ); doc.set_selection(view.id, selection); @@ -5309,14 +5318,14 @@ fn surround_delete(cx: &mut Context) { let text = doc.text().slice(..); let selection = doc.selection(view.id); - let change_pos = match surround::get_surround_pos(text, selection, surround_ch, count) { + let mut change_pos = match surround::get_surround_pos(text, selection, surround_ch, count) { Ok(c) => c, Err(err) => { cx.editor.set_error(err.to_string()); return; } }; - + change_pos.sort_unstable(); // the changeset has to be sorted to allow nested surrounds let transaction = Transaction::change(doc.text(), change_pos.into_iter().map(|p| (p, p + 1, None))); doc.apply(&transaction, view.id); diff --git a/helix-term/tests/test/movement.rs b/helix-term/tests/test/movement.rs index 0873edbe5..4ebaae854 100644 --- a/helix-term/tests/test/movement.rs +++ b/helix-term/tests/test/movement.rs @@ -577,6 +577,23 @@ async fn test_surround_replace() -> anyhow::Result<()> { )) .await?; + test(( + platform_line(indoc! {"\ + {{ + + #(}|)# + #[}|]# + "}), + "mrm)", + platform_line(indoc! {"\ + (( + + #()|)# + #[)|]# + "}), + )) + .await?; + Ok(()) } @@ -604,5 +621,17 @@ async fn test_surround_delete() -> anyhow::Result<()> { )) .await?; + test(( + platform_line(indoc! {"\ + {{ + + #(}|)# + #[}|]# + "}), + "mdm", + platform_line("\n\n#(\n|)##[\n|]#"), + )) + .await?; + Ok(()) } From e27b04735c630140b45ac9fab1b3087ae831f34a Mon Sep 17 00:00:00 2001 From: Mike Trinkala Date: Thu, 7 Mar 2024 11:37:01 -0800 Subject: [PATCH 216/796] Fix panic in select_textobject_around (#9832) Test Document ------------- ``` a)b ``` Steps to Reproduce ------------------ 1. % # select_all 1. ms( # surround_add 1. mam # select_textobject_around Debug and Release ----------------- `thread 'main' panicked at 'Attempt to index past end of RopeSlice: char index 7, RopeSlice char length 6', ropey-1.6.1/src/slice.rs:796:13` Description ----------- An index was selected beyond the end of the slice with chars_at. The fix adds a guard check to `find_nth_open_pair`, like in the other find_nth* functions. --- helix-core/src/surround.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/helix-core/src/surround.rs b/helix-core/src/surround.rs index 513f87493..ed9764883 100644 --- a/helix-core/src/surround.rs +++ b/helix-core/src/surround.rs @@ -167,6 +167,10 @@ fn find_nth_open_pair( mut pos: usize, n: usize, ) -> Option { + if pos >= text.len_chars() { + return None; + } + let mut chars = text.chars_at(pos + 1); // Adjusts pos for the first iteration, and handles the case of the @@ -383,6 +387,21 @@ mod test { ) } + #[test] + fn test_find_nth_closest_pairs_pos_index_range_panic() { + #[rustfmt::skip] + let (doc, selection, _) = + rope_with_selections_and_expectations( + "(a)c)", + "^^^^^" + ); + + assert_eq!( + find_nth_closest_pairs_pos(doc.slice(..), selection.primary(), 1), + Err(Error::PairNotFound) + ) + } + // Create a Rope and a matching Selection using a specification language. // ^ is a single-point selection. // _ is an expected index. These are returned as a Vec for use in assertions. From 301dfb07ccf3df41c381300dddb760bf76745cf5 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 7 Mar 2024 22:39:00 +0000 Subject: [PATCH 217/796] Add PowerShell highlighting (#9827) --- book/src/generated/lang-support.md | 1 + languages.toml | 14 ++ runtime/queries/powershell/highlights.scm | 174 ++++++++++++++++++++++ runtime/queries/powershell/injections.scm | 2 + 4 files changed, 191 insertions(+) create mode 100644 runtime/queries/powershell/highlights.scm create mode 100644 runtime/queries/powershell/injections.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index c9668549e..9149acadf 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -132,6 +132,7 @@ | po | ✓ | ✓ | | | | pod | ✓ | | | | | ponylang | ✓ | ✓ | ✓ | | +| powershell | ✓ | | | | | prisma | ✓ | | | `prisma-language-server` | | prolog | | | | `swipl` | | protobuf | ✓ | ✓ | ✓ | `bufls`, `pb` | diff --git a/languages.toml b/languages.toml index 49672a30b..70953b990 100644 --- a/languages.toml +++ b/languages.toml @@ -3259,3 +3259,17 @@ indent = { tab-width = 4, unit = " " } [[grammar]] name = "fidl" source = { git = "https://github.com/google/tree-sitter-fidl", rev = "bdbb635a7f5035e424f6173f2f11b9cd79703f8d" } + +[[language]] +name = "powershell" +scope = "source.powershell" +injection-regex = "(pwsh|powershell)" +file-types = [ "ps1", "psm1", "psd1", "pscc", "psrc" ] +shebangs = [ "pwsh", "powershell" ] +comment-token = '#' +block-comment-tokens = { start = "<#", end = "#>" } +indent = { tab-width = 4, unit = " " } + +[[grammar]] +name = "powershell" +source = { git = "https://github.com/airbus-cert/tree-sitter-powershell", rev = "c9316be0faca5d5b9fd3b57350de650755f42dc0" } diff --git a/runtime/queries/powershell/highlights.scm b/runtime/queries/powershell/highlights.scm new file mode 100644 index 000000000..c62c72814 --- /dev/null +++ b/runtime/queries/powershell/highlights.scm @@ -0,0 +1,174 @@ +[ + "if" + "elseif" + "else" + "switch" +] @keyword.control.conditional + +[ + "foreach" + "for" + "while" + "do" + "until" +] @keyword.control.repeat + +[ + "break" + "continue" + "return" +] @keyword.control.return + +"in" @keyword.operator + +"function" @keyword.function + +[ + "class" + "enum" +] @keyword.storage.type + +[ + "param" + "dynamicparam" + "begin" + "process" + "end" + "filter" + "workflow" + "throw" + "exit" + "trap" + "try" + "catch" + "finally" + "data" + "inlinescript" + "parallel" + "sequence" +] @keyword + +[ + "-as" + "-ccontains" + "-ceq" + "-cge" + "-cgt" + "-cle" + "-clike" + "-clt" + "-cmatch" + "-cne" + "-cnotcontains" + "-cnotlike" + "-cnotmatch" + "-contains" + "-creplace" + "-csplit" + "-eq" + "-ge" + "-gt" + "-icontains" + "-ieq" + "-ige" + "-igt" + "-ile" + "-ilike" + "-ilt" + "-imatch" + "-in" + "-ine" + "-inotcontains" + "-inotlike" + "-inotmatch" + "-ireplace" + "-is" + "-isnot" + "-isplit" + "-join" + "-le" + "-like" + "-lt" + "-match" + "-ne" + "-not" + "-notcontains" + "-notin" + "-notlike" + "-notmatch" + "-replace" + "-shl" + "-shr" + "-split" + "-and" + "-or" + "-xor" + "-band" + "-bor" + "-bxor" + "+" + "-" + "*" + "/" + "%" + "++" + "--" + "!" + "\\" + ".." + "|" +] @operator + +(assignement_operator) @operator + +[ + "(" + ")" + "{" + "}" + "[" + "]" +] @punctuation.bracket + +[ + ";" + "," + "::" +] @punctuation.delimiter + +(string_literal) @string + +(integer_literal) @constant.numeric +(real_literal) @constant.numeric + +(command + command_name: (command_name) @function) + +(function_name) @function + +(invokation_expression + (member_name) @function) + +(member_access + (member_name) @variable.other.member) + +(command_invokation_operator) @operator + +(type_spec) @type + +(variable) @variable + +(comment) @comment + +(array_expression) @punctuation.bracket + +(assignment_expression + value: (pipeline) @variable) + +(format_operator) @operator + +(command_parameter) @variable.parameter + +(command_elements) @variable.builtin + +(generic_token) @variable diff --git a/runtime/queries/powershell/injections.scm b/runtime/queries/powershell/injections.scm new file mode 100644 index 000000000..321c90add --- /dev/null +++ b/runtime/queries/powershell/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) From fd89c3c8335399e344e038f1141ea0657653a591 Mon Sep 17 00:00:00 2001 From: Alexander Brevig Date: Fri, 8 Mar 2024 02:54:17 +0100 Subject: [PATCH 218/796] fix: close #9771 fix comments with `(` and `)` (#9800) * fix: close #9771 update OCaml * fix: no longer match on ( ) as the underlying grammar handles these * fix: implement excellent corrections from review * fix: module -> namespace to match theme scopes --- languages.toml | 4 +- runtime/queries/comment/highlights.scm | 5 - runtime/queries/ocaml/highlights.scm | 141 ++++++++++++------------- 3 files changed, 67 insertions(+), 83 deletions(-) diff --git a/languages.toml b/languages.toml index 70953b990..e96f06183 100644 --- a/languages.toml +++ b/languages.toml @@ -1095,7 +1095,7 @@ indent = { tab-width = 2, unit = " " } [[grammar]] name = "ocaml" -source = { git = "https://github.com/tree-sitter/tree-sitter-ocaml", rev = "23d419ba45789c5a47d31448061557716b02750a", subpath = "ocaml" } +source = { git = "https://github.com/tree-sitter/tree-sitter-ocaml", rev = "9965d208337d88bbf1a38ad0b0fe49e5f5ec9677", subpath = "ocaml" } [[language]] name = "ocaml-interface" @@ -1115,7 +1115,7 @@ indent = { tab-width = 2, unit = " " } [[grammar]] name = "ocaml-interface" -source = { git = "https://github.com/tree-sitter/tree-sitter-ocaml", rev = "23d419ba45789c5a47d31448061557716b02750a", subpath = "interface" } +source = { git = "https://github.com/tree-sitter/tree-sitter-ocaml", rev = "9965d208337d88bbf1a38ad0b0fe49e5f5ec9677", subpath = "interface" } [[language]] name = "lua" diff --git a/runtime/queries/comment/highlights.scm b/runtime/queries/comment/highlights.scm index 4cefcdf74..ba26ca0bf 100644 --- a/runtime/queries/comment/highlights.scm +++ b/runtime/queries/comment/highlights.scm @@ -1,8 +1,3 @@ -[ - "(" - ")" -] @punctuation.bracket - ":" @punctuation.delimiter ; Hint level tags diff --git a/runtime/queries/ocaml/highlights.scm b/runtime/queries/ocaml/highlights.scm index 9d3bf4c8b..f2a4f0a4c 100644 --- a/runtime/queries/ocaml/highlights.scm +++ b/runtime/queries/ocaml/highlights.scm @@ -6,9 +6,12 @@ ; Types ;------ -[(class_name) (class_type_name) (type_constructor)] @type +( + (type_constructor) @type.builtin + (#match? @type.builtin "^(int|char|bytes|string|float|bool|unit|exn|array|list|option|int32|int64|nativeint|format6|lazy_t)$") +) -(type_variable) @type.parameter +[(class_name) (class_type_name) (type_constructor)] @type [(constructor_name) (tag)] @constructor @@ -29,27 +32,34 @@ (method_name) @function.method -; Variables -;---------- - -(value_pattern) @variable.parameter - ; Application ;------------ +( + (value_name) @function.builtin + (#match? @function.builtin "^(raise(_notrace)?|failwith|invalid_arg)$") +) + (infix_expression left: (value_path (value_name) @function) - (infix_operator) @operator + operator: (concat_operator) @operator (#eq? @operator "@@")) (infix_expression - (infix_operator) @operator + operator: (rel_operator) @operator right: (value_path (value_name) @function) (#eq? @operator "|>")) (application_expression function: (value_path (value_name) @function)) +; Variables +;---------- + +[(value_name) (type_variable)] @variable + +(value_pattern) @variable.parameter + ; Properties ;----------- @@ -58,55 +68,68 @@ ; Constants ;---------- -[(boolean) (unit)] @constant - -[(number) (signed_number)] @constant.numeric.integer +(boolean) @constant.builtin.boolean -(character) @constant.character +[(number) (signed_number)] @constant.numeric -(string) @string +[(string) (character)] @string (quoted_string "{" @string "}" @string) @string (escape_sequence) @constant.character.escape +(conversion_specification) @string.special + +; Operators +;---------- + +(match_expression (match_operator) @keyword) + +(value_definition [(let_operator) (let_and_operator)] @keyword) + [ - (conversion_specification) - (pretty_printing_indication) -] @punctuation.special + (prefix_operator) + (sign_operator) + (pow_operator) + (mult_operator) + (add_operator) + (concat_operator) + (rel_operator) + (and_operator) + (or_operator) + (assign_operator) + (hash_operator) + (indexing_operator) + (let_operator) + (let_and_operator) + (match_operator) +] @operator + +["*" "#" "::" "<-"] @operator ; Keywords ;--------- [ - "and" "as" "assert" "begin" "class" "constraint" - "end" "external" "in" - "inherit" "initializer" "lazy" "let" "match" "method" "module" - "mutable" "new" "nonrec" "object" "of" "private" "rec" "sig" "struct" - "type" "val" "virtual" "when" "with" + "and" "as" "assert" "begin" "class" "constraint" "do" "done" "downto" "else" + "end" "exception" "external" "for" "fun" "function" "functor" "if" "in" + "include" "inherit" "initializer" "lazy" "let" "match" "method" "module" + "mutable" "new" "nonrec" "object" "of" "open" "private" "rec" "sig" "struct" + "then" "to" "try" "type" "val" "virtual" "when" "while" "with" ] @keyword -["fun" "function" "functor"] @keyword.function - -["if" "then" "else"] @keyword.control.conditional - -["exception" "try"] @keyword.control.exception - -["include" "open"] @keyword.control.import - -["for" "to" "downto" "while" "do" "done"] @keyword.control.repeat +; Punctuation +;------------ -; Macros -;------- +(attribute ["[@" "]"] @punctuation.special) +(item_attribute ["[@@" "]"] @punctuation.special) +(floating_attribute ["[@@@" "]"] @punctuation.special) +(extension ["[%" "]"] @punctuation.special) +(item_extension ["[%%" "]"] @punctuation.special) +(quoted_extension ["{%" "}"] @punctuation.special) +(quoted_item_extension ["{%%" "}"] @punctuation.special) -(attribute ["[@" "]"] @attribute) -(item_attribute ["[@@" "]"] @attribute) -(floating_attribute ["[@@@" "]"] @attribute) -(extension ["[%" "]"] @function.macro) -(item_extension ["[%%" "]"] @function.macro) -(quoted_extension ["{%" "}"] @function.macro) -(quoted_item_extension ["{%%" "}"] @function.macro) -"%" @function.macro +"%" @punctuation.special ["(" ")" "[" "]" "{" "}" "[|" "|]" "[<" "[>"] @punctuation.bracket @@ -117,46 +140,12 @@ "->" ";;" ":>" "+=" ":=" ".." ] @punctuation.delimiter -; Operators -;---------- - -[ - (prefix_operator) - (sign_operator) - (infix_operator) - (hash_operator) - (indexing_operator) - (let_operator) - (and_operator) - (match_operator) -] @operator - -(match_expression (match_operator) @keyword) - -(value_definition [(let_operator) (and_operator)] @keyword) - -;; TODO: this is an error now -;(prefix_operator "!" @operator) - -(infix_operator ["&" "+" "-" "=" ">" "|" "%"] @operator) - -(signed_number ["+" "-"] @operator) - -["*" "#" "::" "<-"] @operator - ; Attributes ;----------- -(attribute_id) @variable.other.member +(attribute_id) @tag ; Comments ;--------- [(comment) (line_number_directive) (directive) (shebang)] @comment - -(ERROR) @error - -; Blanket highlights -; ------------------ - -[(value_name) (type_variable)] @variable From e3c6c82828299f1728f16a8d8fcfa5c3603a3d47 Mon Sep 17 00:00:00 2001 From: Matthew Toohey Date: Sat, 9 Mar 2024 02:59:56 -0500 Subject: [PATCH 219/796] add linker script language (#9835) --- book/src/generated/lang-support.md | 1 + languages.toml | 12 ++ runtime/queries/ld/highlights.scm | 173 +++++++++++++++++++++++++++++ runtime/queries/ld/indents.scm | 12 ++ runtime/queries/ld/injections.scm | 2 + 5 files changed, 200 insertions(+) create mode 100644 runtime/queries/ld/highlights.scm create mode 100644 runtime/queries/ld/indents.scm create mode 100644 runtime/queries/ld/injections.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 9149acadf..7792bf594 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -94,6 +94,7 @@ | kdl | ✓ | ✓ | ✓ | | | kotlin | ✓ | | | `kotlin-language-server` | | latex | ✓ | ✓ | | `texlab` | +| ld | ✓ | | ✓ | | | lean | ✓ | | | `lean` | | ledger | ✓ | | | | | llvm | ✓ | ✓ | ✓ | | diff --git a/languages.toml b/languages.toml index e96f06183..2e9f5d195 100644 --- a/languages.toml +++ b/languages.toml @@ -3273,3 +3273,15 @@ indent = { tab-width = 4, unit = " " } [[grammar]] name = "powershell" source = { git = "https://github.com/airbus-cert/tree-sitter-powershell", rev = "c9316be0faca5d5b9fd3b57350de650755f42dc0" } + +[[language]] +name = "ld" +scope = "source.ld" +injection-regex = "ld" +file-types = ["ld"] +block-comment-tokens = { start = "/*", end = "*/" } +indent = { tab-width = 2, unit = " " } + +[[grammar]] +name = "ld" +source = { git = "https://github.com/mtoohey31/tree-sitter-ld", rev = "81978cde3844bfc199851e39c80a20ec6444d35e" } diff --git a/runtime/queries/ld/highlights.scm b/runtime/queries/ld/highlights.scm new file mode 100644 index 000000000..4f935e753 --- /dev/null +++ b/runtime/queries/ld/highlights.scm @@ -0,0 +1,173 @@ +; Identifiers + +(section + . + (NAME) @namespace) + +(NAME) @variable + +; Operators + +[ + "=" + "+=" + "-=" + "*=" + "/=" + "<<=" + ">>=" + "&=" + "|=" + "^=" + "*" + "/" + "%" + "+" + "-" + "<<" + ">>" + "==" + "!=" + "<=" + ">=" + "<" + ">" + "&" + "^" + "|" + "&&" + "||" + "?" +] @operator + +; Keywords + +[ + "ABSOLUTE" + "ADDR" + "ALIGNOF" + "ASSERT" + "BYTE" + "CONSTANT" + "DATA_SEGMENT_ALIGN" + "DATA_SEGMENT_END" + "DATA_SEGMENT_RELRO_END" + "DEFINED" + "LOADADDR" + "LOG2CEIL" + "LONG" + "MAX" + "MIN" + "NEXT" + "QUAD" + "SHORT" + "SIZEOF" + "SQUAD" + "FILL" + "SEGMENT_START" +] @function.builtin + +[ + "CONSTRUCTORS" + "CREATE_OBJECT_SYMBOLS" + "LINKER_VERSION" + "SIZEOF_HEADERS" +] @constant.builtin + +[ + "AFTER" + "ALIGN" + "ALIGN_WITH_INPUT" + "ASCIZ" + "AS_NEEDED" + "AT" + "BEFORE" + "BIND" + "BLOCK" + "COPY" + "DSECT" + "ENTRY" + "EXCLUDE_FILE" + "EXTERN" + "extern" + "FLOAT" + "FORCE_COMMON_ALLOCATION" + "FORCE_GROUP_ALLOCATION" + "global" + "GROUP" + "HIDDEN" + "HLL" + "INCLUDE" + "INFO" + "INHIBIT_COMMON_ALLOCATION" + "INPUT" + "INPUT_SECTION_FLAGS" + "KEEP" + "l" + "LD_FEATURE" + "len" + "LENGTH" + "local" + "MAP" + "MEMORY" + "NOCROSSREFS" + "NOCROSSREFS_TO" + "NOFLOAT" + "NOLOAD" + "o" + "ONLY_IF_RO" + "ONLY_IF_RW" + "org" + "ORIGIN" + "OUTPUT" + "OUTPUT_ARCH" + "OUTPUT_FORMAT" + "OVERLAY" + "PHDRS" + "PROVIDE" + "PROVIDE_HIDDEN" + "READONLY" + "REGION_ALIAS" + "REVERSE" + "SEARCH_DIR" + "SECTIONS" + "SORT" + "SORT_BY_ALIGNMENT" + "SORT_BY_INIT_PRIORITY" + "SORT_BY_NAME" + "SORT_NONE" + "SPECIAL" + "STARTUP" + "SUBALIGN" + "SYSLIB" + "TARGET" + "TYPE" + "VERSION" +] @keyword + +; Delimiters + +[ + "," + ";" + "&" + ":" + ">" +] @punctuation.delimiter + +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +; Literals + +(INT) @constant.numeric.integer + +; Comment + +(comment) @comment diff --git a/runtime/queries/ld/indents.scm b/runtime/queries/ld/indents.scm new file mode 100644 index 000000000..2e2928b5d --- /dev/null +++ b/runtime/queries/ld/indents.scm @@ -0,0 +1,12 @@ +[ + (sections) + (memory) + (section) + (phdrs) + (overlay_section) + (version) + (vers_node) + (vers_defns) +] @indent + +"}" @outdent @extend.prevent-once diff --git a/runtime/queries/ld/injections.scm b/runtime/queries/ld/injections.scm new file mode 100644 index 000000000..2f0e58eb6 --- /dev/null +++ b/runtime/queries/ld/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) From 0dc67ff8852ce99d40ad4464062ebe212b0b03a1 Mon Sep 17 00:00:00 2001 From: "Markus F.X.J. Oberhumer" Date: Sat, 9 Mar 2024 09:02:43 +0100 Subject: [PATCH 220/796] helix-term: allow to backspace out-of the command prompt (#9828) --- helix-term/src/ui/prompt.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs index a6ee7f05d..d46c13138 100644 --- a/helix-term/src/ui/prompt.rs +++ b/helix-term/src/ui/prompt.rs @@ -544,6 +544,10 @@ impl Component for Prompt { (self.callback_fn)(cx, &self.line, PromptEvent::Update); } ctrl!('h') | key!(Backspace) | shift!(Backspace) => { + if self.line.is_empty() { + (self.callback_fn)(cx, &self.line, PromptEvent::Abort); + return close_fn; + } self.delete_char_backwards(cx.editor); (self.callback_fn)(cx, &self.line, PromptEvent::Update); } From 3bd493299fe632a7ff09fbd4c92c702ba3d0853f Mon Sep 17 00:00:00 2001 From: Aidan Gauland Date: Sun, 10 Mar 2024 16:22:04 +1300 Subject: [PATCH 221/796] Use Nu language for NUON files (#9839) --- languages.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index 2e9f5d195..bf726e349 100644 --- a/languages.toml +++ b/languages.toml @@ -1934,7 +1934,7 @@ source = { git = "https://github.com/PrestonKnopp/tree-sitter-godot-resource", r name = "nu" scope = "source.nu" injection-regex = "nu" -file-types = ["nu"] +file-types = ["nu", "nuon"] shebangs = ["nu"] comment-token = "#" indent = { tab-width = 2, unit = " " } From c145999bff81f763a9e2d20d28c79f32bbd1306b Mon Sep 17 00:00:00 2001 From: Kalpaj Chaudhari Date: Sun, 10 Mar 2024 08:53:33 +0530 Subject: [PATCH 222/796] treesitter: Add textobjects for native funcs and constructors (#9806) This allows native functions and constructors to be accessible as part of goto_{next,prev}_func. Change-Id: Ia1234004e8b38e1c5871331a38fcf4f267da935e --- runtime/queries/java/textobjects.scm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/runtime/queries/java/textobjects.scm b/runtime/queries/java/textobjects.scm index a932c7934..b0e73a0a7 100644 --- a/runtime/queries/java/textobjects.scm +++ b/runtime/queries/java/textobjects.scm @@ -1,4 +1,7 @@ (method_declaration + body: (_)? @function.inside) @function.around + +(constructor_declaration body: (_) @function.inside) @function.around (interface_declaration From 2d589e74f057188d113c34a6e52cc614134b5f31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:38:19 +0900 Subject: [PATCH 223/796] build(deps): bump cachix/install-nix-action from 25 to 26 (#9851) Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 25 to 26. - [Release notes](https://github.com/cachix/install-nix-action/releases) - [Commits](https://github.com/cachix/install-nix-action/compare/v25...v26) --- updated-dependencies: - dependency-name: cachix/install-nix-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/cachix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cachix.yml b/.github/workflows/cachix.yml index 57f0a0db4..9638137b8 100644 --- a/.github/workflows/cachix.yml +++ b/.github/workflows/cachix.yml @@ -14,7 +14,7 @@ jobs: uses: actions/checkout@v4 - name: Install nix - uses: cachix/install-nix-action@v25 + uses: cachix/install-nix-action@v26 - name: Authenticate with Cachix uses: cachix/cachix-action@v14 From 2e2a1d6f613eb964dfea655a1e377cb12d1d7b48 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:41:40 +0900 Subject: [PATCH 224/796] build(deps): bump open from 5.0.1 to 5.1.2 (#9854) Bumps [open](https://github.com/Byron/open-rs) from 5.0.1 to 5.1.2. - [Release notes](https://github.com/Byron/open-rs/releases) - [Changelog](https://github.com/Byron/open-rs/blob/main/changelog.md) - [Commits](https://github.com/Byron/open-rs/compare/v5.0.1...v5.1.2) --- updated-dependencies: - dependency-name: open dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- helix-term/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd74140a5..b67796aba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1767,9 +1767,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "open" -version = "5.0.1" +version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90878fb664448b54c4e592455ad02831e23a3f7e157374a8b95654731aac7349" +checksum = "449f0ff855d85ddbf1edd5b646d65249ead3f5e422aaa86b7d2d0b049b103e32" dependencies = [ "is-wsl", "libc", diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index accde567e..bc3117d20 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -58,7 +58,7 @@ pulldown-cmark = { version = "0.10", default-features = false } content_inspector = "0.2.4" # opening URLs -open = "5.0.1" +open = "5.1.2" url = "2.5.0" # config From ab61874efb9e53c55f44741b2455c4c08602bec9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:41:48 +0900 Subject: [PATCH 225/796] build(deps): bump cc from 1.0.88 to 1.0.90 (#9855) Bumps [cc](https://github.com/rust-lang/cc-rs) from 1.0.88 to 1.0.90. - [Release notes](https://github.com/rust-lang/cc-rs/releases) - [Commits](https://github.com/rust-lang/cc-rs/compare/1.0.88...1.0.90) --- updated-dependencies: - dependency-name: cc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b67796aba..6fe238fe0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -145,9 +145,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "cc" -version = "1.0.88" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" [[package]] name = "cfg-if" From 2d15acdf6029fd9b418bf4797c2947d6004dddbd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:45:44 +0900 Subject: [PATCH 226/796] build(deps): bump libloading from 0.8.2 to 0.8.3 (#9857) Bumps [libloading](https://github.com/nagisa/rust_libloading) from 0.8.2 to 0.8.3. - [Commits](https://github.com/nagisa/rust_libloading/compare/0.8.2...0.8.3) --- updated-dependencies: - dependency-name: libloading dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6fe238fe0..22b78986e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1603,9 +1603,9 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2caa5afb8bf9f3a2652760ce7d4f62d21c4d5a423e68466fca30df82f2330164" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", "windows-targets 0.52.0", From 3915b04bd91084477b3076952e6ad6cfdd414e72 Mon Sep 17 00:00:00 2001 From: fnuttens Date: Wed, 13 Mar 2024 05:47:55 +0100 Subject: [PATCH 227/796] fix(themes-catppuccin): make inlay hints more legible (#9859) --- runtime/themes/catppuccin_mocha.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/themes/catppuccin_mocha.toml b/runtime/themes/catppuccin_mocha.toml index 1ea57e960..65ba76e4b 100644 --- a/runtime/themes/catppuccin_mocha.toml +++ b/runtime/themes/catppuccin_mocha.toml @@ -88,7 +88,7 @@ "ui.virtual" = "overlay0" "ui.virtual.ruler" = { bg = "surface0" } "ui.virtual.indent-guide" = "surface0" -"ui.virtual.inlay-hint" = { fg = "surface1", bg = "mantle" } +"ui.virtual.inlay-hint" = { fg = "overlay0", bg = "base" } "ui.selection" = { bg = "surface1" } From e01a5582942ef96d0de9c369d2b9408c6355cd1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 14:13:36 +0900 Subject: [PATCH 228/796] build(deps): bump log from 0.4.20 to 0.4.21 (#9856) Bumps [log](https://github.com/rust-lang/log) from 0.4.20 to 0.4.21. - [Release notes](https://github.com/rust-lang/log/releases) - [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/log/compare/0.4.20...0.4.21) --- updated-dependencies: - dependency-name: log dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 22b78986e..9bea99ac5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1638,9 +1638,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "lsp-types" From b44b627b1403f8a3e23251bc79558d482346c4b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 23:37:10 +0000 Subject: [PATCH 229/796] build(deps): bump chrono from 0.4.34 to 0.4.35 Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.34 to 0.4.35. - [Release notes](https://github.com/chronotope/chrono/releases) - [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md) - [Commits](https://github.com/chronotope/chrono/compare/v0.4.34...v0.4.35) --- updated-dependencies: - dependency-name: chrono dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9bea99ac5..8628d002f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -168,9 +168,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.34" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" dependencies = [ "android-tzdata", "iana-time-zone", From 6c4d986c1b1ac4e350dced513b6608ba4464cde3 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Tue, 12 Mar 2024 09:58:33 -0400 Subject: [PATCH 230/796] Use non-deprecated chrono Duration functions --- helix-core/src/increment/date_time.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/helix-core/src/increment/date_time.rs b/helix-core/src/increment/date_time.rs index 2980bb58b..04cff6b47 100644 --- a/helix-core/src/increment/date_time.rs +++ b/helix-core/src/increment/date_time.rs @@ -27,7 +27,7 @@ pub fn increment(selected_text: &str, amount: i64) -> Option { let date_time = NaiveDateTime::parse_from_str(date_time, format.fmt).ok()?; Some( date_time - .checked_add_signed(Duration::minutes(amount))? + .checked_add_signed(Duration::try_minutes(amount)?)? .format(format.fmt) .to_string(), ) @@ -35,14 +35,15 @@ pub fn increment(selected_text: &str, amount: i64) -> Option { (true, false) => { let date = NaiveDate::parse_from_str(date_time, format.fmt).ok()?; Some( - date.checked_add_signed(Duration::days(amount))? + date.checked_add_signed(Duration::try_days(amount)?)? .format(format.fmt) .to_string(), ) } (false, true) => { let time = NaiveTime::parse_from_str(date_time, format.fmt).ok()?; - let (adjusted_time, _) = time.overflowing_add_signed(Duration::minutes(amount)); + let (adjusted_time, _) = + time.overflowing_add_signed(Duration::try_minutes(amount)?); Some(adjusted_time.format(format.fmt).to_string()) } (false, false) => None, From 0c51ab16d0c65505705297b89ebb1147f3cc8fee Mon Sep 17 00:00:00 2001 From: Kirawi <67773714+kirawi@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:38:22 -0400 Subject: [PATCH 231/796] Add a yank diagnostic command (#9640) * yank diagnostic command * improve success message * move to a typed command * docgen --- book/src/generated/typable-cmd.md | 1 + helix-term/src/commands/typed.rs | 47 +++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/book/src/generated/typable-cmd.md b/book/src/generated/typable-cmd.md index f4fcb6f62..dbb8b5f38 100644 --- a/book/src/generated/typable-cmd.md +++ b/book/src/generated/typable-cmd.md @@ -86,3 +86,4 @@ | `:clear-register` | Clear given register. If no argument is provided, clear all registers. | | `:redraw` | Clear and re-render the whole UI | | `:move` | Move the current buffer and its corresponding file to a different path | +| `:yank-diagnostic` | Yank diagnostic(s) under primary cursor to register, or clipboard by default | diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 3d7ea3fc8..384db4ac3 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -2414,6 +2414,46 @@ fn move_buffer( Ok(()) } +fn yank_diagnostic( + cx: &mut compositor::Context, + args: &[Cow], + event: PromptEvent, +) -> anyhow::Result<()> { + if event != PromptEvent::Validate { + return Ok(()); + } + + let (view, doc) = current_ref!(cx.editor); + let primary = doc.selection(view.id).primary(); + + // Look only for diagnostics that intersect with the primary selection + let diag: Vec<_> = doc + .diagnostics() + .iter() + .filter(|d| primary.overlaps(&helix_core::Range::new(d.range.start, d.range.end))) + .map(|d| d.message.clone()) + .collect(); + let n = diag.len(); + if n == 0 { + bail!("No diagnostics under primary selection"); + } + + let reg = match args.get(0) { + Some(s) => { + ensure!(s.chars().count() == 1, format!("Invalid register {s}")); + s.chars().next().unwrap() + } + None => '+', + }; + + cx.editor.registers.write(reg, diag)?; + cx.editor.set_status(format!( + "Yanked {n} diagnostic{} to register {reg}", + if n == 1 { "" } else { "s" } + )); + Ok(()) +} + pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "quit", @@ -3021,6 +3061,13 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ fun: move_buffer, signature: CommandSignature::positional(&[completers::filename]), }, + TypableCommand { + name: "yank-diagnostic", + aliases: &[], + doc: "Yank diagnostic(s) under primary cursor to register, or clipboard by default", + fun: yank_diagnostic, + signature: CommandSignature::none(), + }, ]; pub static TYPABLE_COMMAND_MAP: Lazy> = From b961acf74601f0ede754d595f52ed4cba300e37f Mon Sep 17 00:00:00 2001 From: Mike Trinkala Date: Fri, 15 Mar 2024 12:44:08 -0700 Subject: [PATCH 232/796] Update regex-cursor (#9891) --- Cargo.lock | 4 ++-- helix-stdx/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8628d002f..7b73b1b1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1950,9 +1950,9 @@ dependencies = [ [[package]] name = "regex-cursor" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a43718aa0040434d45728c43f56bd53bda75a91c46954cdf0f2ff4dbc8aabbe7" +checksum = "ae4327b5fde3ae6fda0152128d3d59b95a5aad7be91c405869300091720f7169" dependencies = [ "log", "memchr", diff --git a/helix-stdx/Cargo.toml b/helix-stdx/Cargo.toml index 5ac7c011f..ed23f4e4f 100644 --- a/helix-stdx/Cargo.toml +++ b/helix-stdx/Cargo.toml @@ -16,7 +16,7 @@ dunce = "1.0" etcetera = "0.8" ropey = { version = "1.6.1", default-features = false } which = "6.0" -regex-cursor = "0.1.3" +regex-cursor = "0.1.4" [dev-dependencies] tempfile = "3.10" From 9282f1b8e546583d9e461cd78bed7d2f21dd1770 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Fri, 15 Mar 2024 19:52:57 -0400 Subject: [PATCH 233/796] Handle starting and continuing the count separately (#9887) --- helix-term/src/ui/editor.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index f3bba5d1c..c1e36bbdd 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -916,13 +916,15 @@ impl EditorView { fn command_mode(&mut self, mode: Mode, cxt: &mut commands::Context, event: KeyEvent) { match (event, cxt.editor.count) { - // count handling - (key!(i @ '0'), Some(_)) | (key!(i @ '1'..='9'), _) - if !self.keymaps.contains_key(mode, event) => - { + // If the count is already started and the input is a number, always continue the count. + (key!(i @ '0'..='9'), Some(count)) => { + let i = i.to_digit(10).unwrap() as usize; + cxt.editor.count = NonZeroUsize::new(count.get() * 10 + i); + } + // A non-zero digit will start the count if that number isn't used by a keymap. + (key!(i @ '1'..='9'), None) if !self.keymaps.contains_key(mode, event) => { let i = i.to_digit(10).unwrap() as usize; - cxt.editor.count = - std::num::NonZeroUsize::new(cxt.editor.count.map_or(i, |c| c.get() * 10 + i)); + cxt.editor.count = NonZeroUsize::new(i); } // special handling for repeat operator (key!('.'), _) if self.keymaps.pending().is_empty() => { From 6fea7876a47df8627a4b40361a6fc0f692c6601f Mon Sep 17 00:00:00 2001 From: Nick Date: Sat, 16 Mar 2024 18:20:47 +0530 Subject: [PATCH 234/796] Fix comment key bind behaviour in OCaml (#9894) --- languages.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/languages.toml b/languages.toml index bf726e349..8fbb98e88 100644 --- a/languages.toml +++ b/languages.toml @@ -1083,7 +1083,6 @@ injection-regex = "ocaml" file-types = ["ml"] shebangs = ["ocaml", "ocamlrun", "ocamlscript"] block-comment-tokens = { start = "(*", end = "*)" } -comment-token = "(**)" language-servers = [ "ocamllsp" ] indent = { tab-width = 2, unit = " " } From 761df60077bf33cb1077eb4cb019885ea3dd6ae7 Mon Sep 17 00:00:00 2001 From: Emi <95967983+EmiOnGit@users.noreply.github.com> Date: Sun, 17 Mar 2024 23:06:24 +0100 Subject: [PATCH 235/796] Keybind for Extend/shrink selection up and down (#9080) * implement another selection modifying command * Selection feels more ergonomic in case of swapping the direction. This also fixes a problem when starting at an empty line. * rename select_line_up/down to select_line_above/below * apply clippy suggestion of using cmp instead of if-chain * revert `Extent` implementing `Clone/Copy` * move select_line functions below extend_line implementations * implement help add function, which saturates at the number of text lines --------- Co-authored-by: Emi --- helix-term/src/commands.rs | 57 +++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 4ac2496eb..133f2d540 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -57,6 +57,7 @@ use crate::{ use crate::job::{self, Jobs}; use std::{ + cmp::Ordering, collections::{HashMap, HashSet}, fmt, future::Future, @@ -300,6 +301,8 @@ impl MappableCommand { extend_line, "Select current line, if already selected, extend to another line based on the anchor", extend_line_below, "Select current line, if already selected, extend to next line", extend_line_above, "Select current line, if already selected, extend to previous line", + select_line_above, "Select current line, if already selected, extend or shrink line above based on the anchor", + select_line_below, "Select current line, if already selected, extend or shrink line below based on the anchor", extend_to_line_bounds, "Extend selection to line bounds", shrink_to_line_bounds, "Shrink selection to line bounds", delete_selection, "Delete selection", @@ -2435,7 +2438,6 @@ fn extend_line_below(cx: &mut Context) { fn extend_line_above(cx: &mut Context) { extend_line_impl(cx, Extend::Above); } - fn extend_line_impl(cx: &mut Context, extend: Extend) { let count = cx.count(); let (view, doc) = current!(cx.editor); @@ -2474,6 +2476,59 @@ fn extend_line_impl(cx: &mut Context, extend: Extend) { doc.set_selection(view.id, selection); } +fn select_line_below(cx: &mut Context) { + select_line_impl(cx, Extend::Below); +} +fn select_line_above(cx: &mut Context) { + select_line_impl(cx, Extend::Above); +} +fn select_line_impl(cx: &mut Context, extend: Extend) { + let mut count = cx.count(); + let (view, doc) = current!(cx.editor); + let text = doc.text(); + let saturating_add = |a: usize, b: usize| (a + b).min(text.len_lines()); + let selection = doc.selection(view.id).clone().transform(|range| { + let (start_line, end_line) = range.line_range(text.slice(..)); + let start = text.line_to_char(start_line); + let end = text.line_to_char(saturating_add(end_line, 1)); + let direction = range.direction(); + + // Extending to line bounds is counted as one step + if range.from() != start || range.to() != end { + count = count.saturating_sub(1) + } + let (anchor_line, head_line) = match (&extend, direction) { + (Extend::Above, Direction::Forward) => (start_line, end_line.saturating_sub(count)), + (Extend::Above, Direction::Backward) => (end_line, start_line.saturating_sub(count)), + (Extend::Below, Direction::Forward) => (start_line, saturating_add(end_line, count)), + (Extend::Below, Direction::Backward) => (end_line, saturating_add(start_line, count)), + }; + let (anchor, head) = match anchor_line.cmp(&head_line) { + Ordering::Less => ( + text.line_to_char(anchor_line), + text.line_to_char(saturating_add(head_line, 1)), + ), + Ordering::Equal => match extend { + Extend::Above => ( + text.line_to_char(saturating_add(anchor_line, 1)), + text.line_to_char(head_line), + ), + Extend::Below => ( + text.line_to_char(head_line), + text.line_to_char(saturating_add(anchor_line, 1)), + ), + }, + + Ordering::Greater => ( + text.line_to_char(saturating_add(anchor_line, 1)), + text.line_to_char(head_line), + ), + }; + Range::new(anchor, head) + }); + + doc.set_selection(view.id, selection); +} fn extend_to_line_bounds(cx: &mut Context) { let (view, doc) = current!(cx.editor); From 61f7d9ce2f2d20f4b0bd2f21036eac1f11cb2c5c Mon Sep 17 00:00:00 2001 From: Arthur Deierlein <110528300+c0rydoras@users.noreply.github.com> Date: Sun, 17 Mar 2024 23:36:54 +0100 Subject: [PATCH 236/796] fix typo "braket" in jsx highlights (#9910) --- runtime/queries/_jsx/highlights.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/queries/_jsx/highlights.scm b/runtime/queries/_jsx/highlights.scm index 853254e5b..2a696641c 100644 --- a/runtime/queries/_jsx/highlights.scm +++ b/runtime/queries/_jsx/highlights.scm @@ -40,4 +40,4 @@ (jsx_closing_element [""] @punctuation.bracket) ; -(jsx_self_closing_element ["<" "/>"] @punctuation.braket) +(jsx_self_closing_element ["<" "/>"] @punctuation.bracket) From 9ec0271873ed484f96342489b4117391e88abcd3 Mon Sep 17 00:00:00 2001 From: Arthur Deierlein <110528300+c0rydoras@users.noreply.github.com> Date: Sun, 17 Mar 2024 23:53:30 +0100 Subject: [PATCH 237/796] Add support for hyprland config (#9899) * feat: add hyprland config language * adjust indents to helix * adjust highlights to helix --- book/src/generated/lang-support.md | 1 + languages.toml | 12 +++++ runtime/queries/hyprlang/highlights.scm | 58 +++++++++++++++++++++++++ runtime/queries/hyprlang/indents.scm | 6 +++ runtime/queries/hyprlang/injections.scm | 3 ++ 5 files changed, 80 insertions(+) create mode 100644 runtime/queries/hyprlang/highlights.scm create mode 100644 runtime/queries/hyprlang/indents.scm create mode 100644 runtime/queries/hyprlang/injections.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 7792bf594..2cb1e926c 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -77,6 +77,7 @@ | hosts | ✓ | | | | | html | ✓ | | | `vscode-html-language-server` | | hurl | ✓ | | ✓ | | +| hyprlang | ✓ | | ✓ | | | idris | | | | `idris2-lsp` | | iex | ✓ | | | | | ini | ✓ | | | | diff --git a/languages.toml b/languages.toml index 8fbb98e88..b01da144d 100644 --- a/languages.toml +++ b/languages.toml @@ -3284,3 +3284,15 @@ indent = { tab-width = 2, unit = " " } [[grammar]] name = "ld" source = { git = "https://github.com/mtoohey31/tree-sitter-ld", rev = "81978cde3844bfc199851e39c80a20ec6444d35e" } + +[[language]] +name = "hyprlang" +scope = "source.hyprlang" +roots = ["hyprland.conf"] +file-types = [ { glob = "hyprland.conf"} ] +comment-token = "#" +grammar = "hyprlang" + +[[grammar]] +name = "hyprlang" +source = { git = "https://github.com/tree-sitter-grammars/tree-sitter-hyprlang", rev = "27af9b74acf89fa6bed4fb8cb8631994fcb2e6f3"} diff --git a/runtime/queries/hyprlang/highlights.scm b/runtime/queries/hyprlang/highlights.scm new file mode 100644 index 000000000..bf898c9cd --- /dev/null +++ b/runtime/queries/hyprlang/highlights.scm @@ -0,0 +1,58 @@ +(comment) @comment + +[ + "source" + "exec" + "exec-once" +] @function.builtin + +(keyword + (name) @keyword) + +(assignment + (name) @variable.other.member) + +(section + (name) @namespace) + +(section + device: (device_name) @type) + +(variable) @variable + +"$" @punctuation.special + +(boolean) @constant.builtin.boolean + +(string) @string + +(mod) @constant + +[ + "rgb" + "rgba" +] @function.builtin + +[ + (number) + (legacy_hex) + (angle) + (hex) +] @constant.numeric + +"deg" @type + +"," @punctuation.delimiter + +[ + "(" + ")" + "{" + "}" +] @punctuation.bracket + +[ + "=" + "-" + "+" +] @operator diff --git a/runtime/queries/hyprlang/indents.scm b/runtime/queries/hyprlang/indents.scm new file mode 100644 index 000000000..88bfe7434 --- /dev/null +++ b/runtime/queries/hyprlang/indents.scm @@ -0,0 +1,6 @@ +(section) @indent + +(section + "}" @outdent) + +"}" @extend diff --git a/runtime/queries/hyprlang/injections.scm b/runtime/queries/hyprlang/injections.scm new file mode 100644 index 000000000..1f0199ed8 --- /dev/null +++ b/runtime/queries/hyprlang/injections.scm @@ -0,0 +1,3 @@ +(exec + (string) @injection.content + (#set! injection.language "bash")) From e36774c2c8f967c16ce2e10f2ba074838b324ec6 Mon Sep 17 00:00:00 2001 From: "George \"Riye\" Hollister" Date: Sun, 17 Mar 2024 22:54:05 +0000 Subject: [PATCH 238/796] Add Support for JSONC (#9906) * Added `jsonc` language with support for comments The `vscode-json-language-server` accepts `jsonc` as a language id. Allowing the use of comments within JSON files. * fix: Update `injdection-rejex` to be unique * fix: use includes to remove redundant queries * ci: Generate language-support docs --- book/src/generated/lang-support.md | 1 + languages.toml | 10 +++++++++- runtime/queries/jsonc/highlights.scm | 2 ++ runtime/queries/jsonc/indents.scm | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 runtime/queries/jsonc/highlights.scm create mode 100644 runtime/queries/jsonc/indents.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 2cb1e926c..40029657f 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -88,6 +88,7 @@ | jsdoc | ✓ | | | | | json | ✓ | | ✓ | `vscode-json-language-server` | | json5 | ✓ | | | | +| jsonc | ✓ | | ✓ | `vscode-json-language-server` | | jsonnet | ✓ | | | `jsonnet-language-server` | | jsx | ✓ | ✓ | ✓ | `typescript-language-server` | | julia | ✓ | ✓ | ✓ | `julia` | diff --git a/languages.toml b/languages.toml index b01da144d..4018fbe0e 100644 --- a/languages.toml +++ b/languages.toml @@ -367,7 +367,6 @@ scope = "source.json" injection-regex = "json" file-types = [ "json", - "jsonc", "arb", "ipynb", "geojson", @@ -396,6 +395,15 @@ indent = { tab-width = 2, unit = " " } name = "json" source = { git = "https://github.com/tree-sitter/tree-sitter-json", rev = "73076754005a460947cafe8e03a8cf5fa4fa2938" } +[[language]] +name = "jsonc" +scope = "source.json" +injection-regex = "jsonc" +file-types = ["jsonc"] +grammar = "json" +language-servers = [ "vscode-json-language-server" ] +auto-format = true +indent = { tab-width = 2, unit = " " } [[language]] name = "json5" diff --git a/runtime/queries/jsonc/highlights.scm b/runtime/queries/jsonc/highlights.scm new file mode 100644 index 000000000..0164321b6 --- /dev/null +++ b/runtime/queries/jsonc/highlights.scm @@ -0,0 +1,2 @@ +; inherits: json +(comment) @comment diff --git a/runtime/queries/jsonc/indents.scm b/runtime/queries/jsonc/indents.scm new file mode 100644 index 000000000..41269219e --- /dev/null +++ b/runtime/queries/jsonc/indents.scm @@ -0,0 +1 @@ +; inherits: json From 3890376a23e84d5bcdac31cb9d0f6913abe0fc7f Mon Sep 17 00:00:00 2001 From: Dan Cardamore Date: Sun, 17 Mar 2024 18:55:49 -0400 Subject: [PATCH 239/796] add 'file-absolute-path' to statusline (#4535) * feat: add 'file-abs-path' to statusline (#4434) * cleanup implementation * rename to be non-abbreviated names --------- Co-authored-by: Michael Davis --- book/src/configuration.md | 1 + helix-term/src/ui/statusline.rs | 17 +++++++++++++++++ helix-view/src/editor.rs | 3 +++ 3 files changed, 21 insertions(+) diff --git a/book/src/configuration.md b/book/src/configuration.md index d87936457..8857af82a 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -108,6 +108,7 @@ The following statusline elements can be configured: | `mode` | The current editor mode (`mode.normal`/`mode.insert`/`mode.select`) | | `spinner` | A progress spinner indicating LSP activity | | `file-name` | The path/name of the opened file | +| `file-absolute-path` | The absolute path/name of the opened file | | `file-base-name` | The basename of the opened file | | `file-modification-indicator` | The indicator to show whether the file is modified (a `[+]` appears when there are unsaved changes) | | `file-encoding` | The encoding of the opened file if it differs from UTF-8 | diff --git a/helix-term/src/ui/statusline.rs b/helix-term/src/ui/statusline.rs index 9871828ee..c3464067f 100644 --- a/helix-term/src/ui/statusline.rs +++ b/helix-term/src/ui/statusline.rs @@ -142,6 +142,7 @@ where helix_view::editor::StatusLineElement::Spinner => render_lsp_spinner, helix_view::editor::StatusLineElement::FileBaseName => render_file_base_name, helix_view::editor::StatusLineElement::FileName => render_file_name, + helix_view::editor::StatusLineElement::FileAbsolutePath => render_file_absolute_path, helix_view::editor::StatusLineElement::FileModificationIndicator => { render_file_modification_indicator } @@ -430,6 +431,22 @@ where write(context, title, None); } +fn render_file_absolute_path(context: &mut RenderContext, write: F) +where + F: Fn(&mut RenderContext, String, Option