vim: Add scroll keybindings for the OutlinePanel (#42438)

Closes #ISSUE

```json
  {
    "context": "OutlinePanel && not_editing",
    "bindings": {
      "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"
    }
  },
  {
    "context": "OutlinePanel && editing",
    "bindings": {
      "enter": "menu::Cancel"
    }
  },
```

Release Notes:

- Added scroll keybindings for the OutlinePanel

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
This commit is contained in:
0x2CAandBen Kunkle authored and GitHub committed 2025-12-15 15:40:37 +00:00
1 parent 3ae473f072
commit 1b93e20e8b
5 files changed
+115 -2

No files matched your search

Generated
+1
View File
@@ -18138,6 +18138,7 @@ dependencies = [
"menu",
"multi_buffer",
"nvim-rs",
"outline_panel",
"parking_lot",
"perf",
"picker",
+30 -2
View File
@@ -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",
},
},
{
+82
View File
@@ -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<ContextMenu>, Point<Pixels>, Subscription)>,
focus_handle: FocusHandle,
pending_serialization: Task<Option<()>>,
@@ -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<Self>) {
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<Self>) {
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<Self>,
) {
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<Self>) {
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<Self>,
) {
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<Self>) {
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<usize>, 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))
+1
View File
@@ -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
+1
View File
@@ -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);