vim: Fix gv after indent/toggle comments (#17986)
Release Notes: - vim: Fixed `gv` after > and < in visual mode
This commit is contained in:
parent
3ac201e448
commit
1a62396b1e
5 changed files with 107 additions and 71 deletions
|
@ -1,6 +1,7 @@
|
|||
use crate::{motion::Motion, object::Object, Vim};
|
||||
use crate::{motion::Motion, object::Object, state::Mode, Vim};
|
||||
use collections::HashMap;
|
||||
use editor::{display_map::ToDisplayPoint, Bias};
|
||||
use editor::{display_map::ToDisplayPoint, Bias, Editor};
|
||||
use gpui::actions;
|
||||
use language::SelectionGoal;
|
||||
use ui::ViewContext;
|
||||
|
||||
|
@ -10,6 +11,46 @@ pub(crate) enum IndentDirection {
|
|||
Out,
|
||||
}
|
||||
|
||||
actions!(vim, [Indent, Outdent,]);
|
||||
|
||||
pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
|
||||
Vim::action(editor, cx, |vim, _: &Indent, cx| {
|
||||
vim.record_current_action(cx);
|
||||
let count = vim.take_count(cx).unwrap_or(1);
|
||||
vim.store_visual_marks(cx);
|
||||
vim.update_editor(cx, |vim, editor, cx| {
|
||||
editor.transact(cx, |editor, cx| {
|
||||
let mut original_positions = vim.save_selection_starts(editor, cx);
|
||||
for _ in 0..count {
|
||||
editor.indent(&Default::default(), cx);
|
||||
}
|
||||
vim.restore_selection_cursors(editor, cx, &mut original_positions);
|
||||
});
|
||||
});
|
||||
if vim.mode.is_visual() {
|
||||
vim.switch_mode(Mode::Normal, true, cx)
|
||||
}
|
||||
});
|
||||
|
||||
Vim::action(editor, cx, |vim, _: &Outdent, cx| {
|
||||
vim.record_current_action(cx);
|
||||
let count = vim.take_count(cx).unwrap_or(1);
|
||||
vim.store_visual_marks(cx);
|
||||
vim.update_editor(cx, |vim, editor, cx| {
|
||||
editor.transact(cx, |editor, cx| {
|
||||
let mut original_positions = vim.save_selection_starts(editor, cx);
|
||||
for _ in 0..count {
|
||||
editor.outdent(&Default::default(), cx);
|
||||
}
|
||||
vim.restore_selection_cursors(editor, cx, &mut original_positions);
|
||||
});
|
||||
});
|
||||
if vim.mode.is_visual() {
|
||||
vim.switch_mode(Mode::Normal, true, cx)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
impl Vim {
|
||||
pub(crate) fn indent_motion(
|
||||
&mut self,
|
||||
|
@ -78,3 +119,20 @@ impl Vim {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::test::NeovimBackedTestContext;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_indent_gv(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = NeovimBackedTestContext::new(cx).await;
|
||||
cx.set_neovim_option("shiftwidth=4").await;
|
||||
|
||||
cx.set_shared_state("ˇhello\nworld\n").await;
|
||||
cx.simulate_shared_keystrokes("v j > g v").await;
|
||||
cx.shared_state()
|
||||
.await
|
||||
.assert_eq("« hello\n ˇ» world\n");
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@ mod case;
|
|||
mod change;
|
||||
mod delete;
|
||||
mod increment;
|
||||
mod indent;
|
||||
pub(crate) mod mark;
|
||||
mod paste;
|
||||
pub(crate) mod repeat;
|
||||
|
@ -16,6 +15,7 @@ use std::collections::HashMap;
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
indent::IndentDirection,
|
||||
motion::{self, first_non_whitespace, next_line_end, right, Motion},
|
||||
object::Object,
|
||||
state::{Mode, Operator},
|
||||
|
@ -34,8 +34,6 @@ use language::{Point, SelectionGoal};
|
|||
use log::error;
|
||||
use multi_buffer::MultiBufferRow;
|
||||
|
||||
use self::indent::IndentDirection;
|
||||
|
||||
actions!(
|
||||
vim,
|
||||
[
|
||||
|
@ -56,8 +54,6 @@ actions!(
|
|||
ConvertToUpperCase,
|
||||
ConvertToLowerCase,
|
||||
JoinLines,
|
||||
Indent,
|
||||
Outdent,
|
||||
ToggleComments,
|
||||
Undo,
|
||||
Redo,
|
||||
|
@ -129,41 +125,7 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut ViewContext<Vim>) {
|
|||
})
|
||||
});
|
||||
if vim.mode.is_visual() {
|
||||
vim.switch_mode(Mode::Normal, false, cx)
|
||||
}
|
||||
});
|
||||
|
||||
Vim::action(editor, cx, |vim, _: &Indent, cx| {
|
||||
vim.record_current_action(cx);
|
||||
let count = vim.take_count(cx).unwrap_or(1);
|
||||
vim.update_editor(cx, |_, editor, cx| {
|
||||
editor.transact(cx, |editor, cx| {
|
||||
let mut original_positions = save_selection_starts(editor, cx);
|
||||
for _ in 0..count {
|
||||
editor.indent(&Default::default(), cx);
|
||||
}
|
||||
restore_selection_cursors(editor, cx, &mut original_positions);
|
||||
});
|
||||
});
|
||||
if vim.mode.is_visual() {
|
||||
vim.switch_mode(Mode::Normal, false, cx)
|
||||
}
|
||||
});
|
||||
|
||||
Vim::action(editor, cx, |vim, _: &Outdent, cx| {
|
||||
vim.record_current_action(cx);
|
||||
let count = vim.take_count(cx).unwrap_or(1);
|
||||
vim.update_editor(cx, |_, editor, cx| {
|
||||
editor.transact(cx, |editor, cx| {
|
||||
let mut original_positions = save_selection_starts(editor, cx);
|
||||
for _ in 0..count {
|
||||
editor.outdent(&Default::default(), cx);
|
||||
}
|
||||
restore_selection_cursors(editor, cx, &mut original_positions);
|
||||
});
|
||||
});
|
||||
if vim.mode.is_visual() {
|
||||
vim.switch_mode(Mode::Normal, false, cx)
|
||||
vim.switch_mode(Mode::Normal, true, cx)
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -428,15 +390,16 @@ impl Vim {
|
|||
|
||||
fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
|
||||
self.record_current_action(cx);
|
||||
self.update_editor(cx, |_, editor, cx| {
|
||||
self.store_visual_marks(cx);
|
||||
self.update_editor(cx, |vim, editor, cx| {
|
||||
editor.transact(cx, |editor, cx| {
|
||||
let mut original_positions = save_selection_starts(editor, cx);
|
||||
let mut original_positions = vim.save_selection_starts(editor, cx);
|
||||
editor.toggle_comments(&Default::default(), cx);
|
||||
restore_selection_cursors(editor, cx, &mut original_positions);
|
||||
vim.restore_selection_cursors(editor, cx, &mut original_positions);
|
||||
});
|
||||
});
|
||||
if self.mode.is_visual() {
|
||||
self.switch_mode(Mode::Normal, false, cx)
|
||||
self.switch_mode(Mode::Normal, true, cx)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -480,33 +443,38 @@ impl Vim {
|
|||
});
|
||||
self.pop_operator(cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn save_selection_starts(editor: &Editor, cx: &mut ViewContext<Editor>) -> HashMap<usize, Anchor> {
|
||||
let (map, selections) = editor.selections.all_display(cx);
|
||||
selections
|
||||
.iter()
|
||||
.map(|selection| {
|
||||
(
|
||||
selection.id,
|
||||
map.display_point_to_anchor(selection.start, Bias::Right),
|
||||
)
|
||||
})
|
||||
.collect::<HashMap<_, _>>()
|
||||
}
|
||||
pub fn save_selection_starts(
|
||||
&self,
|
||||
editor: &Editor,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> HashMap<usize, Anchor> {
|
||||
let (map, selections) = editor.selections.all_display(cx);
|
||||
selections
|
||||
.iter()
|
||||
.map(|selection| {
|
||||
(
|
||||
selection.id,
|
||||
map.display_point_to_anchor(selection.start, Bias::Right),
|
||||
)
|
||||
})
|
||||
.collect::<HashMap<_, _>>()
|
||||
}
|
||||
|
||||
fn restore_selection_cursors(
|
||||
editor: &mut Editor,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
positions: &mut HashMap<usize, Anchor>,
|
||||
) {
|
||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
if let Some(anchor) = positions.remove(&selection.id) {
|
||||
selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
|
||||
}
|
||||
pub fn restore_selection_cursors(
|
||||
&self,
|
||||
editor: &mut Editor,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
positions: &mut HashMap<usize, Anchor>,
|
||||
) {
|
||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.move_with(|map, selection| {
|
||||
if let Some(anchor) = positions.remove(&selection.id) {
|
||||
selection.collapse_to(anchor.to_display_point(map), SelectionGoal::None);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
|
|
@ -54,7 +54,7 @@ impl Vim {
|
|||
);
|
||||
starts.push(
|
||||
map.buffer_snapshot
|
||||
.anchor_after(selection.start.to_offset(&map, Bias::Right)),
|
||||
.anchor_before(selection.start.to_offset(&map, Bias::Left)),
|
||||
);
|
||||
reversed.push(selection.reversed)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ mod test;
|
|||
mod change_list;
|
||||
mod command;
|
||||
mod digraph;
|
||||
mod indent;
|
||||
mod insert;
|
||||
mod mode_indicator;
|
||||
mod motion;
|
||||
|
@ -289,6 +290,7 @@ impl Vim {
|
|||
motion::register(editor, cx);
|
||||
command::register(editor, cx);
|
||||
replace::register(editor, cx);
|
||||
indent::register(editor, cx);
|
||||
object::register(editor, cx);
|
||||
visual::register(editor, cx);
|
||||
change_list::register(editor, cx);
|
||||
|
|
8
crates/vim/test_data/test_indent_gv.json
Normal file
8
crates/vim/test_data/test_indent_gv.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{"SetOption":{"value":"shiftwidth=4"}}
|
||||
{"Put":{"state":"ˇhello\nworld\n"}}
|
||||
{"Key":"v"}
|
||||
{"Key":"j"}
|
||||
{"Key":">"}
|
||||
{"Key":"g"}
|
||||
{"Key":"v"}
|
||||
{"Get":{"state":"« hello\n ˇ» world\n","mode":"Visual"}}
|
Loading…
Add table
Add a link
Reference in a new issue