@@ -7008,6 +7008,8 @@ impl Editor {
}
}
+ let tab_size = buffer.settings_at(selection.head(), cx).tab_size;
+
// Since not all lines in the selection may be at the same indent
// level, choose the indent size that is the most common between all
// of the lines.
@@ -7025,7 +7027,7 @@ impl Editor {
let indent_size = indent_size_occurrences
.into_iter()
- .max_by_key(|(indent, count)| (*count, indent.len))
+ .max_by_key(|(indent, count)| (*count, indent.len_with_expanded_tabs(tab_size)))
.map(|(indent, _)| indent)
.unwrap_or_default();
let row = rows_by_indent_size[&indent_size][0];
@@ -7051,6 +7053,10 @@ impl Editor {
should_rewrap = true;
}
+ if !should_rewrap {
+ continue;
+ }
+
if selection.is_empty() {
'expand_upwards: while start_row > 0 {
let prev_row = start_row - 1;
@@ -7075,10 +7081,6 @@ impl Editor {
}
}
- if !should_rewrap {
- continue;
- }
-
let start = Point::new(start_row, 0);
let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
let selection_text = buffer.text_for_range(start..end).collect::<String>();
@@ -7097,29 +7099,15 @@ impl Editor {
continue;
};
- let unwrapped_text = lines_without_prefixes.join(" ");
let wrap_column = buffer
.settings_at(Point::new(start_row, 0), cx)
.preferred_line_length as usize;
- let mut wrapped_text = String::new();
- let mut current_line = line_prefix.clone();
- for word in unwrapped_text.split_whitespace() {
- if current_line.len() + word.len() >= wrap_column {
- wrapped_text.push_str(¤t_line);
- wrapped_text.push('\n');
- current_line.truncate(line_prefix.len());
- }
-
- if current_line.len() > line_prefix.len() {
- current_line.push(' ');
- }
-
- current_line.push_str(word);
- }
-
- if !current_line.is_empty() {
- wrapped_text.push_str(¤t_line);
- }
+ let wrapped_text = wrap_with_prefix(
+ line_prefix,
+ lines_without_prefixes.join(" "),
+ wrap_column,
+ tab_size,
+ );
let diff = TextDiff::from_lines(&selection_text, &wrapped_text);
let mut offset = start.to_offset(&buffer);
@@ -13056,6 +13044,70 @@ impl Editor {
}
}
+fn len_with_expanded_tabs(offset: usize, comment_prefix: &str, tab_size: NonZeroU32) -> usize {
+ let tab_size = tab_size.get() as usize;
+ let mut width = offset;
+
+ for c in comment_prefix.chars() {
+ width += if c == '\t' {
+ tab_size - (width % tab_size)
+ } else {
+ 1
+ };
+ }
+
+ width - offset
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_string_size_with_expanded_tabs() {
+ let nz = |val| NonZeroU32::new(val).unwrap();
+ assert_eq!(len_with_expanded_tabs(0, "", nz(4)), 0);
+ assert_eq!(len_with_expanded_tabs(0, "hello", nz(4)), 5);
+ assert_eq!(len_with_expanded_tabs(0, "\thello", nz(4)), 9);
+ assert_eq!(len_with_expanded_tabs(0, "abc\tab", nz(4)), 6);
+ assert_eq!(len_with_expanded_tabs(0, "hello\t", nz(4)), 8);
+ assert_eq!(len_with_expanded_tabs(0, "\t\t", nz(8)), 16);
+ assert_eq!(len_with_expanded_tabs(0, "x\t", nz(8)), 8);
+ assert_eq!(len_with_expanded_tabs(7, "x\t", nz(8)), 9);
+ }
+}
+
+fn wrap_with_prefix(
+ line_prefix: String,
+ unwrapped_text: String,
+ wrap_column: usize,
+ tab_size: NonZeroU32,
+) -> String {
+ let line_prefix_display_len = len_with_expanded_tabs(0, &line_prefix, tab_size);
+ let mut wrapped_text = String::new();
+ let mut current_line = line_prefix.to_string();
+ let prefix_extra_chars = line_prefix_display_len - line_prefix.len();
+
+ for word in unwrapped_text.split_whitespace() {
+ if current_line.len() + prefix_extra_chars + word.len() >= wrap_column {
+ wrapped_text.push_str(¤t_line);
+ wrapped_text.push('\n');
+ current_line.truncate(line_prefix.len());
+ }
+
+ if current_line.len() > line_prefix.len() {
+ current_line.push(' ');
+ }
+
+ current_line.push_str(word);
+ }
+
+ if !current_line.is_empty() {
+ wrapped_text.push_str(¤t_line);
+ }
+ wrapped_text
+}
+
fn hunks_for_selections(
multi_buffer_snapshot: &MultiBufferSnapshot,
selections: &[Selection<Anchor>],
@@ -46,6 +46,7 @@ use std::{
future::Future,
iter::{self, Iterator, Peekable},
mem,
+ num::NonZeroU32,
ops::{Deref, DerefMut, Range},
path::{Path, PathBuf},
str,
@@ -4325,6 +4326,13 @@ impl IndentSize {
}
self
}
+
+ pub fn len_with_expanded_tabs(&self, tab_size: NonZeroU32) -> usize {
+ match self.kind {
+ IndentKind::Space => self.len as usize,
+ IndentKind::Tab => self.len as usize * tab_size.get() as usize,
+ }
+ }
}
#[cfg(any(test, feature = "test-support"))]