From b6a2283f01a9a83400a335cbe036369448875de3 Mon Sep 17 00:00:00 2001 From: Josh Robson Chase Date: Thu, 26 Feb 2026 08:45:10 -0500 Subject: [PATCH] editor: Fix JoinLines with selection to end-of-line (#48035) Full line selections should only include the next line if there's a single line selected. Otherwise, if multiple lines are selected, only the selected lines should be joined. Also updates the editor tests to include both of these cases. Closes #48030 Release Notes: - Fixed `editor::JoinLines` when a multi-line selection includes the end of the final line --- crates/editor/src/editor.rs | 9 +++++++++ crates/editor/src/editor_tests.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 1a0a66b7b6074df549d932d4488013d48f7f3f5e..bc62d2a87a4fcd37f88fd013f779dc047a43f6e3 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -11307,6 +11307,15 @@ impl Editor { // would do nothing for single line selections individual cursors. let end = if selection.start.row == selection.end.row { MultiBufferRow(selection.start.row + 1) + } else if selection.end.column == 0 { + // If the selection ends at the start of a line, it's logically at the end of the + // previous line (plus its newline). + // Don't include the end line unless there's only one line selected. + if selection.start.row + 1 == selection.end.row { + MultiBufferRow(selection.end.row) + } else { + MultiBufferRow(selection.end.row - 1) + } } else { MultiBufferRow(selection.end.row) }; diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index d1090b5e0eb676c916ab98c7750ba80237f8e087..645f8053a868e4397207ed996d037cdbcb7bacd9 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -4882,6 +4882,32 @@ fn test_join_lines_with_single_selection(cx: &mut TestAppContext) { &[Point::new(0, 3)..Point::new(0, 3)] ); + editor.undo(&Undo, window, cx); + assert_eq!(buffer.read(cx).text(), "aaa\nbbb\nccc\nddd\n\n"); + + // Select a full line, i.e. start of the first line to the start of the second line + editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { + s.select_ranges([Point::new(0, 0)..Point::new(1, 0)]) + }); + editor.join_lines(&JoinLines, window, cx); + assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n"); + + editor.undo(&Undo, window, cx); + assert_eq!(buffer.read(cx).text(), "aaa\nbbb\nccc\nddd\n\n"); + + // Select two full lines + editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { + s.select_ranges([Point::new(0, 0)..Point::new(2, 0)]) + }); + editor.join_lines(&JoinLines, window, cx); + + // Only the selected lines should be joined, not the third. + assert_eq!( + buffer.read(cx).text(), + "aaa bbb\nccc\nddd\n\n", + "only the two selected lines (a and b) should be joined" + ); + // When multiple lines are selected, remove newlines that are spanned by the selection editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| { s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])