Cargo.lock 🔗
@@ -18138,6 +18138,7 @@ dependencies = [
"menu",
"multi_buffer",
"nvim-rs",
+ "outline_panel",
"parking_lot",
"perf",
"picker",
0x2CA and Ben Kunkle created
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>
Cargo.lock | 1
assets/keymaps/vim.json | 32 +++++++++
crates/outline_panel/src/outline_panel.rs | 82 +++++++++++++++++++++++++
crates/vim/Cargo.toml | 1
crates/vim/src/test/vim_test_context.rs | 1
5 files changed, 115 insertions(+), 2 deletions(-)
@@ -18138,6 +18138,7 @@ dependencies = [
"menu",
"multi_buffer",
"nvim-rs",
+ "outline_panel",
"parking_lot",
"perf",
"picker",
@@ -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",
},
},
{
@@ -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))
@@ -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
@@ -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);