diff --git a/Cargo.lock b/Cargo.lock index 1dfcabfb552e128dfa6b0b47ebb5f33bfa2aa4a6..8c1ade1714c9dd7f609582cc8bdf5184678afcd9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18138,6 +18138,7 @@ dependencies = [ "menu", "multi_buffer", "nvim-rs", + "outline_panel", "parking_lot", "perf", "picker", diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index bbae6e2f4d738ef60b3a1a5ba33a26a9ab68f497..0097480e2775a1048452b2a5e8ec826525da3f2e 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -970,10 +970,38 @@ { "context": "OutlinePanel && not_editing", "bindings": { - "j": "menu::SelectNext", - "k": "menu::SelectPrevious", + "h": "outline_panel::CollapseSelectedEntry", + "j": "vim::MenuSelectNext", + "k": "vim::MenuSelectPrevious", + "down": "vim::MenuSelectNext", + "up": "vim::MenuSelectPrevious", + "l": "outline_panel::ExpandSelectedEntry", "shift-g": "menu::SelectLast", "g g": "menu::SelectFirst", + "-": "outline_panel::SelectParent", + "enter": "editor::ToggleFocus", + "/": "menu::Cancel", + "ctrl-u": "outline_panel::ScrollUp", + "ctrl-d": "outline_panel::ScrollDown", + "z t": "outline_panel::ScrollCursorTop", + "z z": "outline_panel::ScrollCursorCenter", + "z b": "outline_panel::ScrollCursorBottom", + "0": ["vim::Number", 0], + "1": ["vim::Number", 1], + "2": ["vim::Number", 2], + "3": ["vim::Number", 3], + "4": ["vim::Number", 4], + "5": ["vim::Number", 5], + "6": ["vim::Number", 6], + "7": ["vim::Number", 7], + "8": ["vim::Number", 8], + "9": ["vim::Number", 9], + }, + }, + { + "context": "OutlinePanel && editing", + "bindings": { + "enter": "menu::Cancel", }, }, { diff --git a/crates/outline_panel/src/outline_panel.rs b/crates/outline_panel/src/outline_panel.rs index 943025b1d0a96692f34f2ebcefff83a0ad2ddaee..8dbf7b681d9be45bda0fd9803cbb8e2cd434e921 100644 --- a/crates/outline_panel/src/outline_panel.rs +++ b/crates/outline_panel/src/outline_panel.rs @@ -75,6 +75,16 @@ actions!( OpenSelectedEntry, /// Reveals the selected item in the system file manager. RevealInFileManager, + /// Scroll half a page upwards + ScrollUp, + /// Scroll half a page downwards + ScrollDown, + /// Scroll until the cursor displays at the center + ScrollCursorCenter, + /// Scroll until the cursor displays at the top + ScrollCursorTop, + /// Scroll until the cursor displays at the bottom + ScrollCursorBottom, /// Selects the parent of the current entry. SelectParent, /// Toggles the pin status of the active editor. @@ -100,6 +110,7 @@ pub struct OutlinePanel { active: bool, pinned: bool, scroll_handle: UniformListScrollHandle, + rendered_entries_len: usize, context_menu: Option<(Entity, Point, Subscription)>, focus_handle: FocusHandle, pending_serialization: Task>, @@ -839,6 +850,7 @@ impl OutlinePanel { fs: workspace.app_state().fs.clone(), max_width_item_index: None, scroll_handle, + rendered_entries_len: 0, focus_handle, filter_editor, fs_entries: Vec::new(), @@ -1149,6 +1161,70 @@ impl OutlinePanel { } } + fn scroll_up(&mut self, _: &ScrollUp, window: &mut Window, cx: &mut Context) { + for _ in 0..self.rendered_entries_len / 2 { + window.dispatch_action(SelectPrevious.boxed_clone(), cx); + } + } + + fn scroll_down(&mut self, _: &ScrollDown, window: &mut Window, cx: &mut Context) { + for _ in 0..self.rendered_entries_len / 2 { + window.dispatch_action(SelectNext.boxed_clone(), cx); + } + } + + fn scroll_cursor_center( + &mut self, + _: &ScrollCursorCenter, + _: &mut Window, + cx: &mut Context, + ) { + if let Some(selected_entry) = self.selected_entry() { + let index = self + .cached_entries + .iter() + .position(|cached_entry| &cached_entry.entry == selected_entry); + if let Some(index) = index { + self.scroll_handle + .scroll_to_item_strict(index, ScrollStrategy::Center); + cx.notify(); + } + } + } + + fn scroll_cursor_top(&mut self, _: &ScrollCursorTop, _: &mut Window, cx: &mut Context) { + if let Some(selected_entry) = self.selected_entry() { + let index = self + .cached_entries + .iter() + .position(|cached_entry| &cached_entry.entry == selected_entry); + if let Some(index) = index { + self.scroll_handle + .scroll_to_item_strict(index, ScrollStrategy::Top); + cx.notify(); + } + } + } + + fn scroll_cursor_bottom( + &mut self, + _: &ScrollCursorBottom, + _: &mut Window, + cx: &mut Context, + ) { + if let Some(selected_entry) = self.selected_entry() { + let index = self + .cached_entries + .iter() + .position(|cached_entry| &cached_entry.entry == selected_entry); + if let Some(index) = index { + self.scroll_handle + .scroll_to_item_strict(index, ScrollStrategy::Bottom); + cx.notify(); + } + } + } + fn select_next(&mut self, _: &SelectNext, window: &mut Window, cx: &mut Context) { if let Some(entry_to_select) = self.selected_entry().and_then(|selected_entry| { self.cached_entries @@ -4578,6 +4654,7 @@ impl OutlinePanel { "entries", items_len, cx.processor(move |outline_panel, range: Range, window, cx| { + outline_panel.rendered_entries_len = range.end - range.start; let entries = outline_panel.cached_entries.get(range); entries .map(|entries| entries.to_vec()) @@ -4970,7 +5047,12 @@ impl Render for OutlinePanel { .key_context(self.dispatch_context(window, cx)) .on_action(cx.listener(Self::open_selected_entry)) .on_action(cx.listener(Self::cancel)) + .on_action(cx.listener(Self::scroll_up)) + .on_action(cx.listener(Self::scroll_down)) .on_action(cx.listener(Self::select_next)) + .on_action(cx.listener(Self::scroll_cursor_center)) + .on_action(cx.listener(Self::scroll_cursor_top)) + .on_action(cx.listener(Self::scroll_cursor_bottom)) .on_action(cx.listener(Self::select_previous)) .on_action(cx.listener(Self::select_first)) .on_action(cx.listener(Self::select_last)) diff --git a/crates/vim/Cargo.toml b/crates/vim/Cargo.toml index 74409a6c255645378b0b2829f4d0045776bfa019..2db1b51e72fcd862ccb1c35ff920fec7dbd47995 100644 --- a/crates/vim/Cargo.toml +++ b/crates/vim/Cargo.toml @@ -66,6 +66,7 @@ lsp = { workspace = true, features = ["test-support"] } markdown_preview.workspace = true parking_lot.workspace = true project_panel.workspace = true +outline_panel.workspace = true release_channel.workspace = true semver.workspace = true settings_ui.workspace = true diff --git a/crates/vim/src/test/vim_test_context.rs b/crates/vim/src/test/vim_test_context.rs index acd77839f2d8cc09ed72993638ff4ec66f79d3fc..2d5ed4227dcc263f56cfa0bcb337f5673df8ef3c 100644 --- a/crates/vim/src/test/vim_test_context.rs +++ b/crates/vim/src/test/vim_test_context.rs @@ -23,6 +23,7 @@ impl VimTestContext { release_channel::init(Version::new(0, 0, 0), cx); command_palette::init(cx); project_panel::init(cx); + outline_panel::init(cx); git_ui::init(cx); crate::init(cx); search::init(cx);