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