Implement bracket matching using queries
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
co-authored by
Nathan Sobo
parent
e9bd872b06
commit
8b7a314474
@@ -727,41 +727,27 @@ impl Buffer {
|
||||
&self,
|
||||
range: Range<T>,
|
||||
) -> Option<(Range<usize>, Range<usize>)> {
|
||||
let mut bracket_ranges = None;
|
||||
if let Some((lang, tree)) = self.language.as_ref().zip(self.syntax_tree()) {
|
||||
let range = range.start.to_offset(self)..range.end.to_offset(self);
|
||||
let mut cursor = tree.root_node().walk();
|
||||
'outer: loop {
|
||||
let node = cursor.node();
|
||||
if node.child_count() >= 2 {
|
||||
if let Some((first_child, last_child)) =
|
||||
node.child(0).zip(node.child(node.child_count() - 1))
|
||||
{
|
||||
for pair in &lang.config.bracket_pairs {
|
||||
if pair.start == first_child.kind() && pair.end == last_child.kind() {
|
||||
bracket_ranges =
|
||||
Some((first_child.byte_range(), last_child.byte_range()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let (lang, tree) = self.language.as_ref().zip(self.syntax_tree())?;
|
||||
let open_capture_ix = lang.brackets_query.capture_index_for_name("open")?;
|
||||
let close_capture_ix = lang.brackets_query.capture_index_for_name("close")?;
|
||||
|
||||
if !cursor.goto_first_child() {
|
||||
break;
|
||||
}
|
||||
// Find bracket pairs that *inclusively* contain the given range.
|
||||
let range = range.start.to_offset(self).saturating_sub(1)..range.end.to_offset(self) + 1;
|
||||
let mut cursor = QueryCursorHandle::new();
|
||||
let matches = cursor.set_byte_range(range.start, range.end).matches(
|
||||
&lang.brackets_query,
|
||||
tree.root_node(),
|
||||
TextProvider(&self.visible_text),
|
||||
);
|
||||
|
||||
while cursor.node().end_byte() < range.end {
|
||||
if !cursor.goto_next_sibling() {
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
|
||||
if cursor.node().start_byte() > range.start {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
bracket_ranges
|
||||
// Get the ranges of the innermost pair of brackets.
|
||||
matches
|
||||
.filter_map(|mat| {
|
||||
let open = mat.nodes_for_capture_index(open_capture_ix).next()?;
|
||||
let close = mat.nodes_for_capture_index(close_capture_ix).next()?;
|
||||
Some((open.byte_range(), close.byte_range()))
|
||||
})
|
||||
.min_by_key(|(open_range, close_range)| close_range.end - open_range.start)
|
||||
}
|
||||
|
||||
fn diff(&self, new_text: Arc<str>, ctx: &AppContext) -> Task<Diff> {
|
||||
|
||||
@@ -1826,6 +1826,8 @@ impl BufferView {
|
||||
}
|
||||
|
||||
pub fn move_to_enclosing_bracket(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
|
||||
use super::RangeExt as _;
|
||||
|
||||
let buffer = self.buffer.read(ctx.as_ref());
|
||||
let mut selections = self.selections(ctx.as_ref()).to_vec();
|
||||
for selection in &mut selections {
|
||||
@@ -1833,12 +1835,13 @@ impl BufferView {
|
||||
if let Some((open_range, close_range)) =
|
||||
buffer.enclosing_bracket_ranges(selection_range.clone())
|
||||
{
|
||||
let close_range = close_range.to_inclusive();
|
||||
let destination = if close_range.contains(&selection_range.start)
|
||||
&& close_range.contains(&selection_range.end)
|
||||
{
|
||||
open_range.end
|
||||
} else {
|
||||
close_range.start
|
||||
*close_range.start()
|
||||
};
|
||||
selection.start = buffer.anchor_before(destination);
|
||||
selection.end = selection.start.clone();
|
||||
|
||||
@@ -535,6 +535,7 @@ mod tests {
|
||||
},
|
||||
grammar: grammar.clone(),
|
||||
highlight_query,
|
||||
brackets_query: tree_sitter::Query::new(grammar, "").unwrap(),
|
||||
theme_mapping: Default::default(),
|
||||
});
|
||||
lang.set_theme(&theme);
|
||||
|
||||
@@ -9,7 +9,10 @@ pub use buffer_element::*;
|
||||
pub use buffer_view::*;
|
||||
pub use display_map::DisplayPoint;
|
||||
use display_map::*;
|
||||
use std::{cmp, ops::Range};
|
||||
use std::{
|
||||
cmp,
|
||||
ops::{Range, RangeInclusive},
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Bias {
|
||||
@@ -19,10 +22,15 @@ pub enum Bias {
|
||||
|
||||
trait RangeExt<T> {
|
||||
fn sorted(&self) -> Range<T>;
|
||||
fn to_inclusive(&self) -> RangeInclusive<T>;
|
||||
}
|
||||
|
||||
impl<T: Ord + Clone> RangeExt<T> for Range<T> {
|
||||
fn sorted(&self) -> Self {
|
||||
cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
|
||||
}
|
||||
|
||||
fn to_inclusive(&self) -> RangeInclusive<T> {
|
||||
self.start.clone()..=self.end.clone()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user