diff --git a/crates/buffer/src/lib.rs b/crates/buffer/src/lib.rs index edeac8cec6..fffb6179b9 100644 --- a/crates/buffer/src/lib.rs +++ b/crates/buffer/src/lib.rs @@ -151,8 +151,6 @@ impl Drop for QueryCursorHandle { QUERY_CURSORS.lock().push(cursor) } } -const SPACES: &'static str = " "; - pub struct Buffer { fragments: SumTree, visible_text: Rope, @@ -972,8 +970,8 @@ impl Buffer { language: Arc, cx: &mut ModelContext, ) { - let mut autoindent_requests_by_row = BTreeMap::::default(); let mut autoindent_requests = mem::take(&mut self.autoindent_requests); + let mut autoindent_requests_by_row = BTreeMap::::default(); for request in autoindent_requests.drain(..) { let row = request.position.to_point(&*self).row; autoindent_requests_by_row @@ -986,10 +984,9 @@ impl Buffer { } self.autoindent_requests = autoindent_requests; - let mut cursor = QueryCursorHandle::new(); - - self.start_transaction(None).unwrap(); let mut row_range = None; + let mut cursor = QueryCursorHandle::new(); + self.start_transaction(None).unwrap(); for row in autoindent_requests_by_row.keys().copied() { match &mut row_range { None => row_range = Some(row..(row + 1)), @@ -1038,7 +1035,6 @@ impl Buffer { cursor: &mut QueryCursor, cx: &mut ModelContext, ) { - let max_row = self.row_count() - 1; let prev_non_blank_row = self.prev_non_blank_row(row_range.start); // 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(row_range.end, 0).into(), ); - let mut indentation_ranges = Vec::>::new(); + let mut indentation_ranges = Vec::<(Range, &'static str)>::new(); for mat in cursor.matches( &language.indents_query, new_tree.root_node(), TextProvider(&self.visible_text), ) { - let mut start_row = None; - let mut end_row = None; + let mut node_kind = ""; + let mut start: Option = None; + let mut end: Option = None; for capture in mat.captures { if Some(capture.index) == indent_capture_ix { - start_row.get_or_insert(capture.node.start_position().row as u32); - end_row.get_or_insert(max_row.min(capture.node.end_position().row as u32 + 1)); + node_kind = capture.node.kind(); + 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 { - 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; - match indentation_ranges.binary_search_by_key(&range.start, |r| r.start) { - Err(ix) => indentation_ranges.insert(ix, range), + + if let Some((start, end)) = start.zip(end) { + if start.row == end.row { + 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) => { 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)] - struct Adjustment { - row: u32, - indent: bool, - outdent: bool, - } - - let mut adjustments = Vec::::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(); + let mut prev_row = prev_non_blank_row.unwrap_or(0); + let mut prev_indent_column = + prev_non_blank_row.map_or(0, |prev_row| self.indent_column_for_line(prev_row, cx)); for row in row_range { - if let Some(request) = autoindent_requests.get(&row) { - while let Some(adjustment) = adjustments.peek() { - if adjustment.row < row { - adjustments.next(); - } else { - if adjustment.row == row { - match (adjustment.indent, adjustment.outdent) { - (true, false) => self.indent_line(row, request.indent_size, cx), - (false, true) => self.outdent_line(row, request.indent_size, cx), - _ => {} - } - } - break; - } + let request = autoindent_requests.get(&row).unwrap(); + let row_start = Point::new(row, self.indent_column_for_line(row, cx)); + + eprintln!("autoindent row: {:?}", row); + + let mut increase_from_prev_row = false; + let mut dedent_to_row = u32::MAX; + for (range, node_kind) in &indentation_ranges { + 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; + } + + 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 } - fn next_non_blank_row(&self, mut row: u32) -> Option { - let row_count = self.row_count(); - 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.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) { - let mut end_column = 0; + fn indent_column_for_line(&mut self, row: u32, cx: &mut ModelContext) -> u32 { + let mut result = 0; for c in self.chars_at(Point::new(row, 0)) { if c == ' ' { - end_column += 1; - if end_column == size as u32 { - break; - } + result += 1; } else { 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) { + 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::(), + 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 {