task: Allow obtaining custom task variables from tree-sitter queries (#11624)

From now on, only top-level captures are treated as runnable tags and
the rest is appended to task context as custom environmental variables
(unless the name is prefixed with _, in which case the capture is
ignored). This is most likely gonna help with Pest-like test runners.



Release Notes:

- N/A

---------

Co-authored-by: Remco <djsmits12@gmail.com>
This commit is contained in:
Piotr Osiewicz 2024-05-09 23:38:18 +02:00 committed by GitHub
parent 95e246ac1c
commit bff1d8b142
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 126 additions and 67 deletions

View file

@ -13,6 +13,7 @@ use crate::{
SyntaxLayer, SyntaxMap, SyntaxMapCapture, SyntaxMapCaptures, SyntaxMapMatches,
SyntaxSnapshot, ToTreeSitterPoint,
},
task_context::RunnableRange,
LanguageScope, Outline, RunnableTag,
};
use anyhow::{anyhow, Context, Result};
@ -2993,7 +2994,7 @@ impl BufferSnapshot {
pub fn runnable_ranges(
&self,
range: Range<Anchor>,
) -> impl Iterator<Item = (Range<usize>, Runnable)> + '_ {
) -> impl Iterator<Item = RunnableRange> + '_ {
let offset_range = range.start.to_offset(self)..range.end.to_offset(self);
let mut syntax_matches = self.syntax.matches(offset_range, self, |grammar| {
@ -3007,31 +3008,49 @@ impl BufferSnapshot {
.collect::<Vec<_>>();
iter::from_fn(move || {
let test_range = syntax_matches
.peek()
.and_then(|mat| {
test_configs[mat.grammar_index].and_then(|test_configs| {
let tags = SmallVec::from_iter(mat.captures.iter().filter_map(|capture| {
test_configs.runnable_tags.get(&capture.index).cloned()
let test_range = syntax_matches.peek().and_then(|mat| {
test_configs[mat.grammar_index].and_then(|test_configs| {
let mut tags: SmallVec<[(Range<usize>, RunnableTag); 1]> =
SmallVec::from_iter(mat.captures.iter().filter_map(|capture| {
test_configs
.runnable_tags
.get(&capture.index)
.cloned()
.map(|tag_name| (capture.node.byte_range(), tag_name))
}));
if tags.is_empty() {
return None;
}
Some((
mat.captures
.iter()
.find(|capture| capture.index == test_configs.run_capture_ix)?,
Runnable {
tags,
language: mat.language,
buffer: self.remote_id(),
},
))
let maximum_range = tags
.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);
let extra_captures = extra_captures
.into_iter()
.map(|(range, name)| {
(
name.0.to_string(),
self.text_for_range(range.clone()).collect::<String>(),
)
})
.collect();
Some(RunnableRange {
run_range: mat
.captures
.iter()
.find(|capture| capture.index == test_configs.run_capture_ix)
.map(|mat| mat.node.byte_range())?,
runnable: Runnable {
tags: tags.into_iter().cloned().map(|(_, tag)| tag).collect(),
language: mat.language,
buffer: self.remote_id(),
},
extra_captures,
buffer_id: self.remote_id(),
})
})
.map(|(mat, test_tags)| (mat.node.byte_range(), test_tags));
});
syntax_matches.advance();
test_range
})

View file

@ -57,7 +57,9 @@ use std::{
};
use syntax_map::{QueryCursorHandle, SyntaxSnapshot};
use task::RunnableTag;
pub use task_context::{BasicContextProvider, ContextProvider, ContextProviderWithTasks};
pub use task_context::{
BasicContextProvider, ContextProvider, ContextProviderWithTasks, RunnableRange,
};
use theme::SyntaxTheme;
use tree_sitter::{self, wasmtime, Query, QueryCursor, WasmStore};
use util::http::HttpClient;

View file

@ -1,12 +1,19 @@
use std::path::Path;
use std::{ops::Range, path::Path};
use crate::Location;
use crate::{Location, Runnable};
use anyhow::Result;
use collections::HashMap;
use gpui::AppContext;
use task::{TaskTemplates, TaskVariables, VariableName};
use text::{Point, ToPoint};
use text::{BufferId, Point, ToPoint};
pub struct RunnableRange {
pub buffer_id: BufferId,
pub run_range: Range<usize>,
pub runnable: Runnable,
pub extra_captures: HashMap<String, String>,
}
/// Language Contexts are used by Zed tasks to extract information about the source file where the tasks are supposed to be scheduled from.
/// Multiple context providers may be used together: by default, Zed provides a base [`BasicContextProvider`] context that fills all non-custom [`VariableName`] variants.
///