diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 7dd83abc94bd8dc851ed67fa3c631ab75e3aa01d..b9faceebfb007f9a531d57230d84dabe2663d74c 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -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::(); @@ -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], diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index b41ca08c2d2efa4ca51ba83d2d4d3d6b0f9f0a87..9568baeb5d303c80127ee0361ebe63e1d4ffb7e8 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -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"))]