Skip to content

Commit

Permalink
Introduce an outline panel (#12637)
Browse files Browse the repository at this point in the history
Adds a new panel: `OutlinePanel` which looks very close to project
panel:

<img width="256" alt="Screenshot 2024-06-10 at 23 19 05"
src="https://github.com/zed-industries/zed/assets/2690773/c66e6e78-44ec-4de8-8d60-43238bb09ae9">

has similar settings and keymap (actions work in the `OutlinePanel`
context and are under `outline_panel::` namespace), with two notable
differences:
* no "edit" actions such as cut/copy/paste/delete/etc.
* directory auto folding is enabled by default

Empty view: 
<img width="841" alt="Screenshot 2024-06-10 at 23 19 11"
src="https://github.com/zed-industries/zed/assets/2690773/dc8bf37c-5a70-4fd5-9b57-76271eb7a40c">


When editor gets active, the panel displays all related files in a tree
(similar to what the project panel does) and all related excerpts'
outlines under each file.
Same as in the project panel, directories can be expanded or collapsed,
unfolded or folded; clicking file entries or outlines scrolls the buffer
to the corresponding excerpt; changing editor's selection reveals the
corresponding outline in the panel.

The panel is applicable to any singleton buffer:
<img width="1215" alt="Screenshot 2024-06-10 at 23 19 35"
src="https://github.com/zed-industries/zed/assets/2690773/a087631f-5c2d-4d4d-ae25-30ab9731d528">

<img width="1728" alt="image"
src="https://github.com/zed-industries/zed/assets/2690773/e4f8082c-d12d-4473-8500-e8fd1051285b">

or any multi buffer:

(search multi buffer)

<img width="1728" alt="Screenshot 2024-06-10 at 23 19 41"
src="https://github.com/zed-industries/zed/assets/2690773/60f768a3-6716-4520-9b13-42da8fd15f50">

(diagnostics multi buffer)
<img width="1728" alt="image"
src="https://github.com/zed-industries/zed/assets/2690773/64e285bd-9530-4bf2-8f1f-10ee5596067c">

Release Notes:
- Added an outline panel to show a "map" of the active editor
  • Loading branch information
SomeoneToIgnore authored Jun 12, 2024
1 parent 7f56f4e commit 8451dba
Show file tree
Hide file tree
Showing 27 changed files with 2,860 additions and 57 deletions.
27 changes: 26 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ members = [
"crates/ollama",
"crates/open_ai",
"crates/outline",
"crates/outline_panel",
"crates/picker",
"crates/prettier",
"crates/project",
Expand Down Expand Up @@ -212,6 +213,7 @@ notifications = { path = "crates/notifications" }
ollama = { path = "crates/ollama" }
open_ai = { path = "crates/open_ai" }
outline = { path = "crates/outline" }
outline_panel = { path = "crates/outline_panel" }
picker = { path = "crates/picker" }
plugin = { path = "crates/plugin" }
plugin_macros = { path = "crates/plugin_macros" }
Expand Down
1 change: 1 addition & 0 deletions assets/icons/list_tree.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 17 additions & 1 deletion assets/keymaps/default-linux.json
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@
"ctrl-shift-p": "command_palette::Toggle",
"ctrl-shift-m": "diagnostics::Deploy",
"ctrl-shift-e": "project_panel::ToggleFocus",
"ctrl-shift-b": "outline_panel::ToggleFocus",
"ctrl-?": "assistant::ToggleFocus",
"ctrl-alt-s": "workspace::SaveAll",
"ctrl-k m": "language_selector::Toggle",
Expand Down Expand Up @@ -562,6 +563,18 @@
"ctrl-enter": "project_search::SearchInNew"
}
},
{
"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",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrev"
}
},
{
"context": "ProjectPanel",
"bindings": {
Expand All @@ -583,7 +596,10 @@
"ctrl-backspace": ["project_panel::Delete", { "skip_prompt": false }],
"ctrl-delete": ["project_panel::Delete", { "skip_prompt": false }],
"alt-ctrl-r": "project_panel::RevealInFinder",
"alt-shift-f": "project_panel::NewSearchInDirectory"
"alt-shift-f": "project_panel::NewSearchInDirectory",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrev",
"escape": "menu::Cancel"
}
},
{
Expand Down
13 changes: 13 additions & 0 deletions assets/keymaps/default-macos.json
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@
"cmd-shift-p": "command_palette::Toggle",
"cmd-shift-m": "diagnostics::Deploy",
"cmd-shift-e": "project_panel::ToggleFocus",
"cmd-shift-b": "outline_panel::ToggleFocus",
"cmd-?": "assistant::ToggleFocus",
"cmd-alt-s": "workspace::SaveAll",
"cmd-k m": "language_selector::Toggle",
Expand Down Expand Up @@ -584,6 +585,18 @@
"cmd-enter": "project_search::SearchInNew"
}
},
{
"context": "OutlinePanel",
"bindings": {
"left": "outline_panel::CollapseSelectedEntry",
"right": "outline_panel::ExpandSelectedEntry",
"cmd-alt-c": "outline_panel::CopyPath",
"alt-cmd-shift-c": "outline_panel::CopyRelativePath",
"alt-cmd-r": "outline_panel::RevealInFinder",
"shift-down": "menu::SelectNext",
"shift-up": "menu::SelectPrev"
}
},
{
"context": "ProjectPanel",
"bindings": {
Expand Down
23 changes: 23 additions & 0 deletions assets/settings/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,29 @@
/// when a directory has only one directory inside.
"auto_fold_dirs": false
},
"outline_panel": {
// Whether to show the outline panel button in the status bar
"button": true,
// Default width of the outline panel.
"default_width": 240,
// Where to dock the outline panel. Can be 'left' or 'right'.
"dock": "left",
// Whether to show file icons in the outline panel.
"file_icons": true,
// Whether to show folder icons or chevrons for directories in the outline panel.
"folder_icons": true,
// Whether to show the git status in the outline panel.
"git_status": true,
// Amount of indentation for nested items.
"indent_size": 20,
// Whether to reveal it in the outline panel automatically,
// when a corresponding outline entry becomes active.
// Gitignored entries are never auto revealed.
"auto_reveal_entries": true,
/// Whether to fold directories automatically
/// when a directory has only one directory inside.
"auto_fold_dirs": true
},
"collaboration_panel": {
// Whether to show the collaboration panel button in the status bar.
"button": true,
Expand Down
28 changes: 24 additions & 4 deletions crates/editor/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ use workspace::{OpenInTerminal, OpenTerminal, TabBarSettings, Toast};

use crate::hover_links::find_url;

pub const FILE_HEADER_HEIGHT: u8 = 1;
pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u8 = 1;
pub const MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT: u8 = 1;
pub const DEFAULT_MULTIBUFFER_CONTEXT: u32 = 2;
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
const MAX_LINE_LEN: usize = 1024;
Expand Down Expand Up @@ -529,6 +532,7 @@ pub struct Editor {
tasks: BTreeMap<(BufferId, BufferRow), RunnableTasks>,
tasks_update_task: Option<Task<()>>,
previous_search_ranges: Option<Arc<[Range<Anchor>]>>,
file_header_size: u8,
}

#[derive(Clone)]
Expand Down Expand Up @@ -1651,18 +1655,17 @@ impl Editor {
}),
merge_adjacent: true,
};
let file_header_size = if show_excerpt_controls { 3 } else { 2 };
let display_map = cx.new_model(|cx| {
let file_header_size = if show_excerpt_controls { 3 } else { 2 };

DisplayMap::new(
buffer.clone(),
style.font(),
font_size,
None,
show_excerpt_controls,
file_header_size,
1,
1,
MULTI_BUFFER_EXCERPT_HEADER_HEIGHT,
MULTI_BUFFER_EXCERPT_FOOTER_HEIGHT,
fold_placeholder,
cx,
)
Expand Down Expand Up @@ -1812,6 +1815,7 @@ impl Editor {
git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
blame: None,
blame_subscription: None,
file_header_size,
tasks: Default::default(),
_subscriptions: vec![
cx.observe(&buffer, Self::on_buffer_changed),
Expand Down Expand Up @@ -10829,6 +10833,12 @@ impl Editor {
self.refresh_inlay_hints(InlayHintRefreshReason::ExcerptsRemoved(ids.clone()), cx);
cx.emit(EditorEvent::ExcerptsRemoved { ids: ids.clone() })
}
multi_buffer::Event::ExcerptsEdited { ids } => {
cx.emit(EditorEvent::ExcerptsEdited { ids: ids.clone() })
}
multi_buffer::Event::ExcerptsExpanded { ids } => {
cx.emit(EditorEvent::ExcerptsExpanded { ids: ids.clone() })
}
multi_buffer::Event::Reparsed => {
self.tasks_update_task = Some(self.refresh_runnables(cx));

Expand Down Expand Up @@ -11299,6 +11309,10 @@ impl Editor {
}));
self
}

pub fn file_header_size(&self) -> u8 {
self.file_header_size
}
}

fn hunks_for_selections(
Expand Down Expand Up @@ -11743,6 +11757,12 @@ pub enum EditorEvent {
ExcerptsRemoved {
ids: Vec<ExcerptId>,
},
ExcerptsEdited {
ids: Vec<ExcerptId>,
},
ExcerptsExpanded {
ids: Vec<ExcerptId>,
},
BufferEdited,
Edited,
Reparsed,
Expand Down
2 changes: 1 addition & 1 deletion crates/fuzzy/src/char_bag.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::iter::FromIterator;

#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct CharBag(u64);

impl CharBag {
Expand Down
2 changes: 1 addition & 1 deletion crates/git/src/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ fn check_path_to_repo_path_errors(relative_file_path: &Path) -> Result<()> {
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum GitFileStatus {
Added,
Modified,
Expand Down
14 changes: 13 additions & 1 deletion crates/gpui/src/color.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use anyhow::{bail, Context};
use serde::de::{self, Deserialize, Deserializer, Visitor};
use std::fmt;
use std::{
fmt,
hash::{Hash, Hasher},
};

/// Convert an RGB hex color code number to a color type
pub fn rgb(hex: u32) -> Rgba {
Expand Down Expand Up @@ -267,6 +270,15 @@ impl Ord for Hsla {

impl Eq for Hsla {}

impl Hash for Hsla {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u32(u32::from_be_bytes(self.h.to_be_bytes()));
state.write_u32(u32::from_be_bytes(self.s.to_be_bytes()));
state.write_u32(u32::from_be_bytes(self.l.to_be_bytes()));
state.write_u32(u32::from_be_bytes(self.a.to_be_bytes()));
}
}

/// Construct an [`Hsla`] object from plain values
pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
Hsla {
Expand Down
24 changes: 21 additions & 3 deletions crates/gpui/src/style.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use std::{iter, mem, ops::Range};
use std::{
hash::{Hash, Hasher},
iter, mem,
ops::Range,
};

use crate::{
black, phi, point, quad, rems, AbsoluteLength, Bounds, ContentMask, Corners, CornersRefinement,
Expand Down Expand Up @@ -319,6 +323,20 @@ pub struct HighlightStyle {

impl Eq for HighlightStyle {}

impl Hash for HighlightStyle {
fn hash<H: Hasher>(&self, state: &mut H) {
self.color.hash(state);
self.font_weight.hash(state);
self.font_style.hash(state);
self.background_color.hash(state);
self.underline.hash(state);
self.strikethrough.hash(state);
state.write_u32(u32::from_be_bytes(
self.fade_out.map(|f| f.to_be_bytes()).unwrap_or_default(),
));
}
}

impl Style {
/// Returns true if the style is visible and the background is opaque.
pub fn has_opaque_background(&self) -> bool {
Expand Down Expand Up @@ -549,7 +567,7 @@ impl Default for Style {
}

/// The properties that can be applied to an underline.
#[derive(Refineable, Copy, Clone, Default, Debug, PartialEq, Eq)]
#[derive(Refineable, Copy, Clone, Default, Debug, PartialEq, Eq, Hash)]
#[refineable(Debug)]
pub struct UnderlineStyle {
/// The thickness of the underline.
Expand All @@ -563,7 +581,7 @@ pub struct UnderlineStyle {
}

/// The properties that can be applied to a strikethrough.
#[derive(Refineable, Copy, Clone, Default, Debug, PartialEq, Eq)]
#[derive(Refineable, Copy, Clone, Default, Debug, PartialEq, Eq, Hash)]
#[refineable(Debug)]
pub struct StrikethroughStyle {
/// The thickness of the strikethrough.
Expand Down
5 changes: 3 additions & 2 deletions crates/language/src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2738,12 +2738,13 @@ impl BufferSnapshot {
Some(items)
}

fn outline_items_containing(
pub fn outline_items_containing<T: ToOffset>(
&self,
range: Range<usize>,
range: Range<T>,
include_extra_context: bool,
theme: Option<&SyntaxTheme>,
) -> Option<Vec<OutlineItem<Anchor>>> {
let range = range.to_offset(self);
let mut matches = self.syntax.matches(range.clone(), &self.text, |grammar| {
grammar.outline_config.as_ref().map(|c| &c.query)
});
Expand Down
2 changes: 1 addition & 1 deletion crates/language/src/language.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub use language_registry::{
PendingLanguageServer, QUERY_FILENAME_PREFIXES,
};
pub use lsp::LanguageServerId;
pub use outline::{Outline, OutlineItem};
pub use outline::{render_item, Outline, OutlineItem};
pub use syntax_map::{OwnedSyntaxLayer, SyntaxLayer};
pub use text::{AnchorRangeExt, LineEnding};
pub use tree_sitter::{Node, Parser, Tree, TreeCursor};
Expand Down
Loading

0 comments on commit 8451dba

Please sign in to comment.