diff --git a/Cargo.lock b/Cargo.lock index 9c36923964..d119b987e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -719,6 +719,18 @@ dependencies = [ "once_cell", ] +[[package]] +name = "breadcrumbs" +version = "0.1.0" +dependencies = [ + "collections", + "editor", + "gpui", + "language", + "theme", + "workspace", +] + [[package]] name = "brotli" version = "3.3.0" @@ -5963,6 +5975,7 @@ dependencies = [ "async-compression", "async-recursion", "async-trait", + "breadcrumbs", "chat_panel", "client", "clock", diff --git a/crates/breadcrumbs/Cargo.toml b/crates/breadcrumbs/Cargo.toml new file mode 100644 index 0000000000..2e6e07ca19 --- /dev/null +++ b/crates/breadcrumbs/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "breadcrumbs" +version = "0.1.0" +edition = "2021" + +[lib] +path = "src/breadcrumbs.rs" +doctest = false + +[dependencies] +collections = { path = "../collections" } +editor = { path = "../editor" } +gpui = { path = "../gpui" } +language = { path = "../language" } +theme = { path = "../theme" } +workspace = { path = "../workspace" } + +[dev-dependencies] +editor = { path = "../editor", features = ["test-support"] } +gpui = { path = "../gpui", features = ["test-support"] } +workspace = { path = "../workspace", features = ["test-support"] } diff --git a/crates/breadcrumbs/src/breadcrumbs.rs b/crates/breadcrumbs/src/breadcrumbs.rs new file mode 100644 index 0000000000..89f6202a54 --- /dev/null +++ b/crates/breadcrumbs/src/breadcrumbs.rs @@ -0,0 +1,99 @@ +use editor::{Anchor, Editor}; +use gpui::{ + elements::*, AppContext, Entity, RenderContext, Subscription, View, ViewContext, ViewHandle, +}; +use language::{BufferSnapshot, OutlineItem}; +use std::borrow::Cow; +use theme::SyntaxTheme; +use workspace::{ItemHandle, Settings, ToolbarItemView}; + +pub struct Breadcrumbs { + editor: Option>, + editor_subscription: Option, +} + +impl Breadcrumbs { + pub fn new() -> Self { + Self { + editor: Default::default(), + editor_subscription: Default::default(), + } + } + + fn active_symbols( + &self, + theme: &SyntaxTheme, + cx: &AppContext, + ) -> Option<(BufferSnapshot, Vec>)> { + let editor = self.editor.as_ref()?.read(cx); + let cursor = editor.newest_anchor_selection().head(); + let (buffer, symbols) = editor + .buffer() + .read(cx) + .read(cx) + .symbols_containing(cursor, Some(theme))?; + if buffer.path().is_none() && symbols.is_empty() { + None + } else { + Some((buffer, symbols)) + } + } +} + +impl Entity for Breadcrumbs { + type Event = (); +} + +impl View for Breadcrumbs { + fn ui_name() -> &'static str { + "Breadcrumbs" + } + + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { + let theme = cx.global::().theme.clone(); + let (buffer, symbols) = + if let Some((buffer, symbols)) = self.active_symbols(&theme.editor.syntax, cx) { + (buffer, symbols) + } else { + return Empty::new().boxed(); + }; + + let filename = if let Some(path) = buffer.path() { + path.to_string_lossy() + } else { + Cow::Borrowed("untitled") + }; + + Flex::row() + .with_child(Label::new(filename.to_string(), theme.breadcrumbs.text.clone()).boxed()) + .with_children(symbols.into_iter().flat_map(|symbol| { + [ + Label::new(" > ".to_string(), theme.breadcrumbs.text.clone()).boxed(), + Text::new(symbol.text, theme.breadcrumbs.text.clone()) + .with_highlights(symbol.highlight_ranges) + .boxed(), + ] + })) + .boxed() + } +} + +impl ToolbarItemView for Breadcrumbs { + fn set_active_pane_item( + &mut self, + active_pane_item: Option<&dyn ItemHandle>, + cx: &mut ViewContext, + ) { + self.editor_subscription = None; + self.editor = None; + if let Some(editor) = active_pane_item.and_then(|i| i.act_as::(cx)) { + self.editor_subscription = Some(cx.subscribe(&editor, |_, _, event, cx| match event { + editor::Event::BufferEdited => cx.notify(), + editor::Event::SelectionsChanged { local } if *local => cx.notify(), + _ => {} + })); + self.editor = Some(editor); + } + cx.notify(); + } +} diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index bccf44dfff..703079523e 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -26,6 +26,7 @@ pub struct Theme { pub editor: Editor, pub search: Search, pub project_diagnostics: ProjectDiagnostics, + pub breadcrumbs: Breadcrumbs, } #[derive(Deserialize, Default)] @@ -271,6 +272,11 @@ pub struct ProjectDiagnostics { pub tab_summary_spacing: f32, } +#[derive(Clone, Deserialize, Default)] +pub struct Breadcrumbs { + pub text: TextStyle, +} + #[derive(Clone, Deserialize, Default)] pub struct Editor { pub text_color: Color, diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index fc9946b778..ede24aae71 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -29,6 +29,7 @@ test-support = [ ] [dependencies] +breadcrumbs = { path = "../breadcrumbs" } chat_panel = { path = "../chat_panel" } collections = { path = "../collections" } client = { path = "../client" } diff --git a/crates/zed/assets/themes/_base.toml b/crates/zed/assets/themes/_base.toml index be5d0ead08..1852c0d87f 100644 --- a/crates/zed/assets/themes/_base.toml +++ b/crates/zed/assets/themes/_base.toml @@ -92,6 +92,9 @@ item_spacing = 8 padding.left = 8 padding.right = 8 +[breadcrumbs] +text = "$text.1" + [panel] padding = { top = 12, left = 12, bottom = 12, right = 12 } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index ca3f18e233..bfa5b3024d 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -4,6 +4,7 @@ pub mod menus; #[cfg(any(test, feature = "test-support"))] pub mod test; +use breadcrumbs::Breadcrumbs; use chat_panel::ChatPanel; pub use client; pub use contacts_panel; @@ -109,6 +110,9 @@ pub fn build_workspace( let workspace::Event::PaneAdded(pane) = event; pane.update(cx, |pane, cx| { pane.toolbar().update(cx, |toolbar, cx| { + let breadcrumbs = cx.add_view(|_| Breadcrumbs::new()); + toolbar.add_left_item(breadcrumbs, cx); + let search_bar = cx.add_view(|cx| SearchBar::new(cx)); toolbar.add_right_item(search_bar, cx); })