indent guides: Fix tab handling (#12249)

Fixes indent guides when using tabs, 
Fixes: #12209, fixes #12210

Release Notes:

- N/A
This commit is contained in:
Bennet Bo Fenner 2024-05-24 18:24:03 +02:00 committed by GitHub
parent 414f97f702
commit af3d7a60c8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 236 additions and 133 deletions

View file

@ -541,7 +541,7 @@ pub struct IndentGuide {
pub start_row: BufferRow,
pub end_row: BufferRow,
pub depth: u32,
pub indent_size: u32,
pub tab_size: u32,
}
impl IndentGuide {
@ -550,19 +550,19 @@ impl IndentGuide {
start_row: BufferRow,
end_row: BufferRow,
depth: u32,
indent_size: u32,
tab_size: u32,
) -> Self {
Self {
buffer_id,
start_row,
end_row,
depth,
indent_size,
tab_size,
}
}
pub fn indent_width(&self) -> u32 {
self.indent_size * self.depth
pub fn indent_level(&self) -> u32 {
self.depth * self.tab_size
}
}
@ -3118,7 +3118,7 @@ impl BufferSnapshot {
range: Range<Anchor>,
cx: &AppContext,
) -> Vec<IndentGuide> {
fn indent_size_for_row(this: &BufferSnapshot, row: BufferRow, cx: &AppContext) -> u32 {
fn tab_size_for_row(this: &BufferSnapshot, row: BufferRow, cx: &AppContext) -> u32 {
let language = this.language_at(Point::new(row, 0));
language_settings(language, None, cx).tab_size.get() as u32
}
@ -3133,19 +3133,19 @@ impl BufferSnapshot {
let mut indent_stack = SmallVec::<[IndentGuide; 8]>::new();
// TODO: This should be calculated for every row but it is pretty expensive
let indent_size = indent_size_for_row(self, start_row, cx);
let tab_size = tab_size_for_row(self, start_row, cx);
while let Some((first_row, mut line_indent, empty)) = row_indents.next() {
while let Some((first_row, mut line_indent)) = row_indents.next() {
let current_depth = indent_stack.len() as u32;
// When encountering empty, continue until found useful line indent
// then add to the indent stack with the depth found
let mut found_indent = false;
let mut last_row = first_row;
if empty {
if line_indent.is_line_empty() {
let mut trailing_row = end_row;
while !found_indent {
let (target_row, new_line_indent, empty) =
let (target_row, new_line_indent) =
if let Some(display_row) = row_indents.next() {
display_row
} else {
@ -3160,11 +3160,11 @@ impl BufferSnapshot {
{
break;
}
let (new_line_indent, empty) = self.line_indent_for_row(trailing_row);
(trailing_row, new_line_indent, empty)
let new_line_indent = self.line_indent_for_row(trailing_row);
(trailing_row, new_line_indent)
};
if empty {
if new_line_indent.is_line_empty() {
continue;
}
last_row = target_row.min(end_row);
@ -3177,7 +3177,8 @@ impl BufferSnapshot {
}
let depth = if found_indent {
line_indent / indent_size + ((line_indent % indent_size) > 0) as u32
line_indent.len(tab_size) / tab_size
+ ((line_indent.len(tab_size) % tab_size) > 0) as u32
} else {
current_depth
};
@ -3203,7 +3204,7 @@ impl BufferSnapshot {
start_row: first_row,
end_row: last_row,
depth: next_depth,
indent_size,
tab_size,
});
}
}
@ -3221,20 +3222,22 @@ impl BufferSnapshot {
pub async fn enclosing_indent(
&self,
mut buffer_row: BufferRow,
) -> Option<(Range<BufferRow>, u32)> {
) -> Option<(Range<BufferRow>, LineIndent)> {
let max_row = self.max_point().row;
if buffer_row >= max_row {
return None;
}
let (mut target_indent_size, is_blank) = self.line_indent_for_row(buffer_row);
let mut target_indent = self.line_indent_for_row(buffer_row);
// If the current row is at the start of an indented block, we want to return this
// block as the enclosing indent.
if !is_blank && buffer_row < max_row {
let (next_line_indent, is_blank) = self.line_indent_for_row(buffer_row + 1);
if !is_blank && target_indent_size < next_line_indent {
target_indent_size = next_line_indent;
if !target_indent.is_line_empty() && buffer_row < max_row {
let next_line_indent = self.line_indent_for_row(buffer_row + 1);
if !next_line_indent.is_line_empty()
&& target_indent.raw_len() < next_line_indent.raw_len()
{
target_indent = next_line_indent;
buffer_row += 1;
}
}
@ -3246,12 +3249,12 @@ impl BufferSnapshot {
let mut accessed_row_counter = 0;
// If there is a blank line at the current row, search for the next non indented lines
if is_blank {
if target_indent.is_line_empty() {
let start = buffer_row.saturating_sub(SEARCH_WHITESPACE_ROW_LIMIT);
let end = (max_row + 1).min(buffer_row + SEARCH_WHITESPACE_ROW_LIMIT);
let mut non_empty_line_above = None;
for (row, indent_size, is_blank) in self
for (row, indent) in self
.text
.reversed_line_indents_in_row_range(start..buffer_row)
{
@ -3260,30 +3263,28 @@ impl BufferSnapshot {
accessed_row_counter = 0;
yield_now().await;
}
if !is_blank {
non_empty_line_above = Some((row, indent_size));
if !indent.is_line_empty() {
non_empty_line_above = Some((row, indent));
break;
}
}
let mut non_empty_line_below = None;
for (row, indent_size, is_blank) in
self.text.line_indents_in_row_range((buffer_row + 1)..end)
{
for (row, indent) in self.text.line_indents_in_row_range((buffer_row + 1)..end) {
accessed_row_counter += 1;
if accessed_row_counter == YIELD_INTERVAL {
accessed_row_counter = 0;
yield_now().await;
}
if !is_blank {
non_empty_line_below = Some((row, indent_size));
if !indent.is_line_empty() {
non_empty_line_below = Some((row, indent));
break;
}
}
let (row, indent_size) = match (non_empty_line_above, non_empty_line_below) {
let (row, indent) = match (non_empty_line_above, non_empty_line_below) {
(Some((above_row, above_indent)), Some((below_row, below_indent))) => {
if above_indent >= below_indent {
if above_indent.raw_len() >= below_indent.raw_len() {
(above_row, above_indent)
} else {
(below_row, below_indent)
@ -3294,7 +3295,7 @@ impl BufferSnapshot {
_ => return None,
};
target_indent_size = indent_size;
target_indent = indent;
buffer_row = row;
}
@ -3302,7 +3303,7 @@ impl BufferSnapshot {
let end = (max_row + 1).min(buffer_row + SEARCH_ROW_LIMIT);
let mut start_indent = None;
for (row, indent_size, is_blank) in self
for (row, indent) in self
.text
.reversed_line_indents_in_row_range(start..buffer_row)
{
@ -3311,36 +3312,38 @@ impl BufferSnapshot {
accessed_row_counter = 0;
yield_now().await;
}
if !is_blank && indent_size < target_indent_size {
start_indent = Some((row, indent_size));
if !indent.is_line_empty() && indent.raw_len() < target_indent.raw_len() {
start_indent = Some((row, indent));
break;
}
}
let (start_row, start_indent_size) = start_indent?;
let mut end_indent = (end, None);
for (row, indent_size, is_blank) in
self.text.line_indents_in_row_range((buffer_row + 1)..end)
{
for (row, indent) in self.text.line_indents_in_row_range((buffer_row + 1)..end) {
accessed_row_counter += 1;
if accessed_row_counter == YIELD_INTERVAL {
accessed_row_counter = 0;
yield_now().await;
}
if !is_blank && indent_size < target_indent_size {
end_indent = (row.saturating_sub(1), Some(indent_size));
if !indent.is_line_empty() && indent.raw_len() < target_indent.raw_len() {
end_indent = (row.saturating_sub(1), Some(indent));
break;
}
}
let (end_row, end_indent_size) = end_indent;
let indent_size = if let Some(end_indent_size) = end_indent_size {
start_indent_size.max(end_indent_size)
let indent = if let Some(end_indent_size) = end_indent_size {
if start_indent_size.raw_len() > end_indent_size.raw_len() {
start_indent_size
} else {
end_indent_size
}
} else {
start_indent_size
};
Some((start_row..end_row, indent_size))
Some((start_row..end_row, indent))
}
/// Returns selections for remote peers intersecting the given range.