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/helix-core/Cargo.toml b/helix-core/Cargo.toml index 8c63af8ef..fb68ccc08 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -52,6 +52,7 @@ textwrap = "0.16.0" 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 99ff74d36..9b82fa73e 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}; @@ -358,6 +359,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 { @@ -371,9 +388,12 @@ pub struct LanguageServerConfiguration { pub config: Option, #[serde(default = "default_timeout")] pub timeout: u64, - #[serde(default)] - #[serde(skip_serializing_if = "Vec::is_empty")] - pub required_root_patterns: Vec, + #[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/lib.rs b/helix-lsp/src/lib.rs index 7caaa89ca..27678b2c1 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -910,40 +910,15 @@ fn start_client( 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 mut globset = globset::GlobSetBuilder::new(); - let required_root_patterns = &ls_config.required_root_patterns; - if !required_root_patterns.is_empty() { - for required_root_pattern in required_root_patterns { - match globset::Glob::new(required_root_pattern) { - Ok(glob) => { - globset.add(glob); - } - Err(err) => { - log::warn!( - "Failed to build glob '{}' for language server '{}'", - required_root_pattern, - name - ); - log::warn!("{}", err); - } - }; + 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); } - match globset.build() { - Ok(glob) => { - if !root_path - .read_dir()? - .flatten() - .map(|entry| entry.file_name()) - .any(|entry| glob.is_match(entry)) - { - return Ok(None); - } - } - Err(err) => { - log::warn!("Failed to build globset for language server {name}"); - log::warn!("{}", err); - } - }; } let (client, incoming, initialize_notify) = Client::start(