From 7fb5fe036a70e0018739ec1a251f8c1602c74724 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 5 Oct 2022 17:07:35 -0700 Subject: [PATCH] Derive indent size from the language at the cursor when auto-indenting --- crates/editor/src/multi_buffer.rs | 16 ++++++++-- crates/language/src/buffer.rs | 52 ++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index cf9f29d73e..71cbefb78f 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -1967,8 +1967,10 @@ impl MultiBufferSnapshot { let mut rows_for_excerpt = Vec::new(); let mut cursor = self.excerpts.cursor::(); - let mut rows = rows.into_iter().peekable(); + let mut prev_row = u32::MAX; + let mut prev_language_indent_size = IndentSize::default(); + while let Some(row) = rows.next() { cursor.seek(&Point::new(row, 0), Bias::Right, &()); let excerpt = match cursor.item() { @@ -1976,7 +1978,17 @@ impl MultiBufferSnapshot { _ => continue, }; - let single_indent_size = excerpt.buffer.single_indent_size(cx); + // Retrieve the language and indent size once for each disjoint region being indented. + let single_indent_size = if row.saturating_sub(1) == prev_row { + prev_language_indent_size + } else { + excerpt + .buffer + .language_indent_size_at(Point::new(row, 0), cx) + }; + prev_language_indent_size = single_indent_size; + prev_row = row; + let start_buffer_row = excerpt.range.context.start.to_point(&excerpt.buffer).row; let start_multibuffer_row = cursor.start().row; diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index b53a9e5573..9a1c292319 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -84,14 +84,15 @@ pub struct BufferSnapshot { parse_count: usize, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] pub struct IndentSize { pub len: u32, pub kind: IndentKind, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] pub enum IndentKind { + #[default] Space, Tab, } @@ -236,7 +237,6 @@ pub enum AutoindentMode { struct AutoindentRequest { before_edit: BufferSnapshot, entries: Vec, - indent_size: IndentSize, is_block_mode: bool, } @@ -249,6 +249,7 @@ struct AutoindentRequestEntry { /// only be adjusted if the suggested indentation level has *changed* /// since the edit was made. first_line_is_new: bool, + indent_size: IndentSize, original_indent_column: Option, } @@ -794,10 +795,13 @@ impl Buffer { // buffer before this batch of edits. let mut row_ranges = Vec::new(); let mut old_to_new_rows = BTreeMap::new(); + let mut language_indent_sizes_by_new_row = Vec::new(); for entry in &request.entries { let position = entry.range.start; let new_row = position.to_point(&snapshot).row; let new_end_row = entry.range.end.to_point(&snapshot).row + 1; + language_indent_sizes_by_new_row.push((new_row, entry.indent_size)); + if !entry.first_line_is_new { let old_row = position.to_point(&request.before_edit).row; old_to_new_rows.insert(old_row, new_row); @@ -811,6 +815,8 @@ impl Buffer { let mut old_suggestions = BTreeMap::::default(); let old_edited_ranges = contiguous_ranges(old_to_new_rows.keys().copied(), max_rows_between_yields); + let mut language_indent_sizes = language_indent_sizes_by_new_row.iter().peekable(); + let mut language_indent_size = IndentSize::default(); for old_edited_range in old_edited_ranges { let suggestions = request .before_edit @@ -819,6 +825,17 @@ impl Buffer { .flatten(); for (old_row, suggestion) in old_edited_range.zip(suggestions) { if let Some(suggestion) = suggestion { + let new_row = *old_to_new_rows.get(&old_row).unwrap(); + + // Find the indent size based on the language for this row. + while let Some((row, size)) = language_indent_sizes.peek() { + if *row > new_row { + break; + } + language_indent_size = *size; + language_indent_sizes.next(); + } + let suggested_indent = old_to_new_rows .get(&suggestion.basis_row) .and_then(|from_row| old_suggestions.get(from_row).copied()) @@ -827,9 +844,8 @@ impl Buffer { .before_edit .indent_size_for_line(suggestion.basis_row) }) - .with_delta(suggestion.delta, request.indent_size); - old_suggestions - .insert(*old_to_new_rows.get(&old_row).unwrap(), suggested_indent); + .with_delta(suggestion.delta, language_indent_size); + old_suggestions.insert(new_row, suggested_indent); } } yield_now().await; @@ -850,6 +866,8 @@ impl Buffer { // Compute new suggestions for each line, but only include them in the result // if they differ from the old suggestion for that line. + let mut language_indent_sizes = language_indent_sizes_by_new_row.iter().peekable(); + let mut language_indent_size = IndentSize::default(); for new_edited_row_range in new_edited_row_ranges { let suggestions = snapshot .suggest_autoindents(new_edited_row_range.clone()) @@ -857,13 +875,22 @@ impl Buffer { .flatten(); for (new_row, suggestion) in new_edited_row_range.zip(suggestions) { if let Some(suggestion) = suggestion { + // Find the indent size based on the language for this row. + while let Some((row, size)) = language_indent_sizes.peek() { + if *row > new_row { + break; + } + language_indent_size = *size; + language_indent_sizes.next(); + } + let suggested_indent = indent_sizes .get(&suggestion.basis_row) .copied() .unwrap_or_else(|| { snapshot.indent_size_for_line(suggestion.basis_row) }) - .with_delta(suggestion.delta, request.indent_size); + .with_delta(suggestion.delta, language_indent_size); if old_suggestions .get(&new_row) .map_or(true, |old_indentation| { @@ -1194,7 +1221,6 @@ impl Buffer { let edit_id = edit_operation.local_timestamp(); if let Some((before_edit, mode)) = autoindent_request { - let indent_size = before_edit.single_indent_size(cx); let (start_columns, is_block_mode) = match mode { AutoindentMode::Block { original_indent_columns: start_columns, @@ -1243,6 +1269,7 @@ impl Buffer { AutoindentRequestEntry { first_line_is_new, original_indent_column: start_column, + indent_size: before_edit.language_indent_size_at(range.start, cx), range: self.anchor_before(new_start + range_of_insertion_to_indent.start) ..self.anchor_after(new_start + range_of_insertion_to_indent.end), } @@ -1252,7 +1279,6 @@ impl Buffer { self.autoindent_requests.push(Arc::new(AutoindentRequest { before_edit, entries, - indent_size, is_block_mode, })); } @@ -1570,8 +1596,8 @@ impl BufferSnapshot { indent_size_for_line(self, row) } - pub fn single_indent_size(&self, cx: &AppContext) -> IndentSize { - let language_name = self.language().map(|language| language.name()); + pub fn language_indent_size_at(&self, position: T, cx: &AppContext) -> IndentSize { + let language_name = self.language_at(position).map(|language| language.name()); let settings = cx.global::(); if settings.hard_tabs(language_name.as_deref()) { IndentSize::tab() @@ -1832,10 +1858,6 @@ impl BufferSnapshot { } } - pub fn language(&self) -> Option<&Arc> { - self.language.as_ref() - } - pub fn language_at(&self, position: D) -> Option<&Arc> { let offset = position.to_offset(self); self.syntax