aboutsummaryrefslogtreecommitdiff
path: root/helix-core
diff options
context:
space:
mode:
authorSkyler Hawthorne2022-08-17 01:28:41 +0000
committerMichael Davis2023-08-01 14:41:42 +0000
commit93acb538121cab36712f40f26fa287df93817de5 (patch)
treeb0ad0a3037d62750b4da6498a96b82758c25a18c /helix-core
parent1d702ea191e9bf84566ea0008755d1ab53d19265 (diff)
add node boundary movement
Diffstat (limited to 'helix-core')
-rw-r--r--helix-core/src/movement.rs81
1 files changed, 80 insertions, 1 deletions
diff --git a/helix-core/src/movement.rs b/helix-core/src/movement.rs
index 2b29f36d..6c4f3f53 100644
--- a/helix-core/src/movement.rs
+++ b/helix-core/src/movement.rs
@@ -16,7 +16,7 @@ use crate::{
syntax::LanguageConfiguration,
text_annotations::TextAnnotations,
textobject::TextObject,
- visual_offset_from_block, Range, RopeSlice,
+ visual_offset_from_block, Range, RopeSlice, Selection, Syntax,
};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -556,6 +556,85 @@ pub fn goto_treesitter_object(
last_range
}
+fn find_parent_start(mut node: Node) -> Option<Node> {
+ let start = node.start_byte();
+
+ while node.start_byte() >= start || !node.is_named() {
+ node = node.parent()?;
+ }
+
+ Some(node)
+}
+
+pub fn move_parent_node_end(
+ syntax: &Syntax,
+ text: RopeSlice,
+ selection: Selection,
+ dir: Direction,
+ movement: Movement,
+) -> Selection {
+ let tree = syntax.tree();
+
+ selection.transform(|range| {
+ let start_from = text.char_to_byte(range.from());
+ let start_to = text.char_to_byte(range.to());
+
+ let mut node = match tree
+ .root_node()
+ .named_descendant_for_byte_range(start_from, start_to)
+ {
+ Some(node) => node,
+ None => {
+ log::debug!(
+ "no descendant found for byte range: {} - {}",
+ start_from,
+ start_to
+ );
+ return range;
+ }
+ };
+
+ let mut end_head = match dir {
+ // moving forward, we always want to move one past the end of the
+ // current node, so use the end byte of the current node, which is an exclusive
+ // end of the range
+ Direction::Forward => text.byte_to_char(node.end_byte()),
+
+ // moving backward, we want the cursor to land on the start char of
+ // the current node, or if it is already at the start of a node, to traverse up to
+ // the parent
+ Direction::Backward => {
+ let end_head = text.byte_to_char(node.start_byte());
+
+ // if we're already on the beginning, look up to the parent
+ if end_head == range.cursor(text) {
+ node = find_parent_start(node).unwrap_or(node);
+ text.byte_to_char(node.start_byte())
+ } else {
+ end_head
+ }
+ }
+ };
+
+ if movement == Movement::Move {
+ // preserve direction of original range
+ if range.direction() == Direction::Forward {
+ Range::new(end_head, end_head + 1)
+ } else {
+ Range::new(end_head + 1, end_head)
+ }
+ } else {
+ // if we end up with a forward range, then adjust it to be one past
+ // where we want
+ if end_head >= range.anchor {
+ end_head += 1;
+ }
+
+ Range::new(range.anchor, end_head)
+ }
+ })
+}
+
#[cfg(test)]
mod test {
use ropey::Rope;