Generalize strategy for processing indentation ranges

* Take into account the ranges' start and end columns, not just the rows
* Generalize the approach to dedenting
This commit is contained in:
Max Brunsfeld 2021-10-06 22:16:18 -07:00
parent b83b4ad7c7
commit add1467d32

View file

@ -151,8 +151,6 @@ impl Drop for QueryCursorHandle {
QUERY_CURSORS.lock().push(cursor) QUERY_CURSORS.lock().push(cursor)
} }
} }
const SPACES: &'static str = " ";
pub struct Buffer { pub struct Buffer {
fragments: SumTree<Fragment>, fragments: SumTree<Fragment>,
visible_text: Rope, visible_text: Rope,
@ -972,8 +970,8 @@ impl Buffer {
language: Arc<Language>, language: Arc<Language>,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) { ) {
let mut autoindent_requests_by_row = BTreeMap::<u32, AutoindentRequest>::default();
let mut autoindent_requests = mem::take(&mut self.autoindent_requests); let mut autoindent_requests = mem::take(&mut self.autoindent_requests);
let mut autoindent_requests_by_row = BTreeMap::<u32, AutoindentRequest>::default();
for request in autoindent_requests.drain(..) { for request in autoindent_requests.drain(..) {
let row = request.position.to_point(&*self).row; let row = request.position.to_point(&*self).row;
autoindent_requests_by_row autoindent_requests_by_row
@ -986,10 +984,9 @@ impl Buffer {
} }
self.autoindent_requests = autoindent_requests; self.autoindent_requests = autoindent_requests;
let mut cursor = QueryCursorHandle::new();
self.start_transaction(None).unwrap();
let mut row_range = None; let mut row_range = None;
let mut cursor = QueryCursorHandle::new();
self.start_transaction(None).unwrap();
for row in autoindent_requests_by_row.keys().copied() { for row in autoindent_requests_by_row.keys().copied() {
match &mut row_range { match &mut row_range {
None => row_range = Some(row..(row + 1)), None => row_range = Some(row..(row + 1)),
@ -1038,7 +1035,6 @@ impl Buffer {
cursor: &mut QueryCursor, cursor: &mut QueryCursor,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) { ) {
let max_row = self.row_count() - 1;
let prev_non_blank_row = self.prev_non_blank_row(row_range.start); let prev_non_blank_row = self.prev_non_blank_row(row_range.start);
// Get the "indentation ranges" that intersect this row range. // Get the "indentation ranges" that intersect this row range.
@ -1048,107 +1044,77 @@ impl Buffer {
Point::new(prev_non_blank_row.unwrap_or(row_range.start), 0).into() Point::new(prev_non_blank_row.unwrap_or(row_range.start), 0).into()
..Point::new(row_range.end, 0).into(), ..Point::new(row_range.end, 0).into(),
); );
let mut indentation_ranges = Vec::<Range<u32>>::new(); let mut indentation_ranges = Vec::<(Range<Point>, &'static str)>::new();
for mat in cursor.matches( for mat in cursor.matches(
&language.indents_query, &language.indents_query,
new_tree.root_node(), new_tree.root_node(),
TextProvider(&self.visible_text), TextProvider(&self.visible_text),
) { ) {
let mut start_row = None; let mut node_kind = "";
let mut end_row = None; let mut start: Option<Point> = None;
let mut end: Option<Point> = None;
for capture in mat.captures { for capture in mat.captures {
if Some(capture.index) == indent_capture_ix { if Some(capture.index) == indent_capture_ix {
start_row.get_or_insert(capture.node.start_position().row as u32); node_kind = capture.node.kind();
end_row.get_or_insert(max_row.min(capture.node.end_position().row as u32 + 1)); start.get_or_insert(capture.node.start_position().into());
end.get_or_insert(capture.node.end_position().into());
} else if Some(capture.index) == end_capture_ix { } else if Some(capture.index) == end_capture_ix {
end_row = Some(capture.node.start_position().row as u32); end = Some(capture.node.start_position().into());
} }
} }
if let Some((start_row, end_row)) = start_row.zip(end_row) {
let range = start_row..end_row; if let Some((start, end)) = start.zip(end) {
match indentation_ranges.binary_search_by_key(&range.start, |r| r.start) { if start.row == end.row {
Err(ix) => indentation_ranges.insert(ix, range), continue;
}
let range = start..end;
match indentation_ranges.binary_search_by_key(&range.start, |r| r.0.start) {
Err(ix) => indentation_ranges.insert(ix, (range, node_kind)),
Ok(ix) => { Ok(ix) => {
let prev_range = &mut indentation_ranges[ix]; let prev_range = &mut indentation_ranges[ix];
prev_range.end = prev_range.end.max(range.end); prev_range.0.end = prev_range.0.end.max(range.end);
} }
} }
} }
} }
#[derive(Debug)] let mut prev_row = prev_non_blank_row.unwrap_or(0);
struct Adjustment { let mut prev_indent_column =
row: u32, prev_non_blank_row.map_or(0, |prev_row| self.indent_column_for_line(prev_row, cx));
indent: bool,
outdent: bool,
}
let mut adjustments = Vec::<Adjustment>::new();
'outer: for range in &indentation_ranges {
let mut indent_row = range.start;
loop {
indent_row += 1;
if indent_row > max_row {
continue 'outer;
}
if row_range.contains(&indent_row) || !self.is_line_blank(indent_row) {
break;
}
}
let mut outdent_row = range.end;
loop {
outdent_row += 1;
if outdent_row > max_row {
break;
}
if row_range.contains(&outdent_row) || !self.is_line_blank(outdent_row) {
break;
}
}
match adjustments.binary_search_by_key(&indent_row, |a| a.row) {
Ok(ix) => adjustments[ix].indent = true,
Err(ix) => adjustments.insert(
ix,
Adjustment {
row: indent_row,
indent: true,
outdent: false,
},
),
}
match adjustments.binary_search_by_key(&outdent_row, |a| a.row) {
Ok(ix) => adjustments[ix].outdent = true,
Err(ix) => adjustments.insert(
ix,
Adjustment {
row: outdent_row,
indent: false,
outdent: true,
},
),
}
}
let mut adjustments = adjustments.iter().peekable();
for row in row_range { for row in row_range {
if let Some(request) = autoindent_requests.get(&row) { let request = autoindent_requests.get(&row).unwrap();
while let Some(adjustment) = adjustments.peek() { let row_start = Point::new(row, self.indent_column_for_line(row, cx));
if adjustment.row < row {
adjustments.next(); eprintln!("autoindent row: {:?}", row);
} else {
if adjustment.row == row { let mut increase_from_prev_row = false;
match (adjustment.indent, adjustment.outdent) { let mut dedent_to_row = u32::MAX;
(true, false) => self.indent_line(row, request.indent_size, cx), for (range, node_kind) in &indentation_ranges {
(false, true) => self.outdent_line(row, request.indent_size, cx), if range.start.row == prev_row && prev_row < row && range.end > row_start {
_ => {} eprintln!(" indent because of {} {:?}", node_kind, range);
} increase_from_prev_row = true;
} break;
break; }
}
if range.start.row < prev_row
&& (Point::new(prev_row, 0)..=row_start).contains(&range.end)
{
eprintln!(" outdent because of {} {:?}", node_kind, range);
dedent_to_row = dedent_to_row.min(range.start.row);
} }
} }
let mut indent_column = prev_indent_column;
if increase_from_prev_row {
indent_column += request.indent_size as u32;
} else if dedent_to_row < row {
indent_column = self.indent_column_for_line(dedent_to_row, cx);
}
self.set_indent_column_for_line(row, indent_column, cx);
prev_indent_column = indent_column;
prev_row = row;
} }
} }
@ -1162,39 +1128,46 @@ impl Buffer {
None None
} }
fn next_non_blank_row(&self, mut row: u32) -> Option<u32> { fn indent_column_for_line(&mut self, row: u32, cx: &mut ModelContext<Self>) -> u32 {
let row_count = self.row_count(); let mut result = 0;
row += 1;
while row < row_count {
if !self.is_line_blank(row) {
return Some(row);
}
row += 1;
}
None
}
fn indent_line(&mut self, row: u32, size: u8, cx: &mut ModelContext<Self>) {
self.edit(
[Point::new(row, 0)..Point::new(row, 0)],
&SPACES[..(size as usize)],
cx,
)
}
fn outdent_line(&mut self, row: u32, size: u8, cx: &mut ModelContext<Self>) {
let mut end_column = 0;
for c in self.chars_at(Point::new(row, 0)) { for c in self.chars_at(Point::new(row, 0)) {
if c == ' ' { if c == ' ' {
end_column += 1; result += 1;
if end_column == size as u32 {
break;
}
} else { } else {
break; break;
} }
} }
self.edit([Point::new(row, 0)..Point::new(row, end_column)], "", cx); result
}
fn set_indent_column_for_line(&mut self, row: u32, column: u32, cx: &mut ModelContext<Self>) {
let current_column = self.indent_column_for_line(row, cx);
if column > current_column {
let offset = self.visible_text.to_offset(Point::new(row, 0));
// TODO: do this differently. By replacing the preceding newline,
// we force the new indentation to come before any left-biased anchors
// on the line.
let delta = (column - current_column) as usize;
if offset > 0 {
let mut prefix = String::with_capacity(1 + delta);
prefix.push('\n');
prefix.extend(std::iter::repeat(' ').take(delta));
self.edit([(offset - 1)..offset], prefix, cx);
} else {
self.edit(
[offset..offset],
std::iter::repeat(' ').take(delta).collect::<String>(),
cx,
);
}
} else if column < current_column {
self.edit(
[Point::new(row, 0)..Point::new(row, current_column - column)],
"",
cx,
);
}
} }
fn is_line_blank(&self, row: u32) -> bool { fn is_line_blank(&self, row: u32) -> bool {