Add textobjects queries (#20924)
Co-Authored-By: Max <max@zed.dev> Release Notes: - vim: Added motions `[[`, `[]`, `]]`, `][` for navigating by section, `[m`, `]m`, `[M`, `]M` for navigating by method, and `[*`, `]*`, `[/`, `]/` for comments. These currently only work for languages built in to Zed, as they are powered by new tree-sitter queries. - vim: Added new text objects: `ic`, `ac` for inside/around classes, `if`,`af` for functions/methods, and `g c` for comments. These currently only work for languages built in to Zed, as they are powered by new tree-sitter queries. --------- Co-authored-by: Max <max@zed.dev>
This commit is contained in:
parent
c443307c19
commit
75c9dc179b
28 changed files with 1205 additions and 26 deletions
|
@ -14,7 +14,8 @@ use crate::{
|
|||
SyntaxMapMatches, SyntaxSnapshot, ToTreeSitterPoint,
|
||||
},
|
||||
task_context::RunnableRange,
|
||||
LanguageScope, Outline, OutlineConfig, RunnableCapture, RunnableTag,
|
||||
LanguageScope, Outline, OutlineConfig, RunnableCapture, RunnableTag, TextObject,
|
||||
TreeSitterOptions,
|
||||
};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_watch as watch;
|
||||
|
@ -3412,6 +3413,72 @@ impl BufferSnapshot {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn text_object_ranges<T: ToOffset>(
|
||||
&self,
|
||||
range: Range<T>,
|
||||
options: TreeSitterOptions,
|
||||
) -> impl Iterator<Item = (Range<usize>, TextObject)> + '_ {
|
||||
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, options, |grammar| {
|
||||
grammar.text_object_config.as_ref().map(|c| &c.query)
|
||||
});
|
||||
|
||||
let configs = matches
|
||||
.grammars()
|
||||
.iter()
|
||||
.map(|grammar| grammar.text_object_config.as_ref())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut captures = Vec::<(Range<usize>, TextObject)>::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
|
||||
.text_objects_by_capture_ix
|
||||
.binary_search_by_key(&capture.index, |e| e.0)
|
||||
.ok()
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let text_object = config.text_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();
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns enclosing bracket ranges containing the given range
|
||||
pub fn enclosing_bracket_ranges<T: ToOffset>(
|
||||
&self,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue