From 4621e60f65b920936cc8f358ef5c9bb471a85a8d Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 13 Jun 2024 10:46:51 +0300 Subject: [PATCH] A set of outline panel fixes (#12965) Follow-up of https://github.com/zed-industries/zed/pull/12637 * Wrong font size for the outline items (fixes https://github.com/zed-industries/zed/pull/12637#issuecomment-2164084021) * Duplicate context menu item (fixes https://github.com/zed-industries/zed/issues/12957) * Missing `space` keybinding for item opening (fixes https://github.com/zed-industries/zed/issues/12956) * Adds 60px more to the default width (fixes https://github.com/zed-industries/zed/issues/12955) * Incorrect scroll for singleton buffers (fixes https://github.com/zed-industries/zed/issues/12953) Release Notes: - N/A --- assets/keymaps/default-linux.json | 11 +- assets/keymaps/default-macos.json | 1 + assets/settings/default.json | 11 +- crates/outline_panel/src/outline_panel.rs | 296 +++++++++++----------- 4 files changed, 153 insertions(+), 166 deletions(-) diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index c735060364d10c..3e716b35d07b56 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -566,11 +566,12 @@ { "context": "OutlinePanel", "bindings": { - "left": "project_panel::CollapseSelectedEntry", - "right": "project_panel::ExpandSelectedEntry", - "ctrl-alt-c": "project_panel::CopyPath", - "alt-ctrl-shift-c": "project_panel::CopyRelativePath", - "alt-ctrl-r": "project_panel::RevealInFinder", + "left": "outline_panel::CollapseSelectedEntry", + "right": "outline_panel::ExpandSelectedEntry", + "ctrl-alt-c": "outline_panel::CopyPath", + "alt-ctrl-shift-c": "outline_panel::CopyRelativePath", + "alt-ctrl-r": "outline_panel::RevealInFinder", + "space": "outline_panel::Open", "shift-down": "menu::SelectNext", "shift-up": "menu::SelectPrev" } diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index 2ce2e4ea895020..21d1f983b54119 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -593,6 +593,7 @@ "cmd-alt-c": "outline_panel::CopyPath", "alt-cmd-shift-c": "outline_panel::CopyRelativePath", "alt-cmd-r": "outline_panel::RevealInFinder", + "space": "outline_panel::Open", "shift-down": "menu::SelectNext", "shift-up": "menu::SelectPrev" } diff --git a/assets/settings/default.json b/assets/settings/default.json index a64208834c112f..d8821c66cfb94a 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -131,14 +131,7 @@ // The default number of lines to expand excerpts in the multibuffer by. "expand_excerpt_lines": 3, // Globs to match against file paths to determine if a file is private. - "private_files": [ - "**/.env*", - "**/*.pem", - "**/*.key", - "**/*.cert", - "**/*.crt", - "**/secrets.yml" - ], + "private_files": ["**/.env*", "**/*.pem", "**/*.key", "**/*.cert", "**/*.crt", "**/secrets.yml"], // Whether to use additional LSP queries to format (and amend) the code after // every "trigger" symbol input, defined by LSP server capabilities. "use_on_type_format": true, @@ -306,7 +299,7 @@ // Whether to show the outline panel button in the status bar "button": true, // Default width of the outline panel. - "default_width": 240, + "default_width": 300, // Where to dock the outline panel. Can be 'left' or 'right'. "dock": "left", // Whether to show file icons in the outline panel. diff --git a/crates/outline_panel/src/outline_panel.rs b/crates/outline_panel/src/outline_panel.rs index 1c5cc47fda14f3..169ef97631412d 100644 --- a/crates/outline_panel/src/outline_panel.rs +++ b/crates/outline_panel/src/outline_panel.rs @@ -41,7 +41,7 @@ use workspace::{ item::ItemHandle, ui::{ h_flex, v_flex, ActiveTheme, Color, ContextMenu, FluentBuilder, Icon, IconName, IconSize, - Label, LabelCommon, ListItem, Selectable, + Label, LabelCommon, ListItem, Selectable, StyledTypography, }, OpenInTerminal, Workspace, }; @@ -487,6 +487,146 @@ impl OutlinePanel { self.update_fs_entries(&editor, HashSet::default(), None, None, false, cx); } + fn open(&mut self, _: &Open, cx: &mut ViewContext) { + if let Some(selected_entry) = self.selected_entry.clone() { + self.open_entry(&selected_entry, cx); + } + } + + fn open_entry(&mut self, entry: &EntryOwned, cx: &mut ViewContext) { + let Some(active_editor) = self + .active_item + .as_ref() + .and_then(|item| item.active_editor.upgrade()) + else { + return; + }; + let active_multi_buffer = active_editor.read(cx).buffer().clone(); + let multi_buffer_snapshot = active_multi_buffer.read(cx).snapshot(cx); + let offset_from_top = if active_multi_buffer.read(cx).is_singleton() { + Point::default() + } else { + Point::new(0.0, -(active_editor.read(cx).file_header_size() as f32)) + }; + + match &entry { + EntryOwned::Entry(FsEntry::ExternalFile(buffer_id)) => { + let scroll_target = multi_buffer_snapshot.excerpts().find_map( + |(excerpt_id, buffer_snapshot, excerpt_range)| { + if &buffer_snapshot.remote_id() == buffer_id { + multi_buffer_snapshot + .anchor_in_excerpt(excerpt_id, excerpt_range.context.start) + } else { + None + } + }, + ); + if let Some(anchor) = scroll_target { + self.selected_entry = Some(entry.clone()); + active_editor.update(cx, |editor, cx| { + editor.set_scroll_anchor( + ScrollAnchor { + offset: offset_from_top, + anchor, + }, + cx, + ); + }) + } + } + entry @ EntryOwned::Entry(FsEntry::Directory(..)) => { + self.toggle_expanded(entry, cx); + } + entry @ EntryOwned::FoldedDirs(..) => { + self.toggle_expanded(entry, cx); + } + EntryOwned::Entry(FsEntry::File(_, file_entry)) => { + let scroll_target = self + .project + .update(cx, |project, cx| { + project + .path_for_entry(file_entry.id, cx) + .and_then(|path| project.get_open_buffer(&path, cx)) + }) + .map(|buffer| { + active_multi_buffer + .read(cx) + .excerpts_for_buffer(&buffer, cx) + }) + .and_then(|excerpts| { + let (excerpt_id, excerpt_range) = excerpts.first()?; + multi_buffer_snapshot + .anchor_in_excerpt(*excerpt_id, excerpt_range.context.start) + }); + if let Some(anchor) = scroll_target { + self.selected_entry = Some(entry.clone()); + active_editor.update(cx, |editor, cx| { + editor.set_scroll_anchor( + ScrollAnchor { + offset: offset_from_top, + anchor, + }, + cx, + ); + }) + } + } + EntryOwned::Outline(_, outline) => { + let Some(full_buffer_snapshot) = + outline + .range + .start + .buffer_id + .and_then(|buffer_id| active_multi_buffer.read(cx).buffer(buffer_id)) + .or_else(|| { + outline.range.end.buffer_id.and_then(|buffer_id| { + active_multi_buffer.read(cx).buffer(buffer_id) + }) + }) + .map(|buffer| buffer.read(cx).snapshot()) + else { + return; + }; + let outline_offset_range = outline.range.to_offset(&full_buffer_snapshot); + let scroll_target = multi_buffer_snapshot + .excerpts() + .filter(|(_, buffer_snapshot, _)| { + let buffer_id = buffer_snapshot.remote_id(); + Some(buffer_id) == outline.range.start.buffer_id + || Some(buffer_id) == outline.range.end.buffer_id + }) + .min_by_key(|(_, _, excerpt_range)| { + let excerpt_offeset_range = + excerpt_range.context.to_offset(&full_buffer_snapshot); + ((outline_offset_range.start / 2 + outline_offset_range.end / 2) as isize + - (excerpt_offeset_range.start / 2 + excerpt_offeset_range.end / 2) + as isize) + .abs() + }) + .and_then(|(excerpt_id, excerpt_snapshot, excerpt_range)| { + let location = if outline.range.start.is_valid(excerpt_snapshot) { + outline.range.start + } else { + excerpt_range.context.start + }; + multi_buffer_snapshot.anchor_in_excerpt(excerpt_id, location) + }); + if let Some(anchor) = scroll_target { + self.selected_entry = Some(entry.clone()); + active_editor.update(cx, |editor, cx| { + editor.set_scroll_anchor( + ScrollAnchor { + offset: Point::default(), + anchor, + }, + cx, + ); + }) + } + } + } + } + fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext) { if let Some(selected_entry) = &self.selected_entry { let outline_to_select = match selected_entry { @@ -784,7 +924,6 @@ impl OutlinePanel { let context_menu = ContextMenu::build(cx, |menu, _| { menu.context(self.focus_handle.clone()) - .action("Copy Relative Path", Box::new(CopyRelativePath)) .action("Reveal in Finder", Box::new(RevealInFinder)) .action("Open in Terminal", Box::new(OpenInTerminal)) .when(is_unfoldable, |menu| { @@ -1348,6 +1487,7 @@ impl OutlinePanel { let settings = OutlinePanelSettings::get_global(cx); let rendered_entry = rendered_entry.to_owned_entry(); div() + .text_ui(cx) .id(item_id.clone()) .child( ListItem::new(item_id) @@ -1369,156 +1509,7 @@ impl OutlinePanel { if event.down.button == MouseButton::Right || event.down.first_mouse { return; } - - let Some(active_editor) = outline_panel - .active_item - .as_ref() - .and_then(|item| item.active_editor.upgrade()) - else { - return; - }; - let active_multi_buffer = active_editor.read(cx).buffer().clone(); - let multi_buffer_snapshot = active_multi_buffer.read(cx).snapshot(cx); - - match &clicked_entry { - EntryOwned::Entry(FsEntry::ExternalFile(buffer_id)) => { - let scroll_target = multi_buffer_snapshot.excerpts().find_map( - |(excerpt_id, buffer_snapshot, excerpt_range)| { - if &buffer_snapshot.remote_id() == buffer_id { - multi_buffer_snapshot.anchor_in_excerpt( - excerpt_id, - excerpt_range.context.start, - ) - } else { - None - } - }, - ); - if let Some(anchor) = scroll_target { - outline_panel.selected_entry = Some(clicked_entry.clone()); - active_editor.update(cx, |editor, cx| { - editor.set_scroll_anchor( - ScrollAnchor { - offset: Point::new( - 0.0, - -(editor.file_header_size() as f32), - ), - anchor, - }, - cx, - ); - }) - } - } - entry @ EntryOwned::Entry(FsEntry::Directory(..)) => { - outline_panel.toggle_expanded(entry, cx); - } - entry @ EntryOwned::FoldedDirs(..) => { - outline_panel.toggle_expanded(entry, cx); - } - EntryOwned::Entry(FsEntry::File(_, file_entry)) => { - let scroll_target = outline_panel - .project - .update(cx, |project, cx| { - project - .path_for_entry(file_entry.id, cx) - .and_then(|path| project.get_open_buffer(&path, cx)) - }) - .map(|buffer| { - active_multi_buffer - .read(cx) - .excerpts_for_buffer(&buffer, cx) - }) - .and_then(|excerpts| { - let (excerpt_id, excerpt_range) = excerpts.first()?; - multi_buffer_snapshot.anchor_in_excerpt( - *excerpt_id, - excerpt_range.context.start, - ) - }); - if let Some(anchor) = scroll_target { - outline_panel.selected_entry = Some(clicked_entry.clone()); - active_editor.update(cx, |editor, cx| { - editor.set_scroll_anchor( - ScrollAnchor { - offset: Point::new( - 0.0, - -(editor.file_header_size() as f32), - ), - anchor, - }, - cx, - ); - }) - } - } - EntryOwned::Outline(_, outline) => { - let Some(full_buffer_snapshot) = outline - .range - .start - .buffer_id - .and_then(|buffer_id| { - active_multi_buffer.read(cx).buffer(buffer_id) - }) - .or_else(|| { - outline.range.end.buffer_id.and_then(|buffer_id| { - active_multi_buffer.read(cx).buffer(buffer_id) - }) - }) - .map(|buffer| buffer.read(cx).snapshot()) - else { - return; - }; - let outline_offset_range = - outline.range.to_offset(&full_buffer_snapshot); - let scroll_target = multi_buffer_snapshot - .excerpts() - .filter(|(_, buffer_snapshot, _)| { - let buffer_id = buffer_snapshot.remote_id(); - Some(buffer_id) == outline.range.start.buffer_id - || Some(buffer_id) == outline.range.end.buffer_id - }) - .min_by_key(|(_, _, excerpt_range)| { - let excerpt_offeset_range = excerpt_range - .context - .to_offset(&full_buffer_snapshot); - ((outline_offset_range.start / 2 - + outline_offset_range.end / 2) - as isize - - (excerpt_offeset_range.start / 2 - + excerpt_offeset_range.end / 2) - as isize) - .abs() - }) - .and_then( - |(excerpt_id, excerpt_snapshot, excerpt_range)| { - let location = if outline - .range - .start - .is_valid(excerpt_snapshot) - { - outline.range.start - } else { - excerpt_range.context.start - }; - multi_buffer_snapshot - .anchor_in_excerpt(excerpt_id, location) - }, - ); - if let Some(anchor) = scroll_target { - outline_panel.selected_entry = Some(clicked_entry.clone()); - active_editor.update(cx, |editor, cx| { - editor.set_scroll_anchor( - ScrollAnchor { - offset: Point::default(), - anchor, - }, - cx, - ); - }) - } - } - } + outline_panel.open_entry(&clicked_entry, cx); }) }) .on_secondary_mouse_down(cx.listener( @@ -2366,6 +2357,7 @@ impl Render for OutlinePanel { .size_full() .relative() .key_context(self.dispatch_context(cx)) + .on_action(cx.listener(Self::open)) .on_action(cx.listener(Self::select_next)) .on_action(cx.listener(Self::select_prev)) .on_action(cx.listener(Self::select_first))