Merge a828867aeb
into 633ce23ae9
This commit is contained in:
commit
8f6cc2c1f9
2 changed files with 187 additions and 3 deletions
|
@ -433,7 +433,7 @@
|
||||||
"g c": "vim::WindowMiddle",
|
"g c": "vim::WindowMiddle",
|
||||||
"g b": "vim::WindowBottom",
|
"g b": "vim::WindowBottom",
|
||||||
|
|
||||||
"x": "editor::SelectLine",
|
"x": "vim::HelixSelectLine",
|
||||||
"shift-x": "editor::SelectLine",
|
"shift-x": "editor::SelectLine",
|
||||||
"%": "editor::SelectAll",
|
"%": "editor::SelectAll",
|
||||||
// Window mode
|
// Window mode
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use editor::display_map::DisplaySnapshot;
|
use editor::display_map::DisplaySnapshot;
|
||||||
use editor::{DisplayPoint, Editor, SelectionEffects, ToOffset, ToPoint, movement};
|
use editor::{
|
||||||
|
DisplayPoint, Editor, HideMouseCursorOrigin, SelectionEffects, ToOffset, ToPoint, movement,
|
||||||
|
};
|
||||||
use gpui::{Action, actions};
|
use gpui::{Action, actions};
|
||||||
use gpui::{Context, Window};
|
use gpui::{Context, Window};
|
||||||
use language::{CharClassifier, CharKind};
|
use language::{CharClassifier, CharKind, Point};
|
||||||
use text::{Bias, SelectionGoal};
|
use text::{Bias, SelectionGoal};
|
||||||
|
|
||||||
use crate::motion;
|
use crate::motion;
|
||||||
|
@ -23,11 +25,14 @@ actions!(
|
||||||
HelixInsert,
|
HelixInsert,
|
||||||
/// Appends at the end of the selection.
|
/// Appends at the end of the selection.
|
||||||
HelixAppend,
|
HelixAppend,
|
||||||
|
/// Select entire line or multiple lines, extending downwards.
|
||||||
|
HelixSelectLine,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
||||||
Vim::action(editor, cx, Vim::helix_normal_after);
|
Vim::action(editor, cx, Vim::helix_normal_after);
|
||||||
|
Vim::action(editor, cx, Vim::helix_select_lines);
|
||||||
Vim::action(editor, cx, Vim::helix_insert);
|
Vim::action(editor, cx, Vim::helix_insert);
|
||||||
Vim::action(editor, cx, Vim::helix_append);
|
Vim::action(editor, cx, Vim::helix_append);
|
||||||
Vim::action(editor, cx, Vim::helix_yank);
|
Vim::action(editor, cx, Vim::helix_yank);
|
||||||
|
@ -430,6 +435,47 @@ impl Vim {
|
||||||
});
|
});
|
||||||
self.switch_mode(Mode::HelixNormal, true, window, cx);
|
self.switch_mode(Mode::HelixNormal, true, window, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn helix_select_lines(
|
||||||
|
&mut self,
|
||||||
|
_: &HelixSelectLine,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
let count = Vim::take_count(cx).unwrap_or(1);
|
||||||
|
self.update_editor(cx, |_, editor, cx| {
|
||||||
|
editor.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx);
|
||||||
|
let display_map = editor.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
|
let mut selections = editor.selections.all::<Point>(cx);
|
||||||
|
let max_point = display_map.buffer_snapshot.max_point();
|
||||||
|
let buffer_snapshot = &display_map.buffer_snapshot;
|
||||||
|
|
||||||
|
for selection in &mut selections {
|
||||||
|
// Start always goes to column 0 of the first selected line
|
||||||
|
let start_row = selection.start.row;
|
||||||
|
let current_end_row = selection.end.row;
|
||||||
|
|
||||||
|
// Check if cursor is on empty line by checking first character
|
||||||
|
let line_start_offset = buffer_snapshot.point_to_offset(Point::new(start_row, 0));
|
||||||
|
let first_char = buffer_snapshot.chars_at(line_start_offset).next();
|
||||||
|
let extra_line = if first_char == Some('\n') { 1 } else { 0 };
|
||||||
|
|
||||||
|
let end_row = current_end_row + count as u32 + extra_line;
|
||||||
|
|
||||||
|
selection.start = Point::new(start_row, 0);
|
||||||
|
selection.end = if end_row > max_point.row {
|
||||||
|
max_point
|
||||||
|
} else {
|
||||||
|
Point::new(end_row, 0)
|
||||||
|
};
|
||||||
|
selection.reversed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.change_selections(Default::default(), window, cx, |s| {
|
||||||
|
s.select(selections);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -763,4 +809,142 @@ mod test {
|
||||||
cx.shared_clipboard().assert_eq("worl");
|
cx.shared_clipboard().assert_eq("worl");
|
||||||
cx.assert_state("hello «worlˇ»d", Mode::HelixNormal);
|
cx.assert_state("hello «worlˇ»d", Mode::HelixNormal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_helix_select_lines(cx: &mut gpui::TestAppContext) {
|
||||||
|
let mut cx = VimTestContext::new(cx, true).await;
|
||||||
|
cx.set_state(
|
||||||
|
"line one\nline ˇtwo\nline three\nline four",
|
||||||
|
Mode::HelixNormal,
|
||||||
|
);
|
||||||
|
cx.simulate_keystrokes("2 x");
|
||||||
|
cx.assert_state(
|
||||||
|
"line one\n«line two\nline three\nˇ»line four",
|
||||||
|
Mode::HelixNormal,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test extending existing line selection
|
||||||
|
cx.set_state(
|
||||||
|
indoc! {"
|
||||||
|
li«ˇne one
|
||||||
|
li»ne two
|
||||||
|
line three
|
||||||
|
line four"},
|
||||||
|
Mode::HelixNormal,
|
||||||
|
);
|
||||||
|
cx.simulate_keystrokes("x");
|
||||||
|
cx.assert_state(
|
||||||
|
indoc! {"
|
||||||
|
«line one
|
||||||
|
line two
|
||||||
|
ˇ»line three
|
||||||
|
line four"},
|
||||||
|
Mode::HelixNormal,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Pressing x in empty line, select next line (because helix considers cursor a selection)
|
||||||
|
cx.set_state(
|
||||||
|
indoc! {"
|
||||||
|
line one
|
||||||
|
ˇ
|
||||||
|
line three
|
||||||
|
line four"},
|
||||||
|
Mode::HelixNormal,
|
||||||
|
);
|
||||||
|
cx.simulate_keystrokes("x");
|
||||||
|
cx.assert_state(
|
||||||
|
indoc! {"
|
||||||
|
line one
|
||||||
|
«
|
||||||
|
line three
|
||||||
|
ˇ»line four"},
|
||||||
|
Mode::HelixNormal,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Empty line with count selects extra + count lines
|
||||||
|
cx.set_state(
|
||||||
|
indoc! {"
|
||||||
|
line one
|
||||||
|
ˇ
|
||||||
|
line three
|
||||||
|
line four
|
||||||
|
line five"},
|
||||||
|
Mode::HelixNormal,
|
||||||
|
);
|
||||||
|
cx.simulate_keystrokes("2 x");
|
||||||
|
cx.assert_state(
|
||||||
|
indoc! {"
|
||||||
|
line one
|
||||||
|
«
|
||||||
|
line three
|
||||||
|
line four
|
||||||
|
ˇ»line five"},
|
||||||
|
Mode::HelixNormal,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Compare empty vs non-empty line behavior
|
||||||
|
cx.set_state(
|
||||||
|
indoc! {"
|
||||||
|
ˇnon-empty line
|
||||||
|
line two
|
||||||
|
line three"},
|
||||||
|
Mode::HelixNormal,
|
||||||
|
);
|
||||||
|
cx.simulate_keystrokes("x");
|
||||||
|
cx.assert_state(
|
||||||
|
indoc! {"
|
||||||
|
«non-empty line
|
||||||
|
ˇ»line two
|
||||||
|
line three"},
|
||||||
|
Mode::HelixNormal,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Same test but with empty line - should select one extra
|
||||||
|
cx.set_state(
|
||||||
|
indoc! {"
|
||||||
|
ˇ
|
||||||
|
line two
|
||||||
|
line three"},
|
||||||
|
Mode::HelixNormal,
|
||||||
|
);
|
||||||
|
cx.simulate_keystrokes("x");
|
||||||
|
cx.assert_state(
|
||||||
|
indoc! {"
|
||||||
|
«
|
||||||
|
line two
|
||||||
|
ˇ»line three"},
|
||||||
|
Mode::HelixNormal,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test selecting multiple lines with count
|
||||||
|
cx.set_state(
|
||||||
|
indoc! {"
|
||||||
|
ˇline one
|
||||||
|
line two
|
||||||
|
line threeˇ
|
||||||
|
line four
|
||||||
|
line five"},
|
||||||
|
Mode::HelixNormal,
|
||||||
|
);
|
||||||
|
cx.simulate_keystrokes("x");
|
||||||
|
cx.assert_state(
|
||||||
|
indoc! {"
|
||||||
|
«line one
|
||||||
|
ˇ»line two
|
||||||
|
«line three
|
||||||
|
ˇ»line four
|
||||||
|
line five"},
|
||||||
|
Mode::HelixNormal,
|
||||||
|
);
|
||||||
|
cx.simulate_keystrokes("x");
|
||||||
|
cx.assert_state(
|
||||||
|
indoc! {"
|
||||||
|
«line one
|
||||||
|
line two
|
||||||
|
line three
|
||||||
|
line four
|
||||||
|
ˇ»line five"},
|
||||||
|
Mode::HelixNormal,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue