Mercurial > forks > helix
changeset 6784:9900b670afa2
syntax: Move config types to a separate module
author | Michael Davis <mcarsondavis@gmail.com> |
---|---|
date | Thu, 20 Feb 2025 17:08:47 -0500 |
parents | acb76767dab3 |
children | f1e0f64160d7 |
files | helix-core/src/comment.rs helix-core/src/config.rs helix-core/src/indent.rs helix-core/src/movement.rs helix-core/src/syntax.rs helix-core/src/syntax/config.rs helix-core/src/textobject.rs helix-core/tests/indent.rs helix-dap/src/client.rs helix-lsp/src/client.rs helix-lsp/src/lib.rs helix-term/src/commands.rs helix-term/src/commands/dap.rs helix-term/src/commands/lsp.rs helix-term/src/handlers/completion.rs helix-term/src/handlers/completion/request.rs helix-term/src/handlers/document_colors.rs helix-term/src/handlers/signature_help.rs helix-term/src/ui/mod.rs helix-term/tests/integration.rs helix-view/src/document.rs helix-view/src/editor.rs helix-view/src/gutter.rs xtask/src/helpers.rs |
diffstat | 24 files changed, 657 insertions(+), 639 deletions(-) [+] |
line wrap: on
line diff
--- a/helix-core/src/comment.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-core/src/comment.rs Thu Feb 20 17:08:47 2025 -0500 @@ -4,7 +4,8 @@ use smallvec::SmallVec; use crate::{ - syntax::BlockCommentToken, Change, Range, Rope, RopeSlice, Selection, Tendril, Transaction, + syntax::config::BlockCommentToken, Change, Range, Rope, RopeSlice, Selection, Tendril, + Transaction, }; use helix_stdx::rope::RopeSliceExt; use std::borrow::Cow;
--- a/helix-core/src/config.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-core/src/config.rs Thu Feb 20 17:08:47 2025 -0500 @@ -1,4 +1,4 @@ -use crate::syntax::{Configuration, Loader, LoaderError}; +use crate::syntax::{config::Configuration, Loader, LoaderError}; /// Language configuration based on built-in languages.toml. pub fn default_lang_config() -> Configuration {
--- a/helix-core/src/indent.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-core/src/indent.rs Thu Feb 20 17:08:47 2025 -0500 @@ -6,7 +6,10 @@ use crate::{ chars::{char_is_line_ending, char_is_whitespace}, graphemes::{grapheme_width, tab_width_at}, - syntax::{IndentationHeuristic, LanguageConfiguration, RopeProvider, Syntax}, + syntax::{ + config::{IndentationHeuristic, LanguageConfiguration}, + RopeProvider, Syntax, + }, tree_sitter::Node, Position, Rope, RopeSlice, Tendril, };
--- a/helix-core/src/movement.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-core/src/movement.rs Thu Feb 20 17:08:47 2025 -0500 @@ -13,7 +13,7 @@ }, line_ending::rope_is_line_ending, position::char_idx_at_visual_block_offset, - syntax::LanguageConfiguration, + syntax::config::LanguageConfiguration, text_annotations::TextAnnotations, textobject::TextObject, visual_offset_from_block, Range, RopeSlice, Selection, Syntax,
--- a/helix-core/src/syntax.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-core/src/syntax.rs Thu Feb 20 17:08:47 2025 -0500 @@ -1,9 +1,8 @@ +pub mod config; mod tree_cursor; use crate::{ - auto_pairs::AutoPairs, chars::char_is_line_ending, - diagnostic::Severity, regex::Regex, transaction::{ChangeSet, Operation}, RopeSlice, Tendril, @@ -12,7 +11,7 @@ use ahash::RandomState; use arc_swap::{ArcSwap, Guard}; use bitflags::bitflags; -use globset::GlobSet; +use config::{Configuration, FileType, LanguageConfiguration, LanguageServerConfiguration}; use hashbrown::raw::RawTable; use helix_stdx::rope::{self, RopeSliceExt}; use slotmap::{DefaultKey as LayerId, HopSlotMap}; @@ -20,595 +19,20 @@ use std::{ borrow::Cow, cell::RefCell, - collections::{HashMap, HashSet, VecDeque}, - fmt::{self, Display, Write}, + collections::{HashMap, VecDeque}, + fmt::{self, Write}, hash::{Hash, Hasher}, mem::replace, - path::{Path, PathBuf}, - str::FromStr, + path::Path, sync::Arc, }; -use once_cell::sync::{Lazy, OnceCell}; -use serde::{ser::SerializeSeq, Deserialize, Serialize}; +use once_cell::sync::Lazy; use helix_loader::grammar::{get_language, load_runtime_file}; pub use tree_cursor::TreeCursor; -fn deserialize_regex<'de, D>(deserializer: D) -> Result<Option<rope::Regex>, D::Error> -where - D: serde::Deserializer<'de>, -{ - Option::<String>::deserialize(deserializer)? - .map(|buf| rope::Regex::new(&buf).map_err(serde::de::Error::custom)) - .transpose() -} - -fn deserialize_lsp_config<'de, D>(deserializer: D) -> Result<Option<serde_json::Value>, D::Error> -where - D: serde::Deserializer<'de>, -{ - Option::<toml::Value>::deserialize(deserializer)? - .map(|toml| toml.try_into().map_err(serde::de::Error::custom)) - .transpose() -} - -fn deserialize_tab_width<'de, D>(deserializer: D) -> Result<usize, D::Error> -where - D: serde::Deserializer<'de>, -{ - usize::deserialize(deserializer).and_then(|n| { - if n > 0 && n <= 16 { - Ok(n) - } else { - Err(serde::de::Error::custom( - "tab width must be a value from 1 to 16 inclusive", - )) - } - }) -} - -pub fn deserialize_auto_pairs<'de, D>(deserializer: D) -> Result<Option<AutoPairs>, D::Error> -where - D: serde::Deserializer<'de>, -{ - Ok(Option::<AutoPairConfig>::deserialize(deserializer)?.and_then(AutoPairConfig::into)) -} - -fn default_timeout() -> u64 { - 20 -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] -pub struct Configuration { - pub language: Vec<LanguageConfiguration>, - #[serde(default)] - pub language_server: HashMap<String, LanguageServerConfiguration>, -} - -// largely based on tree-sitter/cli/src/loader.rs -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] -pub struct LanguageConfiguration { - #[serde(rename = "name")] - pub language_id: String, // c-sharp, rust, tsx - #[serde(rename = "language-id")] - // see the table under https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentItem - pub language_server_language_id: Option<String>, // csharp, rust, typescriptreact, for the language-server - pub scope: String, // source.rust - pub file_types: Vec<FileType>, // filename extension or ends_with? <Gemfile, rb, etc> - #[serde(default)] - pub shebangs: Vec<String>, // interpreter(s) associated with language - #[serde(default)] - pub roots: Vec<String>, // these indicate project roots <.git, Cargo.toml> - #[serde( - default, - skip_serializing, - deserialize_with = "from_comment_tokens", - alias = "comment-token" - )] - pub comment_tokens: Option<Vec<String>>, - #[serde( - default, - skip_serializing, - deserialize_with = "from_block_comment_tokens" - )] - pub block_comment_tokens: Option<Vec<BlockCommentToken>>, - pub text_width: Option<usize>, - pub soft_wrap: Option<SoftWrap>, - - #[serde(default)] - pub auto_format: bool, - - #[serde(skip_serializing_if = "Option::is_none")] - pub formatter: Option<FormatterConfiguration>, - - /// If set, overrides `editor.path-completion`. - pub path_completion: Option<bool>, - - #[serde(default)] - pub diagnostic_severity: Severity, - - pub grammar: Option<String>, // tree-sitter grammar name, defaults to language_id - - // content_regex - #[serde(default, skip_serializing, deserialize_with = "deserialize_regex")] - pub injection_regex: Option<rope::Regex>, - // first_line_regex - // - #[serde(skip)] - pub(crate) highlight_config: OnceCell<Option<Arc<HighlightConfiguration>>>, - // tags_config OnceCell<> https://github.com/tree-sitter/tree-sitter/pull/583 - #[serde( - default, - skip_serializing_if = "Vec::is_empty", - serialize_with = "serialize_lang_features", - deserialize_with = "deserialize_lang_features" - )] - pub language_servers: Vec<LanguageServerFeatures>, - #[serde(skip_serializing_if = "Option::is_none")] - pub indent: Option<IndentationConfiguration>, - - #[serde(skip)] - pub(crate) indent_query: OnceCell<Option<Query>>, - #[serde(skip)] - pub(crate) textobject_query: OnceCell<Option<TextObjectQuery>>, - #[serde(skip_serializing_if = "Option::is_none")] - pub debugger: Option<DebugAdapterConfig>, - - /// Automatic insertion of pairs to parentheses, brackets, - /// etc. Defaults to true. Optionally, this can be a list of 2-tuples - /// to specify a list of characters to pair. This overrides the - /// global setting. - #[serde(default, skip_serializing, deserialize_with = "deserialize_auto_pairs")] - pub auto_pairs: Option<AutoPairs>, - - pub rulers: Option<Vec<u16>>, // if set, override editor's rulers - - /// 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<Vec<PathBuf>>, - #[serde(default)] - pub persistent_diagnostic_sources: Vec<String>, -} - -#[derive(Debug, PartialEq, Eq, Hash)] -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), - /// 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 { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: serde::Serializer, - { - use serde::ser::SerializeMap; - - match self { - FileType::Extension(extension) => serializer.serialize_str(extension), - FileType::Glob(glob) => { - let mut map = serializer.serialize_map(Some(1))?; - map.serialize_entry("glob", glob.glob())?; - map.end() - } - } - } -} - -impl<'de> Deserialize<'de> for FileType { - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: serde::de::Deserializer<'de>, - { - struct FileTypeVisitor; - - impl<'de> serde::de::Visitor<'de> for FileTypeVisitor { - type Value = FileType; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("string or table") - } - - fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> - where - E: serde::de::Error, - { - Ok(FileType::Extension(value.to_string())) - } - - fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error> - where - M: serde::de::MapAccess<'de>, - { - match map.next_entry::<String, String>()? { - 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 - ))), - None => Err(serde::de::Error::custom( - "expected a `suffix` key in the `file-types` entry", - )), - } - } - } - - deserializer.deserialize_any(FileTypeVisitor) - } -} - -fn from_comment_tokens<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error> -where - D: serde::Deserializer<'de>, -{ - #[derive(Deserialize)] - #[serde(untagged)] - enum CommentTokens { - Multiple(Vec<String>), - Single(String), - } - Ok( - Option::<CommentTokens>::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<Option<Vec<BlockCommentToken>>, D::Error> -where - D: serde::Deserializer<'de>, -{ - #[derive(Deserialize)] - #[serde(untagged)] - enum BlockCommentTokens { - Multiple(Vec<BlockCommentToken>), - Single(BlockCommentToken), - } - Ok( - Option::<BlockCommentTokens>::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 { - Format, - GotoDeclaration, - GotoDefinition, - GotoTypeDefinition, - GotoReference, - GotoImplementation, - // Goto, use bitflags, combining previous Goto members? - SignatureHelp, - Hover, - DocumentHighlight, - Completion, - CodeAction, - WorkspaceCommand, - DocumentSymbols, - WorkspaceSymbols, - // Symbols, use bitflags, see above? - Diagnostics, - RenameSymbol, - InlayHints, - DocumentColors, -} - -impl Display for LanguageServerFeature { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use LanguageServerFeature::*; - let feature = match self { - Format => "format", - GotoDeclaration => "goto-declaration", - GotoDefinition => "goto-definition", - GotoTypeDefinition => "goto-type-definition", - GotoReference => "goto-reference", - GotoImplementation => "goto-implementation", - SignatureHelp => "signature-help", - Hover => "hover", - DocumentHighlight => "document-highlight", - Completion => "completion", - CodeAction => "code-action", - WorkspaceCommand => "workspace-command", - DocumentSymbols => "document-symbols", - WorkspaceSymbols => "workspace-symbols", - Diagnostics => "diagnostics", - RenameSymbol => "rename-symbol", - InlayHints => "inlay-hints", - DocumentColors => "document-colors", - }; - write!(f, "{feature}",) - } -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(untagged, rename_all = "kebab-case", deny_unknown_fields)] -enum LanguageServerFeatureConfiguration { - #[serde(rename_all = "kebab-case")] - Features { - #[serde(default, skip_serializing_if = "HashSet::is_empty")] - only_features: HashSet<LanguageServerFeature>, - #[serde(default, skip_serializing_if = "HashSet::is_empty")] - except_features: HashSet<LanguageServerFeature>, - name: String, - }, - Simple(String), -} - -#[derive(Debug, Default)] -pub struct LanguageServerFeatures { - pub name: String, - pub only: HashSet<LanguageServerFeature>, - pub excluded: HashSet<LanguageServerFeature>, -} - -impl LanguageServerFeatures { - pub fn has_feature(&self, feature: LanguageServerFeature) -> bool { - (self.only.is_empty() || self.only.contains(&feature)) && !self.excluded.contains(&feature) - } -} - -fn deserialize_lang_features<'de, D>( - deserializer: D, -) -> Result<Vec<LanguageServerFeatures>, D::Error> -where - D: serde::Deserializer<'de>, -{ - let raw: Vec<LanguageServerFeatureConfiguration> = Deserialize::deserialize(deserializer)?; - let res = raw - .into_iter() - .map(|config| match config { - LanguageServerFeatureConfiguration::Simple(name) => LanguageServerFeatures { - name, - ..Default::default() - }, - LanguageServerFeatureConfiguration::Features { - only_features, - except_features, - name, - } => LanguageServerFeatures { - name, - only: only_features, - excluded: except_features, - }, - }) - .collect(); - Ok(res) -} -fn serialize_lang_features<S>( - map: &Vec<LanguageServerFeatures>, - serializer: S, -) -> Result<S::Ok, S::Error> -where - S: serde::Serializer, -{ - let mut serializer = serializer.serialize_seq(Some(map.len()))?; - for features in map { - let features = if features.only.is_empty() && features.excluded.is_empty() { - LanguageServerFeatureConfiguration::Simple(features.name.to_owned()) - } else { - LanguageServerFeatureConfiguration::Features { - only_features: features.only.clone(), - except_features: features.excluded.clone(), - name: features.name.to_owned(), - } - }; - serializer.serialize_element(&features)?; - } - serializer.end() -} - -fn deserialize_required_root_patterns<'de, D>(deserializer: D) -> Result<Option<GlobSet>, D::Error> -where - D: serde::Deserializer<'de>, -{ - let patterns = Vec::<String>::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 { - pub command: String, - #[serde(default)] - #[serde(skip_serializing_if = "Vec::is_empty")] - pub args: Vec<String>, - #[serde(default, skip_serializing_if = "HashMap::is_empty")] - pub environment: HashMap<String, String>, - #[serde(default, skip_serializing, deserialize_with = "deserialize_lsp_config")] - pub config: Option<serde_json::Value>, - #[serde(default = "default_timeout")] - pub timeout: u64, - #[serde( - default, - skip_serializing, - deserialize_with = "deserialize_required_root_patterns" - )] - pub required_root_patterns: Option<GlobSet>, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] -pub struct FormatterConfiguration { - pub command: String, - #[serde(default)] - #[serde(skip_serializing_if = "Vec::is_empty")] - pub args: Vec<String>, -} - -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] -#[serde(rename_all = "kebab-case")] -pub struct AdvancedCompletion { - pub name: Option<String>, - pub completion: Option<String>, - pub default: Option<String>, -} - -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] -#[serde(rename_all = "kebab-case", untagged)] -pub enum DebugConfigCompletion { - Named(String), - Advanced(AdvancedCompletion), -} - -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] -#[serde(untagged)] -pub enum DebugArgumentValue { - String(String), - Array(Vec<String>), - Boolean(bool), -} - -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] -#[serde(rename_all = "kebab-case")] -pub struct DebugTemplate { - pub name: String, - pub request: String, - #[serde(default)] - pub completion: Vec<DebugConfigCompletion>, - pub args: HashMap<String, DebugArgumentValue>, -} - -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] -#[serde(rename_all = "kebab-case")] -pub struct DebugAdapterConfig { - pub name: String, - pub transport: String, - #[serde(default)] - pub command: String, - #[serde(default)] - pub args: Vec<String>, - pub port_arg: Option<String>, - pub templates: Vec<DebugTemplate>, - #[serde(default)] - pub quirks: DebuggerQuirks, -} - -// Different workarounds for adapters' differences -#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize)] -pub struct DebuggerQuirks { - #[serde(default)] - pub absolute_paths: bool, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] -pub struct IndentationConfiguration { - #[serde(deserialize_with = "deserialize_tab_width")] - pub tab_width: usize, - pub unit: String, -} - -/// How the indentation for a newly inserted line should be determined. -/// If the selected heuristic is not available (e.g. because the current -/// language has no tree-sitter indent queries), a simpler one will be used. -#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case")] -pub enum IndentationHeuristic { - /// Just copy the indentation of the line that the cursor is currently on. - Simple, - /// Use tree-sitter indent queries to compute the expected absolute indentation level of the new line. - TreeSitter, - /// Use tree-sitter indent queries to compute the expected difference in indentation between the new line - /// and the line before. Add this to the actual indentation level of the line before. - #[default] - Hybrid, -} - -/// Configuration for auto pairs -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "kebab-case", deny_unknown_fields, untagged)] -pub enum AutoPairConfig { - /// Enables or disables auto pairing. False means disabled. True means to use the default pairs. - Enable(bool), - - /// The mappings of pairs. - Pairs(HashMap<char, char>), -} - -impl Default for AutoPairConfig { - fn default() -> Self { - AutoPairConfig::Enable(true) - } -} - -impl From<&AutoPairConfig> for Option<AutoPairs> { - fn from(auto_pair_config: &AutoPairConfig) -> Self { - match auto_pair_config { - AutoPairConfig::Enable(false) => None, - AutoPairConfig::Enable(true) => Some(AutoPairs::default()), - AutoPairConfig::Pairs(pairs) => Some(AutoPairs::new(pairs.iter())), - } - } -} - -impl From<AutoPairConfig> for Option<AutoPairs> { - fn from(auto_pairs_config: AutoPairConfig) -> Self { - (&auto_pairs_config).into() - } -} - -impl FromStr for AutoPairConfig { - type Err = std::str::ParseBoolError; - - // only do bool parsing for runtime setting - fn from_str(s: &str) -> Result<Self, Self::Err> { - let enable: bool = s.parse()?; - Ok(AutoPairConfig::Enable(enable)) - } -} - #[derive(Debug)] pub struct TextObjectQuery { pub query: Query, @@ -743,7 +167,7 @@ .to_string() } -impl LanguageConfiguration { +impl config::LanguageConfiguration { fn initialize_highlight(&self, scopes: &[String]) -> Option<Arc<HighlightConfiguration>> { let highlights_query = read_query(&self.language_id, "highlights.scm"); // always highlight syntax errors @@ -831,35 +255,6 @@ .ok() } } -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] -#[serde(default, rename_all = "kebab-case", deny_unknown_fields)] -pub struct SoftWrap { - /// Soft wrap lines that exceed viewport width. Default to off - // NOTE: Option on purpose because the struct is shared between language config and global config. - // By default the option is None so that the language config falls back to the global config unless explicitly set. - pub enable: Option<bool>, - /// Maximum space left free at the end of the line. - /// This space is used to wrap text at word boundaries. If that is not possible within this limit - /// the word is simply split at the end of the line. - /// - /// This is automatically hard-limited to a quarter of the viewport to ensure correct display on small views. - /// - /// Default to 20 - pub max_wrap: Option<u16>, - /// Maximum number of indentation that can be carried over from the previous line when softwrapping. - /// If a line is indented further then this limit it is rendered at the start of the viewport instead. - /// - /// This is automatically hard-limited to a quarter of the viewport to ensure correct display on small views. - /// - /// Default to 40 - pub max_indent_retain: Option<u16>, - /// Indicator placed at the beginning of softwrapped lines - /// - /// Defaults to ↪ - pub wrap_indicator: Option<String>, - /// Softwrap at `text_width` instead of viewport width if it is shorter - pub wrap_at_text_width: Option<bool>, -} #[derive(Debug)] struct FileTypeGlob {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/helix-core/src/syntax/config.rs Thu Feb 20 17:08:47 2025 -0500 @@ -0,0 +1,616 @@ +use crate::{auto_pairs::AutoPairs, diagnostic::Severity}; + +use globset::GlobSet; +use helix_stdx::rope; +use once_cell::sync::OnceCell; +use serde::{ser::SerializeSeq as _, Deserialize, Serialize}; + +use std::{ + collections::{HashMap, HashSet}, + fmt::{self, Display}, + path::PathBuf, + str::FromStr, + sync::Arc, +}; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct Configuration { + pub language: Vec<LanguageConfiguration>, + #[serde(default)] + pub language_server: HashMap<String, LanguageServerConfiguration>, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case", deny_unknown_fields)] +pub struct LanguageConfiguration { + #[serde(rename = "name")] + pub language_id: String, // c-sharp, rust, tsx + #[serde(rename = "language-id")] + // see the table under https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentItem + pub language_server_language_id: Option<String>, // csharp, rust, typescriptreact, for the language-server + pub scope: String, // source.rust + pub file_types: Vec<FileType>, // filename extension or ends_with? <Gemfile, rb, etc> + #[serde(default)] + pub shebangs: Vec<String>, // interpreter(s) associated with language + #[serde(default)] + pub roots: Vec<String>, // these indicate project roots <.git, Cargo.toml> + #[serde( + default, + skip_serializing, + deserialize_with = "from_comment_tokens", + alias = "comment-token" + )] + pub comment_tokens: Option<Vec<String>>, + #[serde( + default, + skip_serializing, + deserialize_with = "from_block_comment_tokens" + )] + pub block_comment_tokens: Option<Vec<BlockCommentToken>>, + pub text_width: Option<usize>, + pub soft_wrap: Option<SoftWrap>, + + #[serde(default)] + pub auto_format: bool, + + #[serde(skip_serializing_if = "Option::is_none")] + pub formatter: Option<FormatterConfiguration>, + + /// If set, overrides `editor.path-completion`. + pub path_completion: Option<bool>, + + #[serde(default)] + pub diagnostic_severity: Severity, + + pub grammar: Option<String>, // tree-sitter grammar name, defaults to language_id + + // content_regex + #[serde(default, skip_serializing, deserialize_with = "deserialize_regex")] + pub injection_regex: Option<rope::Regex>, + // first_line_regex + // + #[serde(skip)] + pub(crate) highlight_config: OnceCell<Option<Arc<super::HighlightConfiguration>>>, + // tags_config OnceCell<> https://github.com/tree-sitter/tree-sitter/pull/583 + #[serde( + default, + skip_serializing_if = "Vec::is_empty", + serialize_with = "serialize_lang_features", + deserialize_with = "deserialize_lang_features" + )] + pub language_servers: Vec<LanguageServerFeatures>, + #[serde(skip_serializing_if = "Option::is_none")] + pub indent: Option<IndentationConfiguration>, + + #[serde(skip)] + pub(crate) indent_query: OnceCell<Option<tree_sitter::Query>>, + #[serde(skip)] + pub(crate) textobject_query: OnceCell<Option<super::TextObjectQuery>>, + #[serde(skip_serializing_if = "Option::is_none")] + pub debugger: Option<DebugAdapterConfig>, + + /// Automatic insertion of pairs to parentheses, brackets, + /// etc. Defaults to true. Optionally, this can be a list of 2-tuples + /// to specify a list of characters to pair. This overrides the + /// global setting. + #[serde(default, skip_serializing, deserialize_with = "deserialize_auto_pairs")] + pub auto_pairs: Option<AutoPairs>, + + pub rulers: Option<Vec<u16>>, // if set, override editor's rulers + + /// 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<Vec<PathBuf>>, + #[serde(default)] + pub persistent_diagnostic_sources: Vec<String>, +} + +#[derive(Debug, PartialEq, Eq, Hash)] +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), + /// 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 { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + use serde::ser::SerializeMap; + + match self { + FileType::Extension(extension) => serializer.serialize_str(extension), + FileType::Glob(glob) => { + let mut map = serializer.serialize_map(Some(1))?; + map.serialize_entry("glob", glob.glob())?; + map.end() + } + } + } +} + +impl<'de> Deserialize<'de> for FileType { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: serde::de::Deserializer<'de>, + { + struct FileTypeVisitor; + + impl<'de> serde::de::Visitor<'de> for FileTypeVisitor { + type Value = FileType; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("string or table") + } + + fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + Ok(FileType::Extension(value.to_string())) + } + + fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error> + where + M: serde::de::MapAccess<'de>, + { + match map.next_entry::<String, String>()? { + 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 + ))), + None => Err(serde::de::Error::custom( + "expected a `suffix` key in the `file-types` entry", + )), + } + } + } + + deserializer.deserialize_any(FileTypeVisitor) + } +} + +fn from_comment_tokens<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error> +where + D: serde::Deserializer<'de>, +{ + #[derive(Deserialize)] + #[serde(untagged)] + enum CommentTokens { + Multiple(Vec<String>), + Single(String), + } + Ok( + Option::<CommentTokens>::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<Option<Vec<BlockCommentToken>>, D::Error> +where + D: serde::Deserializer<'de>, +{ + #[derive(Deserialize)] + #[serde(untagged)] + enum BlockCommentTokens { + Multiple(Vec<BlockCommentToken>), + Single(BlockCommentToken), + } + Ok( + Option::<BlockCommentTokens>::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 { + Format, + GotoDeclaration, + GotoDefinition, + GotoTypeDefinition, + GotoReference, + GotoImplementation, + // Goto, use bitflags, combining previous Goto members? + SignatureHelp, + Hover, + DocumentHighlight, + Completion, + CodeAction, + WorkspaceCommand, + DocumentSymbols, + WorkspaceSymbols, + // Symbols, use bitflags, see above? + Diagnostics, + RenameSymbol, + InlayHints, + DocumentColors, +} + +impl Display for LanguageServerFeature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use LanguageServerFeature::*; + let feature = match self { + Format => "format", + GotoDeclaration => "goto-declaration", + GotoDefinition => "goto-definition", + GotoTypeDefinition => "goto-type-definition", + GotoReference => "goto-reference", + GotoImplementation => "goto-implementation", + SignatureHelp => "signature-help", + Hover => "hover", + DocumentHighlight => "document-highlight", + Completion => "completion", + CodeAction => "code-action", + WorkspaceCommand => "workspace-command", + DocumentSymbols => "document-symbols", + WorkspaceSymbols => "workspace-symbols", + Diagnostics => "diagnostics", + RenameSymbol => "rename-symbol", + InlayHints => "inlay-hints", + DocumentColors => "document-colors", + }; + write!(f, "{feature}",) + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged, rename_all = "kebab-case", deny_unknown_fields)] +enum LanguageServerFeatureConfiguration { + #[serde(rename_all = "kebab-case")] + Features { + #[serde(default, skip_serializing_if = "HashSet::is_empty")] + only_features: HashSet<LanguageServerFeature>, + #[serde(default, skip_serializing_if = "HashSet::is_empty")] + except_features: HashSet<LanguageServerFeature>, + name: String, + }, + Simple(String), +} + +#[derive(Debug, Default)] +pub struct LanguageServerFeatures { + pub name: String, + pub only: HashSet<LanguageServerFeature>, + pub excluded: HashSet<LanguageServerFeature>, +} + +impl LanguageServerFeatures { + pub fn has_feature(&self, feature: LanguageServerFeature) -> bool { + (self.only.is_empty() || self.only.contains(&feature)) && !self.excluded.contains(&feature) + } +} + +fn deserialize_lang_features<'de, D>( + deserializer: D, +) -> Result<Vec<LanguageServerFeatures>, D::Error> +where + D: serde::Deserializer<'de>, +{ + let raw: Vec<LanguageServerFeatureConfiguration> = Deserialize::deserialize(deserializer)?; + let res = raw + .into_iter() + .map(|config| match config { + LanguageServerFeatureConfiguration::Simple(name) => LanguageServerFeatures { + name, + ..Default::default() + }, + LanguageServerFeatureConfiguration::Features { + only_features, + except_features, + name, + } => LanguageServerFeatures { + name, + only: only_features, + excluded: except_features, + }, + }) + .collect(); + Ok(res) +} +fn serialize_lang_features<S>( + map: &Vec<LanguageServerFeatures>, + serializer: S, +) -> Result<S::Ok, S::Error> +where + S: serde::Serializer, +{ + let mut serializer = serializer.serialize_seq(Some(map.len()))?; + for features in map { + let features = if features.only.is_empty() && features.excluded.is_empty() { + LanguageServerFeatureConfiguration::Simple(features.name.to_owned()) + } else { + LanguageServerFeatureConfiguration::Features { + only_features: features.only.clone(), + except_features: features.excluded.clone(), + name: features.name.to_owned(), + } + }; + serializer.serialize_element(&features)?; + } + serializer.end() +} + +fn deserialize_required_root_patterns<'de, D>(deserializer: D) -> Result<Option<GlobSet>, D::Error> +where + D: serde::Deserializer<'de>, +{ + let patterns = Vec::<String>::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 { + pub command: String, + #[serde(default)] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub args: Vec<String>, + #[serde(default, skip_serializing_if = "HashMap::is_empty")] + pub environment: HashMap<String, String>, + #[serde(default, skip_serializing, deserialize_with = "deserialize_lsp_config")] + pub config: Option<serde_json::Value>, + #[serde(default = "default_timeout")] + pub timeout: u64, + #[serde( + default, + skip_serializing, + deserialize_with = "deserialize_required_root_patterns" + )] + pub required_root_patterns: Option<GlobSet>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct FormatterConfiguration { + pub command: String, + #[serde(default)] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub args: Vec<String>, +} + +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case")] +pub struct AdvancedCompletion { + pub name: Option<String>, + pub completion: Option<String>, + pub default: Option<String>, +} + +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case", untagged)] +pub enum DebugConfigCompletion { + Named(String), + Advanced(AdvancedCompletion), +} + +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum DebugArgumentValue { + String(String), + Array(Vec<String>), + Boolean(bool), +} + +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case")] +pub struct DebugTemplate { + pub name: String, + pub request: String, + #[serde(default)] + pub completion: Vec<DebugConfigCompletion>, + pub args: HashMap<String, DebugArgumentValue>, +} + +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[serde(rename_all = "kebab-case")] +pub struct DebugAdapterConfig { + pub name: String, + pub transport: String, + #[serde(default)] + pub command: String, + #[serde(default)] + pub args: Vec<String>, + pub port_arg: Option<String>, + pub templates: Vec<DebugTemplate>, + #[serde(default)] + pub quirks: DebuggerQuirks, +} + +// Different workarounds for adapters' differences +#[derive(Debug, Default, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct DebuggerQuirks { + #[serde(default)] + pub absolute_paths: bool, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct IndentationConfiguration { + #[serde(deserialize_with = "deserialize_tab_width")] + pub tab_width: usize, + pub unit: String, +} + +/// How the indentation for a newly inserted line should be determined. +/// If the selected heuristic is not available (e.g. because the current +/// language has no tree-sitter indent queries), a simpler one will be used. +#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum IndentationHeuristic { + /// Just copy the indentation of the line that the cursor is currently on. + Simple, + /// Use tree-sitter indent queries to compute the expected absolute indentation level of the new line. + TreeSitter, + /// Use tree-sitter indent queries to compute the expected difference in indentation between the new line + /// and the line before. Add this to the actual indentation level of the line before. + #[default] + Hybrid, +} + +/// Configuration for auto pairs +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case", deny_unknown_fields, untagged)] +pub enum AutoPairConfig { + /// Enables or disables auto pairing. False means disabled. True means to use the default pairs. + Enable(bool), + + /// The mappings of pairs. + Pairs(HashMap<char, char>), +} + +impl Default for AutoPairConfig { + fn default() -> Self { + AutoPairConfig::Enable(true) + } +} + +impl From<&AutoPairConfig> for Option<AutoPairs> { + fn from(auto_pair_config: &AutoPairConfig) -> Self { + match auto_pair_config { + AutoPairConfig::Enable(false) => None, + AutoPairConfig::Enable(true) => Some(AutoPairs::default()), + AutoPairConfig::Pairs(pairs) => Some(AutoPairs::new(pairs.iter())), + } + } +} + +impl From<AutoPairConfig> for Option<AutoPairs> { + fn from(auto_pairs_config: AutoPairConfig) -> Self { + (&auto_pairs_config).into() + } +} + +impl FromStr for AutoPairConfig { + type Err = std::str::ParseBoolError; + + // only do bool parsing for runtime setting + fn from_str(s: &str) -> Result<Self, Self::Err> { + let enable: bool = s.parse()?; + Ok(AutoPairConfig::Enable(enable)) + } +} + +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(default, rename_all = "kebab-case", deny_unknown_fields)] +pub struct SoftWrap { + /// Soft wrap lines that exceed viewport width. Default to off + // NOTE: Option on purpose because the struct is shared between language config and global config. + // By default the option is None so that the language config falls back to the global config unless explicitly set. + pub enable: Option<bool>, + /// Maximum space left free at the end of the line. + /// This space is used to wrap text at word boundaries. If that is not possible within this limit + /// the word is simply split at the end of the line. + /// + /// This is automatically hard-limited to a quarter of the viewport to ensure correct display on small views. + /// + /// Default to 20 + pub max_wrap: Option<u16>, + /// Maximum number of indentation that can be carried over from the previous line when softwrapping. + /// If a line is indented further then this limit it is rendered at the start of the viewport instead. + /// + /// This is automatically hard-limited to a quarter of the viewport to ensure correct display on small views. + /// + /// Default to 40 + pub max_indent_retain: Option<u16>, + /// Indicator placed at the beginning of softwrapped lines + /// + /// Defaults to ↪ + pub wrap_indicator: Option<String>, + /// Softwrap at `text_width` instead of viewport width if it is shorter + pub wrap_at_text_width: Option<bool>, +} + +fn deserialize_regex<'de, D>(deserializer: D) -> Result<Option<rope::Regex>, D::Error> +where + D: serde::Deserializer<'de>, +{ + Option::<String>::deserialize(deserializer)? + .map(|buf| rope::Regex::new(&buf).map_err(serde::de::Error::custom)) + .transpose() +} + +fn deserialize_lsp_config<'de, D>(deserializer: D) -> Result<Option<serde_json::Value>, D::Error> +where + D: serde::Deserializer<'de>, +{ + Option::<toml::Value>::deserialize(deserializer)? + .map(|toml| toml.try_into().map_err(serde::de::Error::custom)) + .transpose() +} + +fn deserialize_tab_width<'de, D>(deserializer: D) -> Result<usize, D::Error> +where + D: serde::Deserializer<'de>, +{ + usize::deserialize(deserializer).and_then(|n| { + if n > 0 && n <= 16 { + Ok(n) + } else { + Err(serde::de::Error::custom( + "tab width must be a value from 1 to 16 inclusive", + )) + } + }) +} + +pub fn deserialize_auto_pairs<'de, D>(deserializer: D) -> Result<Option<AutoPairs>, D::Error> +where + D: serde::Deserializer<'de>, +{ + Ok(Option::<AutoPairConfig>::deserialize(deserializer)?.and_then(AutoPairConfig::into)) +} + +fn default_timeout() -> u64 { + 20 +}
--- a/helix-core/src/textobject.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-core/src/textobject.rs Thu Feb 20 17:08:47 2025 -0500 @@ -7,7 +7,7 @@ use crate::graphemes::{next_grapheme_boundary, prev_grapheme_boundary}; use crate::line_ending::rope_is_line_ending; use crate::movement::Direction; -use crate::syntax::LanguageConfiguration; +use crate::syntax::config::LanguageConfiguration; use crate::Range; use crate::{surround, Syntax};
--- a/helix-core/tests/indent.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-core/tests/indent.rs Thu Feb 20 17:08:47 2025 -0500 @@ -1,7 +1,7 @@ use arc_swap::ArcSwap; use helix_core::{ indent::{indent_level_for_line, treesitter_indent_for_pos, IndentStyle}, - syntax::{Configuration, Loader}, + syntax::{config::Configuration, Loader}, Syntax, }; use helix_stdx::rope::RopeSliceExt;
--- a/helix-dap/src/client.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-dap/src/client.rs Thu Feb 20 17:08:47 2025 -0500 @@ -4,7 +4,7 @@ types::*, Error, Result, }; -use helix_core::syntax::DebuggerQuirks; +use helix_core::syntax::config::DebuggerQuirks; use serde_json::Value;
--- a/helix-lsp/src/client.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-lsp/src/client.rs Thu Feb 20 17:08:47 2025 -0500 @@ -10,7 +10,7 @@ DidChangeWorkspaceFoldersParams, OneOf, PositionEncodingKind, SignatureHelp, Url, WorkspaceFolder, WorkspaceFoldersChangeEvent, }; -use helix_core::{find_workspace, syntax::LanguageServerFeature, ChangeSet, Rope}; +use helix_core::{find_workspace, syntax::config::LanguageServerFeature, ChangeSet, Rope}; use helix_loader::VERSION_AND_GIT_HASH; use helix_stdx::path; use parking_lot::Mutex;
--- a/helix-lsp/src/lib.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-lsp/src/lib.rs Thu Feb 20 17:08:47 2025 -0500 @@ -12,7 +12,7 @@ pub use lsp::{Position, Url}; use futures_util::stream::select_all::SelectAll; -use helix_core::syntax::{ +use helix_core::syntax::config::{ LanguageConfiguration, LanguageServerConfiguration, LanguageServerFeatures, }; use helix_stdx::path;
--- a/helix-term/src/commands.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-term/src/commands.rs Thu Feb 20 17:08:47 2025 -0500 @@ -34,7 +34,7 @@ regex::{self, Regex}, search::{self, CharMatcher}, selection, surround, - syntax::{BlockCommentToken, LanguageServerFeature}, + syntax::config::{BlockCommentToken, LanguageServerFeature}, text_annotations::{Overlay, TextAnnotations}, textobject, unicode::width::UnicodeWidthChar,
--- a/helix-term/src/commands/dap.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-term/src/commands/dap.rs Thu Feb 20 17:08:47 2025 -0500 @@ -5,7 +5,7 @@ ui::{self, overlay::overlaid, Picker, Popup, Prompt, PromptEvent, Text}, }; use dap::{StackFrame, Thread, ThreadStates}; -use helix_core::syntax::{DebugArgumentValue, DebugConfigCompletion, DebugTemplate}; +use helix_core::syntax::config::{DebugArgumentValue, DebugConfigCompletion, DebugTemplate}; use helix_dap::{self as dap, Client}; use helix_lsp::block_on; use helix_view::editor::Breakpoint;
--- a/helix-term/src/commands/lsp.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-term/src/commands/lsp.rs Thu Feb 20 17:08:47 2025 -0500 @@ -14,7 +14,7 @@ use super::{align_view, push_jump, Align, Context, Editor}; use helix_core::{ - diagnostic::DiagnosticProvider, syntax::LanguageServerFeature, + diagnostic::DiagnosticProvider, syntax::config::LanguageServerFeature, text_annotations::InlineAnnotation, Selection, Uri, }; use helix_stdx::path;
--- a/helix-term/src/handlers/completion.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-term/src/handlers/completion.rs Thu Feb 20 17:08:47 2025 -0500 @@ -2,7 +2,7 @@ use helix_core::chars::char_is_word; use helix_core::completion::CompletionProvider; -use helix_core::syntax::LanguageServerFeature; +use helix_core::syntax::config::LanguageServerFeature; use helix_event::{register_hook, TaskHandle}; use helix_lsp::lsp; use helix_stdx::rope::RopeSliceExt;
--- a/helix-term/src/handlers/completion/request.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-term/src/handlers/completion/request.rs Thu Feb 20 17:08:47 2025 -0500 @@ -5,7 +5,7 @@ use arc_swap::ArcSwap; use futures_util::Future; use helix_core::completion::CompletionProvider; -use helix_core::syntax::LanguageServerFeature; +use helix_core::syntax::config::LanguageServerFeature; use helix_event::{cancelable_future, TaskController, TaskHandle}; use helix_lsp::lsp; use helix_lsp::lsp::{CompletionContext, CompletionTriggerKind};
--- a/helix-term/src/handlers/document_colors.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-term/src/handlers/document_colors.rs Thu Feb 20 17:08:47 2025 -0500 @@ -1,7 +1,7 @@ use std::{collections::HashSet, time::Duration}; use futures_util::{stream::FuturesOrdered, StreamExt}; -use helix_core::{syntax::LanguageServerFeature, text_annotations::InlineAnnotation}; +use helix_core::{syntax::config::LanguageServerFeature, text_annotations::InlineAnnotation}; use helix_event::{cancelable_future, register_hook}; use helix_lsp::lsp; use helix_view::{
--- a/helix-term/src/handlers/signature_help.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-term/src/handlers/signature_help.rs Thu Feb 20 17:08:47 2025 -0500 @@ -1,7 +1,7 @@ use std::sync::Arc; use std::time::Duration; -use helix_core::syntax::LanguageServerFeature; +use helix_core::syntax::config::LanguageServerFeature; use helix_event::{cancelable_future, register_hook, send_blocking, TaskController, TaskHandle}; use helix_lsp::lsp::{self, SignatureInformation}; use helix_stdx::rope::RopeSliceExt;
--- a/helix-term/src/ui/mod.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-term/src/ui/mod.rs Thu Feb 20 17:08:47 2025 -0500 @@ -373,7 +373,7 @@ use crate::ui::prompt::Completion; use helix_core::command_line::{self, Tokenizer}; use helix_core::fuzzy::fuzzy_match; - use helix_core::syntax::LanguageServerFeature; + use helix_core::syntax::config::LanguageServerFeature; use helix_view::document::SCRATCH_BUFFER_NAME; use helix_view::theme; use helix_view::{editor::Config, Editor};
--- a/helix-term/tests/integration.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-term/tests/integration.rs Thu Feb 20 17:08:47 2025 -0500 @@ -2,7 +2,7 @@ mod test { mod helpers; - use helix_core::{syntax::AutoPairConfig, Selection}; + use helix_core::{syntax::config::AutoPairConfig, Selection}; use helix_term::config::Config; use indoc::indoc;
--- a/helix-view/src/document.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-view/src/document.rs Thu Feb 20 17:08:47 2025 -0500 @@ -10,7 +10,7 @@ use helix_core::doc_formatter::TextFormat; use helix_core::encoding::Encoding; use helix_core::snippets::{ActiveSnippet, SnippetRenderCtx}; -use helix_core::syntax::{Highlight, LanguageServerFeature}; +use helix_core::syntax::{config::LanguageServerFeature, Highlight}; use helix_core::text_annotations::{InlineAnnotation, Overlay}; use helix_event::TaskController; use helix_lsp::util::lsp_pos_to_pos; @@ -39,7 +39,7 @@ history::{History, State, UndoKind}, indent::{auto_detect_indent_style, IndentStyle}, line_ending::auto_detect_line_ending, - syntax::{self, LanguageConfiguration}, + syntax::{self, config::LanguageConfiguration}, ChangeSet, Diagnostic, LineEnding, Range, Rope, RopeBuilder, Selection, Syntax, Transaction, }; @@ -1134,7 +1134,7 @@ pub fn detect_language_config( &self, config_loader: &syntax::Loader, - ) -> Option<Arc<helix_core::syntax::LanguageConfiguration>> { + ) -> Option<Arc<syntax::config::LanguageConfiguration>> { config_loader .language_config_for_file_name(self.path.as_ref()?) .or_else(|| config_loader.language_config_for_shebang(self.text().slice(..))) @@ -1276,8 +1276,8 @@ /// if it exists. pub fn set_language( &mut self, - language_config: Option<Arc<helix_core::syntax::LanguageConfiguration>>, - loader: Option<Arc<ArcSwap<helix_core::syntax::Loader>>>, + language_config: Option<Arc<syntax::config::LanguageConfiguration>>, + loader: Option<Arc<ArcSwap<syntax::Loader>>>, ) { if let (Some(language_config), Some(loader)) = (language_config, loader) { if let Some(highlight_config) = @@ -1294,7 +1294,7 @@ } /// Set the programming language for the file if you know the language but don't have the - /// [`syntax::LanguageConfiguration`] for it. + /// [`syntax::config::LanguageConfiguration`] for it. pub fn set_language_by_language_id( &mut self, language_id: &str,
--- a/helix-view/src/editor.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-view/src/editor.rs Thu Feb 20 17:08:47 2025 -0500 @@ -46,7 +46,10 @@ use helix_core::{ auto_pairs::AutoPairs, diagnostic::DiagnosticProvider, - syntax::{self, AutoPairConfig, IndentationHeuristic, LanguageServerFeature, SoftWrap}, + syntax::{ + self, + config::{AutoPairConfig, IndentationHeuristic, LanguageServerFeature, SoftWrap}, + }, Change, LineEnding, Position, Range, Selection, Uri, NATIVE_LINE_ENDING, }; use helix_dap as dap;
--- a/helix-view/src/gutter.rs Tue May 13 15:21:51 2025 -0700 +++ b/helix-view/src/gutter.rs Thu Feb 20 17:08:47 2025 -0500 @@ -1,6 +1,6 @@ use std::fmt::Write; -use helix_core::syntax::LanguageServerFeature; +use helix_core::syntax::config::LanguageServerFeature; use crate::{ editor::GutterType,
--- a/xtask/src/helpers.rs Tue May 13 15:21:51 2025 -0700 +++ b/xtask/src/helpers.rs Thu Feb 20 17:08:47 2025 -0500 @@ -1,7 +1,7 @@ use std::path::{Path, PathBuf}; use crate::path; -use helix_core::syntax::Configuration as LangConfig; +use helix_core::syntax::config::Configuration as LangConfig; use helix_term::health::TsFeature; /// Get the list of languages that support a particular tree-sitter