Improve code style for tree-sitter indentation.

Split extend logic into a separate file.
This commit is contained in:
Daniel Ebert 2022-09-17 14:03:45 +02:00 committed by Blaž Hrastnik
parent 05832f90cb
commit 2b02785f19

View file

@ -456,6 +456,67 @@ fn query_indents(
} }
} }
/// Handle extend queries. deepest_preceding is the deepest descendant of node that directly precedes the cursor position.
/// Any ancestor of deepest_preceding which is also a descendant of node may be "extended". In that case, node will be updated,
/// so that the indent computation starts with the correct syntax node.
fn extend_nodes<'a>(
node: &mut Node<'a>,
deepest_preceding: Option<Node<'a>>,
extend_captures: &HashMap<usize, Vec<ExtendCapture>>,
text: RopeSlice,
line: usize,
tab_width: usize,
) {
if let Some(mut deepest_preceding) = deepest_preceding {
let mut stop_extend = false;
while deepest_preceding != *node {
let mut extend_node = false;
if let Some(captures) = extend_captures.get(&deepest_preceding.id()) {
for capture in captures {
match capture {
ExtendCapture::StopExtend => {
stop_extend = true;
}
ExtendCapture::ExtendIndented => {
// We extend the node if
// - the cursor is on the same line as the end of the node OR
// - the line that the cursor is on is more indented than the
// first line of the node
if deepest_preceding.end_position().row == line {
extend_node = true;
} else {
let cursor_indent =
indent_level_for_line(text.line(line), tab_width);
let node_indent = indent_level_for_line(
text.line(deepest_preceding.start_position().row),
tab_width,
);
if cursor_indent > node_indent {
extend_node = true;
}
}
}
}
}
}
// If we encountered some `StopExtend` capture before, we don't
// extend the node even if we otherwise would
match (extend_node, stop_extend) {
(true, true) => {
stop_extend = false;
}
(true, false) => {
*node = deepest_preceding;
break;
}
_ => {}
};
// This parent always exists since node is an ancestor of deepest_preceding
deepest_preceding = deepest_preceding.parent().unwrap();
}
}
}
/// Use the syntax tree to determine the indentation for a given position. /// Use the syntax tree to determine the indentation for a given position.
/// This can be used in 2 ways: /// This can be used in 2 ways:
/// ///
@ -510,100 +571,54 @@ pub fn treesitter_indent_for_pos(
.tree() .tree()
.root_node() .root_node()
.descendant_for_byte_range(byte_pos, byte_pos)?; .descendant_for_byte_range(byte_pos, byte_pos)?;
let (query_result, prev_child) = crate::syntax::PARSER.with(|ts_parser| { let (query_result, deepest_preceding) = {
let mut ts_parser = ts_parser.borrow_mut();
let mut cursor = ts_parser.cursors.pop().unwrap_or_else(QueryCursor::new);
// The query range should intersect with all nodes directly preceding // The query range should intersect with all nodes directly preceding
// the cursor in case one of them is extended. // the cursor in case one of them is extended.
// prev_child is the deepest such node. let mut deepest_preceding = None; // The deepest node preceding the cursor
let (query_range, prev_child) = { let mut tree_cursor = node.walk();
// TODO Is there some way we can reuse this cursor? for child in node.children(&mut tree_cursor) {
let mut tree_cursor = node.walk(); if child.byte_range().end <= byte_pos {
let mut prev_child = None; deepest_preceding = Some(child);
for child in node.children(&mut tree_cursor) {
if child.byte_range().end <= byte_pos {
prev_child = Some(child);
}
} }
match prev_child { }
Some(mut prev_child) => { deepest_preceding = deepest_preceding.map(|mut prec| {
// Get the deepest directly preceding node // Get the deepest directly preceding node
while prev_child.child_count() > 0 { while prec.child_count() > 0 {
prev_child = prev_child.child(prev_child.child_count() - 1).unwrap(); prec = prec.child(prec.child_count() - 1).unwrap();
}
(
prev_child.byte_range().end - 1..byte_pos + 1,
Some(prev_child),
)
}
None => (byte_pos..byte_pos + 1, None),
} }
}; prec
let query_result = query_indents( });
query, let query_range = deepest_preceding
syntax, .map(|prec| prec.byte_range().end - 1..byte_pos + 1)
&mut cursor, .unwrap_or(byte_pos..byte_pos + 1);
text,
query_range, crate::syntax::PARSER.with(|ts_parser| {
new_line.then(|| (line, byte_pos)), let mut ts_parser = ts_parser.borrow_mut();
); let mut cursor = ts_parser.cursors.pop().unwrap_or_else(QueryCursor::new);
ts_parser.cursors.push(cursor); let query_result = query_indents(
(query_result, prev_child) query,
}); syntax,
&mut cursor,
text,
query_range,
new_line.then(|| (line, byte_pos)),
);
ts_parser.cursors.push(cursor);
(query_result, deepest_preceding)
})
};
let indent_captures = query_result.indent_captures; let indent_captures = query_result.indent_captures;
let extend_captures = query_result.extend_captures; let extend_captures = query_result.extend_captures;
// Check for extend captures (starting with the deepest // Check for extend captures, potentially changing the node that the indent calculation starts with
// candidate node and then going up the syntax tree). extend_nodes(
if let Some(mut prev_child) = prev_child { &mut node,
let mut stop_extend = false; deepest_preceding,
while prev_child != node { &extend_captures,
let mut extend_node = false; text,
if let Some(captures) = extend_captures.get(&prev_child.id()) { line,
for capture in captures { tab_width,
match capture { );
ExtendCapture::StopExtend => {
stop_extend = true;
}
ExtendCapture::ExtendIndented => {
// We extend the node if
// - the cursor is on the same line as the end of the node OR
// - the line that the cursor is on is more indented than the
// first line of the node
if prev_child.end_position().row == line {
extend_node = true;
} else {
let cursor_indent =
indent_level_for_line(text.line(line), tab_width);
let node_indent = indent_level_for_line(
text.line(prev_child.start_position().row),
tab_width,
);
if cursor_indent > node_indent {
extend_node = true;
}
}
}
}
}
}
// If we encountered some `StopExtend` capture before, we don't
// extend the node even if we otherwise would
match (extend_node, stop_extend) {
(true, true) => {
stop_extend = false;
}
(true, false) => {
node = prev_child;
break;
}
_ => {}
};
// This parent always exists since node is an ancestor of prev_child
prev_child = prev_child.parent().unwrap();
}
}
let mut first_in_line = get_first_in_line(node, new_line.then(|| byte_pos)); let mut first_in_line = get_first_in_line(node, new_line.then(|| byte_pos));
let mut result = Indentation::default(); let mut result = Indentation::default();