Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow ignoring soft wraps when moving to line ends #11153

Merged
merged 9 commits into from
May 2, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
37 changes: 25 additions & 12 deletions crates/editor/src/actions.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! This module contains all actions supported by [`Editor`].
use super::*;
use util::serde::default_true;

#[derive(PartialEq, Clone, Deserialize, Default)]
pub struct SelectNext {
Expand All @@ -13,6 +14,12 @@ pub struct SelectPrevious {
pub replace_newest: bool,
}

#[derive(PartialEq, Clone, Deserialize, Default)]
pub struct MoveToBeginningOfLine {
#[serde(default = "default_true")]
pub(super) stop_at_soft_wraps: bool,
}

#[derive(PartialEq, Clone, Deserialize, Default)]
pub struct SelectToBeginningOfLine {
#[serde(default)]
Expand All @@ -31,6 +38,12 @@ pub struct MovePageDown {
pub(super) center_cursor: bool,
}

#[derive(PartialEq, Clone, Deserialize, Default)]
pub struct MoveToEndOfLine {
#[serde(default = "default_true")]
pub(super) stop_at_soft_wraps: bool,
}

#[derive(PartialEq, Clone, Deserialize, Default)]
pub struct SelectToEndOfLine {
#[serde(default)]
Expand Down Expand Up @@ -103,23 +116,25 @@ pub struct ExpandExcerpts {
impl_actions!(
editor,
[
ConfirmCodeAction,
ConfirmCompletion,
ExpandExcerpts,
FoldAt,
MoveDownByLines,
MovePageDown,
MovePageUp,
MoveToBeginningOfLine,
MoveToEndOfLine,
MoveUpByLines,
SelectDownByLines,
SelectNext,
SelectPrevious,
SelectToBeginningOfLine,
ExpandExcerpts,
MovePageUp,
MovePageDown,
SelectToEndOfLine,
SelectUpByLines,
ToggleCodeActions,
ConfirmCompletion,
ConfirmCodeAction,
ToggleComments,
FoldAt,
UnfoldAt,
MoveUpByLines,
MoveDownByLines,
SelectUpByLines,
SelectDownByLines,
]
);

Expand Down Expand Up @@ -190,10 +205,8 @@ gpui::actions!(
MoveLineUp,
MoveRight,
MoveToBeginning,
MoveToBeginningOfLine,
MoveToEnclosingBracket,
MoveToEnd,
MoveToEndOfLine,
MoveToEndOfParagraph,
MoveToNextSubwordEnd,
MoveToNextWordEnd,
Expand Down
11 changes: 7 additions & 4 deletions crates/editor/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6232,13 +6232,13 @@ impl Editor {

pub fn move_to_beginning_of_line(
&mut self,
_: &MoveToBeginningOfLine,
action: &MoveToBeginningOfLine,
cx: &mut ViewContext<Self>,
) {
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_cursors_with(|map, head, _| {
(
movement::indented_line_beginning(map, head, true),
movement::indented_line_beginning(map, head, action.stop_at_soft_wraps),
SelectionGoal::None,
)
});
Expand Down Expand Up @@ -6282,10 +6282,13 @@ impl Editor {
});
}

pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
pub fn move_to_end_of_line(&mut self, action: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.move_cursors_with(|map, head, _| {
(movement::line_end(map, head, true), SelectionGoal::None)
(
movement::line_end(map, head, action.stop_at_soft_wraps),
SelectionGoal::None,
)
});
})
}
Expand Down
106 changes: 101 additions & 5 deletions crates/editor/src/editor_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,13 @@ fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
#[gpui::test]
fn test_beginning_end_of_line(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let move_to_beg = MoveToBeginningOfLine {
stop_at_soft_wraps: true,
};

let move_to_end = MoveToEndOfLine {
stop_at_soft_wraps: true,
};

let view = cx.add_window(|cx| {
let buffer = MultiBuffer::build_simple("abc\n def", cx);
Expand All @@ -1060,7 +1067,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
});

_ = view.update(cx, |view, cx| {
view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
view.move_to_beginning_of_line(&move_to_beg, cx);
assert_eq!(
view.selections.display_ranges(cx),
&[
Expand All @@ -1071,7 +1078,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
});

_ = view.update(cx, |view, cx| {
view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
view.move_to_beginning_of_line(&move_to_beg, cx);
assert_eq!(
view.selections.display_ranges(cx),
&[
Expand All @@ -1082,7 +1089,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
});

_ = view.update(cx, |view, cx| {
view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
view.move_to_beginning_of_line(&move_to_beg, cx);
assert_eq!(
view.selections.display_ranges(cx),
&[
Expand All @@ -1093,7 +1100,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
});

_ = view.update(cx, |view, cx| {
view.move_to_end_of_line(&MoveToEndOfLine, cx);
view.move_to_end_of_line(&move_to_end, cx);
assert_eq!(
view.selections.display_ranges(cx),
&[
Expand All @@ -1105,7 +1112,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {

// Moving to the end of line again is a no-op.
_ = view.update(cx, |view, cx| {
view.move_to_end_of_line(&MoveToEndOfLine, cx);
view.move_to_end_of_line(&move_to_end, cx);
assert_eq!(
view.selections.display_ranges(cx),
&[
Expand Down Expand Up @@ -1205,6 +1212,95 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
});
}

#[gpui::test]
fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) {
init_test(cx, |_| {});
let move_to_beg = MoveToBeginningOfLine {
stop_at_soft_wraps: false,
};

let move_to_end = MoveToEndOfLine {
stop_at_soft_wraps: false,
};

let view = cx.add_window(|cx| {
let buffer = MultiBuffer::build_simple("thequickbrownfox\njumpedoverthelazydogs", cx);
build_editor(buffer, cx)
});

_ = view.update(cx, |view, cx| {
view.set_wrap_width(Some(140.0.into()), cx);

// We expect the following lines after wrapping
// ```
// thequickbrownfox
// jumpedoverthelazydo
// gs
// ```
// The final `gs` was soft-wrapped onto a new line.
assert_eq!(
"thequickbrownfox\njumpedoverthelaz\nydogs",
view.display_text(cx),
);

// First, let's assert behavior on the first line, that was not soft-wrapped.
// Start the cursor at the `k` on the first line
view.change_selections(None, cx, |s| {
s.select_display_ranges([DisplayPoint::new(0, 7)..DisplayPoint::new(0, 7)]);
});

// Moving to the beginning of the line should put us at the beginning of the line.
view.move_to_beginning_of_line(&move_to_beg, cx);
assert_eq!(
vec![DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),],
view.selections.display_ranges(cx)
);

// Moving to the end of the line should put us at the end of the line.
view.move_to_end_of_line(&move_to_end, cx);
assert_eq!(
vec![DisplayPoint::new(0, 16)..DisplayPoint::new(0, 16),],
view.selections.display_ranges(cx)
);

// Now, let's assert behavior on the second line, that ended up being soft-wrapped.
// Start the cursor at the last line (`y` that was wrapped to a new line)
view.change_selections(None, cx, |s| {
s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0)]);
});

// Moving to the beginning of the line should put us at the start of the second line of
// display text, i.e., the `j`.
view.move_to_beginning_of_line(&move_to_beg, cx);
assert_eq!(
vec![DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),],
view.selections.display_ranges(cx)
);

// Moving to the beginning of the line again should be a no-op.
view.move_to_beginning_of_line(&move_to_beg, cx);
assert_eq!(
vec![DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),],
view.selections.display_ranges(cx)
);

// Moving to the end of the line should put us right after the `s` that was soft-wrapped to the
// next display line.
view.move_to_end_of_line(&move_to_end, cx);
assert_eq!(
vec![DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),],
view.selections.display_ranges(cx)
);

// Moving to the end of the line again should be a no-op.
view.move_to_end_of_line(&move_to_end, cx);
assert_eq!(
vec![DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),],
view.selections.display_ranges(cx)
);
});
}

#[gpui::test]
fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
init_test(cx, |_| {});
Expand Down
5 changes: 1 addition & 4 deletions crates/language/src/language_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use schemars::{
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsLocation, SettingsSources};
use std::{num::NonZeroU32, path::Path, sync::Arc};
use util::serde::default_true;

impl<'a> Into<SettingsLocation<'a>> for &'a dyn File {
fn into(self) -> SettingsLocation<'a> {
Expand Down Expand Up @@ -438,10 +439,6 @@ pub struct InlayHintSettings {
pub scroll_debounce_ms: u64,
}

fn default_true() -> bool {
true
}

fn edit_debounce_ms() -> u64 {
700
}
Expand Down
3 changes: 3 additions & 0 deletions crates/util/src/serde.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub const fn default_true() -> bool {
true
}
1 change: 1 addition & 0 deletions crates/util/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod git_author;
pub mod github;
pub mod http;
pub mod paths;
pub mod serde;
#[cfg(any(test, feature = "test-support"))]
pub mod test;

Expand Down