tasks: Provide task variables from matching runnable ranges in task modal (#12237)
In #12003 we found ourselves in need for precise region tracking in which a given runnable has an effect in order to grab variables from it. This PR makes it so that in task modal all task variables from queries overlapping current cursor position. However, in the process of working on that I've found that we cannot always use a top-level capture to represent the full match range of runnable (which has been my assumption up to this point). Tree-sitter captures cannot capture sibling groups; we did just that in Rust queries. Thankfully, none of the extensions are affected as in them, a capture is always attached to single node. This PR adds annotations to them nonetheless; we'll be able to get rid of top-level captures in extension runnables.scm once this PR is in stable version of Zed. Release Notes: - N/A
This commit is contained in:
parent
08a3d3a0c2
commit
27229bba6b
11 changed files with 295 additions and 183 deletions
|
@ -14,7 +14,7 @@ use crate::{
|
|||
SyntaxSnapshot, ToTreeSitterPoint,
|
||||
},
|
||||
task_context::RunnableRange,
|
||||
LanguageScope, Outline, RunnableTag,
|
||||
LanguageScope, Outline, RunnableCapture, RunnableTag,
|
||||
};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
pub use clock::ReplicaId;
|
||||
|
@ -3061,41 +3061,76 @@ impl BufferSnapshot {
|
|||
|
||||
iter::from_fn(move || loop {
|
||||
let mat = syntax_matches.peek()?;
|
||||
|
||||
let test_range = test_configs[mat.grammar_index].and_then(|test_configs| {
|
||||
let mut tags: SmallVec<[(Range<usize>, RunnableTag); 1]> =
|
||||
let mut run_range = None;
|
||||
let full_range = mat.captures.iter().fold(
|
||||
Range {
|
||||
start: usize::MAX,
|
||||
end: 0,
|
||||
},
|
||||
|mut acc, next| {
|
||||
let byte_range = next.node.byte_range();
|
||||
if acc.start > byte_range.start {
|
||||
acc.start = byte_range.start;
|
||||
}
|
||||
if acc.end < byte_range.end {
|
||||
acc.end = byte_range.end;
|
||||
}
|
||||
acc
|
||||
},
|
||||
);
|
||||
if full_range.start > full_range.end {
|
||||
// We did not find a full spanning range of this match.
|
||||
return None;
|
||||
}
|
||||
let extra_captures: SmallVec<[_; 1]> =
|
||||
SmallVec::from_iter(mat.captures.iter().filter_map(|capture| {
|
||||
test_configs
|
||||
.runnable_tags
|
||||
.get(&capture.index)
|
||||
.extra_captures
|
||||
.get(capture.index as usize)
|
||||
.cloned()
|
||||
.map(|tag_name| (capture.node.byte_range(), tag_name))
|
||||
.and_then(|tag_name| match tag_name {
|
||||
RunnableCapture::Named(name) => {
|
||||
Some((capture.node.byte_range(), name))
|
||||
}
|
||||
RunnableCapture::Run => {
|
||||
let _ = run_range.insert(capture.node.byte_range());
|
||||
None
|
||||
}
|
||||
})
|
||||
}));
|
||||
let maximum_range = tags
|
||||
let run_range = run_range?;
|
||||
let tags = test_configs
|
||||
.query
|
||||
.property_settings(mat.pattern_index)
|
||||
.iter()
|
||||
.max_by_key(|(byte_range, _)| byte_range.len())
|
||||
.map(|(range, _)| range)?
|
||||
.clone();
|
||||
tags.sort_by_key(|(range, _)| range == &maximum_range);
|
||||
let split_point = tags.partition_point(|(range, _)| range != &maximum_range);
|
||||
let (extra_captures, tags) = tags.split_at(split_point);
|
||||
|
||||
.filter_map(|property| {
|
||||
if *property.key == *"tag" {
|
||||
property
|
||||
.value
|
||||
.as_ref()
|
||||
.map(|value| RunnableTag(value.to_string().into()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let extra_captures = extra_captures
|
||||
.into_iter()
|
||||
.map(|(range, name)| {
|
||||
(
|
||||
name.0.to_string(),
|
||||
name.to_string(),
|
||||
self.text_for_range(range.clone()).collect::<String>(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
// All tags should have the same range.
|
||||
Some(RunnableRange {
|
||||
run_range: mat
|
||||
.captures
|
||||
.iter()
|
||||
.find(|capture| capture.index == test_configs.run_capture_ix)
|
||||
.map(|mat| mat.node.byte_range())?,
|
||||
run_range,
|
||||
full_range,
|
||||
runnable: Runnable {
|
||||
tags: tags.into_iter().cloned().map(|(_, tag)| tag).collect(),
|
||||
tags,
|
||||
language: mat.language,
|
||||
buffer: self.remote_id(),
|
||||
},
|
||||
|
|
|
@ -25,7 +25,7 @@ use anyhow::{anyhow, Context, Result};
|
|||
use async_trait::async_trait;
|
||||
use collections::{HashMap, HashSet};
|
||||
use futures::Future;
|
||||
use gpui::{AppContext, AsyncAppContext, Model, Task};
|
||||
use gpui::{AppContext, AsyncAppContext, Model, SharedString, Task};
|
||||
pub use highlight_map::HighlightMap;
|
||||
use http::HttpClient;
|
||||
use lazy_static::lazy_static;
|
||||
|
@ -882,12 +882,16 @@ struct RedactionConfig {
|
|||
pub redaction_capture_ix: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum RunnableCapture {
|
||||
Named(SharedString),
|
||||
Run,
|
||||
}
|
||||
|
||||
struct RunnableConfig {
|
||||
pub query: Query,
|
||||
/// A mapping from captures indices to known test tags
|
||||
pub runnable_tags: HashMap<u32, RunnableTag>,
|
||||
/// index of the capture that corresponds to @run
|
||||
pub run_capture_ix: u32,
|
||||
/// A mapping from capture indice to capture kind
|
||||
pub extra_captures: Vec<RunnableCapture>,
|
||||
}
|
||||
|
||||
struct OverrideConfig {
|
||||
|
@ -1009,23 +1013,21 @@ impl Language {
|
|||
.ok_or_else(|| anyhow!("cannot mutate grammar"))?;
|
||||
|
||||
let query = Query::new(&grammar.ts_language, source)?;
|
||||
let mut run_capture_index = None;
|
||||
let mut runnable_tags = HashMap::default();
|
||||
for (ix, name) in query.capture_names().iter().enumerate() {
|
||||
if *name == "run" {
|
||||
run_capture_index = Some(ix as u32);
|
||||
let mut extra_captures = Vec::with_capacity(query.capture_names().len());
|
||||
|
||||
for name in query.capture_names().iter() {
|
||||
let kind = if *name == "run" {
|
||||
RunnableCapture::Run
|
||||
} else {
|
||||
runnable_tags.insert(ix as u32, RunnableTag(name.to_string().into()));
|
||||
}
|
||||
RunnableCapture::Named(name.to_string().into())
|
||||
};
|
||||
extra_captures.push(kind);
|
||||
}
|
||||
|
||||
if let Some(run_capture_ix) = run_capture_index {
|
||||
grammar.runnable_config = Some(RunnableConfig {
|
||||
query,
|
||||
run_capture_ix,
|
||||
runnable_tags,
|
||||
});
|
||||
}
|
||||
grammar.runnable_config = Some(RunnableConfig {
|
||||
extra_captures,
|
||||
query,
|
||||
});
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use text::BufferId;
|
|||
pub struct RunnableRange {
|
||||
pub buffer_id: BufferId,
|
||||
pub run_range: Range<usize>,
|
||||
pub full_range: Range<usize>,
|
||||
pub runnable: Runnable,
|
||||
pub extra_captures: HashMap<String, String>,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue