Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions assets/keymaps/default-linux.json
Original file line number Diff line number Diff line change
Expand Up @@ -491,8 +491,8 @@
"bindings": {
"ctrl-[": "editor::Outdent",
"ctrl-]": "editor::Indent",
"shift-alt-up": "editor::AddSelectionAbove", // Insert Cursor Above
"shift-alt-down": "editor::AddSelectionBelow", // Insert Cursor Below
"shift-alt-up": ["editor::AddSelectionAbove", { "skip_soft_wrap": true }], // Insert Cursor Above
"shift-alt-down": ["editor::AddSelectionBelow", { "skip_soft_wrap": true }], // Insert Cursor Below
"ctrl-shift-k": "editor::DeleteLine",
"alt-up": "editor::MoveLineUp",
"alt-down": "editor::MoveLineDown",
Expand Down
8 changes: 4 additions & 4 deletions assets/keymaps/default-macos.json
Original file line number Diff line number Diff line change
Expand Up @@ -539,10 +539,10 @@
"bindings": {
"cmd-[": "editor::Outdent",
"cmd-]": "editor::Indent",
"cmd-ctrl-p": "editor::AddSelectionAbove", // Insert cursor above
"cmd-alt-up": "editor::AddSelectionAbove",
"cmd-ctrl-n": "editor::AddSelectionBelow", // Insert cursor below
"cmd-alt-down": "editor::AddSelectionBelow",
"cmd-ctrl-p": ["editor::AddSelectionAbove", { "skip_soft_wrap": false }], // Insert cursor above
"cmd-alt-up": ["editor::AddSelectionAbove", { "skip_soft_wrap": true }],
"cmd-ctrl-n": ["editor::AddSelectionBelow", { "skip_soft_wrap": false }], // Insert cursor below
"cmd-alt-down": ["editor::AddSelectionBelow", { "skip_soft_wrap": true }],
"cmd-shift-k": "editor::DeleteLine",
"alt-up": "editor::MoveLineUp",
"alt-down": "editor::MoveLineDown",
Expand Down
4 changes: 2 additions & 2 deletions assets/keymaps/default-windows.json
Original file line number Diff line number Diff line change
Expand Up @@ -500,8 +500,8 @@
"bindings": {
"ctrl-[": "editor::Outdent",
"ctrl-]": "editor::Indent",
"ctrl-shift-alt-up": "editor::AddSelectionAbove", // Insert Cursor Above
"ctrl-shift-alt-down": "editor::AddSelectionBelow", // Insert Cursor Below
"ctrl-shift-alt-up": ["editor::AddSelectionAbove", { "skip_soft_wrap": true }], // Insert Cursor Above
"ctrl-shift-alt-down": ["editor::AddSelectionBelow", { "skip_soft_wrap": true }], // Insert Cursor Below
"ctrl-shift-k": "editor::DeleteLine",
"alt-up": "editor::MoveLineUp",
"alt-down": "editor::MoveLineDown",
Expand Down
4 changes: 2 additions & 2 deletions assets/keymaps/linux/atom.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
"ctrl-<": "editor::ScrollCursorCenter", // editor:scroll-to-cursor
"f3": ["editor::SelectNext", { "replace_newest": true }], // find-and-replace:find-next
"shift-f3": ["editor::SelectPrevious", { "replace_newest": true }], //find-and-replace:find-previous
"alt-shift-down": "editor::AddSelectionBelow", // editor:add-selection-below
"alt-shift-up": "editor::AddSelectionAbove", // editor:add-selection-above
"alt-shift-down": ["editor::AddSelectionBelow", { "skip_soft_wrap": true }], // editor:add-selection-below
"alt-shift-up": ["editor::AddSelectionAbove", { "skip_soft_wrap": true }], // editor:add-selection-above
"ctrl-j": "editor::JoinLines", // editor:join-lines
"ctrl-shift-d": "editor::DuplicateLineDown", // editor:duplicate-lines
"ctrl-up": "editor::MoveLineUp", // editor:move-line-up
Expand Down
4 changes: 2 additions & 2 deletions assets/keymaps/linux/sublime_text.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
{
"context": "Editor",
"bindings": {
"ctrl-alt-up": "editor::AddSelectionAbove",
"ctrl-alt-down": "editor::AddSelectionBelow",
"ctrl-alt-up": ["editor::AddSelectionAbove", { "skip_soft_wrap": false }],
"ctrl-alt-down": ["editor::AddSelectionBelow", { "skip_soft_wrap": false }],
"ctrl-shift-up": "editor::MoveLineUp",
"ctrl-shift-down": "editor::MoveLineDown",
"ctrl-shift-m": "editor::SelectLargerSyntaxNode",
Expand Down
4 changes: 2 additions & 2 deletions assets/keymaps/macos/atom.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
"cmd-<": "editor::ScrollCursorCenter",
"cmd-g": ["editor::SelectNext", { "replace_newest": true }],
"cmd-shift-g": ["editor::SelectPrevious", { "replace_newest": true }],
"ctrl-shift-down": "editor::AddSelectionBelow",
"ctrl-shift-up": "editor::AddSelectionAbove",
"ctrl-shift-down": ["editor::AddSelectionBelow", { "skip_soft_wrap": true }],
"ctrl-shift-up": ["editor::AddSelectionAbove", { "skip_soft_wrap": true }],
"alt-enter": "editor::Newline",
"cmd-shift-d": "editor::DuplicateLineDown",
"ctrl-cmd-up": "editor::MoveLineUp",
Expand Down
4 changes: 2 additions & 2 deletions assets/keymaps/macos/sublime_text.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
{
"context": "Editor",
"bindings": {
"ctrl-shift-up": "editor::AddSelectionAbove",
"ctrl-shift-down": "editor::AddSelectionBelow",
"ctrl-shift-up": ["editor::AddSelectionAbove", { "skip_soft_wrap": false }],
"ctrl-shift-down": ["editor::AddSelectionBelow", { "skip_soft_wrap": false }],
"cmd-ctrl-up": "editor::MoveLineUp",
"cmd-ctrl-down": "editor::MoveLineDown",
"cmd-shift-space": "editor::SelectAll",
Expand Down
4 changes: 2 additions & 2 deletions assets/keymaps/vim.json
Original file line number Diff line number Diff line change
Expand Up @@ -500,8 +500,8 @@
"ctrl-c": "editor::ToggleComments",
"d": "vim::HelixDelete",
"c": "vim::Substitute",
"shift-c": "editor::AddSelectionBelow",
"alt-shift-c": "editor::AddSelectionAbove"
"shift-c": ["editor::AddSelectionBelow", { "skip_soft_wrap": true }],
"alt-shift-c": ["editor::AddSelectionAbove", { "skip_soft_wrap": true }]
}
},
{
Expand Down
22 changes: 18 additions & 4 deletions crates/editor/src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,24 @@ pub struct GoToPreviousDiagnostic {
pub severity: GoToDiagnosticSeverityFilter,
}

/// Adds a cursor above the current selection.
#[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema, Action)]
#[action(namespace = editor)]
#[serde(deny_unknown_fields)]
pub struct AddSelectionAbove {
#[serde(default = "default_true")]
pub skip_soft_wrap: bool,
}

/// Adds a cursor below the current selection.
#[derive(PartialEq, Clone, Default, Debug, Deserialize, JsonSchema, Action)]
#[action(namespace = editor)]
#[serde(deny_unknown_fields)]
pub struct AddSelectionBelow {
#[serde(default = "default_true")]
pub skip_soft_wrap: bool,
}

actions!(
debugger,
[
Expand Down Expand Up @@ -345,10 +363,6 @@ actions!(
/// Accepts a partial edit prediction.
#[action(deprecated_aliases = ["editor::AcceptPartialCopilotSuggestion"])]
AcceptPartialEditPrediction,
/// Adds a cursor above the current selection.
AddSelectionAbove,
/// Adds a cursor below the current selection.
AddSelectionBelow,
/// Applies all diff hunks in the editor.
ApplyAllDiffHunks,
/// Applies the diff hunk at the current position.
Expand Down
20 changes: 20 additions & 0 deletions crates/editor/src/display_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1401,6 +1401,26 @@ impl DisplaySnapshot {
pub fn excerpt_header_height(&self) -> u32 {
self.block_snapshot.excerpt_header_height
}

/// Given a `DisplayPoint`, returns another `DisplayPoint` corresponding to
/// the start of the buffer row that is a given number of buffer rows away
/// from the provided point.
///
/// This moves by buffer rows instead of display rows, a distinction that is
/// important when soft wrapping is enabled.
pub fn start_of_relative_buffer_row(&self, point: DisplayPoint, times: isize) -> DisplayPoint {
let start = self.display_point_to_fold_point(point, Bias::Left);
let target = start.row() as isize + times;
let new_row = (target.max(0) as u32).min(self.fold_snapshot().max_point().row());

self.clip_point(
self.fold_point_to_display_point(
self.fold_snapshot()
.clip_point(FoldPoint::new(new_row, 0), Bias::Right),
),
Bias::Right,
)
}
}

#[derive(Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq)]
Expand Down
25 changes: 19 additions & 6 deletions crates/editor/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14235,23 +14235,29 @@ impl Editor {

pub fn add_selection_above(
&mut self,
_: &AddSelectionAbove,
action: &AddSelectionAbove,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.add_selection(true, window, cx);
self.add_selection(true, action.skip_soft_wrap, window, cx);
}

pub fn add_selection_below(
&mut self,
_: &AddSelectionBelow,
action: &AddSelectionBelow,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.add_selection(false, window, cx);
self.add_selection(false, action.skip_soft_wrap, window, cx);
}

fn add_selection(&mut self, above: bool, window: &mut Window, cx: &mut Context<Self>) {
fn add_selection(
&mut self,
above: bool,
skip_soft_wrap: bool,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);

let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
Expand Down Expand Up @@ -14338,12 +14344,19 @@ impl Editor {
};

let mut maybe_new_selection = None;
let direction = if above { -1 } else { 1 };

while row != end_row {
if above {
if skip_soft_wrap {
row = display_map
.start_of_relative_buffer_row(DisplayPoint::new(row, 0), direction)
.row();
} else if above {
row.0 -= 1;
} else {
row.0 += 1;
}

if let Some(new_selection) = self.selections.build_columnar_selection(
&display_map,
row,
Expand Down
77 changes: 77 additions & 0 deletions crates/editor/src/editor_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25671,6 +25671,83 @@ async fn test_add_selection_after_moving_with_multiple_cursors(cx: &mut TestAppC
);
}

#[gpui::test]
async fn test_add_selection_skip_soft_wrap_option(cx: &mut TestAppContext) {
init_test(cx, |_| {});

let mut cx = EditorTestContext::new(cx).await;

cx.set_state(indoc!(
r#"ˇThis is a very long line that will be wrapped when soft wrapping is enabled
Second line here"#
));

cx.update_editor(|editor, window, cx| {
// Enable soft wrapping with a narrow width to force soft wrapping and
// confirm that more than 2 rows are being displayed.
editor.set_wrap_width(Some(100.0.into()), cx);
assert!(editor.display_text(cx).lines().count() > 2);

editor.add_selection_below(
&AddSelectionBelow {
skip_soft_wrap: true,
},
window,
cx,
);

assert_eq!(
editor.selections.display_ranges(cx),
&[
DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
DisplayPoint::new(DisplayRow(8), 0)..DisplayPoint::new(DisplayRow(8), 0),
]
);

editor.add_selection_above(
&AddSelectionAbove {
skip_soft_wrap: true,
},
window,
cx,
);

assert_eq!(
editor.selections.display_ranges(cx),
&[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
);

editor.add_selection_below(
&AddSelectionBelow {
skip_soft_wrap: false,
},
window,
cx,
);

assert_eq!(
editor.selections.display_ranges(cx),
&[
DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0),
DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0),
]
);

editor.add_selection_above(
&AddSelectionAbove {
skip_soft_wrap: false,
},
window,
cx,
);

assert_eq!(
editor.selections.display_ranges(cx),
&[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
);
});
}

#[gpui::test(iterations = 10)]
async fn test_document_colors(cx: &mut TestAppContext) {
let expected_color = Rgba {
Expand Down
5 changes: 5 additions & 0 deletions crates/editor/src/selections_collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,11 @@ impl SelectionsCollection {
.collect()
}

/// Attempts to build a selection in the provided `DisplayRow` within the
/// same range as the provided range of `Pixels`.
/// Returns `None` if the range is not empty but it starts past the line's
/// length, meaning that the line isn't long enough to be contained within
/// part of the provided range.
pub fn build_columnar_selection(
&mut self,
display_map: &DisplaySnapshot,
Expand Down
33 changes: 5 additions & 28 deletions crates/vim/src/motion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1525,29 +1525,6 @@ fn wrapping_right_single(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayP
}
}

/// Given a point, returns the start of the buffer row that is a given number of
/// buffer rows away from the current position.
///
/// This moves by buffer rows instead of display rows, a distinction that is
/// important when soft wrapping is enabled.
pub(crate) fn start_of_relative_buffer_row(
map: &DisplaySnapshot,
point: DisplayPoint,
times: isize,
) -> DisplayPoint {
let start = map.display_point_to_fold_point(point, Bias::Left);
let target = start.row() as isize + times;
let new_row = (target.max(0) as u32).min(map.fold_snapshot().max_point().row());

map.clip_point(
map.fold_point_to_display_point(
map.fold_snapshot()
.clip_point(FoldPoint::new(new_row, 0), Bias::Right),
),
Bias::Right,
)
}

fn up_down_buffer_rows(
map: &DisplaySnapshot,
mut point: DisplayPoint,
Expand Down Expand Up @@ -2127,7 +2104,7 @@ pub(crate) fn end_of_line(
times: usize,
) -> DisplayPoint {
if times > 1 {
point = start_of_relative_buffer_row(map, point, times as isize - 1);
point = map.start_of_relative_buffer_row(point, times as isize - 1);
}
if display_lines {
map.clip_point(
Expand Down Expand Up @@ -2732,17 +2709,17 @@ fn sneak_backward(
}

fn next_line_start(map: &DisplaySnapshot, point: DisplayPoint, times: usize) -> DisplayPoint {
let correct_line = start_of_relative_buffer_row(map, point, times as isize);
let correct_line = map.start_of_relative_buffer_row(point, times as isize);
first_non_whitespace(map, false, correct_line)
}

fn previous_line_start(map: &DisplaySnapshot, point: DisplayPoint, times: usize) -> DisplayPoint {
let correct_line = start_of_relative_buffer_row(map, point, -(times as isize));
let correct_line = map.start_of_relative_buffer_row(point, -(times as isize));
first_non_whitespace(map, false, correct_line)
}

fn go_to_column(map: &DisplaySnapshot, point: DisplayPoint, times: usize) -> DisplayPoint {
let correct_line = start_of_relative_buffer_row(map, point, 0);
let correct_line = map.start_of_relative_buffer_row(point, 0);
right(map, correct_line, times.saturating_sub(1))
}

Expand All @@ -2752,7 +2729,7 @@ pub(crate) fn next_line_end(
times: usize,
) -> DisplayPoint {
if times > 1 {
point = start_of_relative_buffer_row(map, point, times as isize - 1);
point = map.start_of_relative_buffer_row(point, times as isize - 1);
}
end_of_line(map, false, point, 1)
}
Expand Down
2 changes: 1 addition & 1 deletion crates/vim/src/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ impl Vim {
editor.edit_with_autoindent(edits, cx);
editor.change_selections(Default::default(), window, cx, |s| {
s.move_cursors_with(|map, cursor, _| {
let previous_line = motion::start_of_relative_buffer_row(map, cursor, -1);
let previous_line = map.start_of_relative_buffer_row(cursor, -1);
let insert_point = motion::end_of_line(map, false, previous_line, 1);
(insert_point, SelectionGoal::None)
});
Expand Down
Loading
Loading