debugger: Generate inline values based on debugger.scm file (#33081)
## Context To support inline values a language will have to implement their own provider trait that walks through tree sitter nodes. This is overly complicated, hard to accurately implement for each language, and lacks proper extension support. This PR switches to a singular inline provider that uses a language's `debugger.scm` query field to capture variables and scopes. The inline provider is able to use this information to generate inlays that take scope into account and work with any language that defines a debugger query file. ### Todos - [x] Implement a utility test function to easily test inline values - [x] Generate inline values based on captures - [x] Reimplement Python, Rust, and Go support - [x] Take scope into account when iterating through variable captures - [x] Add tests for Go inline values - [x] Remove old inline provider code and trait implementations Release Notes: - debugger: Generate inline values based on a language debugger.scm file
This commit is contained in:
parent
800b925fd7
commit
fc1fc264ec
17 changed files with 786 additions and 751 deletions
|
@ -20,6 +20,7 @@ test-support = [
|
|||
"text/test-support",
|
||||
"tree-sitter-rust",
|
||||
"tree-sitter-python",
|
||||
"tree-sitter-rust",
|
||||
"tree-sitter-typescript",
|
||||
"settings/test-support",
|
||||
"util/test-support",
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
pub use crate::{
|
||||
Grammar, Language, LanguageRegistry,
|
||||
diagnostic_set::DiagnosticSet,
|
||||
highlight_map::{HighlightId, HighlightMap},
|
||||
proto,
|
||||
};
|
||||
use crate::{
|
||||
LanguageScope, Outline, OutlineConfig, RunnableCapture, RunnableTag, TextObject,
|
||||
TreeSitterOptions,
|
||||
DebuggerTextObject, LanguageScope, Outline, OutlineConfig, RunnableCapture, RunnableTag,
|
||||
TextObject, TreeSitterOptions,
|
||||
diagnostic_set::{DiagnosticEntry, DiagnosticGroup},
|
||||
language_settings::{LanguageSettings, language_settings},
|
||||
outline::OutlineItem,
|
||||
|
@ -17,6 +11,12 @@ use crate::{
|
|||
task_context::RunnableRange,
|
||||
text_diff::text_diff,
|
||||
};
|
||||
pub use crate::{
|
||||
Grammar, Language, LanguageRegistry,
|
||||
diagnostic_set::DiagnosticSet,
|
||||
highlight_map::{HighlightId, HighlightMap},
|
||||
proto,
|
||||
};
|
||||
use anyhow::{Context as _, Result};
|
||||
pub use clock::ReplicaId;
|
||||
use clock::{AGENT_REPLICA_ID, Lamport};
|
||||
|
@ -3848,6 +3848,74 @@ impl BufferSnapshot {
|
|||
.filter(|pair| !pair.newline_only)
|
||||
}
|
||||
|
||||
pub fn debug_variables_query<T: ToOffset>(
|
||||
&self,
|
||||
range: Range<T>,
|
||||
) -> impl Iterator<Item = (Range<usize>, DebuggerTextObject)> + '_ {
|
||||
let range = range.start.to_offset(self).saturating_sub(1)
|
||||
..self.len().min(range.end.to_offset(self) + 1);
|
||||
|
||||
let mut matches = self.syntax.matches_with_options(
|
||||
range.clone(),
|
||||
&self.text,
|
||||
TreeSitterOptions::default(),
|
||||
|grammar| grammar.debug_variables_config.as_ref().map(|c| &c.query),
|
||||
);
|
||||
|
||||
let configs = matches
|
||||
.grammars()
|
||||
.iter()
|
||||
.map(|grammar| grammar.debug_variables_config.as_ref())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut captures = Vec::<(Range<usize>, DebuggerTextObject)>::new();
|
||||
|
||||
iter::from_fn(move || {
|
||||
loop {
|
||||
while let Some(capture) = captures.pop() {
|
||||
if capture.0.overlaps(&range) {
|
||||
return Some(capture);
|
||||
}
|
||||
}
|
||||
|
||||
let mat = matches.peek()?;
|
||||
|
||||
let Some(config) = configs[mat.grammar_index].as_ref() else {
|
||||
matches.advance();
|
||||
continue;
|
||||
};
|
||||
|
||||
for capture in mat.captures {
|
||||
let Some(ix) = config
|
||||
.objects_by_capture_ix
|
||||
.binary_search_by_key(&capture.index, |e| e.0)
|
||||
.ok()
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let text_object = config.objects_by_capture_ix[ix].1;
|
||||
let byte_range = capture.node.byte_range();
|
||||
|
||||
let mut found = false;
|
||||
for (range, existing) in captures.iter_mut() {
|
||||
if existing == &text_object {
|
||||
range.start = range.start.min(byte_range.start);
|
||||
range.end = range.end.max(byte_range.end);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
captures.push((byte_range, text_object));
|
||||
}
|
||||
}
|
||||
|
||||
matches.advance();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn text_object_ranges<T: ToOffset>(
|
||||
&self,
|
||||
range: Range<T>,
|
||||
|
|
|
@ -1082,6 +1082,7 @@ pub struct Grammar {
|
|||
pub embedding_config: Option<EmbeddingConfig>,
|
||||
pub(crate) injection_config: Option<InjectionConfig>,
|
||||
pub(crate) override_config: Option<OverrideConfig>,
|
||||
pub(crate) debug_variables_config: Option<DebugVariablesConfig>,
|
||||
pub(crate) highlight_map: Mutex<HighlightMap>,
|
||||
}
|
||||
|
||||
|
@ -1104,6 +1105,22 @@ pub struct OutlineConfig {
|
|||
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,
|
||||
|
@ -1206,6 +1223,11 @@ struct BracketsPatternConfig {
|
|||
newline_only: bool,
|
||||
}
|
||||
|
||||
pub struct DebugVariablesConfig {
|
||||
pub query: Query,
|
||||
pub objects_by_capture_ix: Vec<(u32, DebuggerTextObject)>,
|
||||
}
|
||||
|
||||
impl Language {
|
||||
pub fn new(config: LanguageConfig, ts_language: Option<tree_sitter::Language>) -> Self {
|
||||
Self::new_with_id(LanguageId::new(), config, ts_language)
|
||||
|
@ -1237,6 +1259,7 @@ impl Language {
|
|||
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(),
|
||||
})
|
||||
|
@ -1307,6 +1330,11 @@ impl Language {
|
|||
.with_text_object_query(query.as_ref())
|
||||
.context("Error loading textobject query")?;
|
||||
}
|
||||
if let Some(query) = queries.debugger {
|
||||
self = self
|
||||
.with_debug_variables_query(query.as_ref())
|
||||
.context("Error loading debug variables query")?;
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
|
@ -1425,6 +1453,24 @@ impl Language {
|
|||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn with_debug_variables_query(mut self, source: &str) -> Result<Self> {
|
||||
let grammar = self.grammar_mut().context("cannot mutate grammar")?;
|
||||
let query = Query::new(&grammar.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));
|
||||
}
|
||||
}
|
||||
|
||||
grammar.debug_variables_config = Some(DebugVariablesConfig {
|
||||
query,
|
||||
objects_by_capture_ix,
|
||||
});
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn with_brackets_query(mut self, source: &str) -> Result<Self> {
|
||||
let grammar = self.grammar_mut().context("cannot mutate grammar")?;
|
||||
let query = Query::new(&grammar.ts_language, source)?;
|
||||
|
@ -1930,6 +1976,10 @@ impl Grammar {
|
|||
.capture_index_for_name(name)?;
|
||||
Some(self.highlight_map.lock().get(capture_id))
|
||||
}
|
||||
|
||||
pub fn debug_variables_config(&self) -> Option<&DebugVariablesConfig> {
|
||||
self.debug_variables_config.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeLabel {
|
||||
|
|
|
@ -226,7 +226,7 @@ pub const QUERY_FILENAME_PREFIXES: &[(
|
|||
("overrides", |q| &mut q.overrides),
|
||||
("redactions", |q| &mut q.redactions),
|
||||
("runnables", |q| &mut q.runnables),
|
||||
("debug_variables", |q| &mut q.debug_variables),
|
||||
("debugger", |q| &mut q.debugger),
|
||||
("textobjects", |q| &mut q.text_objects),
|
||||
];
|
||||
|
||||
|
@ -243,7 +243,7 @@ pub struct LanguageQueries {
|
|||
pub redactions: Option<Cow<'static, str>>,
|
||||
pub runnables: Option<Cow<'static, str>>,
|
||||
pub text_objects: Option<Cow<'static, str>>,
|
||||
pub debug_variables: Option<Cow<'static, str>>,
|
||||
pub debugger: Option<Cow<'static, str>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue