Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-core/src/indent.rs')
| -rw-r--r-- | helix-core/src/indent.rs | 66 |
1 files changed, 65 insertions, 1 deletions
diff --git a/helix-core/src/indent.rs b/helix-core/src/indent.rs index 1e90db47..2a5f5d93 100644 --- a/helix-core/src/indent.rs +++ b/helix-core/src/indent.rs @@ -1,16 +1,36 @@ use std::{borrow::Cow, collections::HashMap}; +use anyhow::{anyhow, bail}; +use helix_config::{config_serde_adapter, options, IntegerRangeValidator}; +use serde::{Deserialize, Serialize}; use tree_sitter::{Query, QueryCursor, QueryPredicateArg}; use crate::{ chars::{char_is_line_ending, char_is_whitespace}, find_first_non_whitespace_char, graphemes::{grapheme_width, tab_width_at}, - syntax::{IndentationHeuristic, LanguageConfiguration, RopeProvider, Syntax}, + syntax::{LanguageConfiguration, RopeProvider, Syntax}, tree_sitter::Node, Position, Rope, RopeGraphemes, RopeSlice, }; +/// 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, Copy, 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, +} +config_serde_adapter!(IndentationHeuristic); + /// Enum representing indentation style. /// /// Only values 1-8 are valid for the `Spaces` variant. @@ -20,6 +40,50 @@ pub enum IndentStyle { Spaces(u8), } +options! { + struct IndentationConfig { + /// The number columns that a tabs are aligned to. + #[name = "ident.tab_width"] + #[read = copy] + tab_width: usize = 4, + /// Indentation inserted/removed into the document when indenting/dedenting. + /// This can be set to an integer representing N spaces or "tab" for tabs. + #[name = "ident.unit"] + #[read = copy] + indent_style: IndentStyle = IndentStyle::Tabs, + /// How the indentation for a newly inserted line is computed: + /// `simple` just copies the indentation level from the previous line, + /// `tree-sitter` computes the indentation based on the syntax tree and + /// `hybrid` combines both approaches. + /// If the chosen heuristic is not available, a different one will + /// be used as a fallback (the fallback order being `hybrid` -> + /// `tree-sitter` -> `simple`). + #[read = copy] + indent_heuristic: IndentationHeuristic = IndentationHeuristic::Hybrid + } +} + +impl helix_config::Ty for IndentStyle { + fn from_value(val: helix_config::Value) -> anyhow::Result<Self> { + match val { + helix_config::Value::String(s) if s == "t" || s == "tab" => Ok(IndentStyle::Tabs), + helix_config::Value::Int(_) => { + let spaces = IntegerRangeValidator::new(0, MAX_INDENT) + .validate(val) + .map_err(|err| anyhow!("invalid number of spaces! {err}"))?; + Ok(IndentStyle::Spaces(spaces)) + } + _ => bail!("expected an integer (spaces) or 'tab'"), + } + } + fn to_value(&self) -> helix_config::Value { + match *self { + IndentStyle::Tabs => helix_config::Value::String("tab".into()), + IndentStyle::Spaces(spaces) => helix_config::Value::Int(spaces as _), + } + } +} + // 16 spaces const INDENTS: &str = " "; pub const MAX_INDENT: u8 = 16; |