summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--book/src/guides/textobject.md15
-rw-r--r--book/src/usage.md2
-rw-r--r--helix-core/src/movement.rs10
-rw-r--r--helix-core/src/syntax.rs16
-rw-r--r--helix-core/src/textobject.rs5
5 files changed, 43 insertions, 5 deletions
diff --git a/book/src/guides/textobject.md b/book/src/guides/textobject.md
index dd726b7c..7200a514 100644
--- a/book/src/guides/textobject.md
+++ b/book/src/guides/textobject.md
@@ -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.
+## 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-nav]: ../usage.md#tree-sitter-textobject-based-navigation
[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
[textobject-examples]: https://github.com/search?q=repo%3Ahelix-editor%2Fhelix+filename%3Atextobjects.scm&type=Code&ref=advsearch&l=&l=
diff --git a/book/src/usage.md b/book/src/usage.md
index 3f9499ca..039628bf 100644
--- a/book/src/usage.md
+++ b/book/src/usage.md
@@ -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.
Contributions are welcome!
-## Tree-sitter Based Navigation
+## Tree-sitter Textobject Based Navigation
Navigating between functions, classes, parameters, etc is made
possible by leveraging tree-sitter and textobjects queries. For
diff --git a/helix-core/src/movement.rs b/helix-core/src/movement.rs
index e9cd299b..e559f1ea 100644
--- a/helix-core/src/movement.rs
+++ b/helix-core/src/movement.rs
@@ -321,10 +321,14 @@ pub fn goto_treesitter_object(
let get_range = move || -> Option<Range> {
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 nodes = lang_config.textobject_query()?.capture_nodes(
- &capture_name,
+ let nodes = lang_config.textobject_query()?.capture_nodes_any(
+ &[
+ &cap_name(TextObject::Movement),
+ &cap_name(TextObject::Around),
+ &cap_name(TextObject::Inside),
+ ],
slice_tree,
slice,
&mut cursor,
diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs
index a5c5e498..ccf91100 100644
--- a/helix-core/src/syntax.rs
+++ b/helix-core/src/syntax.rs
@@ -188,7 +188,21 @@ impl TextObjectQuery {
slice: RopeSlice<'a>,
cursor: &'a mut QueryCursor,
) -> 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));
captures
diff --git a/helix-core/src/textobject.rs b/helix-core/src/textobject.rs
index 21ceec04..5a55a6f1 100644
--- a/helix-core/src/textobject.rs
+++ b/helix-core/src/textobject.rs
@@ -53,6 +53,8 @@ fn find_word_boundary(slice: RopeSlice, mut pos: usize, direction: Direction, lo
pub enum TextObject {
Around,
Inside,
+ /// Used for moving between objects.
+ Movement,
}
impl Display for TextObject {
@@ -60,6 +62,7 @@ impl Display for TextObject {
f.write_str(match self {
Self::Around => "around",
Self::Inside => "inside",
+ Self::Movement => "movement",
})
}
}
@@ -104,6 +107,7 @@ pub fn textobject_word(
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 {
TextObject::Inside => Range::new(next_grapheme_boundary(slice, anchor), head),
TextObject::Around => Range::new(anchor, next_grapheme_boundary(slice, head)),
+ TextObject::Movement => unreachable!(),
})
.unwrap_or(range)
}