Derive indent size from the language at the cursor when auto-indenting

This commit is contained in:
Max Brunsfeld 2022-10-05 17:07:35 -07:00
parent 8b86781ad1
commit 7fb5fe036a
2 changed files with 51 additions and 17 deletions

View File

@ -1967,8 +1967,10 @@ impl MultiBufferSnapshot {
let mut rows_for_excerpt = Vec::new();
let mut cursor = self.excerpts.cursor::<Point>();
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;

View File

@ -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<AutoindentRequestEntry>,
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<u32>,
}
@ -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::<u32, IndentSize>::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<T: ToOffset>(&self, position: T, cx: &AppContext) -> IndentSize {
let language_name = self.language_at(position).map(|language| language.name());
let settings = cx.global::<Settings>();
if settings.hard_tabs(language_name.as_deref()) {
IndentSize::tab()
@ -1832,10 +1858,6 @@ impl BufferSnapshot {
}
}
pub fn language(&self) -> Option<&Arc<Language>> {
self.language.as_ref()
}
pub fn language_at<D: ToOffset>(&self, position: D) -> Option<&Arc<Language>> {
let offset = position.to_offset(self);
self.syntax