grammar.rs

  1use crate::{
  2    HighlightId, HighlightMap, LanguageConfig, LanguageConfigOverride, LanguageName,
  3    LanguageQueries, language_config::BracketPairConfig,
  4};
  5use anyhow::{Context as _, Result};
  6use collections::HashMap;
  7use gpui_shared_string::SharedString;
  8use lsp::LanguageServerName;
  9use parking_lot::Mutex;
 10use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
 11use tree_sitter::Query;
 12
 13pub static NEXT_GRAMMAR_ID: AtomicUsize = AtomicUsize::new(0);
 14
 15#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
 16pub struct GrammarId(pub usize);
 17
 18impl GrammarId {
 19    pub fn new() -> Self {
 20        Self(NEXT_GRAMMAR_ID.fetch_add(1, SeqCst))
 21    }
 22}
 23
 24impl Default for GrammarId {
 25    fn default() -> Self {
 26        Self::new()
 27    }
 28}
 29
 30pub struct Grammar {
 31    id: GrammarId,
 32    pub ts_language: tree_sitter::Language,
 33    pub error_query: Option<Query>,
 34    pub highlights_config: Option<HighlightsConfig>,
 35    pub brackets_config: Option<BracketsConfig>,
 36    pub redactions_config: Option<RedactionConfig>,
 37    pub runnable_config: Option<RunnableConfig>,
 38    pub indents_config: Option<IndentConfig>,
 39    pub outline_config: Option<OutlineConfig>,
 40    pub text_object_config: Option<TextObjectConfig>,
 41    pub injection_config: Option<InjectionConfig>,
 42    pub override_config: Option<OverrideConfig>,
 43    pub debug_variables_config: Option<DebugVariablesConfig>,
 44    pub highlight_map: Mutex<HighlightMap>,
 45}
 46
 47pub struct HighlightsConfig {
 48    pub query: Query,
 49    pub identifier_capture_indices: Vec<u32>,
 50}
 51
 52pub struct IndentConfig {
 53    pub query: Query,
 54    pub indent_capture_ix: u32,
 55    pub start_capture_ix: Option<u32>,
 56    pub end_capture_ix: Option<u32>,
 57    pub outdent_capture_ix: Option<u32>,
 58    pub suffixed_start_captures: HashMap<u32, SharedString>,
 59}
 60
 61pub struct OutlineConfig {
 62    pub query: Query,
 63    pub item_capture_ix: u32,
 64    pub name_capture_ix: u32,
 65    pub context_capture_ix: Option<u32>,
 66    pub extra_context_capture_ix: Option<u32>,
 67    pub open_capture_ix: Option<u32>,
 68    pub close_capture_ix: Option<u32>,
 69    pub annotation_capture_ix: Option<u32>,
 70}
 71
 72#[derive(Debug, Clone, Copy, PartialEq)]
 73pub enum DebuggerTextObject {
 74    Variable,
 75    Scope,
 76}
 77
 78impl DebuggerTextObject {
 79    pub fn from_capture_name(name: &str) -> Option<DebuggerTextObject> {
 80        match name {
 81            "debug-variable" => Some(DebuggerTextObject::Variable),
 82            "debug-scope" => Some(DebuggerTextObject::Scope),
 83            _ => None,
 84        }
 85    }
 86}
 87
 88#[derive(Debug, Clone, Copy, PartialEq)]
 89pub enum TextObject {
 90    InsideFunction,
 91    AroundFunction,
 92    InsideClass,
 93    AroundClass,
 94    InsideComment,
 95    AroundComment,
 96}
 97
 98impl TextObject {
 99    pub fn from_capture_name(name: &str) -> Option<TextObject> {
100        match name {
101            "function.inside" => Some(TextObject::InsideFunction),
102            "function.around" => Some(TextObject::AroundFunction),
103            "class.inside" => Some(TextObject::InsideClass),
104            "class.around" => Some(TextObject::AroundClass),
105            "comment.inside" => Some(TextObject::InsideComment),
106            "comment.around" => Some(TextObject::AroundComment),
107            _ => None,
108        }
109    }
110
111    pub fn around(&self) -> Option<Self> {
112        match self {
113            TextObject::InsideFunction => Some(TextObject::AroundFunction),
114            TextObject::InsideClass => Some(TextObject::AroundClass),
115            TextObject::InsideComment => Some(TextObject::AroundComment),
116            _ => None,
117        }
118    }
119}
120
121pub struct TextObjectConfig {
122    pub query: Query,
123    pub text_objects_by_capture_ix: Vec<(u32, TextObject)>,
124}
125
126pub struct InjectionConfig {
127    pub query: Query,
128    pub content_capture_ix: u32,
129    pub language_capture_ix: Option<u32>,
130    pub patterns: Vec<InjectionPatternConfig>,
131}
132
133pub struct RedactionConfig {
134    pub query: Query,
135    pub redaction_capture_ix: u32,
136}
137
138#[derive(Clone, Debug, PartialEq)]
139pub enum RunnableCapture {
140    Named(SharedString),
141    Run,
142}
143
144pub struct RunnableConfig {
145    pub query: Query,
146    /// A mapping from capture index to capture kind
147    pub extra_captures: Vec<RunnableCapture>,
148}
149
150pub struct OverrideConfig {
151    pub query: Query,
152    pub values: HashMap<u32, OverrideEntry>,
153}
154
155#[derive(Debug)]
156pub struct OverrideEntry {
157    pub name: String,
158    pub range_is_inclusive: bool,
159    pub value: LanguageConfigOverride,
160}
161
162#[derive(Default, Clone)]
163pub struct InjectionPatternConfig {
164    pub language: Option<Box<str>>,
165    pub combined: bool,
166}
167
168#[derive(Debug)]
169pub struct BracketsConfig {
170    pub query: Query,
171    pub open_capture_ix: u32,
172    pub close_capture_ix: u32,
173    pub patterns: Vec<BracketsPatternConfig>,
174}
175
176#[derive(Clone, Debug, Default)]
177pub struct BracketsPatternConfig {
178    pub newline_only: bool,
179    pub rainbow_exclude: bool,
180}
181
182pub struct DebugVariablesConfig {
183    pub query: Query,
184    pub objects_by_capture_ix: Vec<(u32, DebuggerTextObject)>,
185}
186
187enum Capture<'a> {
188    Required(&'static str, &'a mut u32),
189    Optional(&'static str, &'a mut Option<u32>),
190}
191
192fn populate_capture_indices(
193    query: &Query,
194    language_name: &LanguageName,
195    query_type: &str,
196    expected_prefixes: &[&str],
197    captures: &mut [Capture<'_>],
198) -> bool {
199    let mut found_required_indices = Vec::new();
200    'outer: for (ix, name) in query.capture_names().iter().enumerate() {
201        for (required_ix, capture) in captures.iter_mut().enumerate() {
202            match capture {
203                Capture::Required(capture_name, index) if capture_name == name => {
204                    **index = ix as u32;
205                    found_required_indices.push(required_ix);
206                    continue 'outer;
207                }
208                Capture::Optional(capture_name, index) if capture_name == name => {
209                    **index = Some(ix as u32);
210                    continue 'outer;
211                }
212                _ => {}
213            }
214        }
215        if !name.starts_with("_")
216            && !expected_prefixes
217                .iter()
218                .any(|&prefix| name.starts_with(prefix))
219        {
220            log::warn!(
221                "unrecognized capture name '{}' in {} {} TreeSitter query \
222                (suppress this warning by prefixing with '_')",
223                name,
224                language_name,
225                query_type
226            );
227        }
228    }
229    let mut missing_required_captures = Vec::new();
230    for (capture_ix, capture) in captures.iter().enumerate() {
231        if let Capture::Required(capture_name, _) = capture
232            && !found_required_indices.contains(&capture_ix)
233        {
234            missing_required_captures.push(*capture_name);
235        }
236    }
237    let success = missing_required_captures.is_empty();
238    if !success {
239        log::error!(
240            "missing required capture(s) in {} {} TreeSitter query: {}",
241            language_name,
242            query_type,
243            missing_required_captures.join(", ")
244        );
245    }
246    success
247}
248
249impl Grammar {
250    pub fn new(ts_language: tree_sitter::Language) -> Self {
251        Self {
252            id: GrammarId::new(),
253            highlights_config: None,
254            brackets_config: None,
255            outline_config: None,
256            text_object_config: None,
257            indents_config: None,
258            injection_config: None,
259            override_config: None,
260            redactions_config: None,
261            runnable_config: None,
262            error_query: Query::new(&ts_language, "(ERROR) @error").ok(),
263            debug_variables_config: None,
264            ts_language,
265            highlight_map: Default::default(),
266        }
267    }
268
269    pub fn id(&self) -> GrammarId {
270        self.id
271    }
272
273    pub fn highlight_map(&self) -> HighlightMap {
274        self.highlight_map.lock().clone()
275    }
276
277    pub fn highlight_id_for_name(&self, name: &str) -> Option<HighlightId> {
278        self.highlights_config
279            .as_ref()?
280            .query
281            .capture_index_for_name(name)
282            .and_then(|capture_id| self.highlight_map.lock().get(capture_id))
283    }
284
285    pub fn debug_variables_config(&self) -> Option<&DebugVariablesConfig> {
286        self.debug_variables_config.as_ref()
287    }
288
289    /// Load all queries from `LanguageQueries` into this grammar, mutating the
290    /// associated `LanguageConfig` (the override query clears
291    /// `brackets.disabled_scopes_by_bracket_ix`).
292    pub fn with_queries(
293        mut self,
294        queries: LanguageQueries,
295        config: &mut LanguageConfig,
296    ) -> Result<Self> {
297        let name = &config.name;
298        if let Some(query) = queries.highlights {
299            self = self
300                .with_highlights_query(query.as_ref())
301                .context("Error loading highlights query")?;
302        }
303        if let Some(query) = queries.brackets {
304            self = self
305                .with_brackets_query(query.as_ref(), name)
306                .context("Error loading brackets query")?;
307        }
308        if let Some(query) = queries.indents {
309            self = self
310                .with_indents_query(query.as_ref(), name)
311                .context("Error loading indents query")?;
312        }
313        if let Some(query) = queries.outline {
314            self = self
315                .with_outline_query(query.as_ref(), name)
316                .context("Error loading outline query")?;
317        }
318        if let Some(query) = queries.injections {
319            self = self
320                .with_injection_query(query.as_ref(), name)
321                .context("Error loading injection query")?;
322        }
323        if let Some(query) = queries.overrides {
324            self = self
325                .with_override_query(
326                    query.as_ref(),
327                    name,
328                    &config.overrides,
329                    &mut config.brackets,
330                    &config.scope_opt_in_language_servers,
331                )
332                .context("Error loading override query")?;
333        }
334        if let Some(query) = queries.redactions {
335            self = self
336                .with_redaction_query(query.as_ref(), name)
337                .context("Error loading redaction query")?;
338        }
339        if let Some(query) = queries.runnables {
340            self = self
341                .with_runnable_query(query.as_ref())
342                .context("Error loading runnables query")?;
343        }
344        if let Some(query) = queries.text_objects {
345            self = self
346                .with_text_object_query(query.as_ref(), name)
347                .context("Error loading textobject query")?;
348        }
349        if let Some(query) = queries.debugger {
350            self = self
351                .with_debug_variables_query(query.as_ref(), name)
352                .context("Error loading debug variables query")?;
353        }
354        Ok(self)
355    }
356
357    pub fn with_highlights_query(mut self, source: &str) -> Result<Self> {
358        let query = Query::new(&self.ts_language, source)?;
359
360        let mut identifier_capture_indices = Vec::new();
361        for name in [
362            "variable",
363            "constant",
364            "constructor",
365            "function",
366            "function.method",
367            "function.method.call",
368            "function.special",
369            "property",
370            "type",
371            "type.interface",
372        ] {
373            identifier_capture_indices.extend(query.capture_index_for_name(name));
374        }
375
376        self.highlights_config = Some(HighlightsConfig {
377            query,
378            identifier_capture_indices,
379        });
380
381        Ok(self)
382    }
383
384    pub fn with_runnable_query(mut self, source: &str) -> Result<Self> {
385        let query = Query::new(&self.ts_language, source)?;
386        let extra_captures: Vec<_> = query
387            .capture_names()
388            .iter()
389            .map(|&name| match name {
390                "run" => RunnableCapture::Run,
391                name => RunnableCapture::Named(name.to_string().into()),
392            })
393            .collect();
394
395        self.runnable_config = Some(RunnableConfig {
396            extra_captures,
397            query,
398        });
399
400        Ok(self)
401    }
402
403    pub fn with_outline_query(
404        mut self,
405        source: &str,
406        language_name: &LanguageName,
407    ) -> Result<Self> {
408        let query = Query::new(&self.ts_language, source)?;
409        let mut item_capture_ix = 0;
410        let mut name_capture_ix = 0;
411        let mut context_capture_ix = None;
412        let mut extra_context_capture_ix = None;
413        let mut open_capture_ix = None;
414        let mut close_capture_ix = None;
415        let mut annotation_capture_ix = None;
416        if populate_capture_indices(
417            &query,
418            language_name,
419            "outline",
420            &[],
421            &mut [
422                Capture::Required("item", &mut item_capture_ix),
423                Capture::Required("name", &mut name_capture_ix),
424                Capture::Optional("context", &mut context_capture_ix),
425                Capture::Optional("context.extra", &mut extra_context_capture_ix),
426                Capture::Optional("open", &mut open_capture_ix),
427                Capture::Optional("close", &mut close_capture_ix),
428                Capture::Optional("annotation", &mut annotation_capture_ix),
429            ],
430        ) {
431            self.outline_config = Some(OutlineConfig {
432                query,
433                item_capture_ix,
434                name_capture_ix,
435                context_capture_ix,
436                extra_context_capture_ix,
437                open_capture_ix,
438                close_capture_ix,
439                annotation_capture_ix,
440            });
441        }
442        Ok(self)
443    }
444
445    pub fn with_text_object_query(
446        mut self,
447        source: &str,
448        language_name: &LanguageName,
449    ) -> Result<Self> {
450        let query = Query::new(&self.ts_language, source)?;
451
452        let mut text_objects_by_capture_ix = Vec::new();
453        for (ix, name) in query.capture_names().iter().enumerate() {
454            if let Some(text_object) = TextObject::from_capture_name(name) {
455                text_objects_by_capture_ix.push((ix as u32, text_object));
456            } else {
457                log::warn!(
458                    "unrecognized capture name '{}' in {} textobjects TreeSitter query",
459                    name,
460                    language_name,
461                );
462            }
463        }
464
465        self.text_object_config = Some(TextObjectConfig {
466            query,
467            text_objects_by_capture_ix,
468        });
469        Ok(self)
470    }
471
472    pub fn with_debug_variables_query(
473        mut self,
474        source: &str,
475        language_name: &LanguageName,
476    ) -> Result<Self> {
477        let query = Query::new(&self.ts_language, source)?;
478
479        let mut objects_by_capture_ix = Vec::new();
480        for (ix, name) in query.capture_names().iter().enumerate() {
481            if let Some(text_object) = DebuggerTextObject::from_capture_name(name) {
482                objects_by_capture_ix.push((ix as u32, text_object));
483            } else {
484                log::warn!(
485                    "unrecognized capture name '{}' in {} debugger TreeSitter query",
486                    name,
487                    language_name,
488                );
489            }
490        }
491
492        self.debug_variables_config = Some(DebugVariablesConfig {
493            query,
494            objects_by_capture_ix,
495        });
496        Ok(self)
497    }
498
499    pub fn with_brackets_query(
500        mut self,
501        source: &str,
502        language_name: &LanguageName,
503    ) -> Result<Self> {
504        let query = Query::new(&self.ts_language, source)?;
505        let mut open_capture_ix = 0;
506        let mut close_capture_ix = 0;
507        if populate_capture_indices(
508            &query,
509            language_name,
510            "brackets",
511            &[],
512            &mut [
513                Capture::Required("open", &mut open_capture_ix),
514                Capture::Required("close", &mut close_capture_ix),
515            ],
516        ) {
517            let patterns = (0..query.pattern_count())
518                .map(|ix| {
519                    let mut config = BracketsPatternConfig::default();
520                    for setting in query.property_settings(ix) {
521                        let setting_key = setting.key.as_ref();
522                        if setting_key == "newline.only" {
523                            config.newline_only = true
524                        }
525                        if setting_key == "rainbow.exclude" {
526                            config.rainbow_exclude = true
527                        }
528                    }
529                    config
530                })
531                .collect();
532            self.brackets_config = Some(BracketsConfig {
533                query,
534                open_capture_ix,
535                close_capture_ix,
536                patterns,
537            });
538        }
539        Ok(self)
540    }
541
542    pub fn with_indents_query(
543        mut self,
544        source: &str,
545        language_name: &LanguageName,
546    ) -> Result<Self> {
547        let query = Query::new(&self.ts_language, source)?;
548        let mut indent_capture_ix = 0;
549        let mut start_capture_ix = None;
550        let mut end_capture_ix = None;
551        let mut outdent_capture_ix = None;
552        if populate_capture_indices(
553            &query,
554            language_name,
555            "indents",
556            &["start."],
557            &mut [
558                Capture::Required("indent", &mut indent_capture_ix),
559                Capture::Optional("start", &mut start_capture_ix),
560                Capture::Optional("end", &mut end_capture_ix),
561                Capture::Optional("outdent", &mut outdent_capture_ix),
562            ],
563        ) {
564            let mut suffixed_start_captures = HashMap::default();
565            for (ix, name) in query.capture_names().iter().enumerate() {
566                if let Some(suffix) = name.strip_prefix("start.") {
567                    suffixed_start_captures.insert(ix as u32, suffix.to_owned().into());
568                }
569            }
570
571            self.indents_config = Some(IndentConfig {
572                query,
573                indent_capture_ix,
574                start_capture_ix,
575                end_capture_ix,
576                outdent_capture_ix,
577                suffixed_start_captures,
578            });
579        }
580        Ok(self)
581    }
582
583    pub fn with_injection_query(
584        mut self,
585        source: &str,
586        language_name: &LanguageName,
587    ) -> Result<Self> {
588        let query = Query::new(&self.ts_language, source)?;
589        let mut language_capture_ix = None;
590        let mut injection_language_capture_ix = None;
591        let mut content_capture_ix = None;
592        let mut injection_content_capture_ix = None;
593        if populate_capture_indices(
594            &query,
595            language_name,
596            "injections",
597            &[],
598            &mut [
599                Capture::Optional("language", &mut language_capture_ix),
600                Capture::Optional("injection.language", &mut injection_language_capture_ix),
601                Capture::Optional("content", &mut content_capture_ix),
602                Capture::Optional("injection.content", &mut injection_content_capture_ix),
603            ],
604        ) {
605            language_capture_ix = match (language_capture_ix, injection_language_capture_ix) {
606                (None, Some(ix)) => Some(ix),
607                (Some(_), Some(_)) => {
608                    anyhow::bail!("both language and injection.language captures are present");
609                }
610                _ => language_capture_ix,
611            };
612            content_capture_ix = match (content_capture_ix, injection_content_capture_ix) {
613                (None, Some(ix)) => Some(ix),
614                (Some(_), Some(_)) => {
615                    anyhow::bail!("both content and injection.content captures are present")
616                }
617                _ => content_capture_ix,
618            };
619            let patterns = (0..query.pattern_count())
620                .map(|ix| {
621                    let mut config = InjectionPatternConfig::default();
622                    for setting in query.property_settings(ix) {
623                        match setting.key.as_ref() {
624                            "language" | "injection.language" => {
625                                config.language.clone_from(&setting.value);
626                            }
627                            "combined" | "injection.combined" => {
628                                config.combined = true;
629                            }
630                            _ => {}
631                        }
632                    }
633                    config
634                })
635                .collect();
636            if let Some(content_capture_ix) = content_capture_ix {
637                self.injection_config = Some(InjectionConfig {
638                    query,
639                    language_capture_ix,
640                    content_capture_ix,
641                    patterns,
642                });
643            } else {
644                log::error!(
645                    "missing required capture in injections {} TreeSitter query: \
646                    content or injection.content",
647                    language_name,
648                );
649            }
650        }
651        Ok(self)
652    }
653
654    pub fn with_override_query(
655        mut self,
656        source: &str,
657        language_name: &LanguageName,
658        overrides: &HashMap<String, LanguageConfigOverride>,
659        brackets: &mut BracketPairConfig,
660        scope_opt_in_language_servers: &[LanguageServerName],
661    ) -> Result<Self> {
662        let query = Query::new(&self.ts_language, source)?;
663
664        let mut override_configs_by_id = HashMap::default();
665        for (ix, mut name) in query.capture_names().iter().copied().enumerate() {
666            let mut range_is_inclusive = false;
667            if name.starts_with('_') {
668                continue;
669            }
670            if let Some(prefix) = name.strip_suffix(".inclusive") {
671                name = prefix;
672                range_is_inclusive = true;
673            }
674
675            let value = overrides.get(name).cloned().unwrap_or_default();
676            for server_name in &value.opt_into_language_servers {
677                if !scope_opt_in_language_servers.contains(server_name) {
678                    util::debug_panic!(
679                        "Server {server_name:?} has been opted-in by scope {name:?} but has not been marked as an opt-in server"
680                    );
681                }
682            }
683
684            override_configs_by_id.insert(
685                ix as u32,
686                OverrideEntry {
687                    name: name.to_string(),
688                    range_is_inclusive,
689                    value,
690                },
691            );
692        }
693
694        let referenced_override_names = overrides
695            .keys()
696            .chain(brackets.disabled_scopes_by_bracket_ix.iter().flatten());
697
698        for referenced_name in referenced_override_names {
699            if !override_configs_by_id
700                .values()
701                .any(|entry| entry.name == *referenced_name)
702            {
703                anyhow::bail!(
704                    "language {:?} has overrides in config not in query: {referenced_name:?}",
705                    language_name
706                );
707            }
708        }
709
710        for entry in override_configs_by_id.values_mut() {
711            entry.value.disabled_bracket_ixs = brackets
712                .disabled_scopes_by_bracket_ix
713                .iter()
714                .enumerate()
715                .filter_map(|(ix, disabled_scope_names)| {
716                    if disabled_scope_names.contains(&entry.name) {
717                        Some(ix as u16)
718                    } else {
719                        None
720                    }
721                })
722                .collect();
723        }
724
725        brackets.disabled_scopes_by_bracket_ix.clear();
726
727        self.override_config = Some(OverrideConfig {
728            query,
729            values: override_configs_by_id,
730        });
731        Ok(self)
732    }
733
734    pub fn with_redaction_query(
735        mut self,
736        source: &str,
737        language_name: &LanguageName,
738    ) -> Result<Self> {
739        let query = Query::new(&self.ts_language, source)?;
740        let mut redaction_capture_ix = 0;
741        if populate_capture_indices(
742            &query,
743            language_name,
744            "redactions",
745            &[],
746            &mut [Capture::Required("redact", &mut redaction_capture_ix)],
747        ) {
748            self.redactions_config = Some(RedactionConfig {
749                query,
750                redaction_capture_ix,
751            });
752        }
753        Ok(self)
754    }
755}