Skip to content

Commit

Permalink
add tree sitter query implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
mikayla-maki committed Apr 24, 2024
1 parent 4dcbd3c commit 4e2f911
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 22 deletions.
23 changes: 23 additions & 0 deletions crates/editor/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ use inlay_hint_cache::{InlayHintCache, InlaySplice, InvalidationStrategy};
pub use inline_completion_provider::*;
pub use items::MAX_TAB_TITLE_LEN;
use itertools::Itertools;
use language::TestTag;
use language::{
char_kind,
language_settings::{self, all_language_settings, InlayHintSettings},
Expand Down Expand Up @@ -7375,6 +7376,28 @@ impl Editor {
self.select_larger_syntax_node_stack = stack;
}

pub fn test_lines(
&mut self,
range: Range<Anchor>,
cx: &mut ViewContext<Self>,
) -> Vec<(u32, SmallVec<[TestTag; 1]>)> {
let snapshot = self.snapshot(cx);

snapshot
.buffer_snapshot
.test_ranges(range)
.map(|(multi_buffer_range, tags)| {
(
multi_buffer_range
.start
.to_display_point(&snapshot.display_snapshot)
.row(),
tags,
)
})
.collect()
}

pub fn move_to_enclosing_bracket(
&mut self,
_: &MoveToEnclosingBracket,
Expand Down
46 changes: 45 additions & 1 deletion crates/language/src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::{
SyntaxLayer, SyntaxMap, SyntaxMapCapture, SyntaxMapCaptures, SyntaxMapMatches,
SyntaxSnapshot, ToTreeSitterPoint,
},
LanguageScope, Outline,
LanguageScope, Outline, TestTag,
};
use anyhow::{anyhow, Context, Result};
pub use clock::ReplicaId;
Expand Down Expand Up @@ -2948,6 +2948,50 @@ impl BufferSnapshot {
})
}

pub fn test_ranges(
&self,
range: Range<Anchor>,
) -> impl Iterator<Item = (Range<usize>, SmallVec<[TestTag; 1]>)> + '_ {
let offset_range = range.start.to_offset(self)..range.end.to_offset(self);

let mut syntax_matches = self
.syntax
.matches(offset_range, self, |grammar| grammar.tests_query.as_ref());

let test_configs = syntax_matches
.grammars()
.iter()
.map(|grammar| grammar.tests_config.as_ref())
.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 test_tags =
SmallVec::from_iter(mat.captures.iter().filter_map(|capture| {
test_configs.test_tags.get(&capture.index).cloned()
}));

if test_tags.is_empty() {
return None;
}

Some((
mat.captures
.iter()
.find(|capture| capture.index == test_configs.run_index)?,
test_tags,
))
})
})
.map(|(mat, test_tags)| (mat.node.byte_range(), test_tags));
syntax_matches.advance();
test_range
})
}

/// Returns selections for remote peers intersecting the given range.
#[allow(clippy::type_complexity)]
pub fn remote_selections_in_range(
Expand Down
51 changes: 34 additions & 17 deletions crates/language/src/language.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,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 lazy_static::lazy_static;
use lsp::{CodeActionKind, LanguageServerBinary};
Expand Down Expand Up @@ -529,6 +529,10 @@ pub struct CodeLabel {
pub filter_range: Range<usize>,
}

// This is a new type representing a specific capture name in the langauge's tests.scm query
#[derive(Clone)]
pub struct TestTag(SharedString);

#[derive(Clone, Deserialize, JsonSchema)]
pub struct LanguageConfig {
/// Human-readable name of the language.
Expand Down Expand Up @@ -601,16 +605,6 @@ pub struct LanguageConfig {
/// How to soft-wrap long lines of text.
#[serde(default)]
pub soft_wrap: Option<SoftWrap>,

#[serde(default)]
pub test_templates: Option<Vec<TestTemplates>>,
}

#[derive(Clone, Debug, Serialize, Deserialize, Default, JsonSchema)]
pub struct TestTemplates {
capture_name: String,
template: String,
label: String,
}

#[derive(Clone, Debug, Serialize, Deserialize, Default, JsonSchema)]
Expand Down Expand Up @@ -698,7 +692,6 @@ impl Default for LanguageConfig {
hard_tabs: Default::default(),
tab_size: Default::default(),
soft_wrap: Default::default(),
test_templates: Default::default(),
}
}
}
Expand Down Expand Up @@ -840,6 +833,7 @@ pub struct Grammar {
pub(crate) tests_query: Option<Query>,
pub(crate) brackets_config: Option<BracketConfig>,
pub(crate) redactions_config: Option<RedactionConfig>,
pub(crate) tests_config: Option<TestConfig>,
pub(crate) indents_config: Option<IndentConfig>,
pub outline_config: Option<OutlineConfig>,
pub embedding_config: Option<EmbeddingConfig>,
Expand Down Expand Up @@ -886,6 +880,13 @@ struct RedactionConfig {
pub redaction_capture_ix: u32,
}

struct TestConfig {
pub query: Query,
// A mapping from captures indices to known test tags
pub test_tags: HashMap<u32, TestTag>,
pub run_index: u32,
}

struct OverrideConfig {
query: Query,
values: HashMap<u32, (String, LanguageConfigOverride)>,
Expand Down Expand Up @@ -927,6 +928,7 @@ impl Language {
injection_config: None,
override_config: None,
redactions_config: None,
tests_config: None,
tests_query: None,
error_query: Query::new(&ts_language, "(ERROR) @error").unwrap(),
ts_language,
Expand Down Expand Up @@ -1003,7 +1005,26 @@ impl Language {
let grammar = self
.grammar_mut()
.ok_or_else(|| anyhow!("cannot mutate grammar"))?;
grammar.tests_query = Some(Query::new(&grammar.ts_language, source)?);

let query = Query::new(&grammar.ts_language, source)?;
let mut run_capture_index = None;
let mut test_tags = HashMap::default();
for (ix, name) in query.capture_names().iter().enumerate() {
if *name == "run" {
run_capture_index = Some(ix as u32);
} else if let Some(tag_name) = name.strip_prefix("zed-") {
test_tags.insert(ix as u32, TestTag(tag_name.to_string().into()));
}
}

if let Some(run_index) = run_capture_index {
grammar.tests_config = Some(TestConfig {
query,
run_index,
test_tags,
});
}

Ok(self)
}

Expand Down Expand Up @@ -1317,10 +1338,6 @@ impl Language {
&self.config.matcher.path_suffixes
}

pub fn test_templates(&self) -> Option<&[TestTemplates]> {
self.config.test_templates.as_deref()
}

pub fn should_autoclose_before(&self, c: char) -> bool {
c.is_whitespace() || self.config.autoclose_before.contains(c)
}
Expand Down
3 changes: 0 additions & 3 deletions crates/languages/src/rust/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,3 @@ brackets = [
{ start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
]
collapsed_placeholder = " /* ... */ "
test_templates = [
{capture_name = "cargo-test", label = "cargo test", template = "cargo test -p ZED_PACKAGE ZED_TEST_CAPTURES[0]"}
]
2 changes: 1 addition & 1 deletion crates/languages/src/rust/tests.scm
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
(#match? @attribute ".*test.*"))
(function_item
name: (_) @capture @run)
) @cargo-test
) @zed-rust-test @zed-test
25 changes: 25 additions & 0 deletions crates/multi_buffer/src/multi_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3132,6 +3132,31 @@ impl MultiBufferSnapshot {
.flatten()
}

pub fn test_ranges(
&self,
range: Range<Anchor>,
) -> impl Iterator<Item = (Range<usize>, SmallVec<[language::TestTag; 1]>)> + '_ {
let range = range.start.to_offset(self)..range.end.to_offset(self);
self.excerpts_for_range(range.clone())
.flat_map(move |(excerpt, excerpt_offset)| {
let excerpt_buffer_start = excerpt.range.context.start.to_offset(&excerpt.buffer);

excerpt
.buffer
.test_ranges(excerpt.range.context.clone())
.map(move |(mut match_range, test_tags)| {
// Re-base onto the excerpts coordinates in the multibuffer
match_range.start =
excerpt_offset + (match_range.start - excerpt_buffer_start);
match_range.end = excerpt_offset + (match_range.end - excerpt_buffer_start);

(match_range, test_tags)
})
.skip_while(move |(match_range, _)| match_range.end < range.start)
.take_while(move |(match_range, _)| match_range.start < range.end)
})
}

pub fn diagnostics_update_count(&self) -> usize {
self.diagnostics_update_count
}
Expand Down

0 comments on commit 4e2f911

Please sign in to comment.