Skip to content

Commit 76b7c1d

Browse files
editor: Improve rewrapping when working with comments at different indentation levels (cherry-pick #18146) (#18147)
Cherry-picked editor: Improve rewrapping when working with comments at different indentation levels (#18146) This PR improves the `editor::Rewrap` command when working with comments that were not all at the same indentation level. We now use a heuristic of finding the most common indentation level for each line, using the deepest indent in the event of a tie. It also removes an `.unwrap()` that would previously lead to a panic in this case. Instead of unwrapping we now log an error to the logs and skip rewrapping for that selection. Release Notes: - Improved the behavior of `editor: rewrap` when working with a selection that contained comments at different indentation levels. Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
1 parent a0a1b1c commit 76b7c1d

File tree

3 files changed

+116
-8
lines changed

3 files changed

+116
-8
lines changed

crates/editor/src/editor.rs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6743,9 +6743,31 @@ impl Editor {
67436743
}
67446744
}
67456745

6746-
let row = selection.head().row;
6747-
let indent_size = buffer.indent_size_for_line(MultiBufferRow(row));
6748-
let indent_end = Point::new(row, indent_size.len);
6746+
// Since not all lines in the selection may be at the same indent
6747+
// level, choose the indent size that is the most common between all
6748+
// of the lines.
6749+
//
6750+
// If there is a tie, we use the deepest indent.
6751+
let (indent_size, indent_end) = {
6752+
let mut indent_size_occurrences = HashMap::default();
6753+
let mut rows_by_indent_size = HashMap::<IndentSize, Vec<u32>>::default();
6754+
6755+
for row in start_row..=end_row {
6756+
let indent = buffer.indent_size_for_line(MultiBufferRow(row));
6757+
rows_by_indent_size.entry(indent).or_default().push(row);
6758+
*indent_size_occurrences.entry(indent).or_insert(0) += 1;
6759+
}
6760+
6761+
let indent_size = indent_size_occurrences
6762+
.into_iter()
6763+
.max_by_key(|(indent, count)| (*count, indent.len))
6764+
.map(|(indent, _)| indent)
6765+
.unwrap_or_default();
6766+
let row = rows_by_indent_size[&indent_size][0];
6767+
let indent_end = Point::new(row, indent_size.len);
6768+
6769+
(indent_size, indent_end)
6770+
};
67496771

67506772
let mut line_prefix = indent_size.chars().collect::<String>();
67516773

@@ -6795,10 +6817,22 @@ impl Editor {
67956817
let start = Point::new(start_row, 0);
67966818
let end = Point::new(end_row, buffer.line_len(MultiBufferRow(end_row)));
67976819
let selection_text = buffer.text_for_range(start..end).collect::<String>();
6798-
let unwrapped_text = selection_text
6820+
let Some(lines_without_prefixes) = selection_text
67996821
.lines()
6800-
.map(|line| line.strip_prefix(&line_prefix).unwrap())
6801-
.join(" ");
6822+
.map(|line| {
6823+
line.strip_prefix(&line_prefix)
6824+
.or_else(|| line.trim_start().strip_prefix(&line_prefix.trim_start()))
6825+
.ok_or_else(|| {
6826+
anyhow!("line did not start with prefix {line_prefix:?}: {line:?}")
6827+
})
6828+
})
6829+
.collect::<Result<Vec<_>, _>>()
6830+
.log_err()
6831+
else {
6832+
continue;
6833+
};
6834+
6835+
let unwrapped_text = lines_without_prefixes.join(" ");
68026836
let wrap_column = buffer
68036837
.settings_at(Point::new(start_row, 0), cx)
68046838
.preferred_line_length as usize;

crates/editor/src/editor_tests.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4249,6 +4249,80 @@ async fn test_rewrap(cx: &mut TestAppContext) {
42494249
cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
42504250
cx.assert_editor_state(wrapped_text);
42514251
}
4252+
4253+
// Test rewrapping unaligned comments in a selection.
4254+
{
4255+
let language = Arc::new(Language::new(
4256+
LanguageConfig {
4257+
line_comments: vec!["// ".into(), "/// ".into()],
4258+
..LanguageConfig::default()
4259+
},
4260+
Some(tree_sitter_rust::LANGUAGE.into()),
4261+
));
4262+
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
4263+
4264+
let unwrapped_text = indoc! {"
4265+
fn foo() {
4266+
if true {
4267+
« // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
4268+
// Praesent semper egestas tellus id dignissim.ˇ»
4269+
do_something();
4270+
} else {
4271+
//
4272+
}
4273+
4274+
}
4275+
"};
4276+
4277+
let wrapped_text = indoc! {"
4278+
fn foo() {
4279+
if true {
4280+
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
4281+
// mollis elit purus, a ornare lacus gravida vitae. Praesent semper
4282+
// egestas tellus id dignissim.ˇ
4283+
do_something();
4284+
} else {
4285+
//
4286+
}
4287+
4288+
}
4289+
"};
4290+
4291+
cx.set_state(unwrapped_text);
4292+
cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
4293+
cx.assert_editor_state(wrapped_text);
4294+
4295+
let unwrapped_text = indoc! {"
4296+
fn foo() {
4297+
if true {
4298+
«ˇ // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mollis elit purus, a ornare lacus gravida vitae.
4299+
// Praesent semper egestas tellus id dignissim.»
4300+
do_something();
4301+
} else {
4302+
//
4303+
}
4304+
4305+
}
4306+
"};
4307+
4308+
let wrapped_text = indoc! {"
4309+
fn foo() {
4310+
if true {
4311+
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
4312+
// mollis elit purus, a ornare lacus gravida vitae. Praesent semper
4313+
// egestas tellus id dignissim.ˇ
4314+
do_something();
4315+
} else {
4316+
//
4317+
}
4318+
4319+
}
4320+
"};
4321+
4322+
cx.set_state(unwrapped_text);
4323+
cx.update_editor(|e, cx| e.rewrap(&Rewrap, cx));
4324+
cx.assert_editor_state(wrapped_text);
4325+
}
42524326
}
42534327

42544328
#[gpui::test]

crates/language/src/buffer.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ pub struct BufferSnapshot {
144144

145145
/// The kind and amount of indentation in a particular line. For now,
146146
/// assumes that indentation is all the same character.
147-
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
147+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
148148
pub struct IndentSize {
149149
/// The number of bytes that comprise the indentation.
150150
pub len: u32,
@@ -153,7 +153,7 @@ pub struct IndentSize {
153153
}
154154

155155
/// A whitespace character that's used for indentation.
156-
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
156+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
157157
pub enum IndentKind {
158158
/// An ASCII space character.
159159
#[default]

0 commit comments

Comments
 (0)