Detailed changes
@@ -407,6 +407,7 @@
"g w": "vim::PushRewrap",
"insert": "vim::InsertBefore",
"alt-.": "vim::RepeatFind",
+ "alt-s": ["editor::SplitSelectionIntoLines", { "keep_selections": true }],
// tree-sitter related commands
"[ x": "editor::SelectLargerSyntaxNode",
"] x": "editor::SelectSmallerSyntaxNode",
@@ -273,6 +273,16 @@ pub enum UuidVersion {
V7,
}
+/// Splits selection into individual lines.
+#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema, Action)]
+#[action(namespace = editor)]
+#[serde(deny_unknown_fields)]
+pub struct SplitSelectionIntoLines {
+ /// Keep the text selected after splitting instead of collapsing to cursors.
+ #[serde(default)]
+ pub keep_selections: bool,
+}
+
/// Goes to the next diagnostic in the file.
#[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema, Action)]
#[action(namespace = editor)]
@@ -672,8 +682,6 @@ actions!(
SortLinesCaseInsensitive,
/// Sorts selected lines case-sensitively.
SortLinesCaseSensitive,
- /// Splits selection into individual lines.
- SplitSelectionIntoLines,
/// Stops the language server for the current file.
StopLanguageServer,
/// Switches between source and header files.
@@ -13612,7 +13612,7 @@ impl Editor {
pub fn split_selection_into_lines(
&mut self,
- _: &SplitSelectionIntoLines,
+ action: &SplitSelectionIntoLines,
window: &mut Window,
cx: &mut Context<Self>,
) {
@@ -13629,8 +13629,21 @@ impl Editor {
let buffer = self.buffer.read(cx).read(cx);
for selection in selections {
for row in selection.start.row..selection.end.row {
- let cursor = Point::new(row, buffer.line_len(MultiBufferRow(row)));
- new_selection_ranges.push(cursor..cursor);
+ let line_start = Point::new(row, 0);
+ let line_end = Point::new(row, buffer.line_len(MultiBufferRow(row)));
+
+ if action.keep_selections {
+ // Keep the selection range for each line
+ let selection_start = if row == selection.start.row {
+ selection.start
+ } else {
+ line_start
+ };
+ new_selection_ranges.push(selection_start..line_end);
+ } else {
+ // Collapse to cursor at end of line
+ new_selection_ranges.push(line_end..line_end);
+ }
}
let is_multiline_selection = selection.start.row != selection.end.row;
@@ -13638,7 +13651,16 @@ impl Editor {
// so this action feels more ergonomic when paired with other selection operations
let should_skip_last = is_multiline_selection && selection.end.column == 0;
if !should_skip_last {
- new_selection_ranges.push(selection.end..selection.end);
+ if action.keep_selections {
+ if is_multiline_selection {
+ let line_start = Point::new(selection.end.row, 0);
+ new_selection_ranges.push(line_start..selection.end);
+ } else {
+ new_selection_ranges.push(selection.start..selection.end);
+ }
+ } else {
+ new_selection_ranges.push(selection.end..selection.end);
+ }
}
}
}
@@ -6401,7 +6401,7 @@ async fn test_split_selection_into_lines(cx: &mut TestAppContext) {
fn test(cx: &mut EditorTestContext, initial_state: &'static str, expected_state: &'static str) {
cx.set_state(initial_state);
cx.update_editor(|e, window, cx| {
- e.split_selection_into_lines(&SplitSelectionIntoLines, window, cx)
+ e.split_selection_into_lines(&Default::default(), window, cx)
});
cx.assert_editor_state(expected_state);
}
@@ -6489,7 +6489,7 @@ async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestA
DisplayPoint::new(DisplayRow(4), 4)..DisplayPoint::new(DisplayRow(4), 4),
])
});
- editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
+ editor.split_selection_into_lines(&Default::default(), window, cx);
assert_eq!(
editor.display_text(cx),
"aaaaa\nbbbbb\nccc⋯eeee\nfffff\nggggg\n⋯i"
@@ -6505,7 +6505,7 @@ async fn test_split_selection_into_lines_interacting_with_creases(cx: &mut TestA
DisplayPoint::new(DisplayRow(5), 0)..DisplayPoint::new(DisplayRow(0), 1)
])
});
- editor.split_selection_into_lines(&SplitSelectionIntoLines, window, cx);
+ editor.split_selection_into_lines(&Default::default(), window, cx);
assert_eq!(
editor.display_text(cx),
"aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"