on master help sign in
Add tree-sitter based function, class navigation
commit966fbc59849701dc56f70af406444c351bc9f54a authorGokul Soumya <gokulps15@gmail.com> dateThu 3 Feb 2022, 13:41 (5 years ago) parent1422449
3 files changed+109−1
helix-core/src/movement.rs+51−1 helix-term/src/commands.rs+52 helix-term/src/keymap.rs+6
helix-core/src/movement.rs+51−1
@@ -1,6 +1,7 @@
11use std::iter;
22
33use ropey::iter::Chars;
4+use tree_sitter::{Node, QueryCursor};
45
56use crate::{
67 chars::{categorize_char, char_is_line_ending, CharCategory},
@@ -9,7 +10,10 @@ use crate::{
910 next_grapheme_boundary, nth_next_grapheme_boundary, nth_prev_grapheme_boundary,
1011 prev_grapheme_boundary,
1112 },
12− pos_at_coords, Position, Range, RopeSlice,
13+ pos_at_coords,
14+ syntax::LanguageConfiguration,
15+ textobject::TextObject,
16+ Position, Range, RopeSlice,
1317};
1418
1519#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -305,6 +309,52 @@ fn reached_target(target: WordMotionTarget, prev_ch: char, next_ch: char) -> boo
305309 }
306310}
307311
312+pub fn goto_treesitter_object(
313+ slice: RopeSlice,
314+ range: Range,
315+ object_name: &str,
316+ dir: Direction,
317+ slice_tree: Node,
318+ lang_config: &LanguageConfiguration,
319+ _count: usize,
320+) -> Range {
321+ let get_range = move || -> Option<Range> {
322+ let byte_pos = slice.char_to_byte(range.cursor(slice));
323+
324+ let capture_name = format!("{}.{}", object_name, TextObject::Around);
325+ let mut cursor = QueryCursor::new();
326+ let nodes = lang_config.textobject_query()?.capture_nodes(
327+ &capture_name,
328+ slice_tree,
329+ slice,
330+ &mut cursor,
331+ )?;
332+
333+ let node = match dir {
334+ Direction::Forward => nodes
335+ .filter(|n| n.start_byte() > byte_pos)
336+ .min_by_key(|n| n.start_byte())?,
337+ Direction::Backward => nodes
338+ .filter(|n| n.start_byte() < byte_pos)
339+ .max_by_key(|n| n.start_byte())?,
340+ };
341+
342+ let len = slice.len_bytes();
343+ let start_byte = node.start_byte();
344+ let end_byte = node.end_byte();
345+ if start_byte >= len || end_byte >= len {
346+ return None;
347+ }
348+
349+ let start_char = slice.byte_to_char(start_byte);
350+ let end_char = slice.byte_to_char(end_byte);
351+
352+ // head of range should be at beginning
353+ Some(Range::new(end_char, start_char))
354+ };
355+ get_range().unwrap_or(range)
356+}
357+
308358#[cfg(test)]
309359mod test {
310360 use ropey::Rope;
helix-term/src/commands.rs+52
@@ -398,6 +398,12 @@ impl MappableCommand {
398398 surround_delete, "Surround delete",
399399 select_textobject_around, "Select around object",
400400 select_textobject_inner, "Select inside object",
401+ goto_next_function, "Goto next function",
402+ goto_prev_function, "Goto previous function",
403+ goto_next_class, "Goto next class",
404+ goto_prev_class, "Goto previous class",
405+ goto_next_parameter, "Goto next parameter",
406+ goto_prev_parameter, "Goto previous parameter",
401407 dap_launch, "Launch debug target",
402408 dap_toggle_breakpoint, "Toggle breakpoint",
403409 dap_continue, "Continue program execution",
@@ -5907,6 +5913,52 @@ fn scroll_down(cx: &mut Context) {
59075913 scroll(cx, cx.count(), Direction::Forward);
59085914}
59095915
5916+fn goto_ts_object_impl(cx: &mut Context, object: &str, direction: Direction) {
5917+ let count = cx.count();
5918+ let (view, doc) = current!(cx.editor);
5919+ let text = doc.text().slice(..);
5920+ let range = doc.selection(view.id).primary();
5921+
5922+ let new_range = match doc.language_config().zip(doc.syntax()) {
5923+ Some((lang_config, syntax)) => movement::goto_treesitter_object(
5924+ text,
5925+ range,
5926+ object,
5927+ direction,
5928+ syntax.tree().root_node(),
5929+ lang_config,
5930+ count,
5931+ ),
5932+ None => range,
5933+ };
5934+
5935+ doc.set_selection(view.id, Selection::single(new_range.anchor, new_range.head));
5936+}
5937+
5938+fn goto_next_function(cx: &mut Context) {
5939+ goto_ts_object_impl(cx, "function", Direction::Forward)
5940+}
5941+
5942+fn goto_prev_function(cx: &mut Context) {
5943+ goto_ts_object_impl(cx, "function", Direction::Backward)
5944+}
5945+
5946+fn goto_next_class(cx: &mut Context) {
5947+ goto_ts_object_impl(cx, "class", Direction::Forward)
5948+}
5949+
5950+fn goto_prev_class(cx: &mut Context) {
5951+ goto_ts_object_impl(cx, "class", Direction::Backward)
5952+}
5953+
5954+fn goto_next_parameter(cx: &mut Context) {
5955+ goto_ts_object_impl(cx, "parameter", Direction::Forward)
5956+}
5957+
5958+fn goto_prev_parameter(cx: &mut Context) {
5959+ goto_ts_object_impl(cx, "parameter", Direction::Backward)
5960+}
5961+
59105962fn select_textobject_around(cx: &mut Context) {
59115963 select_textobject(cx, textobject::TextObject::Around);
59125964}
helix-term/src/keymap.rs+6
@@ -572,11 +572,17 @@ impl Default for Keymaps {
572572 "[" => { "Left bracket"
573573 "d" => goto_prev_diag,
574574 "D" => goto_first_diag,
575+ "f" => goto_prev_function,
576+ "c" => goto_prev_class,
577+ "p" => goto_prev_parameter,
575578 "space" => add_newline_above,
576579 },
577580 "]" => { "Right bracket"
578581 "d" => goto_next_diag,
579582 "D" => goto_last_diag,
583+ "f" => goto_next_function,
584+ "c" => goto_next_class,
585+ "p" => goto_next_parameter,
580586 "space" => add_newline_below,
581587 },
582588