Make editor::Rewrap respect paragraphs (#32046)

Closes #32021 

Release Notes:

- Changed the behavior of `editor::Rewrap` to not join paragraphs
together.
This commit is contained in:
Cole Miller 2025-06-04 18:14:38 -04:00 committed by GitHub
parent 17c3b741ec
commit 8191a5339d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 120 additions and 20 deletions

View file

@ -10873,14 +10873,54 @@ impl Editor {
pub fn rewrap_impl(&mut self, options: RewrapOptions, cx: &mut Context<Self>) {
let buffer = self.buffer.read(cx).snapshot(cx);
let selections = self.selections.all::<Point>(cx);
let mut selections = selections.iter().peekable();
// Shrink and split selections to respect paragraph boundaries.
let ranges = selections.into_iter().flat_map(|selection| {
let language_settings = buffer.language_settings_at(selection.head(), cx);
let language_scope = buffer.language_scope_at(selection.head());
let Some(start_row) = (selection.start.row..=selection.end.row)
.find(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
else {
return vec![];
};
let Some(end_row) = (selection.start.row..=selection.end.row)
.rev()
.find(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
else {
return vec![];
};
let mut row = start_row;
let mut ranges = Vec::new();
while let Some(blank_row) =
(row..end_row).find(|row| buffer.is_line_blank(MultiBufferRow(*row)))
{
let next_paragraph_start = (blank_row + 1..=end_row)
.find(|row| !buffer.is_line_blank(MultiBufferRow(*row)))
.unwrap();
ranges.push((
language_settings.clone(),
language_scope.clone(),
Point::new(row, 0)..Point::new(blank_row - 1, 0),
));
row = next_paragraph_start;
}
ranges.push((
language_settings.clone(),
language_scope.clone(),
Point::new(row, 0)..Point::new(end_row, 0),
));
ranges
});
let mut edits = Vec::new();
let mut rewrapped_row_ranges = Vec::<RangeInclusive<u32>>::new();
while let Some(selection) = selections.next() {
let mut start_row = selection.start.row;
let mut end_row = selection.end.row;
for (language_settings, language_scope, range) in ranges {
let mut start_row = range.start.row;
let mut end_row = range.end.row;
// Skip selections that overlap with a range that has already been rewrapped.
let selection_range = start_row..end_row;
@ -10891,7 +10931,7 @@ impl Editor {
continue;
}
let tab_size = buffer.language_settings_at(selection.head(), cx).tab_size;
let tab_size = language_settings.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
@ -10922,25 +10962,20 @@ impl Editor {
let mut line_prefix = indent_size.chars().collect::<String>();
let mut inside_comment = false;
if let Some(comment_prefix) =
buffer
.language_scope_at(selection.head())
.and_then(|language| {
language
.line_comment_prefixes()
.iter()
.find(|prefix| buffer.contains_str_at(indent_end, prefix))
.cloned()
})
{
if let Some(comment_prefix) = language_scope.and_then(|language| {
language
.line_comment_prefixes()
.iter()
.find(|prefix| buffer.contains_str_at(indent_end, prefix))
.cloned()
}) {
line_prefix.push_str(&comment_prefix);
inside_comment = true;
}
let language_settings = buffer.language_settings_at(selection.head(), cx);
let allow_rewrap_based_on_language = match language_settings.allow_rewrap {
RewrapBehavior::InComments => inside_comment,
RewrapBehavior::InSelections => !selection.is_empty(),
RewrapBehavior::InSelections => !range.is_empty(),
RewrapBehavior::Anywhere => true,
};
@ -10951,11 +10986,12 @@ impl Editor {
continue;
}
if selection.is_empty() {
if range.is_empty() {
'expand_upwards: while start_row > 0 {
let prev_row = start_row - 1;
if buffer.contains_str_at(Point::new(prev_row, 0), &line_prefix)
&& buffer.line_len(MultiBufferRow(prev_row)) as usize > line_prefix.len()
&& !buffer.is_line_blank(MultiBufferRow(prev_row))
{
start_row = prev_row;
} else {
@ -10967,6 +11003,7 @@ impl Editor {
let next_row = end_row + 1;
if buffer.contains_str_at(Point::new(next_row, 0), &line_prefix)
&& buffer.line_len(MultiBufferRow(next_row)) as usize > line_prefix.len()
&& !buffer.is_line_blank(MultiBufferRow(next_row))
{
end_row = next_row;
} else {

View file

@ -5111,7 +5111,7 @@ async fn test_rewrap(cx: &mut TestAppContext) {
nisl venenatis tempus. Donec molestie blandit quam, et porta nunc laoreet in.
Integer sit amet scelerisque nisi.
"},
plaintext_language,
plaintext_language.clone(),
&mut cx,
);
@ -5174,6 +5174,69 @@ async fn test_rewrap(cx: &mut TestAppContext) {
&mut cx,
);
assert_rewrap(
indoc! {"
«ˇone one one one one one one one one one one one one one one one one one one one one one one one one
two»
three
«ˇ\t
four four four four four four four four four four four four four four four four four four four four»
«ˇfive five five five five five five five five five five five five five five five five five five five
\t»
six six six six six six six six six six six six six six six six six six six six six six six six six
"},
indoc! {"
«ˇone one one one one one one one one one one one one one one one one one one one
one one one one one
two»
three
«ˇ\t
four four four four four four four four four four four four four four four four
four four four four»
«ˇfive five five five five five five five five five five five five five five five
five five five five
\t»
six six six six six six six six six six six six six six six six six six six six six six six six six
"},
plaintext_language.clone(),
&mut cx,
);
assert_rewrap(
indoc! {"
//ˇ long long long long long long long long long long long long long long long long long long long long long long long long long long long long
//ˇ
//ˇ long long long long long long long long long long long long long long long long long long long long long long long long long long long long
//ˇ short short short
int main(void) {
return 17;
}
"},
indoc! {"
//ˇ long long long long long long long long long long long long long long long
// long long long long long long long long long long long long long
//ˇ
//ˇ long long long long long long long long long long long long long long long
//ˇ long long long long long long long long long long long long long short short
// short
int main(void) {
return 17;
}
"},
language_with_c_comments,
&mut cx,
);
#[track_caller]
fn assert_rewrap(
unwrapped_text: &str,