Skip to content

Commit

Permalink
Simplify first-in-line computation for indent queries.
Browse files Browse the repository at this point in the history
This fixes issues for grammars with nodes containing only whitespace (e.g. Go).
  • Loading branch information
Triton171 committed Apr 20, 2024
1 parent 211f368 commit 269cd2e
Showing 1 changed file with 11 additions and 38 deletions.
49 changes: 11 additions & 38 deletions helix-core/src/indent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,41 +247,18 @@ fn add_indent_level(
}
}

/// Computes for node and all ancestors whether they are the first node on their line.
/// The first entry in the return value represents the root node, the last one the node itself
fn get_first_in_line(mut node: Node, new_line_byte_pos: Option<usize>) -> Vec<bool> {
let mut first_in_line = Vec::new();
loop {
if let Some(prev) = node.prev_sibling() {
// If we insert a new line, the first node at/after the cursor is considered to be the first in its line
let first = prev.end_position().row != node.start_position().row
|| new_line_byte_pos.map_or(false, |byte_pos| {
node.start_byte() >= byte_pos && prev.start_byte() < byte_pos
});
first_in_line.push(Some(first));
} else {
// Nodes that have no previous siblings are first in their line if and only if their parent is
// (which we don't know yet)
first_in_line.push(None);
}
if let Some(parent) = node.parent() {
node = parent;
} else {
break;
}
}

let mut result = Vec::with_capacity(first_in_line.len());
let mut parent_is_first = true; // The root node is by definition the first node in its line
for first in first_in_line.into_iter().rev() {
if let Some(first) = first {
result.push(first);
parent_is_first = first;
} else {
result.push(parent_is_first);
/// Return true if only whitespace comes before the node on its line.
/// If given, new_line_byte_pos is treated the same way as any existing newline.
fn is_first_in_line(node: Node, text: RopeSlice, new_line_byte_pos: Option<usize>) -> bool {
let mut line_start_byte_pos = text.line_to_byte(node.start_position().row);
if let Some(pos) = new_line_byte_pos {
if line_start_byte_pos < pos && pos <= node.start_byte() {
line_start_byte_pos = pos;
}
}
result
text.byte_slice(line_start_byte_pos..node.start_byte())
.chars()
.all(|c| c.is_whitespace())
}

/// The total indent for some line of code.
Expand Down Expand Up @@ -852,7 +829,6 @@ pub fn treesitter_indent_for_pos<'a>(
byte_pos,
new_line_byte_pos,
)?;
let mut first_in_line = get_first_in_line(node, new_line.then_some(byte_pos));

let mut result = Indentation::default();
// We always keep track of all the indent changes on one line, in order to only indent once
Expand All @@ -861,9 +837,7 @@ pub fn treesitter_indent_for_pos<'a>(
let mut indent_for_line_below = Indentation::default();

loop {
// This can safely be unwrapped because `first_in_line` contains
// one entry for each ancestor of the node (which is what we iterate over)
let is_first = *first_in_line.last().unwrap();
let is_first = is_first_in_line(node, text, new_line_byte_pos);

// Apply all indent definitions for this node.
// Since we only iterate over each node once, we can remove the
Expand Down Expand Up @@ -906,7 +880,6 @@ pub fn treesitter_indent_for_pos<'a>(
}

node = parent;
first_in_line.pop();
} else {
// Only add the indentation for the line below if that line
// is not after the line that the indentation is calculated for.
Expand Down

0 comments on commit 269cd2e

Please sign in to comment.