From 606b95717211eab05ce9564cec8312b015220c3d Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Thu, 8 Aug 2024 11:03:29 -0400 Subject: [PATCH 01/57] Replace uses of lsp::Location with a custom Location type The lsp location type has the lsp's URI type and a range. We can replace that with a custom type private to the lsp commands module that uses the core URI type instead. We can't entirely replace the type with a new Location type in core. That type might look like: pub struct Location { uri: crate::Uri, range: crate::Range, } But we can't convert every `lsp::Location` to this type because for definitions, references and diagnostics language servers send documents which we haven't opened yet, so we don't have the information to convert an `lsp::Range` (line+col) to a `helix_core::Range` (char indexing). This cleans up the picker definitions in this file so that they can all use helpers like `jump_to_location` and `location_to_file_location` for the picker preview. It also removes the only use of the deprecated `PathOrId::from_path_buf` function, allowing us to drop the owned variant of that type in the child commit. --- helix-core/src/uri.rs | 24 +++- helix-term/src/commands/lsp.rs | 196 ++++++++++++++------------------- 2 files changed, 105 insertions(+), 115 deletions(-) diff --git a/helix-core/src/uri.rs b/helix-core/src/uri.rs index 4e03c58b1..ddb9fb7a8 100644 --- a/helix-core/src/uri.rs +++ b/helix-core/src/uri.rs @@ -1,4 +1,7 @@ -use std::path::{Path, PathBuf}; +use std::{ + fmt, + path::{Path, PathBuf}, +}; /// A generic pointer to a file location. /// @@ -47,6 +50,14 @@ impl TryFrom for PathBuf { } } +impl fmt::Display for Uri { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::File(path) => write!(f, "{}", path.display()), + } + } +} + #[derive(Debug)] pub struct UrlConversionError { source: url::Url, @@ -59,11 +70,16 @@ pub enum UrlConversionErrorKind { UnableToConvert, } -impl std::fmt::Display for UrlConversionError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for UrlConversionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.kind { UrlConversionErrorKind::UnsupportedScheme => { - write!(f, "unsupported scheme in URL: {}", self.source.scheme()) + write!( + f, + "unsupported scheme '{}' in URL {}", + self.source.scheme(), + self.source + ) } UrlConversionErrorKind::UnableToConvert => { write!(f, "unable to convert URL to file path: {}", self.source) diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index 93ac2a849..fcc0333e8 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -34,7 +34,7 @@ use crate::{ use std::{ cmp::Ordering, collections::{BTreeMap, HashSet}, - fmt::{Display, Write}, + fmt::Display, future::Future, path::Path, }; @@ -61,10 +61,31 @@ macro_rules! language_server_with_feature { }}; } +/// A wrapper around `lsp::Location` that swaps out the LSP URI for `helix_core::Uri`. +#[derive(Debug, Clone, PartialEq, Eq)] +struct Location { + uri: Uri, + range: lsp::Range, +} + +fn lsp_location_to_location(location: lsp::Location) -> Option { + let uri = match location.uri.try_into() { + Ok(uri) => uri, + Err(err) => { + log::warn!("discarding invalid or unsupported URI: {err}"); + return None; + } + }; + Some(Location { + uri, + range: location.range, + }) +} + struct SymbolInformationItem { + location: Location, symbol: lsp::SymbolInformation, offset_encoding: OffsetEncoding, - uri: Uri, } struct DiagnosticStyles { @@ -75,35 +96,35 @@ struct DiagnosticStyles { } struct PickerDiagnostic { - uri: Uri, + location: Location, diag: lsp::Diagnostic, offset_encoding: OffsetEncoding, } -fn uri_to_file_location<'a>(uri: &'a Uri, range: &lsp::Range) -> Option> { - let path = uri.as_path()?; - let line = Some((range.start.line as usize, range.end.line as usize)); +fn location_to_file_location(location: &Location) -> Option { + let path = location.uri.as_path()?; + let line = Some(( + location.range.start.line as usize, + location.range.end.line as usize, + )); Some((path.into(), line)) } fn jump_to_location( editor: &mut Editor, - location: &lsp::Location, + location: &Location, offset_encoding: OffsetEncoding, action: Action, ) { let (view, doc) = current!(editor); push_jump(view, doc); - let path = match location.uri.to_file_path() { - Ok(path) => path, - Err(_) => { - let err = format!("unable to convert URI to filepath: {}", location.uri); - editor.set_error(err); - return; - } + let Some(path) = location.uri.as_path() else { + let err = format!("unable to convert URI to filepath: {:?}", location.uri); + editor.set_error(err); + return; }; - jump_to_position(editor, &path, location.range, offset_encoding, action); + jump_to_position(editor, path, location.range, offset_encoding, action); } fn jump_to_position( @@ -196,7 +217,10 @@ fn diag_picker( for (diag, ls) in diags { if let Some(ls) = cx.editor.language_server_by_id(ls) { flat_diag.push(PickerDiagnostic { - uri: uri.clone(), + location: Location { + uri: uri.clone(), + range: diag.range, + }, diag, offset_encoding: ls.offset_encoding(), }); @@ -243,7 +267,7 @@ fn diag_picker( // between message code and message 2, ui::PickerColumn::new("path", |item: &PickerDiagnostic, _| { - if let Some(path) = item.uri.as_path() { + if let Some(path) = item.location.uri.as_path() { path::get_truncated_path(path) .to_string_lossy() .to_string() @@ -261,26 +285,14 @@ fn diag_picker( primary_column, flat_diag, styles, - move |cx, - PickerDiagnostic { - uri, - diag, - offset_encoding, - }, - action| { - let Some(path) = uri.as_path() else { - return; - }; - jump_to_position(cx.editor, path, diag.range, *offset_encoding, action); + move |cx, diag, action| { + jump_to_location(cx.editor, &diag.location, diag.offset_encoding, action); let (view, doc) = current!(cx.editor); view.diagnostics_handler .immediately_show_diagnostic(doc, view.id); }, ) - .with_preview(move |_editor, PickerDiagnostic { uri, diag, .. }| { - let line = Some((diag.range.start.line as usize, diag.range.end.line as usize)); - Some((uri.as_path()?.into(), line)) - }) + .with_preview(move |_editor, diag| location_to_file_location(&diag.location)) .truncate_start(false) } @@ -303,7 +315,10 @@ pub fn symbol_picker(cx: &mut Context) { container_name: None, }, offset_encoding, - uri: uri.clone(), + location: Location { + uri: uri.clone(), + range: symbol.selection_range, + }, }); for child in symbol.children.into_iter().flatten() { nested_to_flat(list, file, uri, child, offset_encoding); @@ -337,7 +352,10 @@ pub fn symbol_picker(cx: &mut Context) { lsp::DocumentSymbolResponse::Flat(symbols) => symbols .into_iter() .map(|symbol| SymbolInformationItem { - uri: doc_uri.clone(), + location: Location { + uri: doc_uri.clone(), + range: symbol.location.range, + }, symbol, offset_encoding, }) @@ -392,17 +410,10 @@ pub fn symbol_picker(cx: &mut Context) { symbols, (), move |cx, item, action| { - jump_to_location( - cx.editor, - &item.symbol.location, - item.offset_encoding, - action, - ); + jump_to_location(cx.editor, &item.location, item.offset_encoding, action); }, ) - .with_preview(move |_editor, item| { - uri_to_file_location(&item.uri, &item.symbol.location.range) - }) + .with_preview(move |_editor, item| location_to_file_location(&item.location)) .truncate_start(false); compositor.push(Box::new(overlaid(picker))) @@ -453,8 +464,11 @@ pub fn workspace_symbol_picker(cx: &mut Context) { } }; Some(SymbolInformationItem { + location: Location { + uri, + range: symbol.location.range, + }, symbol, - uri, offset_encoding, }) }) @@ -490,7 +504,7 @@ pub fn workspace_symbol_picker(cx: &mut Context) { }) .without_filtering(), ui::PickerColumn::new("path", |item: &SymbolInformationItem, _| { - if let Some(path) = item.uri.as_path() { + if let Some(path) = item.location.uri.as_path() { path::get_relative_path(path) .to_string_lossy() .to_string() @@ -507,15 +521,10 @@ pub fn workspace_symbol_picker(cx: &mut Context) { [], (), move |cx, item, action| { - jump_to_location( - cx.editor, - &item.symbol.location, - item.offset_encoding, - action, - ); + jump_to_location(cx.editor, &item.location, item.offset_encoding, action); }, ) - .with_preview(|_editor, item| uri_to_file_location(&item.uri, &item.symbol.location.range)) + .with_preview(|_editor, item| location_to_file_location(&item.location)) .with_dynamic_query(get_symbols, None) .truncate_start(false); @@ -847,7 +856,7 @@ impl Display for ApplyEditErrorKind { fn goto_impl( editor: &mut Editor, compositor: &mut Compositor, - locations: Vec, + locations: Vec, offset_encoding: OffsetEncoding, ) { let cwdir = helix_stdx::env::current_working_dir(); @@ -860,80 +869,41 @@ fn goto_impl( _locations => { let columns = [ui::PickerColumn::new( "location", - |item: &lsp::Location, cwdir: &std::path::PathBuf| { - // The preallocation here will overallocate a few characters since it will account for the - // URL's scheme, which is not used most of the time since that scheme will be "file://". - // Those extra chars will be used to avoid allocating when writing the line number (in the - // common case where it has 5 digits or less, which should be enough for a cast majority - // of usages). - let mut res = String::with_capacity(item.uri.as_str().len()); - - if item.uri.scheme() == "file" { - // With the preallocation above and UTF-8 paths already, this closure will do one (1) - // allocation, for `to_file_path`, else there will be two (2), with `to_string_lossy`. - if let Ok(path) = item.uri.to_file_path() { - // We don't convert to a `helix_core::Uri` here because we've already checked the scheme. - // This path won't be normalized but it's only used for display. - res.push_str( - &path.strip_prefix(cwdir).unwrap_or(&path).to_string_lossy(), - ); - } + |item: &Location, cwdir: &std::path::PathBuf| { + let path = if let Some(path) = item.uri.as_path() { + path.strip_prefix(cwdir).unwrap_or(path).to_string_lossy() } else { - // Never allocates since we declared the string with this capacity already. - res.push_str(item.uri.as_str()); - } + item.uri.to_string().into() + }; - // Most commonly, this will not allocate, especially on Unix systems where the root prefix - // is a simple `/` and not `C:\` (with whatever drive letter) - write!(&mut res, ":{}", item.range.start.line + 1) - .expect("Will only failed if allocating fail"); - res.into() + format!("{path}:{}", item.range.start.line + 1).into() }, )]; let picker = Picker::new(columns, 0, locations, cwdir, move |cx, location, action| { jump_to_location(cx.editor, location, offset_encoding, action) }) - .with_preview(move |_editor, location| { - use crate::ui::picker::PathOrId; - - let lines = Some(( - location.range.start.line as usize, - location.range.end.line as usize, - )); - - // TODO: we should avoid allocating by doing the Uri conversion ahead of time. - // - // To do this, introduce a `Location` type in `helix-core` that reuses the core - // `Uri` type instead of the LSP `Url` type and replaces the LSP `Range` type. - // Refactor the callers of `goto_impl` to pass iterators that translate the - // LSP location type to the custom one in core, or have them collect and pass - // `Vec`s. Replace the `uri_to_file_location` function with - // `location_to_file_location` that takes only `&helix_core::Location` as - // parameters. - // - // By doing this we can also eliminate the duplicated URI info in the - // `SymbolInformationItem` type and introduce a custom Symbol type in `helix-core` - // which will be reused in the future for tree-sitter based symbol pickers. - let path = Uri::try_from(&location.uri).ok()?.as_path_buf()?; - #[allow(deprecated)] - Some((PathOrId::from_path_buf(path), lines)) - }); + .with_preview(move |_editor, location| location_to_file_location(location)); compositor.push(Box::new(overlaid(picker))); } } } -fn to_locations(definitions: Option) -> Vec { +fn to_locations(definitions: Option) -> Vec { match definitions { - Some(lsp::GotoDefinitionResponse::Scalar(location)) => vec![location], - Some(lsp::GotoDefinitionResponse::Array(locations)) => locations, + Some(lsp::GotoDefinitionResponse::Scalar(location)) => { + lsp_location_to_location(location).into_iter().collect() + } + Some(lsp::GotoDefinitionResponse::Array(locations)) => locations + .into_iter() + .flat_map(lsp_location_to_location) + .collect(), Some(lsp::GotoDefinitionResponse::Link(locations)) => locations .into_iter() - .map(|location_link| lsp::Location { - uri: location_link.target_uri, - range: location_link.target_range, + .map(|location_link| { + lsp::Location::new(location_link.target_uri, location_link.target_range) }) + .flat_map(lsp_location_to_location) .collect(), None => Vec::new(), } @@ -1018,7 +988,11 @@ pub fn goto_reference(cx: &mut Context) { cx.callback( future, move |editor, compositor, response: Option>| { - let items = response.unwrap_or_default(); + let items: Vec = response + .into_iter() + .flatten() + .flat_map(lsp_location_to_location) + .collect(); if items.is_empty() { editor.set_error("No references found."); } else { From 48e93577882e58de85d451225494efe48fe9b606 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Thu, 8 Aug 2024 11:05:12 -0400 Subject: [PATCH 02/57] picker: Removed owned variant of PathOrId The only caller of `from_path_buf` was removed in the parent commit allowing us to drop owned variant of path's `Cow`. With this change we never need to allocate in the picker preview callback. --- helix-term/src/ui/picker.rs | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 82fe96891..ecf8111ab 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -32,7 +32,7 @@ use std::{ borrow::Cow, collections::HashMap, io::Read, - path::{Path, PathBuf}, + path::Path, sync::{ atomic::{self, AtomicUsize}, Arc, @@ -63,26 +63,12 @@ pub const MAX_FILE_SIZE_FOR_PREVIEW: u64 = 10 * 1024 * 1024; #[derive(PartialEq, Eq, Hash)] pub enum PathOrId<'a> { Id(DocumentId), - // See [PathOrId::from_path_buf]: this will eventually become `Path(&Path)`. - Path(Cow<'a, Path>), -} - -impl<'a> PathOrId<'a> { - /// Creates a [PathOrId] from a PathBuf - /// - /// # Deprecated - /// The owned version of PathOrId will be removed in a future refactor - /// and replaced with `&'a Path`. See the caller of this function for - /// more details on its removal. - #[deprecated] - pub fn from_path_buf(path_buf: PathBuf) -> Self { - Self::Path(Cow::Owned(path_buf)) - } + Path(&'a Path), } impl<'a> From<&'a Path> for PathOrId<'a> { fn from(path: &'a Path) -> Self { - Self::Path(Cow::Borrowed(path)) + Self::Path(path) } } @@ -581,7 +567,6 @@ impl Picker { match path_or_id { PathOrId::Path(path) => { - let path = path.as_ref(); if let Some(doc) = editor.document_by_path(path) { return Some((Preview::EditorDocument(doc), range)); } From da2b0a74844f77ba233638c2b5a0ee2367a66871 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Thu, 8 Aug 2024 11:17:20 -0400 Subject: [PATCH 03/57] Make helix_core::Uri cheap to clone We clone this type very often in LSP pickers, for example diagnostics and symbols. We can use a single Arc in many cases to avoid the unnecessary clones. --- helix-core/src/uri.rs | 25 ++++++------------------- helix-view/src/handlers/lsp.rs | 18 +++++++++++------- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/helix-core/src/uri.rs b/helix-core/src/uri.rs index ddb9fb7a8..cbe0fadda 100644 --- a/helix-core/src/uri.rs +++ b/helix-core/src/uri.rs @@ -1,15 +1,18 @@ use std::{ fmt, path::{Path, PathBuf}, + sync::Arc, }; /// A generic pointer to a file location. /// /// Currently this type only supports paths to local files. +/// +/// Cloning this type is cheap: the internal representation uses an Arc. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] #[non_exhaustive] pub enum Uri { - File(PathBuf), + File(Arc), } impl Uri { @@ -26,27 +29,11 @@ impl Uri { Self::File(path) => Some(path), } } - - pub fn as_path_buf(self) -> Option { - match self { - Self::File(path) => Some(path), - } - } } impl From for Uri { fn from(path: PathBuf) -> Self { - Self::File(path) - } -} - -impl TryFrom for PathBuf { - type Error = (); - - fn try_from(uri: Uri) -> Result { - match uri { - Uri::File(path) => Ok(path), - } + Self::File(path.into()) } } @@ -93,7 +80,7 @@ impl std::error::Error for UrlConversionError {} fn convert_url_to_uri(url: &url::Url) -> Result { if url.scheme() == "file" { url.to_file_path() - .map(|path| Uri::File(helix_stdx::path::normalize(path))) + .map(|path| Uri::File(helix_stdx::path::normalize(path).into())) .map_err(|_| UrlConversionErrorKind::UnableToConvert) } else { Err(UrlConversionErrorKind::UnsupportedScheme) diff --git a/helix-view/src/handlers/lsp.rs b/helix-view/src/handlers/lsp.rs index 6aff2e50c..1fd2289db 100644 --- a/helix-view/src/handlers/lsp.rs +++ b/helix-view/src/handlers/lsp.rs @@ -243,7 +243,7 @@ impl Editor { match op { ResourceOp::Create(op) => { let uri = Uri::try_from(&op.uri)?; - let path = uri.as_path_buf().expect("URIs are valid paths"); + let path = uri.as_path().expect("URIs are valid paths"); let ignore_if_exists = op.options.as_ref().map_or(false, |options| { !options.overwrite.unwrap_or(false) && options.ignore_if_exists.unwrap_or(false) }); @@ -255,13 +255,15 @@ impl Editor { } } - fs::write(&path, [])?; - self.language_servers.file_event_handler.file_changed(path); + fs::write(path, [])?; + self.language_servers + .file_event_handler + .file_changed(path.to_path_buf()); } } ResourceOp::Delete(op) => { let uri = Uri::try_from(&op.uri)?; - let path = uri.as_path_buf().expect("URIs are valid paths"); + let path = uri.as_path().expect("URIs are valid paths"); if path.is_dir() { let recursive = op .options @@ -270,11 +272,13 @@ impl Editor { .unwrap_or(false); if recursive { - fs::remove_dir_all(&path)? + fs::remove_dir_all(path)? } else { - fs::remove_dir(&path)? + fs::remove_dir(path)? } - self.language_servers.file_event_handler.file_changed(path); + self.language_servers + .file_event_handler + .file_changed(path.to_path_buf()); } else if path.is_file() { fs::remove_file(path)?; } From 5ce77de0dc7106c6f1460d80a3c5a51eaea3108c Mon Sep 17 00:00:00 2001 From: Niklas Gruhn Date: Sun, 15 Sep 2024 12:08:35 +0200 Subject: [PATCH 04/57] fix: Lean language server consuming excessive memory (#11683) The Lean process, spawned by the language server, might use excessive memory in certain situation, causing the entire system to freeze. See: https://github.com/leanprover/lean4/issues/5321 The language server accepts a CLI flag for limiting memory use. I set it to 1024MB, which might be a bit arbitrary, but definitly prevents the system from crashing. --- languages.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index cc437f78c..e115a4092 100644 --- a/languages.toml +++ b/languages.toml @@ -49,7 +49,7 @@ jsonnet-language-server = { command = "jsonnet-language-server", args= ["-t", "- julia = { command = "julia", timeout = 60, args = [ "--startup-file=no", "--history-file=no", "--quiet", "-e", "using LanguageServer; runserver()", ] } koka = { command = "koka", args = ["--language-server", "--lsstdio"] } kotlin-language-server = { command = "kotlin-language-server" } -lean = { command = "lean", args = [ "--server" ] } +lean = { command = "lean", args = [ "--server", "--memory=1024" ] } ltex-ls = { command = "ltex-ls" } markdoc-ls = { command = "markdoc-ls", args = ["--stdio"] } markdown-oxide = { command = "markdown-oxide" } From 9851edf477139c1c0db2437c0c25867fd2bc0bf5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 11:31:36 +0900 Subject: [PATCH 05/57] build(deps): bump cachix/install-nix-action from V27 to 28 (#11713) Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from V27 to 28. This release includes the previously tagged commit. - [Release notes](https://github.com/cachix/install-nix-action/releases) - [Commits](https://github.com/cachix/install-nix-action/compare/V27...V28) --- updated-dependencies: - dependency-name: cachix/install-nix-action dependency-type: direct:production ... 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 b8be02e16..3685a7c61 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@V27 + uses: cachix/install-nix-action@V28 - name: Authenticate with Cachix uses: cachix/cachix-action@v15 From c754949454a6c757a41f69bb0cadee6b8fc689d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 11:32:26 +0900 Subject: [PATCH 06/57] build(deps): bump the rust-dependencies group with 4 updates (#11712) Bumps the rust-dependencies group with 4 updates: [unicode-segmentation](https://github.com/unicode-rs/unicode-segmentation), [anyhow](https://github.com/dtolnay/anyhow), [rustix](https://github.com/bytecodealliance/rustix) and [cc](https://github.com/rust-lang/cc-rs). Updates `unicode-segmentation` from 1.11.0 to 1.12.0 - [Commits](https://github.com/unicode-rs/unicode-segmentation/compare/v1.11.0...v1.12.0) Updates `anyhow` from 1.0.87 to 1.0.89 - [Release notes](https://github.com/dtolnay/anyhow/releases) - [Commits](https://github.com/dtolnay/anyhow/compare/1.0.87...1.0.89) Updates `rustix` from 0.38.36 to 0.38.37 - [Release notes](https://github.com/bytecodealliance/rustix/releases) - [Changelog](https://github.com/bytecodealliance/rustix/blob/main/CHANGELOG.md) - [Commits](https://github.com/bytecodealliance/rustix/compare/v0.38.36...v0.38.37) Updates `cc` from 1.1.18 to 1.1.19 - [Release notes](https://github.com/rust-lang/cc-rs/releases) - [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.18...cc-v1.1.19) --- updated-dependencies: - dependency-name: unicode-segmentation dependency-type: direct:production update-type: version-update:semver-minor dependency-group: rust-dependencies - dependency-name: anyhow dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: rustix dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: cc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 16 ++++++++-------- helix-core/Cargo.toml | 2 +- helix-tui/Cargo.toml | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e4ebbb8f..7156fc27e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,9 +68,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.87" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "arc-swap" @@ -136,9 +136,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "cc" -version = "1.1.18" +version = "1.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" +checksum = "2d74707dde2ba56f86ae90effb3b43ddd369504387e718014de010cec7959800" dependencies = [ "shlex", ] @@ -1972,9 +1972,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.38.36" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags", "errno", @@ -2401,9 +2401,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index ce8a09a99..bc890e007 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.11" +unicode-segmentation = "1.12" # unicode-width is changing width definitions # that both break our logic and disagree with common # width definitions in terminals, we need to replace it. diff --git a/helix-tui/Cargo.toml b/helix-tui/Cargo.toml index ac56c7242..a349623b1 100644 --- a/helix-tui/Cargo.toml +++ b/helix-tui/Cargo.toml @@ -20,7 +20,7 @@ helix-core = { path = "../helix-core" } bitflags = "2.6" cassowary = "0.3" -unicode-segmentation = "1.11" +unicode-segmentation = "1.12" crossterm = { version = "0.28", optional = true } termini = "1.0" serde = { version = "1", "optional" = true, features = ["derive"]} From f4df4bf5f297c594ced2f9a7147a313f9895872a Mon Sep 17 00:00:00 2001 From: ves Date: Tue, 17 Sep 2024 05:33:21 +0300 Subject: [PATCH 07/57] Stylize horizon-dark picker v2 columns (#11649) --- runtime/themes/horizon-dark.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime/themes/horizon-dark.toml b/runtime/themes/horizon-dark.toml index e51ec09f5..d8d181291 100644 --- a/runtime/themes/horizon-dark.toml +++ b/runtime/themes/horizon-dark.toml @@ -33,7 +33,7 @@ tag = "red" "ui.selection" = { bg = "selection" } "ui.virtual.indent-guide" = { fg = "gray" } "ui.virtual.whitespace" = { fg = "light-gray" } -"ui.virtual.ruler" = { bg ="dark-bg" } +"ui.virtual.ruler" = { bg = "dark-bg" } "ui.statusline" = { bg = "dark-bg", fg = "light-gray" } "ui.popup" = { bg = "dark-bg", fg = "orange" } "ui.help" = { bg = "dark-bg", fg = "orange" } @@ -43,6 +43,8 @@ tag = "red" "ui.bufferline" = { bg = "dark-bg", fg = "light-gray" } "ui.bufferline.active" = { bg = "dark-bg", fg = "orange" } "ui.virtual.jump-label" = { fg = "pink", modifiers = ["bold"] } +"ui.picker.header.column" = { fg = "orange", underline.style = "line" } +"ui.picker.header.column.active" = { fg = "purple", modifiers = ["bold"], underline.style = "line" } # Diagnostics "diagnostic" = { underline = { style = "curl" } } From 84fbadbdde255d87e6e04141ccd2d75f4e86edf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Gonz=C3=A1lez?= Date: Mon, 16 Sep 2024 23:34:10 -0300 Subject: [PATCH 08/57] Update picker headers styling in Darcula themes (#11620) * Apply styling to picker headers in Darcula themes * Add background to active picker column in Darcula. --- runtime/themes/darcula.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/themes/darcula.toml b/runtime/themes/darcula.toml index 7e0907c09..5b83afba8 100644 --- a/runtime/themes/darcula.toml +++ b/runtime/themes/darcula.toml @@ -28,6 +28,8 @@ "ui.virtual.jump-label" = { fg = "lightblue", modifiers = ["italic", "bold"] } "ui.bufferline" = { fg = "grey04", bg = "grey00" } "ui.bufferline.active" = { fg = "grey07", bg = "grey02" } +"ui.picker.header.column" = { fg = "grey05", modifiers = ["italic", "bold"] } +"ui.picker.header.column.active" = { fg = "grey05", bg = "grey03", modifiers = ["italic", "bold"] } "operator" = "grey05" "variable" = "white" From a7b8b27abfb2b6a414d12730ec3634ece7bfc050 Mon Sep 17 00:00:00 2001 From: Nicolas Karolak Date: Wed, 18 Sep 2024 05:12:31 +0200 Subject: [PATCH 09/57] chore: add ruff and jedi lsp servers (#11630) * chore: add ruff lsp server Ruff provide a `server` command that starts a LSP server: https://docs.astral.sh/ruff/editors/#language-server-protocol * chore: add jedi lsp server [jedi-language-server](https://github.com/pappasam/jedi-language-server) is a Python LSP server based the popular [jedi](https://jedi.readthedocs.io/en/latest/) library. * docs: add ruff and jedi as python lsp servers --- book/src/generated/lang-support.md | 2 +- languages.toml | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index cb1c815f2..f223c8b22 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -163,7 +163,7 @@ | protobuf | ✓ | ✓ | ✓ | `bufls`, `pb` | | prql | ✓ | | | | | purescript | ✓ | ✓ | | `purescript-language-server` | -| python | ✓ | ✓ | ✓ | `pylsp` | +| python | ✓ | ✓ | ✓ | `ruff`, `jedi-language-server`, `pylsp` | | qml | ✓ | | ✓ | `qmlls` | | r | ✓ | | | `R` | | racket | ✓ | | ✓ | `racket` | diff --git a/languages.toml b/languages.toml index e115a4092..9e1be0ac2 100644 --- a/languages.toml +++ b/languages.toml @@ -44,6 +44,7 @@ haskell-language-server = { command = "haskell-language-server-wrapper", args = idris2-lsp = { command = "idris2-lsp" } intelephense = { command = "intelephense", args = ["--stdio"] } jdtls = { command = "jdtls" } +jedi = { command = "jedi-language-server" } jq-lsp = { command = "jq-lsp" } jsonnet-language-server = { command = "jsonnet-language-server", args= ["-t", "--lint"] } julia = { command = "julia", timeout = 60, args = [ "--startup-file=no", "--history-file=no", "--quiet", "-e", "using LanguageServer; runserver()", ] } @@ -84,6 +85,7 @@ racket = { command = "racket", args = ["-l", "racket-langserver"] } regols = { command = "regols" } rescript-language-server = { command = "rescript-language-server", args = ["--stdio"] } robotframework_ls = { command = "robotframework_ls" } +ruff = { command = "ruff", args = ["server"] } serve-d = { command = "serve-d" } slint-lsp = { command = "slint-lsp", args = [] } solargraph = { command = "solargraph", args = ["stdio"] } @@ -852,7 +854,7 @@ file-types = ["py", "pyi", "py3", "pyw", "ptl", "rpy", "cpy", "ipy", "pyt", { gl shebangs = ["python"] roots = ["pyproject.toml", "setup.py", "poetry.lock", "pyrightconfig.json"] comment-token = "#" -language-servers = [ "pylsp" ] +language-servers = ["ruff", "jedi", "pylsp"] # TODO: pyls needs utf-8 offsets indent = { tab-width = 4, unit = " " } @@ -3785,4 +3787,4 @@ indent = { tab-width = 2, unit = " " } [[grammar]] name = "thrift" -source = { git = "https://github.com/tree-sitter-grammars/tree-sitter-thrift" , rev = "68fd0d80943a828d9e6f49c58a74be1e9ca142cf" } \ No newline at end of file +source = { git = "https://github.com/tree-sitter-grammars/tree-sitter-thrift" , rev = "68fd0d80943a828d9e6f49c58a74be1e9ca142cf" } From b85e824ba9cfb2dba739281d88d9a4576aea9a1f Mon Sep 17 00:00:00 2001 From: Ayoub Benali Date: Wed, 18 Sep 2024 21:43:06 +0200 Subject: [PATCH 10/57] Handle window/showMessage and display it bellow status line (#5535) * Handle window/showMessage and display it bellow status line * Enable `editor.lsp.display_messages` by default --------- Co-authored-by: Michael Davis --- helix-term/src/application.rs | 12 ++++++++++-- helix-view/src/editor.rs | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index bd6b5a870..a567815fc 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -846,7 +846,15 @@ impl Application { } } Notification::ShowMessage(params) => { - log::warn!("unhandled window/showMessage: {:?}", params); + if self.config.load().editor.lsp.display_messages { + match params.typ { + lsp::MessageType::ERROR => self.editor.set_error(params.message), + lsp::MessageType::WARNING => { + self.editor.set_warning(params.message) + } + _ => self.editor.set_status(params.message), + } + } } Notification::LogMessage(params) => { log::info!("window/logMessage: {:?}", params); @@ -930,7 +938,7 @@ impl Application { self.lsp_progress.update(server_id, token, work); } - if self.config.load().editor.lsp.display_messages { + if self.config.load().editor.lsp.display_progress_messages { self.editor.set_status(status); } } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 1708b3b4e..26dea3a21 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -421,7 +421,9 @@ pub fn get_terminal_provider() -> Option { pub struct LspConfig { /// Enables LSP pub enable: bool, - /// Display LSP progress messages below statusline + /// Display LSP messagess from $/progress below statusline + pub display_progress_messages: bool, + /// Display LSP messages from window/showMessage below statusline pub display_messages: bool, /// Enable automatic pop up of signature help (parameter hints) pub auto_signature_help: bool, @@ -439,7 +441,8 @@ impl Default for LspConfig { fn default() -> Self { Self { enable: true, - display_messages: false, + display_progress_messages: false, + display_messages: true, auto_signature_help: true, display_signature_help_docs: true, display_inlay_hints: false, @@ -1271,6 +1274,13 @@ impl Editor { self.status_msg = Some((error, Severity::Error)); } + #[inline] + pub fn set_warning>>(&mut self, warning: T) { + let warning = warning.into(); + log::warn!("editor warning: {}", warning); + self.status_msg = Some((warning, Severity::Warning)); + } + #[inline] pub fn get_status(&self) -> Option<(&Cow<'static, str>, &Severity)> { self.status_msg.as_ref().map(|(status, sev)| (status, sev)) From 9f93de5a4b2b52a1a153f4ea5eacfc1a63600496 Mon Sep 17 00:00:00 2001 From: timd Date: Wed, 18 Sep 2024 19:15:51 -0600 Subject: [PATCH 11/57] fix(themes): fix diagnostics in snazzy (#11731) * fix(themes): fix diagnostics in snazzy Before this change, the color scheme makes most diagnostics difficult to read. This fix makes diagnostic much less obtrusive when using snazzy. * chore(fmt): nicely format snazzy theme file --- runtime/themes/snazzy.toml | 167 +++++++++++++++++++------------------ 1 file changed, 86 insertions(+), 81 deletions(-) diff --git a/runtime/themes/snazzy.toml b/runtime/themes/snazzy.toml index 09d0cdd57..c5e8a2a44 100644 --- a/runtime/themes/snazzy.toml +++ b/runtime/themes/snazzy.toml @@ -1,119 +1,124 @@ # Author : Timothy DeHerrera "comment".fg = "comment" -"constant".fg = "purple" -"constant.builtin".fg = "olive" -"constant.character".fg = "carnation" +"constant.builtin".fg = "olive" "constant.character.escape".fg = "magenta" -"constant.numeric".fg = "cyan" -"constant.numeric.float".fg = "red" +"constant.character".fg = "carnation" +"constant".fg = "purple" +"constant.numeric".fg = "cyan" +"constant.numeric.float".fg = "red" -"function".fg = "green" "function.builtin".fg = "sand" -"function.macro".fg = "blue" -"function.method".fg = "opal" +"function".fg = "green" +"function.macro".fg = "blue" +"function.method".fg = "opal" -"keyword" = { fg = "magenta", modifiers = ["bold"] } -"keyword.operator" = { fg = "coral", modifiers = ["bold"] } -"keyword.function" = { fg = "lilac", modifiers = ["bold"] } -"keyword.control" = { fg = "carnation", modifiers = ["bold"]} +"keyword" = { fg = "magenta", modifiers = ["bold"] } +"keyword.control" = { fg = "carnation", modifiers = ["bold"] } "keyword.control.exception" = { fg = "red", modifiers = ["bold"] } -"keyword.storage" = { fg = "coral", modifiers = ["bold"] } +"keyword.function" = { fg = "lilac", modifiers = ["bold"] } +"keyword.operator" = { fg = "coral", modifiers = ["bold"] } +"keyword.storage" = { fg = "coral", modifiers = ["bold"] } "operator".fg = "coral" -"punctuation".fg = "magenta" +"punctuation.bracket".fg = "foreground" "punctuation.delimiter".fg = "coral" -"punctuation.bracket".fg = "foreground" +"punctuation".fg = "magenta" -"string".fg = "yellow" +"attribute".fg = "opal" +"string".fg = "yellow" +"string.regexp".fg = "red" "string.special".fg = "blue" -"string.regexp".fg = "red" -"tag".fg = "carnation" -"attribute".fg = "opal" +"tag".fg = "carnation" -"type".fg = "opal" -"type.variant".fg = "sand" -"type.builtin".fg = "yellow" +"type.builtin".fg = "yellow" "type.enum.variant".fg = "sand" +"type".fg = "opal" +"type.variant".fg = "sand" -"variable".fg = "cyan" -"variable.builtin".fg = "olive" +"variable.builtin".fg = "olive" +"variable".fg = "cyan" "variable.other.member".fg = "lilac" -"variable.parameter" = { fg ="blue", modifiers = ["italic"] } +"variable.parameter" = { fg = "blue", modifiers = ["italic"] } -"namespace".fg = "olive" "constructor".fg = "sand" -"special".fg = "magenta" -"label".fg = "magenta" +"label".fg = "magenta" +"namespace".fg = "olive" +"special".fg = "magenta" -"diff.plus".fg = "green" "diff.delta".fg = "blue" "diff.minus".fg = "red" - -"ui.background" = { fg = "foreground", bg = "background" } -"ui.cursor" = { fg = "background", bg = "blue", modifiers = ["dim"] } -"ui.cursor.match" = { fg = "green", modifiers = ["underlined"] } -"ui.cursor.primary" = { fg = "background", bg = "cyan", modifiers = ["dim"] } -"ui.help" = { fg = "foreground", bg = "background_dark" } -"ui.linenr" = { fg = "comment" } -"ui.linenr.selected" = { fg = "foreground" } -"ui.menu" = { fg = "foreground", bg = "background_dark" } -"ui.menu.selected" = { fg = "cyan", bg = "background_dark" } -"ui.popup" = { fg = "foreground", bg = "background_dark" } -"ui.selection" = { bg = "secondary_highlight" } -"ui.selection.primary" = { bg = "primary_highlight" } -"ui.cursorline" = { bg = "background_dark" } -"ui.statusline" = { fg = "foreground", bg = "background_dark" } -"ui.statusline.inactive" = { fg = "comment", bg = "background_dark" } -"ui.statusline.insert" = { fg = "olive", bg = "background_dark" } -"ui.statusline.normal" = { fg = "opal", bg = "background_dark" } -"ui.statusline.select" = { fg = "carnation", bg = "background_dark" } -"ui.text" = { fg = "foreground" } -"ui.text.focus" = { fg = "cyan" } -"ui.window" = { fg = "foreground" } -"ui.virtual.whitespace" = { fg = "comment" } +"diff.plus".fg = "green" + +"ui.background" = { fg = "foreground", bg = "background" } +"ui.cursor" = { fg = "background", bg = "blue", modifiers = ["dim"] } +"ui.cursor.match" = { fg = "green", modifiers = ["underlined"] } +"ui.cursor.primary" = { fg = "background", bg = "cyan", modifiers = ["dim"] } +"ui.cursorline" = { bg = "background_dark" } +"ui.help" = { fg = "foreground", bg = "background_dark" } +"ui.linenr" = { fg = "comment" } +"ui.linenr.selected" = { fg = "foreground" } +"ui.menu" = { fg = "foreground", bg = "background_dark" } +"ui.menu.selected" = { fg = "cyan", bg = "background_dark" } +"ui.popup" = { fg = "foreground", bg = "background_dark" } +"ui.selection" = { bg = "secondary_highlight" } +"ui.selection.primary" = { bg = "primary_highlight" } +"ui.statusline" = { fg = "foreground", bg = "background_dark" } +"ui.statusline.inactive" = { fg = "comment", bg = "background_dark" } +"ui.statusline.insert" = { fg = "olive", bg = "background_dark" } +"ui.statusline.normal" = { fg = "opal", bg = "background_dark" } +"ui.statusline.select" = { fg = "carnation", bg = "background_dark" } +"ui.text" = { fg = "foreground" } +"ui.text.focus" = { fg = "cyan" } "ui.virtual.indent-guide" = { fg = "opal" } -"ui.virtual.ruler" = { bg = "background_dark" } +"ui.virtual.ruler" = { bg = "background_dark" } +"ui.virtual.whitespace" = { fg = "comment" } +"ui.window" = { fg = "foreground" } -"error" = { fg = "red" } +"error" = { fg = "red" } "warning" = { fg = "cyan" } +"diagnostic" = { underline = { style = "line", color = "coral" }, bg = "cyan" } +"diagnostic.deprecated" = { modifiers = ["crossed_out"] } +"diagnostic.error" = { underline = { style = "curl", color = "red" } } +"diagnostic.hint" = { underline = { style = "line", color = "cyan" } } +"diagnostic.info" = { underline = { style = "line" } } "diagnostic.unnecessary" = { modifiers = ["dim"] } -"diagnostic.deprecated" = { modifiers = ["crossed_out"] } - -"markup.heading" = { fg = "purple", modifiers = ["bold"] } -"markup.link.label" = { fg = "blue", modifiers = ["italic"] } -"markup.list" = "cyan" -"markup.bold" = { fg = "blue", modifiers = ["bold"] } -"markup.italic" = { fg = "yellow", modifiers = ["italic"] } +"diagnostic.warning" = { underline = { style = "curl", color = "yellow" } } + +"markup.bold" = { fg = "blue", modifiers = ["bold"] } +"markup.heading" = { fg = "purple", modifiers = ["bold"] } +"markup.italic" = { fg = "yellow", modifiers = ["italic"] } +"markup.link.label" = { fg = "blue", modifiers = ["italic"] } +"markup.link.text" = "magenta" +"markup.link.url" = "cyan" +"markup.list" = "cyan" +"markup.quote" = { fg = "yellow", modifiers = ["italic"] } +"markup.raw" = { fg = "foreground" } "markup.strikethrough" = { modifiers = ["crossed_out"] } -"markup.link.url" = "cyan" -"markup.link.text" = "magenta" -"markup.quote" = { fg = "yellow", modifiers = ["italic"] } -"markup.raw" = { fg = "foreground" } [palette] -background = "#282a36" -background_dark = "#21222c" -primary_highlight = "#800049" +background = "#282a36" +background_dark = "#21222c" +comment = "#a39e9b" +foreground = "#eff0eb" +primary_highlight = "#800049" secondary_highlight = "#4d4f66" -foreground = "#eff0eb" -comment = "#a39e9b" # main colors -red = "#ff5c57" -blue = "#57c7ff" -yellow = "#f3f99d" -green = "#5af78e" -purple = "#bd93f9" -cyan = "#9aedfe" +blue = "#57c7ff" +cyan = "#9aedfe" +green = "#5af78e" magenta = "#ff6ac1" +purple = "#bd93f9" +red = "#ff5c57" +yellow = "#f3f99d" # aux colors -lilac = "#c9c5fb" -coral = "#f97c7c" -sand = "#ffab6f" carnation = "#f99fc6" -olive = "#b6d37c" -opal = "#b1d7c7" +coral = "#f97c7c" +lilac = "#c9c5fb" +olive = "#b6d37c" +opal = "#b1d7c7" +sand = "#ffab6f" From 5717aa8e35b12120de067f86dbc620a6dfac91ed Mon Sep 17 00:00:00 2001 From: rhogenson <05huvhec@duck.com> Date: Sat, 21 Sep 2024 07:05:17 -0700 Subject: [PATCH 12/57] Fix Rope.starts_with. (#11739) Co-authored-by: Rose Hogenson --- helix-stdx/src/rope.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/helix-stdx/src/rope.rs b/helix-stdx/src/rope.rs index 2695555e3..f7e31924a 100644 --- a/helix-stdx/src/rope.rs +++ b/helix-stdx/src/rope.rs @@ -51,7 +51,7 @@ impl<'a> RopeSliceExt<'a> for RopeSlice<'a> { if len < text.len() { return false; } - self.get_byte_slice(..len - text.len()) + self.get_byte_slice(..text.len()) .map_or(false, |start| start == text) } @@ -137,4 +137,14 @@ mod tests { } } } + + #[test] + fn starts_with() { + assert!(RopeSlice::from("asdf").starts_with("a")); + } + + #[test] + fn ends_with() { + assert!(RopeSlice::from("asdf").ends_with("f")); + } } From 274c660a0e2f2395f240df78787108ca256f6aea Mon Sep 17 00:00:00 2001 From: Mykyta <114003900+Nikita0x@users.noreply.github.com> Date: Sat, 21 Sep 2024 20:12:39 +0300 Subject: [PATCH 13/57] small fix syntax highlighting in vue.js files (#11706) * small fix syntax highlighting in vue.js files * changes after review by mikedavis --- runtime/queries/vue/highlights.scm | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/runtime/queries/vue/highlights.scm b/runtime/queries/vue/highlights.scm index f90ae4297..1d93832fb 100644 --- a/runtime/queries/vue/highlights.scm +++ b/runtime/queries/vue/highlights.scm @@ -6,9 +6,13 @@ (attribute (attribute_name) @attribute - (quoted_attribute_value - (attribute_value) @string) -) + [(attribute_value) (quoted_attribute_value)]? @string) + +(directive_attribute + (directive_name) @attribute + (directive_argument)? @attribute + (directive_modifiers)? @attribute + [(attribute_value) (quoted_attribute_value)]? @string) (comment) @comment @@ -18,4 +22,7 @@ "" +] @punctuation.bracket +"=" @punctuation.delimiter + From c850b90f677eee731995765fb04532746b7d408e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thor=20=F0=9F=AA=81?= <7041313+thor314@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:13:02 +0100 Subject: [PATCH 14/57] add circom tree-sitter, syntax-highlighting, and lsp support (#11676) * add circom tree-sitter and lsp support * add circom syntax highlighting queries * cargo xtask docgen * updated highlights to reflect helix themes typing * bugfix: ~= operator causing issues * minor adjustment: add = and ; operator and delimiter --- book/src/generated/lang-support.md | 1 + languages.toml | 17 +++ runtime/queries/circom/highlights.scm | 142 ++++++++++++++++++++++++++ runtime/queries/circom/locals.scm | 9 ++ 4 files changed, 169 insertions(+) create mode 100644 runtime/queries/circom/highlights.scm create mode 100644 runtime/queries/circom/locals.scm diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index f223c8b22..8a8c9fa83 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -19,6 +19,7 @@ | cairo | ✓ | ✓ | ✓ | `cairo-language-server` | | capnp | ✓ | | ✓ | | | cel | ✓ | | | | +| circom | ✓ | | | `circom-lsp` | | clojure | ✓ | | | `clojure-lsp` | | cmake | ✓ | ✓ | ✓ | `cmake-language-server` | | comment | ✓ | | | | diff --git a/languages.toml b/languages.toml index 9e1be0ac2..cf1d5ae13 100644 --- a/languages.toml +++ b/languages.toml @@ -16,6 +16,7 @@ bicep-langserver = { command = "bicep-langserver" } bitbake-language-server = { command = "bitbake-language-server" } bufls = { command = "bufls", args = ["serve"] } cairo-language-server = { command = "cairo-language-server", args = [] } +circom-lsp = { command = "circom-lsp" } cl-lsp = { command = "cl-lsp", args = [ "stdio" ] } clangd = { command = "clangd" } clojure-lsp = { command = "clojure-lsp" } @@ -3788,3 +3789,19 @@ indent = { tab-width = 2, unit = " " } [[grammar]] name = "thrift" source = { git = "https://github.com/tree-sitter-grammars/tree-sitter-thrift" , rev = "68fd0d80943a828d9e6f49c58a74be1e9ca142cf" } + +[[language]] +name = "circom" +scope = "source.circom" +injection-regex = "circom" +file-types = ["circom"] +roots = ["package.json"] +comment-tokens = "//" +indent = { tab-width = 4, unit = " " } +auto-format = false +language-servers = ["circom-lsp"] + +[[grammar]] +name = "circom" +source = { git = "https://github.com/Decurity/tree-sitter-circom", rev = "02150524228b1e6afef96949f2d6b7cc0aaf999e" } + diff --git a/runtime/queries/circom/highlights.scm b/runtime/queries/circom/highlights.scm new file mode 100644 index 000000000..1d310bd8e --- /dev/null +++ b/runtime/queries/circom/highlights.scm @@ -0,0 +1,142 @@ +; identifiers +; ----------- +(identifier) @variable + +; Pragma +; ----------- +(pragma_directive) @keyword.directive + +; Include +; ----------- +(include_directive) @keyword.directive + +; Literals +; -------- +(string) @string +(int_literal) @constant.numeric.integer +(comment) @comment + +; Definitions +; ----------- +(function_definition + name: (identifier) @keyword.function) + +(template_definition + name: (identifier) @keyword.function) + +; Use contructor coloring for special functions +(main_component_definition) @constructor + +; Invocations +(call_expression . (identifier) @function) + +; Function parameters +(parameter name: (identifier) @variable.parameter) + +; Members +(member_expression property: (property_identifier) @variable.other.member) + +; Tokens +; ------- + +; Keywords +[ + "signal" + "var" + "component" +] @keyword.storage.type + +[ "include" ] @keyword.control.import + +[ + "public" + "input" + "output" + ] @keyword.storage.modifier + +[ + "for" + "while" +] @keyword.control.repeat + +[ + "if" + "else" +] @keyword.control.conditional + +[ + "return" +] @keyword.control.return + +[ + "function" + "template" +] @keyword.function + +; Punctuation +[ + "(" + ")" + "[" + "]" + "{" + "}" +] @punctuation.bracket + +[ + "." + "," + ";" +] @punctuation.delimiter + +; Operators +; https://docs.circom.io/circom-language/basic-operators +[ + "=" + "?" + "&&" + "||" + "!" + "<" + ">" + "<=" + ">=" + "==" + "!=" + "+" + "-" + "*" + "**" + "/" + "\\" + "%" + "+=" + "-=" + "*=" + "**=" + "/=" + "\\=" + "%=" + "++" + "--" + "&" + "|" + "~" + "^" + ">>" + "<<" + "&=" + "|=" + ; "\~=" ; bug, uncomment and circom will not highlight + "^=" + ">>=" + "<<=" +] @operator + +[ + "<==" + "==>" + "<--" + "-->" + "===" +] @operator diff --git a/runtime/queries/circom/locals.scm b/runtime/queries/circom/locals.scm new file mode 100644 index 000000000..e0ea12de0 --- /dev/null +++ b/runtime/queries/circom/locals.scm @@ -0,0 +1,9 @@ +(function_definition) @local.scope +(template_definition) @local.scope +(main_component_definition) @local.scope +(block_statement) @local.scope + +(parameter name: (identifier) @local.definition) @local.definition + + +(identifier) @local.reference \ No newline at end of file From 896bf47d8dc1916265fa342b4209d1e406839853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Daron?= Date: Sat, 21 Sep 2024 19:22:50 +0200 Subject: [PATCH 15/57] adding support for jujutsu VCS inside find_workspace resolution (#11685) --- helix-loader/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/helix-loader/src/lib.rs b/helix-loader/src/lib.rs index f36c76c4f..0e7c134d0 100644 --- a/helix-loader/src/lib.rs +++ b/helix-loader/src/lib.rs @@ -225,7 +225,7 @@ pub fn merge_toml_values(left: toml::Value, right: toml::Value, merge_depth: usi /// Used as a ceiling dir for LSP root resolution, the filepicker and potentially as a future filewatching root /// /// This function starts searching the FS upward from the CWD -/// and returns the first directory that contains either `.git`, `.svn` or `.helix`. +/// and returns the first directory that contains either `.git`, `.svn`, `.jj` or `.helix`. /// If no workspace was found returns (CWD, true). /// Otherwise (workspace, false) is returned pub fn find_workspace() -> (PathBuf, bool) { @@ -233,6 +233,7 @@ pub fn find_workspace() -> (PathBuf, bool) { for ancestor in current_dir.ancestors() { if ancestor.join(".git").exists() || ancestor.join(".svn").exists() + || ancestor.join(".jj").exists() || ancestor.join(".helix").exists() { return (ancestor.to_owned(), false); From d6eb10d9f907139597ededa38a2cab44b26f5da6 Mon Sep 17 00:00:00 2001 From: James Munger Date: Sat, 21 Sep 2024 12:26:01 -0500 Subject: [PATCH 16/57] Update README.md (#11665) Readability Clarification --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3b639214d..90ebc9d16 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,8 @@ All shortcuts/keymaps can be found [in the documentation on the website](https:/ - Built-in language server support - Smart, incremental syntax highlighting and code editing via tree-sitter -It's a terminal-based editor first, but I'd like to explore a custom renderer -(similar to Emacs) in wgpu or skulpin. +Although it's primarily a terminal-based editor, I am interested in exploring +a custom renderer (similar to Emacs) using wgpu or skulpin. Note: Only certain languages have indentation definitions at the moment. Check `runtime/queries//` for `indents.scm`. From 8b1764d164d85ed0a089f3c660a7236a17b26d34 Mon Sep 17 00:00:00 2001 From: rhogenson <05huvhec@duck.com> Date: Sun, 22 Sep 2024 10:16:24 -0700 Subject: [PATCH 17/57] Join single-line comments with J. (#11742) Fixes #8565. Co-authored-by: Rose Hogenson --- helix-term/src/commands.rs | 29 +++++++++++++++++++++++++ helix-term/tests/test/commands.rs | 35 +++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 6e037a471..b1c29378d 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -4626,6 +4626,14 @@ fn join_selections_impl(cx: &mut Context, select_space: bool) { let text = doc.text(); let slice = text.slice(..); + let comment_tokens = doc + .language_config() + .and_then(|config| config.comment_tokens.as_deref()) + .unwrap_or(&[]); + // Sort by length to handle Rust's /// vs // + let mut comment_tokens: Vec<&str> = comment_tokens.iter().map(|x| x.as_str()).collect(); + comment_tokens.sort_unstable_by_key(|x| std::cmp::Reverse(x.len())); + let mut changes = Vec::new(); for selection in doc.selection(view.id) { @@ -4637,10 +4645,31 @@ fn join_selections_impl(cx: &mut Context, select_space: bool) { changes.reserve(lines.len()); + let first_line_idx = slice.line_to_char(start); + let first_line_idx = skip_while(slice, first_line_idx, |ch| matches!(ch, ' ' | 't')) + .unwrap_or(first_line_idx); + let first_line = slice.slice(first_line_idx..); + let mut current_comment_token = comment_tokens + .iter() + .find(|token| first_line.starts_with(token)); + for line in lines { let start = line_end_char_index(&slice, line); let mut end = text.line_to_char(line + 1); end = skip_while(slice, end, |ch| matches!(ch, ' ' | '\t')).unwrap_or(end); + let slice_from_end = slice.slice(end..); + if let Some(token) = comment_tokens + .iter() + .find(|token| slice_from_end.starts_with(token)) + { + if Some(token) == current_comment_token { + end += token.chars().count(); + end = skip_while(slice, end, |ch| matches!(ch, ' ' | '\t')).unwrap_or(end); + } else { + // update current token, but don't delete this one. + current_comment_token = Some(token); + } + } let separator = if end == line_end_char_index(&slice, line + 1) { // the joining line contains only space-characters => don't include a whitespace when joining diff --git a/helix-term/tests/test/commands.rs b/helix-term/tests/test/commands.rs index 9f196827f..f71ae308d 100644 --- a/helix-term/tests/test/commands.rs +++ b/helix-term/tests/test/commands.rs @@ -632,6 +632,41 @@ async fn test_join_selections_space() -> anyhow::Result<()> { Ok(()) } +#[tokio::test(flavor = "multi_thread")] +async fn test_join_selections_comment() -> anyhow::Result<()> { + test(( + indoc! {"\ + /// #[a|]#bc + /// def + "}, + ":lang rustJ", + indoc! {"\ + /// #[a|]#bc def + "}, + )) + .await?; + + // Only join if the comment token matches the previous line. + test(( + indoc! {"\ + #[| // a + // b + /// c + /// d + e + /// f + // g]# + "}, + ":lang rustJ", + indoc! {"\ + #[| // a b /// c d e f // g]# + "}, + )) + .await?; + + Ok(()) +} + #[tokio::test(flavor = "multi_thread")] async fn test_read_file() -> anyhow::Result<()> { let mut file = tempfile::NamedTempFile::new()?; From 73deabaa408c505905271e11065846ac87e1afd0 Mon Sep 17 00:00:00 2001 From: rhogenson <05huvhec@duck.com> Date: Sun, 22 Sep 2024 10:17:02 -0700 Subject: [PATCH 18/57] Fix panic when drawing at the edge of the screen. (#11737) When pressing tab at the edge of the screen, Helix panics in debug mode subtracting position.col - self.offset.col. To correctly account for graphemes that are partially visible, column_in_bounds takes a width and returns whether the whole range is in bounds. Co-authored-by: Rose Hogenson --- helix-term/src/ui/document.rs | 7 +++---- helix-term/src/ui/text_decorations.rs | 2 +- helix-term/src/ui/text_decorations/diagnostics.rs | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/helix-term/src/ui/document.rs b/helix-term/src/ui/document.rs index 79145ba04..ae00ea149 100644 --- a/helix-term/src/ui/document.rs +++ b/helix-term/src/ui/document.rs @@ -433,7 +433,7 @@ impl<'a> TextRenderer<'a> { Grapheme::Newline => &self.newline, }; - let in_bounds = self.column_in_bounds(position.col + width - 1); + let in_bounds = self.column_in_bounds(position.col, width); if in_bounds { self.surface.set_string( @@ -452,7 +452,6 @@ impl<'a> TextRenderer<'a> { ); self.surface.set_style(rect, style); } - if *is_in_indent_area && !is_whitespace { *last_indent_level = position.col; *is_in_indent_area = false; @@ -461,8 +460,8 @@ impl<'a> TextRenderer<'a> { width } - pub fn column_in_bounds(&self, colum: usize) -> bool { - self.offset.col <= colum && colum < self.viewport.width as usize + self.offset.col + pub fn column_in_bounds(&self, colum: usize, width: usize) -> bool { + self.offset.col <= colum && colum + width <= self.offset.col + self.viewport.width as usize } /// Overlay indentation guides ontop of a rendered line diff --git a/helix-term/src/ui/text_decorations.rs b/helix-term/src/ui/text_decorations.rs index 630af5817..931ea4311 100644 --- a/helix-term/src/ui/text_decorations.rs +++ b/helix-term/src/ui/text_decorations.rs @@ -164,7 +164,7 @@ impl Decoration for Cursor<'_> { renderer: &mut TextRenderer, grapheme: &FormattedGrapheme, ) -> usize { - if renderer.column_in_bounds(grapheme.visual_pos.col) + if renderer.column_in_bounds(grapheme.visual_pos.col, grapheme.width()) && renderer.offset.row < grapheme.visual_pos.row { let position = grapheme.visual_pos - renderer.offset; diff --git a/helix-term/src/ui/text_decorations/diagnostics.rs b/helix-term/src/ui/text_decorations/diagnostics.rs index 2d9e83700..0bb0026f7 100644 --- a/helix-term/src/ui/text_decorations/diagnostics.rs +++ b/helix-term/src/ui/text_decorations/diagnostics.rs @@ -98,7 +98,7 @@ impl Renderer<'_, '_> { fn draw_eol_diagnostic(&mut self, diag: &Diagnostic, row: u16, col: usize) -> u16 { let style = self.styles.severity_style(diag.severity()); let width = self.renderer.viewport.width; - if !self.renderer.column_in_bounds(col + 1) { + if !self.renderer.column_in_bounds(col + 1, 1) { return 0; } let col = (col - self.renderer.offset.col) as u16; From 30aa375f2d1fbbbd57fbb59652fc34b99bb28712 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 02:39:41 +0900 Subject: [PATCH 19/57] build(deps): bump the rust-dependencies group with 2 updates (#11761) Bumps the rust-dependencies group with 2 updates: [thiserror](https://github.com/dtolnay/thiserror) and [cc](https://github.com/rust-lang/cc-rs). Updates `thiserror` from 1.0.63 to 1.0.64 - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.63...1.0.64) Updates `cc` from 1.1.19 to 1.1.21 - [Release notes](https://github.com/rust-lang/cc-rs/releases) - [Changelog](https://github.com/rust-lang/cc-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.19...cc-v1.1.21) --- updated-dependencies: - dependency-name: thiserror dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies - dependency-name: cc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: rust-dependencies ... 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 7156fc27e..5b8c87705 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,9 +136,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "cc" -version = "1.1.19" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d74707dde2ba56f86ae90effb3b43ddd369504387e718014de010cec7959800" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "shlex", ] @@ -2225,18 +2225,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", From 50ba848b5970499f63afc5f13ce4da78a954d55b Mon Sep 17 00:00:00 2001 From: Lukas Knuth Date: Wed, 25 Sep 2024 08:23:38 +0200 Subject: [PATCH 20/57] Update HCL grammar (#11749) * Point HCL grammer to newest This adds support for provider-defined function calls in Terraform. * Update HCL grammar repo The repository was moved from the original authors personal GitHub to the `tree-sitter-grammars` organization. Co-authored-by: Michael Davis --------- Co-authored-by: Michael Davis --- languages.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index cf1d5ae13..a15d8bd79 100644 --- a/languages.toml +++ b/languages.toml @@ -1829,7 +1829,7 @@ auto-format = true [[grammar]] name = "hcl" -source = { git = "https://github.com/MichaHoffmann/tree-sitter-hcl", rev = "3cb7fc28247efbcb2973b97e71c78838ad98a583" } +source = { git = "https://github.com/tree-sitter-grammars/tree-sitter-hcl", rev = "9e3ec9848f28d26845ba300fd73c740459b83e9b" } [[language]] name = "tfvars" From f49b18d157f5122dc8b5e3569ea3c5d6e598b0c4 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Wed, 25 Sep 2024 08:23:52 +0200 Subject: [PATCH 21/57] chore: Update slint tree-sitter grammar to version 1.8 (#11757) Bump the commit to the tree-sitter corresponding to the latest Slint release. --- languages.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index a15d8bd79..7c7ec86da 100644 --- a/languages.toml +++ b/languages.toml @@ -2400,7 +2400,7 @@ language-servers = [ "slint-lsp" ] [[grammar]] name = "slint" -source = { git = "https://github.com/slint-ui/tree-sitter-slint", rev = "4a0558cc0fcd7a6110815b9bbd7cc12d7ab31e74" } +source = { git = "https://github.com/slint-ui/tree-sitter-slint", rev = "34ccfd58d3baee7636f62d9326f32092264e8407" } [[language]] name = "task" From b18a471ed189fb326a781181a28f3073f5c1fe1e Mon Sep 17 00:00:00 2001 From: Akseli Date: Wed, 25 Sep 2024 09:24:11 +0300 Subject: [PATCH 22/57] Remove "true" from odinfmt line (#11759) The `-stdin` in `odinfmt` does not take any arguments, the `true` part here just confuses the formatter, and makes it ignore `odinfmt.json` file. Removing it fixes the issue. --- languages.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages.toml b/languages.toml index 7c7ec86da..cca5155ad 100644 --- a/languages.toml +++ b/languages.toml @@ -2133,7 +2133,7 @@ language-servers = [ "ols" ] comment-token = "//" block-comment-tokens = { start = "/*", end = "*/" } indent = { tab-width = 4, unit = "\t" } -formatter = { command = "odinfmt", args = [ "-stdin", "true" ] } +formatter = { command = "odinfmt", args = [ "-stdin" ] } [language.debugger] name = "lldb-dap" From 70bbc9d526938a1cff9ad6467605df6a8297b364 Mon Sep 17 00:00:00 2001 From: Konstantin Munteanu <637499+mkon@users.noreply.github.com> Date: Sat, 28 Sep 2024 06:22:13 +0200 Subject: [PATCH 23/57] Add .rbs files to ruby language (#11786) --- languages.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/languages.toml b/languages.toml index cca5155ad..802f346cb 100644 --- a/languages.toml +++ b/languages.toml @@ -912,6 +912,7 @@ file-types = [ "podspec", "rjs", "rbi", + "rbs", { glob = "rakefile" }, { glob = "gemfile" }, { glob = "Rakefile" }, From 82dd96369302f60a9c83a2d54d021458f82bcd36 Mon Sep 17 00:00:00 2001 From: Tim <63202655+sarsapar1lla@users.noreply.github.com> Date: Sat, 28 Sep 2024 12:52:09 +0100 Subject: [PATCH 24/57] Add: validation of bundled themes in build workflow (#11627) * Add: xtask to check themes for validation warnings * Update: tidied up runtime paths * Update: test build workflow * Update: address clippy lints * Revert: only trigger workflow on push to master branch * Add: Theme::from_keys factory method to construct theme from Toml keys * Update: returning validation failures in Loader.load method * Update: commented out invalid keys from affected themes * Update: correct invalid keys so that valid styles still applied * Update: include default and base16_default themes in check * Update: renamed validation_failures to load_errors * Update: introduce load_with_warnings helper function and centralise logging of theme warnings * Update: use consistent naming throughout --- .github/workflows/build.yml | 4 ++ helix-view/src/theme.rs | 116 ++++++++++++++++++------------- runtime/themes/autumn.toml | 6 +- runtime/themes/emacs.toml | 3 +- runtime/themes/flatwhite.toml | 7 +- runtime/themes/kanagawa.toml | 3 +- runtime/themes/monokai.toml | 3 +- runtime/themes/monokai_aqua.toml | 9 ++- runtime/themes/zed_onedark.toml | 3 +- xtask/src/main.rs | 7 ++ xtask/src/path.rs | 10 ++- xtask/src/theme_check.rs | 33 +++++++++ 12 files changed, 143 insertions(+), 61 deletions(-) create mode 100644 xtask/src/theme_check.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 93fcb9816..c9f198d0c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,6 +16,7 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + - name: Install stable toolchain uses: dtolnay/rust-toolchain@1.70 @@ -107,6 +108,9 @@ jobs: - name: Validate queries run: cargo xtask query-check + - name: Validate themes + run: cargo xtask theme-check + - name: Generate docs run: cargo xtask docgen diff --git a/helix-view/src/theme.rs b/helix-view/src/theme.rs index 4acc56648..9dc326444 100644 --- a/helix-view/src/theme.rs +++ b/helix-view/src/theme.rs @@ -53,20 +53,34 @@ impl Loader { /// Loads a theme searching directories in priority order. pub fn load(&self, name: &str) -> Result { + let (theme, warnings) = self.load_with_warnings(name)?; + + for warning in warnings { + warn!("Theme '{}': {}", name, warning); + } + + Ok(theme) + } + + /// Loads a theme searching directories in priority order, returning any warnings + pub fn load_with_warnings(&self, name: &str) -> Result<(Theme, Vec)> { if name == "default" { - return Ok(self.default()); + return Ok((self.default(), Vec::new())); } if name == "base16_default" { - return Ok(self.base16_default()); + return Ok((self.base16_default(), Vec::new())); } let mut visited_paths = HashSet::new(); - let theme = self.load_theme(name, &mut visited_paths).map(Theme::from)?; + let (theme, warnings) = self + .load_theme(name, &mut visited_paths) + .map(Theme::from_toml)?; - Ok(Theme { + let theme = Theme { name: name.into(), ..theme - }) + }; + Ok((theme, warnings)) } /// Recursively load a theme, merging with any inherited parent themes. @@ -87,10 +101,7 @@ impl Loader { let theme_toml = if let Some(parent_theme_name) = inherits { let parent_theme_name = parent_theme_name.as_str().ok_or_else(|| { - anyhow!( - "Theme: expected 'inherits' to be a string: {}", - parent_theme_name - ) + anyhow!("Expected 'inherits' to be a string: {}", parent_theme_name) })?; let parent_theme_toml = match parent_theme_name { @@ -181,9 +192,9 @@ impl Loader { }) .ok_or_else(|| { if cycle_found { - anyhow!("Theme: cycle found in inheriting: {}", name) + anyhow!("Cycle found in inheriting: {}", name) } else { - anyhow!("Theme: file not found for: {}", name) + anyhow!("File not found for: {}", name) } }) } @@ -220,19 +231,11 @@ pub struct Theme { impl From for Theme { fn from(value: Value) -> Self { - if let Value::Table(table) = value { - let (styles, scopes, highlights) = build_theme_values(table); - - Self { - styles, - scopes, - highlights, - ..Default::default() - } - } else { - warn!("Expected theme TOML value to be a table, found {:?}", value); - Default::default() + let (theme, warnings) = Theme::from_toml(value); + for warning in warnings { + warn!("{}", warning); } + theme } } @@ -242,31 +245,29 @@ impl<'de> Deserialize<'de> for Theme { D: Deserializer<'de>, { let values = Map::::deserialize(deserializer)?; - - let (styles, scopes, highlights) = build_theme_values(values); - - Ok(Self { - styles, - scopes, - highlights, - ..Default::default() - }) + let (theme, warnings) = Theme::from_keys(values); + for warning in warnings { + warn!("{}", warning); + } + Ok(theme) } } fn build_theme_values( mut values: Map, -) -> (HashMap, Vec, Vec