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 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 let capture_id = self
279 .highlights_config
280 .as_ref()?
281 .query
282 .capture_index_for_name(name)?;
283 Some(self.highlight_map.lock().get(capture_id))
284 }
285
286 pub fn debug_variables_config(&self) -> Option<&DebugVariablesConfig> {
287 self.debug_variables_config.as_ref()
288 }
289
290 /// Load all queries from `LanguageQueries` into this grammar, mutating the
291 /// associated `LanguageConfig` (the override query clears
292 /// `brackets.disabled_scopes_by_bracket_ix`).
293 pub fn with_queries(
294 mut self,
295 queries: LanguageQueries,
296 config: &mut LanguageConfig,
297 ) -> Result<Self> {
298 let name = &config.name;
299 if let Some(query) = queries.highlights {
300 self = self
301 .with_highlights_query(query.as_ref())
302 .context("Error loading highlights query")?;
303 }
304 if let Some(query) = queries.brackets {
305 self = self
306 .with_brackets_query(query.as_ref(), name)
307 .context("Error loading brackets query")?;
308 }
309 if let Some(query) = queries.indents {
310 self = self
311 .with_indents_query(query.as_ref(), name)
312 .context("Error loading indents query")?;
313 }
314 if let Some(query) = queries.outline {
315 self = self
316 .with_outline_query(query.as_ref(), name)
317 .context("Error loading outline query")?;
318 }
319 if let Some(query) = queries.injections {
320 self = self
321 .with_injection_query(query.as_ref(), name)
322 .context("Error loading injection query")?;
323 }
324 if let Some(query) = queries.overrides {
325 self = self
326 .with_override_query(
327 query.as_ref(),
328 name,
329 &config.overrides,
330 &mut config.brackets,
331 &config.scope_opt_in_language_servers,
332 )
333 .context("Error loading override query")?;
334 }
335 if let Some(query) = queries.redactions {
336 self = self
337 .with_redaction_query(query.as_ref(), name)
338 .context("Error loading redaction query")?;
339 }
340 if let Some(query) = queries.runnables {
341 self = self
342 .with_runnable_query(query.as_ref())
343 .context("Error loading runnables query")?;
344 }
345 if let Some(query) = queries.text_objects {
346 self = self
347 .with_text_object_query(query.as_ref(), name)
348 .context("Error loading textobject query")?;
349 }
350 if let Some(query) = queries.debugger {
351 self = self
352 .with_debug_variables_query(query.as_ref(), name)
353 .context("Error loading debug variables query")?;
354 }
355 Ok(self)
356 }
357
358 pub fn with_highlights_query(mut self, source: &str) -> Result<Self> {
359 let query = Query::new(&self.ts_language, source)?;
360
361 let mut identifier_capture_indices = Vec::new();
362 for name in [
363 "variable",
364 "constant",
365 "constructor",
366 "function",
367 "function.method",
368 "function.method.call",
369 "function.special",
370 "property",
371 "type",
372 "type.interface",
373 ] {
374 identifier_capture_indices.extend(query.capture_index_for_name(name));
375 }
376
377 self.highlights_config = Some(HighlightsConfig {
378 query,
379 identifier_capture_indices,
380 });
381
382 Ok(self)
383 }
384
385 pub fn with_runnable_query(mut self, source: &str) -> Result<Self> {
386 let query = Query::new(&self.ts_language, source)?;
387 let extra_captures: Vec<_> = query
388 .capture_names()
389 .iter()
390 .map(|&name| match name {
391 "run" => RunnableCapture::Run,
392 name => RunnableCapture::Named(name.to_string().into()),
393 })
394 .collect();
395
396 self.runnable_config = Some(RunnableConfig {
397 extra_captures,
398 query,
399 });
400
401 Ok(self)
402 }
403
404 pub fn with_outline_query(
405 mut self,
406 source: &str,
407 language_name: &LanguageName,
408 ) -> Result<Self> {
409 let query = Query::new(&self.ts_language, source)?;
410 let mut item_capture_ix = 0;
411 let mut name_capture_ix = 0;
412 let mut context_capture_ix = None;
413 let mut extra_context_capture_ix = None;
414 let mut open_capture_ix = None;
415 let mut close_capture_ix = None;
416 let mut annotation_capture_ix = None;
417 if populate_capture_indices(
418 &query,
419 language_name,
420 "outline",
421 &[],
422 &mut [
423 Capture::Required("item", &mut item_capture_ix),
424 Capture::Required("name", &mut name_capture_ix),
425 Capture::Optional("context", &mut context_capture_ix),
426 Capture::Optional("context.extra", &mut extra_context_capture_ix),
427 Capture::Optional("open", &mut open_capture_ix),
428 Capture::Optional("close", &mut close_capture_ix),
429 Capture::Optional("annotation", &mut annotation_capture_ix),
430 ],
431 ) {
432 self.outline_config = Some(OutlineConfig {
433 query,
434 item_capture_ix,
435 name_capture_ix,
436 context_capture_ix,
437 extra_context_capture_ix,
438 open_capture_ix,
439 close_capture_ix,
440 annotation_capture_ix,
441 });
442 }
443 Ok(self)
444 }
445
446 pub fn with_text_object_query(
447 mut self,
448 source: &str,
449 language_name: &LanguageName,
450 ) -> Result<Self> {
451 let query = Query::new(&self.ts_language, source)?;
452
453 let mut text_objects_by_capture_ix = Vec::new();
454 for (ix, name) in query.capture_names().iter().enumerate() {
455 if let Some(text_object) = TextObject::from_capture_name(name) {
456 text_objects_by_capture_ix.push((ix as u32, text_object));
457 } else {
458 log::warn!(
459 "unrecognized capture name '{}' in {} textobjects TreeSitter query",
460 name,
461 language_name,
462 );
463 }
464 }
465
466 self.text_object_config = Some(TextObjectConfig {
467 query,
468 text_objects_by_capture_ix,
469 });
470 Ok(self)
471 }
472
473 pub fn with_debug_variables_query(
474 mut self,
475 source: &str,
476 language_name: &LanguageName,
477 ) -> Result<Self> {
478 let query = Query::new(&self.ts_language, source)?;
479
480 let mut objects_by_capture_ix = Vec::new();
481 for (ix, name) in query.capture_names().iter().enumerate() {
482 if let Some(text_object) = DebuggerTextObject::from_capture_name(name) {
483 objects_by_capture_ix.push((ix as u32, text_object));
484 } else {
485 log::warn!(
486 "unrecognized capture name '{}' in {} debugger TreeSitter query",
487 name,
488 language_name,
489 );
490 }
491 }
492
493 self.debug_variables_config = Some(DebugVariablesConfig {
494 query,
495 objects_by_capture_ix,
496 });
497 Ok(self)
498 }
499
500 pub fn with_brackets_query(
501 mut self,
502 source: &str,
503 language_name: &LanguageName,
504 ) -> Result<Self> {
505 let query = Query::new(&self.ts_language, source)?;
506 let mut open_capture_ix = 0;
507 let mut close_capture_ix = 0;
508 if populate_capture_indices(
509 &query,
510 language_name,
511 "brackets",
512 &[],
513 &mut [
514 Capture::Required("open", &mut open_capture_ix),
515 Capture::Required("close", &mut close_capture_ix),
516 ],
517 ) {
518 let patterns = (0..query.pattern_count())
519 .map(|ix| {
520 let mut config = BracketsPatternConfig::default();
521 for setting in query.property_settings(ix) {
522 let setting_key = setting.key.as_ref();
523 if setting_key == "newline.only" {
524 config.newline_only = true
525 }
526 if setting_key == "rainbow.exclude" {
527 config.rainbow_exclude = true
528 }
529 }
530 config
531 })
532 .collect();
533 self.brackets_config = Some(BracketsConfig {
534 query,
535 open_capture_ix,
536 close_capture_ix,
537 patterns,
538 });
539 }
540 Ok(self)
541 }
542
543 pub fn with_indents_query(
544 mut self,
545 source: &str,
546 language_name: &LanguageName,
547 ) -> Result<Self> {
548 let query = Query::new(&self.ts_language, source)?;
549 let mut indent_capture_ix = 0;
550 let mut start_capture_ix = None;
551 let mut end_capture_ix = None;
552 let mut outdent_capture_ix = None;
553 if populate_capture_indices(
554 &query,
555 language_name,
556 "indents",
557 &["start."],
558 &mut [
559 Capture::Required("indent", &mut indent_capture_ix),
560 Capture::Optional("start", &mut start_capture_ix),
561 Capture::Optional("end", &mut end_capture_ix),
562 Capture::Optional("outdent", &mut outdent_capture_ix),
563 ],
564 ) {
565 let mut suffixed_start_captures = HashMap::default();
566 for (ix, name) in query.capture_names().iter().enumerate() {
567 if let Some(suffix) = name.strip_prefix("start.") {
568 suffixed_start_captures.insert(ix as u32, suffix.to_owned().into());
569 }
570 }
571
572 self.indents_config = Some(IndentConfig {
573 query,
574 indent_capture_ix,
575 start_capture_ix,
576 end_capture_ix,
577 outdent_capture_ix,
578 suffixed_start_captures,
579 });
580 }
581 Ok(self)
582 }
583
584 pub fn with_injection_query(
585 mut self,
586 source: &str,
587 language_name: &LanguageName,
588 ) -> Result<Self> {
589 let query = Query::new(&self.ts_language, source)?;
590 let mut language_capture_ix = None;
591 let mut injection_language_capture_ix = None;
592 let mut content_capture_ix = None;
593 let mut injection_content_capture_ix = None;
594 if populate_capture_indices(
595 &query,
596 language_name,
597 "injections",
598 &[],
599 &mut [
600 Capture::Optional("language", &mut language_capture_ix),
601 Capture::Optional("injection.language", &mut injection_language_capture_ix),
602 Capture::Optional("content", &mut content_capture_ix),
603 Capture::Optional("injection.content", &mut injection_content_capture_ix),
604 ],
605 ) {
606 language_capture_ix = match (language_capture_ix, injection_language_capture_ix) {
607 (None, Some(ix)) => Some(ix),
608 (Some(_), Some(_)) => {
609 anyhow::bail!("both language and injection.language captures are present");
610 }
611 _ => language_capture_ix,
612 };
613 content_capture_ix = match (content_capture_ix, injection_content_capture_ix) {
614 (None, Some(ix)) => Some(ix),
615 (Some(_), Some(_)) => {
616 anyhow::bail!("both content and injection.content captures are present")
617 }
618 _ => content_capture_ix,
619 };
620 let patterns = (0..query.pattern_count())
621 .map(|ix| {
622 let mut config = InjectionPatternConfig::default();
623 for setting in query.property_settings(ix) {
624 match setting.key.as_ref() {
625 "language" | "injection.language" => {
626 config.language.clone_from(&setting.value);
627 }
628 "combined" | "injection.combined" => {
629 config.combined = true;
630 }
631 _ => {}
632 }
633 }
634 config
635 })
636 .collect();
637 if let Some(content_capture_ix) = content_capture_ix {
638 self.injection_config = Some(InjectionConfig {
639 query,
640 language_capture_ix,
641 content_capture_ix,
642 patterns,
643 });
644 } else {
645 log::error!(
646 "missing required capture in injections {} TreeSitter query: \
647 content or injection.content",
648 language_name,
649 );
650 }
651 }
652 Ok(self)
653 }
654
655 pub fn with_override_query(
656 mut self,
657 source: &str,
658 language_name: &LanguageName,
659 overrides: &HashMap<String, LanguageConfigOverride>,
660 brackets: &mut BracketPairConfig,
661 scope_opt_in_language_servers: &[LanguageServerName],
662 ) -> Result<Self> {
663 let query = Query::new(&self.ts_language, source)?;
664
665 let mut override_configs_by_id = HashMap::default();
666 for (ix, mut name) in query.capture_names().iter().copied().enumerate() {
667 let mut range_is_inclusive = false;
668 if name.starts_with('_') {
669 continue;
670 }
671 if let Some(prefix) = name.strip_suffix(".inclusive") {
672 name = prefix;
673 range_is_inclusive = true;
674 }
675
676 let value = overrides.get(name).cloned().unwrap_or_default();
677 for server_name in &value.opt_into_language_servers {
678 if !scope_opt_in_language_servers.contains(server_name) {
679 util::debug_panic!(
680 "Server {server_name:?} has been opted-in by scope {name:?} but has not been marked as an opt-in server"
681 );
682 }
683 }
684
685 override_configs_by_id.insert(
686 ix as u32,
687 OverrideEntry {
688 name: name.to_string(),
689 range_is_inclusive,
690 value,
691 },
692 );
693 }
694
695 let referenced_override_names = overrides
696 .keys()
697 .chain(brackets.disabled_scopes_by_bracket_ix.iter().flatten());
698
699 for referenced_name in referenced_override_names {
700 if !override_configs_by_id
701 .values()
702 .any(|entry| entry.name == *referenced_name)
703 {
704 anyhow::bail!(
705 "language {:?} has overrides in config not in query: {referenced_name:?}",
706 language_name
707 );
708 }
709 }
710
711 for entry in override_configs_by_id.values_mut() {
712 entry.value.disabled_bracket_ixs = brackets
713 .disabled_scopes_by_bracket_ix
714 .iter()
715 .enumerate()
716 .filter_map(|(ix, disabled_scope_names)| {
717 if disabled_scope_names.contains(&entry.name) {
718 Some(ix as u16)
719 } else {
720 None
721 }
722 })
723 .collect();
724 }
725
726 brackets.disabled_scopes_by_bracket_ix.clear();
727
728 self.override_config = Some(OverrideConfig {
729 query,
730 values: override_configs_by_id,
731 });
732 Ok(self)
733 }
734
735 pub fn with_redaction_query(
736 mut self,
737 source: &str,
738 language_name: &LanguageName,
739 ) -> Result<Self> {
740 let query = Query::new(&self.ts_language, source)?;
741 let mut redaction_capture_ix = 0;
742 if populate_capture_indices(
743 &query,
744 language_name,
745 "redactions",
746 &[],
747 &mut [Capture::Required("redact", &mut redaction_capture_ix)],
748 ) {
749 self.redactions_config = Some(RedactionConfig {
750 query,
751 redaction_capture_ix,
752 });
753 }
754 Ok(self)
755 }
756}