mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
vim: Add support for <count>gt and <count>gT (#38570)
Vim mode currently supports `gt` (go to next tab) and `gT` (go to previous tab) but not with count. Implement the expected behavior as defined by vim: - `<count>gt` moves to tab `<count>` - `<count>gT` moves to previous tab `<count>` times (with wraparound) Release Notes: - Improved vim `gt` and `gT` to support count, e.g. `5gt` - go to tab 5, `8gT` - go to 8th previous tab with wraparound.
This commit is contained in:
parent
4e6e424fd7
commit
d4fd59f0a2
2 changed files with 133 additions and 3 deletions
|
|
@ -95,8 +95,8 @@
|
|||
"g g": "vim::StartOfDocument",
|
||||
"g h": "editor::Hover",
|
||||
"g B": "editor::BlameHover",
|
||||
"g t": "pane::ActivateNextItem",
|
||||
"g shift-t": "pane::ActivatePreviousItem",
|
||||
"g t": "vim::GoToTab",
|
||||
"g shift-t": "vim::GoToPreviousTab",
|
||||
"g d": "editor::GoToDefinition",
|
||||
"g shift-d": "editor::GoToDeclaration",
|
||||
"g y": "editor::GoToTypeDefinition",
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ use editor::Editor;
|
|||
use editor::{Anchor, SelectionEffects};
|
||||
use editor::{Bias, ToPoint};
|
||||
use editor::{display_map::ToDisplayPoint, movement};
|
||||
use gpui::{Context, Window, actions};
|
||||
use gpui::{Action, Context, Window, actions};
|
||||
use language::{Point, SelectionGoal};
|
||||
use log::error;
|
||||
use multi_buffer::MultiBufferRow;
|
||||
|
|
@ -94,6 +94,10 @@ actions!(
|
|||
Redo,
|
||||
/// Undoes all changes to the most recently changed line.
|
||||
UndoLastLine,
|
||||
/// Go to tab page (with count support).
|
||||
GoToTab,
|
||||
/// Go to previous tab page (with count support).
|
||||
GoToPreviousTab,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
@ -116,6 +120,8 @@ pub(crate) fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
|
|||
Vim::action(editor, cx, Vim::toggle_comments);
|
||||
Vim::action(editor, cx, Vim::paste);
|
||||
Vim::action(editor, cx, Vim::show_location);
|
||||
Vim::action(editor, cx, Vim::go_to_tab);
|
||||
Vim::action(editor, cx, Vim::go_to_previous_tab);
|
||||
|
||||
Vim::action(editor, cx, |vim, _: &DeleteLeft, window, cx| {
|
||||
vim.record_current_action(cx);
|
||||
|
|
@ -984,6 +990,54 @@ impl Vim {
|
|||
self.switch_mode(Mode::Insert, true, window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn go_to_tab(&mut self, _: &GoToTab, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let count = Vim::take_count(cx);
|
||||
Vim::take_forced_motion(cx);
|
||||
|
||||
if let Some(tab_index) = count {
|
||||
// <count>gt goes to tab <count> (1-based).
|
||||
let zero_based_index = tab_index.saturating_sub(1);
|
||||
window.dispatch_action(
|
||||
workspace::pane::ActivateItem(zero_based_index).boxed_clone(),
|
||||
cx,
|
||||
);
|
||||
} else {
|
||||
// If no count is provided, go to the next tab.
|
||||
window.dispatch_action(workspace::pane::ActivateNextItem.boxed_clone(), cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn go_to_previous_tab(
|
||||
&mut self,
|
||||
_: &GoToPreviousTab,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let count = Vim::take_count(cx);
|
||||
Vim::take_forced_motion(cx);
|
||||
|
||||
if let Some(count) = count {
|
||||
// gT with count goes back that many tabs with wraparound (not the same as gt!).
|
||||
if let Some(workspace) = self.workspace(window) {
|
||||
let pane = workspace.read(cx).active_pane().read(cx);
|
||||
let item_count = pane.items().count();
|
||||
if item_count > 0 {
|
||||
let current_index = pane.active_item_index();
|
||||
let target_index = (current_index as isize - count as isize)
|
||||
.rem_euclid(item_count as isize)
|
||||
as usize;
|
||||
window.dispatch_action(
|
||||
workspace::pane::ActivateItem(target_index).boxed_clone(),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No count provided, go to the previous tab.
|
||||
window.dispatch_action(workspace::pane::ActivatePreviousItem.boxed_clone(), cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
|
@ -2119,4 +2173,80 @@ mod test {
|
|||
Mode::Normal,
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_go_to_tab_with_count(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = VimTestContext::new(cx, true).await;
|
||||
|
||||
// Open 4 tabs.
|
||||
cx.simulate_keystrokes(": tabnew");
|
||||
cx.simulate_keystrokes("enter");
|
||||
cx.simulate_keystrokes(": tabnew");
|
||||
cx.simulate_keystrokes("enter");
|
||||
cx.simulate_keystrokes(": tabnew");
|
||||
cx.simulate_keystrokes("enter");
|
||||
cx.workspace(|workspace, _, cx| {
|
||||
assert_eq!(workspace.items(cx).count(), 4);
|
||||
assert_eq!(workspace.active_pane().read(cx).active_item_index(), 3);
|
||||
});
|
||||
|
||||
cx.simulate_keystrokes("1 g t");
|
||||
cx.workspace(|workspace, _, cx| {
|
||||
assert_eq!(workspace.active_pane().read(cx).active_item_index(), 0);
|
||||
});
|
||||
|
||||
cx.simulate_keystrokes("3 g t");
|
||||
cx.workspace(|workspace, _, cx| {
|
||||
assert_eq!(workspace.active_pane().read(cx).active_item_index(), 2);
|
||||
});
|
||||
|
||||
cx.simulate_keystrokes("4 g t");
|
||||
cx.workspace(|workspace, _, cx| {
|
||||
assert_eq!(workspace.active_pane().read(cx).active_item_index(), 3);
|
||||
});
|
||||
|
||||
cx.simulate_keystrokes("1 g t");
|
||||
cx.simulate_keystrokes("g t");
|
||||
cx.workspace(|workspace, _, cx| {
|
||||
assert_eq!(workspace.active_pane().read(cx).active_item_index(), 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_go_to_previous_tab_with_count(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = VimTestContext::new(cx, true).await;
|
||||
|
||||
// Open 4 tabs.
|
||||
cx.simulate_keystrokes(": tabnew");
|
||||
cx.simulate_keystrokes("enter");
|
||||
cx.simulate_keystrokes(": tabnew");
|
||||
cx.simulate_keystrokes("enter");
|
||||
cx.simulate_keystrokes(": tabnew");
|
||||
cx.simulate_keystrokes("enter");
|
||||
cx.workspace(|workspace, _, cx| {
|
||||
assert_eq!(workspace.items(cx).count(), 4);
|
||||
assert_eq!(workspace.active_pane().read(cx).active_item_index(), 3);
|
||||
});
|
||||
|
||||
cx.simulate_keystrokes("2 g shift-t");
|
||||
cx.workspace(|workspace, _, cx| {
|
||||
assert_eq!(workspace.active_pane().read(cx).active_item_index(), 1);
|
||||
});
|
||||
|
||||
cx.simulate_keystrokes("g shift-t");
|
||||
cx.workspace(|workspace, _, cx| {
|
||||
assert_eq!(workspace.active_pane().read(cx).active_item_index(), 0);
|
||||
});
|
||||
|
||||
// Wraparound: gT from first tab should go to last.
|
||||
cx.simulate_keystrokes("g shift-t");
|
||||
cx.workspace(|workspace, _, cx| {
|
||||
assert_eq!(workspace.active_pane().read(cx).active_item_index(), 3);
|
||||
});
|
||||
|
||||
cx.simulate_keystrokes("6 g shift-t");
|
||||
cx.workspace(|workspace, _, cx| {
|
||||
assert_eq!(workspace.active_pane().read(cx).active_item_index(), 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue