debugger: Add support for inline value hints (#28656)

This PR uses Tree Sitter to show inline values while a user is in a
debug session.

We went with Tree Sitter over the LSP Inline Values request because the
LSP request isn't widely supported. Tree Sitter is easy for
languages/extensions to add support to. Tree Sitter can compute the
inline values locally, so there's no need to add extra RPC messages for
Collab. Tree Sitter also gives Zed more control over how we want to show
variables.

There's still more work to be done after this PR, namely differentiating
between global/local scoped variables, but it's a great starting point
to start iteratively improving it.

Release Notes:

- N/A

---------

Co-authored-by: Piotr Osiewicz <peterosiewicz@gmail.com>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Cole Miller <m@cole-miller.net>
Co-authored-by: Anthony <anthony@zed.dev>
Co-authored-by: Kirill <kirill@zed.dev>
This commit is contained in:
Remco Smits 2025-04-24 00:27:27 +02:00 committed by GitHub
parent d095bab8ad
commit 218496744c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 709 additions and 54 deletions

View file

@ -1015,6 +1015,7 @@ pub struct Grammar {
pub(crate) brackets_config: Option<BracketsConfig>,
pub(crate) redactions_config: Option<RedactionConfig>,
pub(crate) runnable_config: Option<RunnableConfig>,
pub(crate) debug_variables_config: Option<DebugVariablesConfig>,
pub(crate) indents_config: Option<IndentConfig>,
pub outline_config: Option<OutlineConfig>,
pub text_object_config: Option<TextObjectConfig>,
@ -1115,6 +1116,18 @@ struct RunnableConfig {
pub extra_captures: Vec<RunnableCapture>,
}
#[derive(Clone, Debug, PartialEq)]
enum DebugVariableCapture {
Named(SharedString),
Variable,
}
#[derive(Debug)]
struct DebugVariablesConfig {
pub query: Query,
pub captures: Vec<DebugVariableCapture>,
}
struct OverrideConfig {
query: Query,
values: HashMap<u32, OverrideEntry>,
@ -1175,6 +1188,7 @@ impl Language {
override_config: None,
redactions_config: None,
runnable_config: None,
debug_variables_config: None,
error_query: Query::new(&ts_language, "(ERROR) @error").ok(),
ts_language,
highlight_map: Default::default(),
@ -1246,6 +1260,11 @@ impl Language {
.with_text_object_query(query.as_ref())
.context("Error loading textobject query")?;
}
if let Some(query) = queries.debug_variables {
self = self
.with_debug_variables_query(query.as_ref())
.context("Error loading debug variable query")?;
}
Ok(self)
}
@ -1341,6 +1360,25 @@ impl Language {
Ok(self)
}
pub fn with_debug_variables_query(mut self, source: &str) -> Result<Self> {
let grammar = self
.grammar_mut()
.ok_or_else(|| anyhow!("cannot mutate grammar"))?;
let query = Query::new(&grammar.ts_language, source)?;
let mut captures = Vec::new();
for name in query.capture_names() {
captures.push(if *name == "debug_variable" {
DebugVariableCapture::Variable
} else {
DebugVariableCapture::Named(name.to_string().into())
});
}
grammar.debug_variables_config = Some(DebugVariablesConfig { query, captures });
Ok(self)
}
pub fn with_embedding_query(mut self, source: &str) -> Result<Self> {
let grammar = self
.grammar_mut()