mirror of
https://github.com/ilyakooo0/helix.git
synced 2024-11-28 21:20:23 +03:00
Add object.movement for tree-sitter navigation
This commit is contained in:
parent
989407f190
commit
ab2a0f325b
@ -24,7 +24,22 @@ The following [captures][tree-sitter-captures] are recognized:
|
|||||||
|
|
||||||
[Example query files][textobject-examples] can be found in the helix GitHub repository.
|
[Example query files][textobject-examples] can be found in the helix GitHub repository.
|
||||||
|
|
||||||
|
## Queries for Textobject Based Navigation
|
||||||
|
|
||||||
|
[Tree-sitter based navigation][textobjects-nav] is done using captures in the
|
||||||
|
following order:
|
||||||
|
|
||||||
|
- `object.movement`
|
||||||
|
- `object.around`
|
||||||
|
- `object.inside`
|
||||||
|
|
||||||
|
For example if a `function.around` capture has been already defined for a language
|
||||||
|
in it's `textobjects.scm` file, function navigation should also work automatically.
|
||||||
|
`function.movement` should be defined only if the node captured by `function.around`
|
||||||
|
doesn't make sense in a navigation context.
|
||||||
|
|
||||||
[textobjects]: ../usage.md#textobjects
|
[textobjects]: ../usage.md#textobjects
|
||||||
|
[textobjects-nav]: ../usage.md#tree-sitter-textobject-based-navigation
|
||||||
[tree-sitter-queries]: https://tree-sitter.github.io/tree-sitter/using-parsers#query-syntax
|
[tree-sitter-queries]: https://tree-sitter.github.io/tree-sitter/using-parsers#query-syntax
|
||||||
[tree-sitter-captures]: https://tree-sitter.github.io/tree-sitter/using-parsers#capturing-nodes
|
[tree-sitter-captures]: https://tree-sitter.github.io/tree-sitter/using-parsers#capturing-nodes
|
||||||
[textobject-examples]: https://github.com/search?q=repo%3Ahelix-editor%2Fhelix+filename%3Atextobjects.scm&type=Code&ref=advsearch&l=&l=
|
[textobject-examples]: https://github.com/search?q=repo%3Ahelix-editor%2Fhelix+filename%3Atextobjects.scm&type=Code&ref=advsearch&l=&l=
|
||||||
|
@ -75,7 +75,7 @@ document and a special tree-sitter query file to work properly. [Only
|
|||||||
some grammars][lang-support] currently have the query file implemented.
|
some grammars][lang-support] currently have the query file implemented.
|
||||||
Contributions are welcome!
|
Contributions are welcome!
|
||||||
|
|
||||||
## Tree-sitter Based Navigation
|
## Tree-sitter Textobject Based Navigation
|
||||||
|
|
||||||
Navigating between functions, classes, parameters, etc is made
|
Navigating between functions, classes, parameters, etc is made
|
||||||
possible by leveraging tree-sitter and textobjects queries. For
|
possible by leveraging tree-sitter and textobjects queries. For
|
||||||
|
@ -321,10 +321,14 @@ pub fn goto_treesitter_object(
|
|||||||
let get_range = move || -> Option<Range> {
|
let get_range = move || -> Option<Range> {
|
||||||
let byte_pos = slice.char_to_byte(range.cursor(slice));
|
let byte_pos = slice.char_to_byte(range.cursor(slice));
|
||||||
|
|
||||||
let capture_name = format!("{}.{}", object_name, TextObject::Around);
|
let cap_name = |t: TextObject| format!("{}.{}", object_name, t);
|
||||||
let mut cursor = QueryCursor::new();
|
let mut cursor = QueryCursor::new();
|
||||||
let nodes = lang_config.textobject_query()?.capture_nodes(
|
let nodes = lang_config.textobject_query()?.capture_nodes_any(
|
||||||
&capture_name,
|
&[
|
||||||
|
&cap_name(TextObject::Movement),
|
||||||
|
&cap_name(TextObject::Around),
|
||||||
|
&cap_name(TextObject::Inside),
|
||||||
|
],
|
||||||
slice_tree,
|
slice_tree,
|
||||||
slice,
|
slice,
|
||||||
&mut cursor,
|
&mut cursor,
|
||||||
|
@ -188,7 +188,21 @@ impl TextObjectQuery {
|
|||||||
slice: RopeSlice<'a>,
|
slice: RopeSlice<'a>,
|
||||||
cursor: &'a mut QueryCursor,
|
cursor: &'a mut QueryCursor,
|
||||||
) -> Option<impl Iterator<Item = Node<'a>>> {
|
) -> Option<impl Iterator<Item = Node<'a>>> {
|
||||||
let capture_idx = self.query.capture_index_for_name(capture_name)?;
|
self.capture_nodes_any(&[capture_name], node, slice, cursor)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the first capture that exists out of all given `capture_names`
|
||||||
|
/// and return sub nodes that match this capture.
|
||||||
|
pub fn capture_nodes_any<'a>(
|
||||||
|
&'a self,
|
||||||
|
capture_names: &[&str],
|
||||||
|
node: Node<'a>,
|
||||||
|
slice: RopeSlice<'a>,
|
||||||
|
cursor: &'a mut QueryCursor,
|
||||||
|
) -> Option<impl Iterator<Item = Node<'a>>> {
|
||||||
|
let capture_idx = capture_names
|
||||||
|
.iter()
|
||||||
|
.find_map(|cap| self.query.capture_index_for_name(cap))?;
|
||||||
let captures = cursor.captures(&self.query, node, RopeProvider(slice));
|
let captures = cursor.captures(&self.query, node, RopeProvider(slice));
|
||||||
|
|
||||||
captures
|
captures
|
||||||
|
@ -53,6 +53,8 @@ fn find_word_boundary(slice: RopeSlice, mut pos: usize, direction: Direction, lo
|
|||||||
pub enum TextObject {
|
pub enum TextObject {
|
||||||
Around,
|
Around,
|
||||||
Inside,
|
Inside,
|
||||||
|
/// Used for moving between objects.
|
||||||
|
Movement,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for TextObject {
|
impl Display for TextObject {
|
||||||
@ -60,6 +62,7 @@ impl Display for TextObject {
|
|||||||
f.write_str(match self {
|
f.write_str(match self {
|
||||||
Self::Around => "around",
|
Self::Around => "around",
|
||||||
Self::Inside => "inside",
|
Self::Inside => "inside",
|
||||||
|
Self::Movement => "movement",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,6 +107,7 @@ pub fn textobject_word(
|
|||||||
Range::new(word_start - whitespace_count_left, word_end)
|
Range::new(word_start - whitespace_count_left, word_end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TextObject::Movement => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,6 +122,7 @@ pub fn textobject_surround(
|
|||||||
.map(|(anchor, head)| match textobject {
|
.map(|(anchor, head)| match textobject {
|
||||||
TextObject::Inside => Range::new(next_grapheme_boundary(slice, anchor), head),
|
TextObject::Inside => Range::new(next_grapheme_boundary(slice, anchor), head),
|
||||||
TextObject::Around => Range::new(anchor, next_grapheme_boundary(slice, head)),
|
TextObject::Around => Range::new(anchor, next_grapheme_boundary(slice, head)),
|
||||||
|
TextObject::Movement => unreachable!(),
|
||||||
})
|
})
|
||||||
.unwrap_or(range)
|
.unwrap_or(range)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user