vim: Fix key navigation on folded buffer headers (#25944)

Closes #24243

Release Notes:

- vim: Fix j/k on folded multibuffer headers

---------

Co-authored-by: João Marcos <marcospb19@hotmail.com>
This commit is contained in:
Conrad Irwin 2025-03-03 14:44:39 -07:00 committed by GitHub
parent 3bec4eb117
commit 0bd40da546
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 348 additions and 17 deletions

View file

@ -55,6 +55,7 @@ git_ui.workspace = true
gpui = { workspace = true, features = ["test-support"] }
indoc.workspace = true
language = { workspace = true, features = ["test-support"] }
project = { workspace = true, features = ["test-support"] }
lsp = { workspace = true, features = ["test-support"] }
parking_lot.workspace = true
project_panel.workspace = true

View file

@ -1329,12 +1329,25 @@ pub(crate) fn start_of_relative_buffer_row(
fn up_down_buffer_rows(
map: &DisplaySnapshot,
point: DisplayPoint,
mut point: DisplayPoint,
mut goal: SelectionGoal,
times: isize,
mut times: isize,
text_layout_details: &TextLayoutDetails,
) -> (DisplayPoint, SelectionGoal) {
let bias = if times < 0 { Bias::Left } else { Bias::Right };
while map.is_folded_buffer_header(point.row()) {
if times < 0 {
(point, _) = movement::up(map, point, goal, true, text_layout_details);
times += 1;
} else if times > 0 {
(point, _) = movement::down(map, point, goal, true, text_layout_details);
times -= 1;
} else {
break;
}
}
let start = map.display_point_to_fold_point(point, Bias::Left);
let begin_folded_line = map.fold_point_to_display_point(
map.fold_snapshot

View file

@ -6,9 +6,13 @@ use std::time::Duration;
use collections::HashMap;
use command_palette::CommandPalette;
use editor::{actions::DeleteLine, display_map::DisplayRow, DisplayPoint};
use editor::{
actions::DeleteLine, display_map::DisplayRow, test::editor_test_context::EditorTestContext,
DisplayPoint, Editor, EditorMode, MultiBuffer,
};
use futures::StreamExt;
use gpui::{KeyBinding, Modifiers, MouseButton, TestAppContext};
use language::Point;
pub use neovim_backed_test_context::*;
use settings::SettingsStore;
pub use vim_test_context::*;
@ -1707,3 +1711,202 @@ async fn test_ctrl_o_dot(cx: &mut gpui::TestAppContext) {
cx.simulate_shared_keystrokes("l l escape .").await;
cx.shared_state().await.assert_eq("hellˇllo world.");
}
#[gpui::test]
async fn test_folded_multibuffer_excerpts(cx: &mut gpui::TestAppContext) {
VimTestContext::init(cx);
cx.update(|cx| {
VimTestContext::init_keybindings(true, cx);
});
let (editor, cx) = cx.add_window_view(|window, cx| {
let multi_buffer = MultiBuffer::build_multi(
[
("111\n222\n333\n444\n", vec![Point::row_range(0..2)]),
("aaa\nbbb\nccc\nddd\n", vec![Point::row_range(0..2)]),
("AAA\nBBB\nCCC\nDDD\n", vec![Point::row_range(0..2)]),
("one\ntwo\nthr\nfou\n", vec![Point::row_range(0..2)]),
],
cx,
);
let mut editor = Editor::new(
EditorMode::Full,
multi_buffer.clone(),
None,
true,
window,
cx,
);
let buffer_ids = multi_buffer.read(cx).excerpt_buffer_ids();
// fold all but the second buffer, so that we test navigating between two
// adjacent folded buffers, as well as folded buffers at the start and
// end the multibuffer
editor.fold_buffer(buffer_ids[0], cx);
editor.fold_buffer(buffer_ids[2], cx);
editor.fold_buffer(buffer_ids[3], cx);
editor
});
let mut cx = EditorTestContext::for_editor_in(editor.clone(), cx).await;
cx.assert_excerpts_with_selections(indoc! {"
[EXCERPT]
ˇ[FOLDED]
[EXCERPT]
aaa
bbb
[EXCERPT]
[FOLDED]
[EXCERPT]
[FOLDED]
"
});
cx.simulate_keystroke("j");
cx.assert_excerpts_with_selections(indoc! {"
[EXCERPT]
[FOLDED]
[EXCERPT]
ˇaaa
bbb
[EXCERPT]
[FOLDED]
[EXCERPT]
[FOLDED]
"
});
cx.simulate_keystroke("j");
cx.simulate_keystroke("j");
cx.assert_excerpts_with_selections(indoc! {"
[EXCERPT]
[FOLDED]
[EXCERPT]
aaa
bbb
ˇ[EXCERPT]
[FOLDED]
[EXCERPT]
[FOLDED]
"
});
cx.simulate_keystroke("j");
cx.assert_excerpts_with_selections(indoc! {"
[EXCERPT]
[FOLDED]
[EXCERPT]
aaa
bbb
[EXCERPT]
ˇ[FOLDED]
[EXCERPT]
[FOLDED]
"
});
cx.simulate_keystroke("j");
cx.assert_excerpts_with_selections(indoc! {"
[EXCERPT]
[FOLDED]
[EXCERPT]
aaa
bbb
[EXCERPT]
[FOLDED]
[EXCERPT]
ˇ[FOLDED]
"
});
cx.simulate_keystroke("k");
cx.assert_excerpts_with_selections(indoc! {"
[EXCERPT]
[FOLDED]
[EXCERPT]
aaa
bbb
[EXCERPT]
ˇ[FOLDED]
[EXCERPT]
[FOLDED]
"
});
cx.simulate_keystroke("k");
cx.simulate_keystroke("k");
cx.simulate_keystroke("k");
cx.assert_excerpts_with_selections(indoc! {"
[EXCERPT]
[FOLDED]
[EXCERPT]
ˇaaa
bbb
[EXCERPT]
[FOLDED]
[EXCERPT]
[FOLDED]
"
});
cx.simulate_keystroke("k");
cx.assert_excerpts_with_selections(indoc! {"
[EXCERPT]
ˇ[FOLDED]
[EXCERPT]
aaa
bbb
[EXCERPT]
[FOLDED]
[EXCERPT]
[FOLDED]
"
});
cx.simulate_keystroke("shift-g");
cx.assert_excerpts_with_selections(indoc! {"
[EXCERPT]
[FOLDED]
[EXCERPT]
aaa
bbb
[EXCERPT]
[FOLDED]
[EXCERPT]
ˇ[FOLDED]
"
});
cx.simulate_keystrokes("g g");
cx.assert_excerpts_with_selections(indoc! {"
[EXCERPT]
ˇ[FOLDED]
[EXCERPT]
aaa
bbb
[EXCERPT]
[FOLDED]
[EXCERPT]
[FOLDED]
"
});
cx.update_editor(|editor, _, cx| {
let buffer_ids = editor.buffer().read(cx).excerpt_buffer_ids();
editor.fold_buffer(buffer_ids[1], cx);
});
cx.assert_excerpts_with_selections(indoc! {"
[EXCERPT]
ˇ[FOLDED]
[EXCERPT]
[FOLDED]
[EXCERPT]
[FOLDED]
[EXCERPT]
[FOLDED]
"
});
cx.simulate_keystrokes("2 j");
cx.assert_excerpts_with_selections(indoc! {"
[EXCERPT]
[FOLDED]
[EXCERPT]
[FOLDED]
[EXCERPT]
ˇ[FOLDED]
[EXCERPT]
[FOLDED]
"
});
}

View file

@ -24,6 +24,10 @@ impl VimTestContext {
git_ui::init(cx);
crate::init(cx);
search::init(cx);
language::init(cx);
editor::init_settings(cx);
project::Project::init_settings(cx);
theme::init(theme::LoadThemes::JustBase, cx);
});
}
@ -56,22 +60,26 @@ impl VimTestContext {
)
}
pub fn init_keybindings(enabled: bool, cx: &mut App) {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(enabled));
});
let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
"keymaps/default-macos.json",
cx,
)
.unwrap();
cx.bind_keys(default_key_bindings);
if enabled {
let vim_key_bindings =
settings::KeymapFile::load_asset("keymaps/vim.json", cx).unwrap();
cx.bind_keys(vim_key_bindings);
}
}
pub fn new_with_lsp(mut cx: EditorLspTestContext, enabled: bool) -> VimTestContext {
cx.update(|_, cx| {
SettingsStore::update_global(cx, |store, cx| {
store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(enabled));
});
let default_key_bindings = settings::KeymapFile::load_asset_allow_partial_failure(
"keymaps/default-macos.json",
cx,
)
.unwrap();
cx.bind_keys(default_key_bindings);
if enabled {
let vim_key_bindings =
settings::KeymapFile::load_asset("keymaps/vim.json", cx).unwrap();
cx.bind_keys(vim_key_bindings);
}
Self::init_keybindings(enabled, cx);
});
// Setup search toolbars and keypress hook