Unnamed repository; edit this file 'description' to name the repository.
syntax: Move config types to a separate module
Michael Davis 10 months ago
parent 84e95d3 · commit c94fde8
-rw-r--r--helix-core/src/comment.rs3
-rw-r--r--helix-core/src/config.rs2
-rw-r--r--helix-core/src/indent.rs5
-rw-r--r--helix-core/src/movement.rs2
-rw-r--r--helix-core/src/syntax.rs619
-rw-r--r--helix-core/src/syntax/config.rs616
-rw-r--r--helix-core/src/textobject.rs2
-rw-r--r--helix-core/tests/indent.rs2
-rw-r--r--helix-dap/src/client.rs2
-rw-r--r--helix-lsp/src/client.rs2
-rw-r--r--helix-lsp/src/lib.rs2
-rw-r--r--helix-term/src/commands.rs2
-rw-r--r--helix-term/src/commands/dap.rs2
-rw-r--r--helix-term/src/commands/lsp.rs2
-rw-r--r--helix-term/src/handlers/completion.rs2
-rw-r--r--helix-term/src/handlers/completion/request.rs2
-rw-r--r--helix-term/src/handlers/document_colors.rs2
-rw-r--r--helix-term/src/handlers/signature_help.rs2
-rw-r--r--helix-term/src/ui/mod.rs2
-rw-r--r--helix-term/tests/integration.rs2
-rw-r--r--helix-view/src/document.rs12
-rw-r--r--helix-view/src/editor.rs5
-rw-r--r--helix-view/src/gutter.rs2
-rw-r--r--xtask/src/helpers.rs2
24 files changed, 657 insertions, 639 deletions
diff --git a/helix-core/src/comment.rs b/helix-core/src/comment.rs
index 6bb1f300..5985cac7 100644
--- a/helix-core/src/comment.rs
+++ b/helix-core/src/comment.rs
@@ -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;
diff --git a/helix-core/src/config.rs b/helix-core/src/config.rs
index 27cd4e29..559aa2cb 100644
--- a/helix-core/src/config.rs
+++ b/helix-core/src/config.rs
@@ -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 {
diff --git a/helix-core/src/indent.rs b/helix-core/src/indent.rs
index 04ce9a28..52369bb7 100644
--- a/helix-core/src/indent.rs
+++ b/helix-core/src/indent.rs
@@ -6,7 +6,10 @@ use tree_sitter::{Query, QueryCursor, QueryPredicateArg};
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,
};
diff --git a/helix-core/src/movement.rs b/helix-core/src/movement.rs
index e446d8cc..2a1fa94f 100644
--- a/helix-core/src/movement.rs
+++ b/helix-core/src/movement.rs
@@ -13,7 +13,7 @@ use crate::{
},
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,
diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs
index 677cdfa0..6a2c28d1 100644
--- a/helix-core/src/syntax.rs
+++ b/helix-core/src/syntax.rs
@@ -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 crate::{
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 slotmap::{DefaultKey as LayerId, HopSlotMap};
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 @@ pub fn read_query(language: &str, filename: &str) -> String {
.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 @@ impl LanguageConfiguration {
.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 {
diff --git a/helix-core/src/syntax/config.rs b/helix-core/src/syntax/config.rs
new file mode 100644
index 00000000..f73103c2
--- /dev/null
+++ b/helix-core/src/syntax/config.rs
@@ -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
+}
diff --git a/helix-core/src/textobject.rs b/helix-core/src/textobject.rs
index 7576b3a7..9015e957 100644
--- a/helix-core/src/textobject.rs
+++ b/helix-core/src/textobject.rs
@@ -7,7 +7,7 @@ use crate::chars::{categorize_char, char_is_whitespace, CharCategory};
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};
diff --git a/helix-core/tests/indent.rs b/helix-core/tests/indent.rs
index 56b4d2ba..b41b2f64 100644
--- a/helix-core/tests/indent.rs
+++ b/helix-core/tests/indent.rs
@@ -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;
diff --git a/helix-dap/src/client.rs b/helix-dap/src/client.rs
index 6aa656e1..1529b6f9 100644
--- a/helix-dap/src/client.rs
+++ b/helix-dap/src/client.rs
@@ -4,7 +4,7 @@ use crate::{
types::*,
Error, Result,
};
-use helix_core::syntax::DebuggerQuirks;
+use helix_core::syntax::config::DebuggerQuirks;
use serde_json::Value;
diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs
index 01b1b2c3..1894dc5b 100644
--- a/helix-lsp/src/client.rs
+++ b/helix-lsp/src/client.rs
@@ -10,7 +10,7 @@ use crate::lsp::{
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;
diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs
index 1a2557de..567e8a70 100644
--- a/helix-lsp/src/lib.rs
+++ b/helix-lsp/src/lib.rs
@@ -12,7 +12,7 @@ pub use jsonrpc::Call;
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;
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 86d58627..3a2db261 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -34,7 +34,7 @@ use helix_core::{
regex::{self, Regex},
search::{self, CharMatcher},
selection, surround,
- syntax::{BlockCommentToken, LanguageServerFeature},
+ syntax::config::{BlockCommentToken, LanguageServerFeature},
text_annotations::{Overlay, TextAnnotations},
textobject,
unicode::width::UnicodeWidthChar,
diff --git a/helix-term/src/commands/dap.rs b/helix-term/src/commands/dap.rs
index 83dd936c..4f20af4a 100644
--- a/helix-term/src/commands/dap.rs
+++ b/helix-term/src/commands/dap.rs
@@ -5,7 +5,7 @@ use crate::{
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;
diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs
index 8377f7c7..9c55c830 100644
--- a/helix-term/src/commands/lsp.rs
+++ b/helix-term/src/commands/lsp.rs
@@ -14,7 +14,7 @@ use tui::{text::Span, widgets::Row};
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;
diff --git a/helix-term/src/handlers/completion.rs b/helix-term/src/handlers/completion.rs
index 20fac514..5017399b 100644
--- a/helix-term/src/handlers/completion.rs
+++ b/helix-term/src/handlers/completion.rs
@@ -2,7 +2,7 @@ use std::collections::HashMap;
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;
diff --git a/helix-term/src/handlers/completion/request.rs b/helix-term/src/handlers/completion/request.rs
index 26f252a4..51a3129a 100644
--- a/helix-term/src/handlers/completion/request.rs
+++ b/helix-term/src/handlers/completion/request.rs
@@ -5,7 +5,7 @@ use std::time::Duration;
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};
diff --git a/helix-term/src/handlers/document_colors.rs b/helix-term/src/handlers/document_colors.rs
index 956cecbf..f46ef2ac 100644
--- a/helix-term/src/handlers/document_colors.rs
+++ b/helix-term/src/handlers/document_colors.rs
@@ -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::{
diff --git a/helix-term/src/handlers/signature_help.rs b/helix-term/src/handlers/signature_help.rs
index 33c9e16c..8a0c9754 100644
--- a/helix-term/src/handlers/signature_help.rs
+++ b/helix-term/src/handlers/signature_help.rs
@@ -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;
diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs
index 47b046c9..106bfbfb 100644
--- a/helix-term/src/ui/mod.rs
+++ b/helix-term/src/ui/mod.rs
@@ -373,7 +373,7 @@ pub mod completers {
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};
diff --git a/helix-term/tests/integration.rs b/helix-term/tests/integration.rs
index 5e418ceb..469242e4 100644
--- a/helix-term/tests/integration.rs
+++ b/helix-term/tests/integration.rs
@@ -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;
diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs
index ae31bab6..417483e0 100644
--- a/helix-view/src/document.rs
+++ b/helix-view/src/document.rs
@@ -10,7 +10,7 @@ use helix_core::diagnostic::DiagnosticProvider;
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 @@ use helix_core::{
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 @@ impl Document {
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 @@ impl Document {
/// 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 @@ impl Document {
}
/// 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,
diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs
index eab291e4..5509bba3 100644
--- a/helix-view/src/editor.rs
+++ b/helix-view/src/editor.rs
@@ -46,7 +46,10 @@ pub use helix_core::diagnostic::Severity;
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;
diff --git a/helix-view/src/gutter.rs b/helix-view/src/gutter.rs
index 665a78bc..bc87d836 100644
--- a/helix-view/src/gutter.rs
+++ b/helix-view/src/gutter.rs
@@ -1,6 +1,6 @@
use std::fmt::Write;
-use helix_core::syntax::LanguageServerFeature;
+use helix_core::syntax::config::LanguageServerFeature;
use crate::{
editor::GutterType,
diff --git a/xtask/src/helpers.rs b/xtask/src/helpers.rs
index f96cdfb3..d2c955bc 100644
--- a/xtask/src/helpers.rs
+++ b/xtask/src/helpers.rs
@@ -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