Add function parameter and return commands

pull/7494/head
Oliver Sargison 2 months ago
parent 237cbe4bca
commit 0ab1a3f973

@ -558,6 +558,54 @@ fn reached_target(target: WordMotionTarget, prev_ch: char, next_ch: char) -> boo
} }
} }
pub fn goto_current_function_parameters(
slice: RopeSlice,
range: Range,
slice_tree: Node,
lang_config: &LanguageConfiguration,
) -> Option<Range> {
let byte_pos = slice.char_to_byte(range.cursor(slice));
let mut cursor = QueryCursor::new();
let current_function_range = current_node_byte_range(
lang_config,
slice_tree,
slice,
&mut cursor,
"function.around",
byte_pos,
)?;
cursor.set_byte_range(current_function_range);
let parameters_node = lang_config
.textobject_query()?
.capture_nodes_any(&["parameters.around"], slice_tree, slice, &mut cursor)?
.next()?;
Some(Range::new(
slice.byte_to_char(parameters_node.start_byte()),
slice.byte_to_char(parameters_node.end_byte()),
))
}
pub fn current_node_byte_range(
lang_config: &LanguageConfiguration,
slice_tree: Node<'_>,
slice: RopeSlice<'_>,
cursor: &mut QueryCursor,
node: &'static str,
byte_pos: usize,
) -> Option<std::ops::Range<usize>> {
let current_function_range = lang_config
.textobject_query()?
.capture_nodes_any(&[node], slice_tree, slice, cursor)?
.filter(|func| func.byte_range().contains(&byte_pos))
.max_by_key(|func| func.end_byte())?
.byte_range();
Some(current_function_range)
}
/// Finds the range of the next or previous textobject in the syntax sub-tree of `node`. /// Finds the range of the next or previous textobject in the syntax sub-tree of `node`.
/// Returns the range in the forwards direction. /// Returns the range in the forwards direction.
pub fn goto_treesitter_object( pub fn goto_treesitter_object(

@ -22,11 +22,11 @@ use helix_core::{
encoding, find_workspace, encoding, find_workspace,
graphemes::{self, next_grapheme_boundary, RevRopeGraphemes}, graphemes::{self, next_grapheme_boundary, RevRopeGraphemes},
history::UndoKind, history::UndoKind,
increment, indent, increment,
indent::IndentStyle, indent::{self, IndentStyle},
line_ending::{get_line_ending_of_str, line_end_char_index}, line_ending::{get_line_ending_of_str, line_end_char_index},
match_brackets, match_brackets,
movement::{self, move_vertically_visual, Direction}, movement::{self, current_node_byte_range, move_vertically_visual, Direction},
object, pos_at_coords, object, pos_at_coords,
regex::{self, Regex}, regex::{self, Regex},
search::{self, CharMatcher}, search::{self, CharMatcher},
@ -396,6 +396,7 @@ impl MappableCommand {
goto_declaration, "Goto declaration", goto_declaration, "Goto declaration",
add_newline_above, "Add newline above", add_newline_above, "Add newline above",
add_newline_below, "Add newline below", add_newline_below, "Add newline below",
add_function_parameter, "Add function parameter",
goto_type_definition, "Goto type definition", goto_type_definition, "Goto type definition",
goto_implementation, "Goto implementation", goto_implementation, "Goto implementation",
goto_file_start, "Goto line number <n> else file start", goto_file_start, "Goto line number <n> else file start",
@ -426,6 +427,7 @@ impl MappableCommand {
goto_previous_buffer, "Goto previous buffer", goto_previous_buffer, "Goto previous buffer",
goto_line_end_newline, "Goto newline at line end", goto_line_end_newline, "Goto newline at line end",
goto_first_nonwhitespace, "Goto first non-blank in line", goto_first_nonwhitespace, "Goto first non-blank in line",
goto_return_type, "Goto the return type of the current function",
trim_selections, "Trim whitespace from selections", trim_selections, "Trim whitespace from selections",
extend_to_line_start, "Extend to line start", extend_to_line_start, "Extend to line start",
extend_to_first_nonwhitespace, "Extend to first non-blank in line", extend_to_first_nonwhitespace, "Extend to first non-blank in line",
@ -5968,6 +5970,120 @@ fn add_newline_impl(cx: &mut Context, open: Open) {
doc.apply(&transaction, view.id); doc.apply(&transaction, view.id);
} }
fn add_function_parameter(cx: &mut Context) {
let (view, doc) = current!(cx.editor);
let text = doc.text();
let slice = text.slice(..);
if let Some((lang_config, syntax)) = doc.language_config().zip(doc.syntax()) {
let root = syntax.tree().root_node();
let selection = doc.selection(view.id).clone().into_single();
let Some(new_range) = movement::goto_current_function_parameters(
slice,
selection.ranges()[0],
root,
lang_config,
) else {
return;
};
let mut cursor = helix_core::tree_sitter::QueryCursor::new();
cursor.set_match_limit(1);
cursor
.set_byte_range(text.char_to_byte(new_range.anchor)..text.char_to_byte(new_range.head));
let has_args = lang_config
.textobject_query()
.and_then(|q| {
q.capture_nodes_any(&["parameter.around"], root, slice, &mut cursor)?
.next()
})
.is_some();
push_jump(view, doc);
doc.set_selection(view.id, selection.transform(|_| new_range));
if has_args {
insert_char(cx, ',');
}
collapse_selection(cx);
insert_mode(cx);
}
// };
}
fn goto_return_type(cx: &mut Context) {
let (view, doc) = current!(cx.editor);
let text = doc.text();
let slice = text.slice(..);
if let Some((lang_config, syntax)) = doc.language_config().zip(doc.syntax()) {
let root = syntax.tree().root_node();
let selection = doc.selection(view.id).clone().into_single();
let range = selection.ranges()[0];
let mut cursor = helix_core::tree_sitter::QueryCursor::new();
let byte_pos = slice.char_to_byte(range.cursor(slice));
let Some(func_range) = current_node_byte_range(
lang_config,
root,
slice,
&mut cursor,
"function.around",
byte_pos,
) else {
return;
};
cursor.set_match_limit(1);
cursor.set_byte_range(func_range.clone());
let new_byte_range = if let Some(return_node) =
lang_config.textobject_query().and_then(|q| {
q.capture_nodes_any(&["return_type.around"], root, slice, &mut cursor)?
.next()
}) {
return_node.byte_range()
} else {
// let parameters_range =
let parameters_node = lang_config.textobject_query().and_then(|q| {
q.capture_nodes_any(&["parameters.around"], root, slice, &mut cursor)?
.next()
.map(|node| node.byte_range())
});
cursor.set_byte_range(0..func_range.end + 1);
let function_body_range = current_node_byte_range(
lang_config,
root,
slice,
&mut cursor,
"function.inside",
byte_pos,
);
match (parameters_node, function_body_range) {
(Some(pr), Some(br)) => pr.end..br.start,
(_, Some(br)) => br.start..br.start,
(Some(pr), _) => pr.end..pr.end,
_ => {
return;
}
}
};
push_jump(view, doc);
let new_range = Range::new(
slice.byte_to_char(new_byte_range.start),
slice.byte_to_char(new_byte_range.end),
);
doc.set_selection(view.id, selection.transform(|_| new_range));
}
// };
}
enum IncrementDirection { enum IncrementDirection {
Increase, Increase,
Decrease, Decrease,

@ -47,6 +47,7 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
"D" => goto_declaration, "D" => goto_declaration,
"y" => goto_type_definition, "y" => goto_type_definition,
"r" => goto_reference, "r" => goto_reference,
"R" => goto_return_type,
"i" => goto_implementation, "i" => goto_implementation,
"t" => goto_window_top, "t" => goto_window_top,
"c" => goto_window_center, "c" => goto_window_center,
@ -285,6 +286,7 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
"c" => toggle_comments, "c" => toggle_comments,
"C" => toggle_block_comments, "C" => toggle_block_comments,
"A-c" => toggle_line_comments, "A-c" => toggle_line_comments,
"i" => add_function_parameter,
"?" => command_palette, "?" => command_palette,
}, },
"z" => { "View" "z" => { "View"

@ -1,6 +1,11 @@
(function_item (function_item
body: (_) @function.inside) @function.around body: (_) @function.inside) @function.around
(parameters) @parameters.around
(function_item
return_type: (_) @return_type.around )
(closure_expression (closure_expression
body: (_) @function.inside) @function.around body: (_) @function.inside) @function.around

Loading…
Cancel
Save