770 lines · 24.9 KB · Rust
RawDownload
use crate::{    HighlightId, HighlightMap, LanguageConfig, LanguageConfigOverride, LanguageName,    LanguageQueries, language_config::BracketPairConfig,};use anyhow::{Context as _, Result};use collections::HashMap;use gpui_shared_string::SharedString;use parking_lot::Mutex;use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};use tree_sitter::Query; pub static NEXT_GRAMMAR_ID: AtomicUsize = AtomicUsize::new(0); #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]pub struct GrammarId(pub usize); impl GrammarId {    pub fn new() -> Self {        Self(NEXT_GRAMMAR_ID.fetch_add(1, SeqCst))    }} impl Default for GrammarId {    fn default() -> Self {        Self::new()    }} pub struct Grammar {    id: GrammarId,    pub ts_language: tree_sitter::Language,    pub error_query: Option<Query>,    pub highlights_config: Option<HighlightsConfig>,    pub brackets_config: Option<BracketsConfig>,    pub redactions_config: Option<RedactionConfig>,    pub runnable_config: Option<RunnableConfig>,    pub indents_config: Option<IndentConfig>,    pub outline_config: Option<OutlineConfig>,    pub text_object_config: Option<TextObjectConfig>,    pub injection_config: Option<InjectionConfig>,    pub override_config: Option<OverrideConfig>,    pub debug_variables_config: Option<DebugVariablesConfig>,    pub highlight_map: Mutex<HighlightMap>,} pub struct HighlightsConfig {    pub query: Query,    pub identifier_capture_indices: Vec<u32>,} pub struct IndentConfig {    pub query: Query,    pub indent_capture_ix: u32,    pub start_capture_ix: Option<u32>,    pub end_capture_ix: Option<u32>,    pub outdent_capture_ix: Option<u32>,    pub suffixed_start_captures: HashMap<u32, SharedString>,} pub struct OutlineConfig {    pub query: Query,    pub item_capture_ix: u32,    pub name_capture_ix: u32,    pub context_capture_ix: Option<u32>,    pub extra_context_capture_ix: Option<u32>,    pub open_capture_ix: Option<u32>,    pub close_capture_ix: Option<u32>,    pub annotation_capture_ix: Option<u32>,} #[derive(Debug, Clone, Copy, PartialEq)]pub enum DebuggerTextObject {    Variable,    Scope,} impl DebuggerTextObject {    pub fn from_capture_name(name: &str) -> Option<DebuggerTextObject> {        match name {            "debug-variable" => Some(DebuggerTextObject::Variable),            "debug-scope" => Some(DebuggerTextObject::Scope),            _ => None,        }    }} #[derive(Debug, Clone, Copy, PartialEq)]pub enum TextObject {    InsideFunction,    AroundFunction,    InsideClass,    AroundClass,    InsideComment,    AroundComment,} impl TextObject {    pub fn from_capture_name(name: &str) -> Option<TextObject> {        match name {            "function.inside" => Some(TextObject::InsideFunction),            "function.around" => Some(TextObject::AroundFunction),            "class.inside" => Some(TextObject::InsideClass),            "class.around" => Some(TextObject::AroundClass),            "comment.inside" => Some(TextObject::InsideComment),            "comment.around" => Some(TextObject::AroundComment),            _ => None,        }    }     pub fn around(&self) -> Option<Self> {        match self {            TextObject::InsideFunction => Some(TextObject::AroundFunction),            TextObject::InsideClass => Some(TextObject::AroundClass),            TextObject::InsideComment => Some(TextObject::AroundComment),            _ => None,        }    }} pub struct TextObjectConfig {    pub query: Query,    pub text_objects_by_capture_ix: Vec<(u32, TextObject)>,} pub struct InjectionConfig {    pub query: Query,    pub content_capture_ix: u32,    pub language_capture_ix: Option<u32>,    pub patterns: Vec<InjectionPatternConfig>,} pub struct RedactionConfig {    pub query: Query,    pub redaction_capture_ix: u32,} #[derive(Clone, Debug, PartialEq)]pub enum RunnableCapture {    /// `@run` — marks the node that identifies the runnable (e.g. the test    /// function name, the subtest string literal).    Run,    /// `@run_item` — marks one candidate runnable inside a larger grammar    /// match. Used by the per-match dispatch pipeline so a single tree-sitter    /// match can produce multiple runnables (e.g. Go table-test rows).    RunItem,    /// Any other named capture, exposed by name to language resolvers and used    /// to populate `RunnableRange::extra_captures`.    Named(SharedString),} pub struct RunnableConfig {    pub query: Query,    /// A mapping from capture index to capture kind    pub extra_captures: Vec<RunnableCapture>,    /// `true` if the query uses `@run_item`, the marker for matches that    /// emit multiple runnables.    pub supports_grouped_runnables: bool,} pub struct OverrideConfig {    pub query: Query,    pub values: HashMap<u32, OverrideEntry>,} #[derive(Debug)]pub struct OverrideEntry {    pub name: String,    pub range_is_inclusive: bool,    pub value: LanguageConfigOverride,} #[derive(Default, Clone)]pub struct InjectionPatternConfig {    pub language: Option<Box<str>>,    pub combined: bool,} #[derive(Debug)]pub struct BracketsConfig {    pub query: Query,    pub open_capture_ix: u32,    pub close_capture_ix: u32,    pub patterns: Vec<BracketsPatternConfig>,} #[derive(Clone, Debug, Default)]pub struct BracketsPatternConfig {    pub newline_only: bool,    pub rainbow_exclude: bool,} pub struct DebugVariablesConfig {    pub query: Query,    pub objects_by_capture_ix: Vec<(u32, DebuggerTextObject)>,} enum Capture<'a> {    Required(&'static str, &'a mut u32),    Optional(&'static str, &'a mut Option<u32>),} fn populate_capture_indices(    query: &Query,    language_name: &LanguageName,    query_type: &str,    expected_prefixes: &[&str],    captures: &mut [Capture<'_>],) -> bool {    let mut found_required_indices = Vec::new();    'outer: for (ix, name) in query.capture_names().iter().enumerate() {        for (required_ix, capture) in captures.iter_mut().enumerate() {            match capture {                Capture::Required(capture_name, index) if capture_name == name => {                    **index = ix as u32;                    found_required_indices.push(required_ix);                    continue 'outer;                }                Capture::Optional(capture_name, index) if capture_name == name => {                    **index = Some(ix as u32);                    continue 'outer;                }                _ => {}            }        }        if !name.starts_with("_")            && !expected_prefixes                .iter()                .any(|&prefix| name.starts_with(prefix))        {            log::warn!(                "unrecognized capture name '{}' in {} {} TreeSitter query \                (suppress this warning by prefixing with '_')",                name,                language_name,                query_type            );        }    }    let mut missing_required_captures = Vec::new();    for (capture_ix, capture) in captures.iter().enumerate() {        if let Capture::Required(capture_name, _) = capture            && !found_required_indices.contains(&capture_ix)        {            missing_required_captures.push(*capture_name);        }    }    let success = missing_required_captures.is_empty();    if !success {        log::error!(            "missing required capture(s) in {} {} TreeSitter query: {}",            language_name,            query_type,            missing_required_captures.join(", ")        );    }    success} impl Grammar {    pub fn new(ts_language: tree_sitter::Language) -> Self {        Self {            id: GrammarId::new(),            highlights_config: None,            brackets_config: None,            outline_config: None,            text_object_config: None,            indents_config: None,            injection_config: None,            override_config: None,            redactions_config: None,            runnable_config: None,            error_query: Query::new(&ts_language, "(ERROR) @error").ok(),            debug_variables_config: None,            ts_language,            highlight_map: Default::default(),        }    }     pub fn id(&self) -> GrammarId {        self.id    }     pub fn highlight_map(&self) -> HighlightMap {        self.highlight_map.lock().clone()    }     pub fn highlight_id_for_name(&self, name: &str) -> Option<HighlightId> {        self.highlights_config            .as_ref()?            .query            .capture_index_for_name(name)            .and_then(|capture_id| self.highlight_map.lock().get(capture_id))    }     pub fn debug_variables_config(&self) -> Option<&DebugVariablesConfig> {        self.debug_variables_config.as_ref()    }     /// Load all queries from `LanguageQueries` into this grammar, mutating the    /// associated `LanguageConfig` (the override query clears    /// `brackets.disabled_scopes_by_bracket_ix`).    pub fn with_queries(        mut self,        queries: LanguageQueries,        config: &mut LanguageConfig,    ) -> Result<Self> {        let name = &config.name;        if let Some(query) = queries.highlights {            self = self                .with_highlights_query(query.as_ref())                .context("Error loading highlights query")?;        }        if let Some(query) = queries.brackets {            self = self                .with_brackets_query(query.as_ref(), name)                .context("Error loading brackets query")?;        }        if let Some(query) = queries.indents {            self = self                .with_indents_query(query.as_ref(), name)                .context("Error loading indents query")?;        }        if let Some(query) = queries.outline {            self = self                .with_outline_query(query.as_ref(), name)                .context("Error loading outline query")?;        }        if let Some(query) = queries.injections {            self = self                .with_injection_query(query.as_ref(), name)                .context("Error loading injection query")?;        }        if let Some(query) = queries.overrides {            self = self                .with_override_query(                    query.as_ref(),                    name,                    &config.overrides,                    &mut config.brackets,                    &config.scope_opt_in_language_servers,                )                .context("Error loading override query")?;        }        if let Some(query) = queries.redactions {            self = self                .with_redaction_query(query.as_ref(), name)                .context("Error loading redaction query")?;        }        if let Some(query) = queries.runnables {            self = self                .with_runnable_query(query.as_ref())                .context("Error loading runnables query")?;        }        if let Some(query) = queries.text_objects {            self = self                .with_text_object_query(query.as_ref(), name)                .context("Error loading textobject query")?;        }        if let Some(query) = queries.debugger {            self = self                .with_debug_variables_query(query.as_ref(), name)                .context("Error loading debug variables query")?;        }        Ok(self)    }     pub fn with_highlights_query(mut self, source: &str) -> Result<Self> {        let query = Query::new(&self.ts_language, source)?;         let mut identifier_capture_indices = Vec::new();        for name in [            "variable",            "constant",            "constructor",            "function",            "function.method",            "function.method.call",            "function.special",            "property",            "type",            "type.interface",        ] {            identifier_capture_indices.extend(query.capture_index_for_name(name));        }         self.highlights_config = Some(HighlightsConfig {            query,            identifier_capture_indices,        });         Ok(self)    }     pub fn with_runnable_query(mut self, source: &str) -> Result<Self> {        let query = Query::new(&self.ts_language, source)?;        let extra_captures: Vec<_> = query            .capture_names()            .iter()            .map(|&name| match name {                "run" => RunnableCapture::Run,                "run_item" => RunnableCapture::RunItem,                name => RunnableCapture::Named(name.to_string().into()),            })            .collect();        let supports_grouped_runnables = extra_captures            .iter()            .any(|capture| matches!(capture, RunnableCapture::RunItem));         self.runnable_config = Some(RunnableConfig {            extra_captures,            query,            supports_grouped_runnables,        });         Ok(self)    }     pub fn with_outline_query(        mut self,        source: &str,        language_name: &LanguageName,    ) -> Result<Self> {        let query = Query::new(&self.ts_language, source)?;        let mut item_capture_ix = 0;        let mut name_capture_ix = 0;        let mut context_capture_ix = None;        let mut extra_context_capture_ix = None;        let mut open_capture_ix = None;        let mut close_capture_ix = None;        let mut annotation_capture_ix = None;        if populate_capture_indices(            &query,            language_name,            "outline",            &[],            &mut [                Capture::Required("item", &mut item_capture_ix),                Capture::Required("name", &mut name_capture_ix),                Capture::Optional("context", &mut context_capture_ix),                Capture::Optional("context.extra", &mut extra_context_capture_ix),                Capture::Optional("open", &mut open_capture_ix),                Capture::Optional("close", &mut close_capture_ix),                Capture::Optional("annotation", &mut annotation_capture_ix),            ],        ) {            self.outline_config = Some(OutlineConfig {                query,                item_capture_ix,                name_capture_ix,                context_capture_ix,                extra_context_capture_ix,                open_capture_ix,                close_capture_ix,                annotation_capture_ix,            });        }        Ok(self)    }     pub fn with_text_object_query(        mut self,        source: &str,        language_name: &LanguageName,    ) -> Result<Self> {        let query = Query::new(&self.ts_language, source)?;         let mut text_objects_by_capture_ix = Vec::new();        for (ix, name) in query.capture_names().iter().enumerate() {            if let Some(text_object) = TextObject::from_capture_name(name) {                text_objects_by_capture_ix.push((ix as u32, text_object));            } else {                log::warn!(                    "unrecognized capture name '{}' in {} textobjects TreeSitter query",                    name,                    language_name,                );            }        }         self.text_object_config = Some(TextObjectConfig {            query,            text_objects_by_capture_ix,        });        Ok(self)    }     pub fn with_debug_variables_query(        mut self,        source: &str,        language_name: &LanguageName,    ) -> Result<Self> {        let query = Query::new(&self.ts_language, source)?;         let mut objects_by_capture_ix = Vec::new();        for (ix, name) in query.capture_names().iter().enumerate() {            if let Some(text_object) = DebuggerTextObject::from_capture_name(name) {                objects_by_capture_ix.push((ix as u32, text_object));            } else {                log::warn!(                    "unrecognized capture name '{}' in {} debugger TreeSitter query",

Lines 1–500 of 770.