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}