Make cursor move to duplicated line when duplicating line up (#41004)

![Screen Recording 2025-10-23 at 14 50
26](https://github.com/user-attachments/assets/3427aa06-faf4-4f76-a604-bfc5af30f8ce)

Closes #40919
Follow-up of #39610

Release Notes:
- When duplicating line up, fixed cursor to move to the duplicated line
This commit is contained in:
Tom Planche
2025-10-24 02:20:16 +03:00
committed by GitHub
parent af0d2ad491
commit 79eff1fe05
2 changed files with 53 additions and 7 deletions
+48 -2
View File
@@ -11551,7 +11551,7 @@ impl Editor {
end
} else {
text.push('\n');
Point::new(rows.end.0, 0)
Point::new(rows.start.0, 0)
}
} else {
text.push('\n');
@@ -11567,11 +11567,57 @@ impl Editor {
}
}
self.transact(window, cx, |this, _, cx| {
self.transact(window, cx, |this, window, cx| {
this.buffer.update(cx, |buffer, cx| {
buffer.edit(edits, None, cx);
});
// When duplicating upward with whole lines, move the cursor to the duplicated line
if upwards && whole_lines {
let display_map = this.display_map.update(cx, |map, cx| map.snapshot(cx));
this.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
let mut new_ranges = Vec::new();
let selections = s.all::<Point>(&display_map);
let mut selections_iter = selections.iter().peekable();
while let Some(first_selection) = selections_iter.next() {
// Group contiguous selections together to find the total row span
let mut group_selections = vec![first_selection];
let mut rows = first_selection.spanned_rows(false, &display_map);
while let Some(next_selection) = selections_iter.peek() {
let next_rows = next_selection.spanned_rows(false, &display_map);
if next_rows.start < rows.end {
rows.end = next_rows.end;
group_selections.push(selections_iter.next().unwrap());
} else {
break;
}
}
let row_count = rows.end.0 - rows.start.0;
// Move all selections in this group up by the total number of duplicated rows
for selection in group_selections {
let new_start = Point::new(
selection.start.row.saturating_sub(row_count),
selection.start.column,
);
let new_end = Point::new(
selection.end.row.saturating_sub(row_count),
selection.end.column,
);
new_ranges.push(new_start..new_end);
}
}
s.select_ranges(new_ranges);
});
}
this.request_autoscroll(Autoscroll::fit(), cx);
});
}
+5 -5
View File
@@ -5646,8 +5646,8 @@ fn test_duplicate_line(cx: &mut TestAppContext) {
);
});
// With `move_upwards` the selections stay in place, except for
// the lines inserted above them
// With `duplicate_line_up` the selections move to the duplicated lines,
// which are inserted above the original lines
let editor = cx.add_window(|window, cx| {
let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
build_editor(buffer, window, cx)
@@ -5669,7 +5669,7 @@ fn test_duplicate_line(cx: &mut TestAppContext) {
DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 1),
DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 2),
DisplayPoint::new(DisplayRow(2), 0)..DisplayPoint::new(DisplayRow(2), 0),
DisplayPoint::new(DisplayRow(6), 0)..DisplayPoint::new(DisplayRow(6), 0),
DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(5), 0),
]
);
});
@@ -26888,8 +26888,8 @@ fn test_duplicate_line_up_on_last_line_without_newline(cx: &mut TestAppContext)
assert_eq!(
editor.selections.display_ranges(cx),
vec![DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0)],
"Selection should remain on the original line"
vec![DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)],
"Selection should move to the duplicated line"
);
})
.unwrap();