Cargo.lock 🔗
@@ -613,6 +613,7 @@ dependencies = [
"collections",
"editor",
"gpui",
+ "itertools",
"language",
"project",
"search",
K Simmons created
Terminal Breadcrumbs
Cargo.lock | 1
crates/breadcrumbs/Cargo.toml | 1
crates/breadcrumbs/src/breadcrumbs.rs | 138 +++++------------
crates/diagnostics/src/diagnostics.rs | 8
crates/editor/src/items.rs | 73 ++++++++-
crates/search/src/buffer_search.rs | 25 +-
crates/search/src/project_search.rs | 23 ++
crates/terminal/src/terminal.rs | 3
crates/terminal/src/terminal_container_view.rs | 38 ++++
crates/workspace/src/searchable.rs | 4
crates/workspace/src/workspace.rs | 150 ++++++++++++-------
crates/zed/src/zed.rs | 3
12 files changed, 267 insertions(+), 200 deletions(-)
@@ -613,6 +613,7 @@ dependencies = [
"collections",
"editor",
"gpui",
+ "itertools",
"language",
"project",
"search",
@@ -17,6 +17,7 @@ search = { path = "../search" }
settings = { path = "../settings" }
theme = { path = "../theme" }
workspace = { path = "../workspace" }
+itertools = "0.10"
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }
@@ -1,48 +1,29 @@
-use editor::{Anchor, Editor};
use gpui::{
- elements::*, AppContext, Entity, ModelHandle, RenderContext, Subscription, View, ViewContext,
- ViewHandle,
+ elements::*, AppContext, Entity, RenderContext, Subscription, View, ViewContext, ViewHandle,
};
-use language::{Buffer, OutlineItem};
-use project::Project;
+use itertools::Itertools;
use search::ProjectSearchView;
use settings::Settings;
-use theme::SyntaxTheme;
-use workspace::{ItemHandle, ToolbarItemLocation, ToolbarItemView};
+use workspace::{ItemEvent, ItemHandle, ToolbarItemLocation, ToolbarItemView};
pub enum Event {
UpdateLocation,
}
pub struct Breadcrumbs {
- project: ModelHandle<Project>,
- editor: Option<ViewHandle<Editor>>,
+ active_item: Option<Box<dyn ItemHandle>>,
project_search: Option<ViewHandle<ProjectSearchView>>,
- subscriptions: Vec<Subscription>,
+ subscription: Option<Subscription>,
}
impl Breadcrumbs {
- pub fn new(project: ModelHandle<Project>) -> Self {
+ pub fn new() -> Self {
Self {
- project,
- editor: Default::default(),
- subscriptions: Default::default(),
+ active_item: Default::default(),
+ subscription: Default::default(),
project_search: Default::default(),
}
}
-
- fn active_symbols(
- &self,
- theme: &SyntaxTheme,
- cx: &AppContext,
- ) -> Option<(ModelHandle<Buffer>, Vec<OutlineItem<Anchor>>)> {
- let editor = self.editor.as_ref()?.read(cx);
- let cursor = editor.selections.newest_anchor().head();
- let multibuffer = &editor.buffer().read(cx);
- let (buffer_id, symbols) = multibuffer.symbols_containing(cursor, Some(theme), cx)?;
- let buffer = multibuffer.buffer(buffer_id)?;
- Some((buffer, symbols))
- }
}
impl Entity for Breadcrumbs {
@@ -56,40 +37,23 @@ impl View for Breadcrumbs {
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
let theme = cx.global::<Settings>().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 buffer = buffer.read(cx);
- let filename = if let Some(file) = buffer.file() {
- if file.path().file_name().is_none()
- || self.project.read(cx).visible_worktrees(cx).count() > 1
- {
- file.full_path(cx).to_string_lossy().to_string()
- } else {
- file.path().to_string_lossy().to_string()
- }
+ if let Some(breadcrumbs) = self
+ .active_item
+ .as_ref()
+ .and_then(|item| item.breadcrumbs(&theme, cx))
+ {
+ Flex::row()
+ .with_children(Itertools::intersperse_with(breadcrumbs.into_iter(), || {
+ Label::new(" 〉 ".to_string(), theme.breadcrumbs.text.clone()).boxed()
+ }))
+ .contained()
+ .with_style(theme.breadcrumbs.container)
+ .aligned()
+ .left()
+ .boxed()
} else {
- "untitled".to_string()
- };
-
- Flex::row()
- .with_child(Label::new(filename, 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(),
- ]
- }))
- .contained()
- .with_style(theme.breadcrumbs.container)
- .aligned()
- .left()
- .boxed()
+ Empty::new().boxed()
+ }
}
}
@@ -100,39 +64,25 @@ impl ToolbarItemView for Breadcrumbs {
cx: &mut ViewContext<Self>,
) -> ToolbarItemLocation {
cx.notify();
- self.subscriptions.clear();
- self.editor = None;
+ self.active_item = None;
self.project_search = None;
if let Some(item) = active_pane_item {
- if let Some(editor) = item.act_as::<Editor>(cx) {
- self.subscriptions
- .push(cx.subscribe(&editor, |_, _, event, cx| match event {
- editor::Event::BufferEdited
- | editor::Event::TitleChanged
- | editor::Event::Saved
- | editor::Event::Reparsed => cx.notify(),
- editor::Event::SelectionsChanged { local } if *local => cx.notify(),
- _ => {}
- }));
- self.editor = Some(editor);
- if let Some(project_search) = item.downcast::<ProjectSearchView>() {
- self.subscriptions
- .push(cx.subscribe(&project_search, |_, _, _, cx| {
- cx.emit(Event::UpdateLocation);
- }));
- self.project_search = Some(project_search.clone());
-
- if project_search.read(cx).has_matches() {
- ToolbarItemLocation::Secondary
- } else {
- ToolbarItemLocation::Hidden
+ let this = cx.weak_handle();
+ self.subscription = Some(item.subscribe_to_item_events(
+ cx,
+ Box::new(move |event, cx| {
+ if let Some(this) = this.upgrade(cx) {
+ if let ItemEvent::UpdateBreadcrumbs = event {
+ this.update(cx, |_, cx| {
+ cx.emit(Event::UpdateLocation);
+ cx.notify();
+ });
+ }
}
- } else {
- ToolbarItemLocation::PrimaryLeft { flex: None }
- }
- } else {
- ToolbarItemLocation::Hidden
- }
+ }),
+ ));
+ self.active_item = Some(item.boxed_clone());
+ item.breadcrumb_location(cx)
} else {
ToolbarItemLocation::Hidden
}
@@ -144,12 +94,8 @@ impl ToolbarItemView for Breadcrumbs {
current_location: ToolbarItemLocation,
cx: &AppContext,
) -> ToolbarItemLocation {
- if let Some(project_search) = self.project_search.as_ref() {
- if project_search.read(cx).has_matches() {
- ToolbarItemLocation::Secondary
- } else {
- ToolbarItemLocation::Hidden
- }
+ if let Some(active_item) = self.active_item.as_ref() {
+ active_item.breadcrumb_location(cx)
} else {
current_location
}
@@ -566,12 +566,8 @@ impl workspace::Item for ProjectDiagnosticsEditor {
unreachable!()
}
- fn should_update_tab_on_event(event: &Event) -> bool {
- Editor::should_update_tab_on_event(event)
- }
-
- fn is_edit_event(event: &Self::Event) -> bool {
- Editor::is_edit_event(event)
+ fn to_item_events(event: &Self::Event) -> Vec<workspace::ItemEvent> {
+ Editor::to_item_events(event)
}
fn set_nav_history(&mut self, nav_history: ItemNavHistory, cx: &mut ViewContext<Self>) {
@@ -26,7 +26,8 @@ use text::{Point, Selection};
use util::TryFutureExt;
use workspace::{
searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle},
- FollowableItem, Item, ItemHandle, ItemNavHistory, ProjectItem, StatusItemView,
+ FollowableItem, Item, ItemEvent, ItemHandle, ItemNavHistory, ProjectItem, StatusItemView,
+ ToolbarItemLocation,
};
pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
@@ -475,23 +476,71 @@ impl Item for Editor {
})
}
- fn should_close_item_on_event(event: &Event) -> bool {
- matches!(event, Event::Closed)
+ fn to_item_events(event: &Self::Event) -> Vec<workspace::ItemEvent> {
+ let mut result = Vec::new();
+ match event {
+ Event::Closed => result.push(ItemEvent::CloseItem),
+ Event::Saved | Event::TitleChanged => {
+ result.push(ItemEvent::UpdateTab);
+ result.push(ItemEvent::UpdateBreadcrumbs);
+ }
+ Event::Reparsed => {
+ result.push(ItemEvent::UpdateBreadcrumbs);
+ }
+ Event::SelectionsChanged { local } if *local => {
+ result.push(ItemEvent::UpdateBreadcrumbs);
+ }
+ Event::DirtyChanged => {
+ result.push(ItemEvent::UpdateTab);
+ }
+ Event::BufferEdited => {
+ result.push(ItemEvent::Edit);
+ result.push(ItemEvent::UpdateBreadcrumbs);
+ }
+ _ => {}
+ }
+ result
}
- fn should_update_tab_on_event(event: &Event) -> bool {
- matches!(
- event,
- Event::Saved | Event::DirtyChanged | Event::TitleChanged
- )
+ fn as_searchable(&self, handle: &ViewHandle<Self>) -> Option<Box<dyn SearchableItemHandle>> {
+ Some(Box::new(handle.clone()))
}
- fn is_edit_event(event: &Self::Event) -> bool {
- matches!(event, Event::BufferEdited)
+ fn breadcrumb_location(&self) -> ToolbarItemLocation {
+ ToolbarItemLocation::PrimaryLeft { flex: None }
}
- fn as_searchable(&self, handle: &ViewHandle<Self>) -> Option<Box<dyn SearchableItemHandle>> {
- Some(Box::new(handle.clone()))
+ fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<ElementBox>> {
+ let cursor = self.selections.newest_anchor().head();
+ let multibuffer = &self.buffer().read(cx);
+ let (buffer_id, symbols) =
+ multibuffer.symbols_containing(cursor, Some(&theme.editor.syntax), cx)?;
+ let buffer = multibuffer.buffer(buffer_id)?;
+
+ let buffer = buffer.read(cx);
+ let filename = if let Some(file) = buffer.file() {
+ if file.path().file_name().is_none()
+ || self
+ .project
+ .as_ref()
+ .map(|project| project.read(cx).visible_worktrees(cx).count() > 1)
+ .unwrap_or_default()
+ {
+ file.full_path(cx).to_string_lossy().to_string()
+ } else {
+ file.path().to_string_lossy().to_string()
+ }
+ } else {
+ "untitled".to_string()
+ };
+
+ let mut breadcrumbs = vec![Label::new(filename, theme.breadcrumbs.text.clone()).boxed()];
+ breadcrumbs.extend(symbols.into_iter().map(|symbol| {
+ Text::new(symbol.text, theme.breadcrumbs.text.clone())
+ .with_highlights(symbol.highlight_ranges)
+ .boxed()
+ }));
+ Some(breadcrumbs)
}
}
@@ -189,18 +189,21 @@ impl ToolbarItemView for BufferSearchBar {
self.active_searchable_item.take();
self.pending_search.take();
- if let Some(searchable_item_handle) = item.and_then(|item| item.as_searchable(cx)) {
+ if let Some(searchable_item_handle) =
+ item.and_then(|item| item.to_searchable_item_handle(cx))
+ {
let handle = cx.weak_handle();
- self.active_searchable_item_subscription = Some(searchable_item_handle.subscribe(
- cx,
- Box::new(move |search_event, cx| {
- if let Some(this) = handle.upgrade(cx) {
- this.update(cx, |this, cx| {
- this.on_active_searchable_item_event(search_event, cx)
- });
- }
- }),
- ));
+ self.active_searchable_item_subscription =
+ Some(searchable_item_handle.subscribe_to_search_events(
+ cx,
+ Box::new(move |search_event, cx| {
+ if let Some(this) = handle.upgrade(cx) {
+ this.update(cx, |this, cx| {
+ this.on_active_searchable_item_event(search_event, cx)
+ });
+ }
+ }),
+ ));
self.active_searchable_item = Some(searchable_item_handle);
self.update_matches(false, cx);
@@ -24,7 +24,8 @@ use std::{
use util::ResultExt as _;
use workspace::{
searchable::{Direction, SearchableItem, SearchableItemHandle},
- Item, ItemHandle, ItemNavHistory, Pane, ToolbarItemLocation, ToolbarItemView, Workspace,
+ Item, ItemEvent, ItemHandle, ItemNavHistory, Pane, ToolbarItemLocation, ToolbarItemView,
+ Workspace,
};
actions!(project_search, [SearchInNew, ToggleFocus]);
@@ -326,17 +327,25 @@ impl Item for ProjectSearchView {
.update(cx, |editor, cx| editor.navigate(data, cx))
}
- fn should_update_tab_on_event(event: &ViewEvent) -> bool {
- matches!(event, ViewEvent::UpdateTab)
+ fn to_item_events(event: &Self::Event) -> Vec<ItemEvent> {
+ match event {
+ ViewEvent::UpdateTab => vec![ItemEvent::UpdateBreadcrumbs, ItemEvent::UpdateTab],
+ ViewEvent::EditorEvent(editor_event) => Editor::to_item_events(editor_event),
+ _ => Vec::new(),
+ }
}
- fn is_edit_event(event: &Self::Event) -> bool {
- if let ViewEvent::EditorEvent(editor_event) = event {
- Editor::is_edit_event(editor_event)
+ fn breadcrumb_location(&self) -> ToolbarItemLocation {
+ if self.has_matches() {
+ ToolbarItemLocation::Secondary
} else {
- false
+ ToolbarItemLocation::Hidden
}
}
+
+ fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<ElementBox>> {
+ self.results_editor.breadcrumbs(theme, cx)
+ }
}
impl ProjectSearchView {
@@ -83,6 +83,7 @@ const DEBUG_LINE_HEIGHT: f32 = 5.;
#[derive(Clone, Copy, Debug)]
pub enum Event {
TitleChanged,
+ BreadcrumbsChanged,
CloseTerminal,
Bell,
Wakeup,
@@ -494,9 +495,11 @@ impl Terminal {
match event {
AlacTermEvent::Title(title) => {
self.breadcrumb_text = title.to_string();
+ cx.emit(Event::BreadcrumbsChanged);
}
AlacTermEvent::ResetTitle => {
self.breadcrumb_text = String::new();
+ cx.emit(Event::BreadcrumbsChanged);
}
AlacTermEvent::ClipboardStore(_, data) => {
cx.write_to_clipboard(ClipboardItem::new(data.to_string()))
@@ -9,7 +9,7 @@ use gpui::{
};
use util::truncate_and_trailoff;
use workspace::searchable::{SearchEvent, SearchOptions, SearchableItem, SearchableItemHandle};
-use workspace::{Item, Workspace};
+use workspace::{Item, ItemEvent, ToolbarItemLocation, Workspace};
use crate::TerminalSize;
use project::{LocalWorktree, Project, ProjectPath};
@@ -359,16 +359,40 @@ impl Item for TerminalContainer {
false
}
- fn should_update_tab_on_event(event: &Self::Event) -> bool {
- matches!(event, &Event::TitleChanged | &Event::Wakeup)
+ fn as_searchable(&self, handle: &ViewHandle<Self>) -> Option<Box<dyn SearchableItemHandle>> {
+ Some(Box::new(handle.clone()))
}
- fn should_close_item_on_event(event: &Self::Event) -> bool {
- matches!(event, &Event::CloseTerminal)
+ fn to_item_events(event: &Self::Event) -> Vec<ItemEvent> {
+ match event {
+ Event::BreadcrumbsChanged => vec![ItemEvent::UpdateBreadcrumbs],
+ Event::TitleChanged | Event::Wakeup => vec![ItemEvent::UpdateTab],
+ Event::CloseTerminal => vec![ItemEvent::CloseItem],
+ _ => vec![],
+ }
}
- fn as_searchable(&self, handle: &ViewHandle<Self>) -> Option<Box<dyn SearchableItemHandle>> {
- Some(Box::new(handle.clone()))
+ fn breadcrumb_location(&self) -> ToolbarItemLocation {
+ if self.connected().is_some() {
+ ToolbarItemLocation::PrimaryLeft { flex: None }
+ } else {
+ ToolbarItemLocation::Hidden
+ }
+ }
+
+ fn breadcrumbs(&self, theme: &theme::Theme, cx: &AppContext) -> Option<Vec<ElementBox>> {
+ let connected = self.connected()?;
+
+ Some(vec![Text::new(
+ connected
+ .read(cx)
+ .terminal()
+ .read(cx)
+ .breadcrumb_text
+ .to_string(),
+ theme.breadcrumbs.text.clone(),
+ )
+ .boxed()])
}
}
@@ -88,7 +88,7 @@ pub trait SearchableItemHandle: ItemHandle {
fn downgrade(&self) -> Box<dyn WeakSearchableItemHandle>;
fn boxed_clone(&self) -> Box<dyn SearchableItemHandle>;
fn supported_options(&self) -> SearchOptions;
- fn subscribe(
+ fn subscribe_to_search_events(
&self,
cx: &mut MutableAppContext,
handler: Box<dyn Fn(SearchEvent, &mut MutableAppContext)>,
@@ -134,7 +134,7 @@ impl<T: SearchableItem> SearchableItemHandle for ViewHandle<T> {
T::supported_options()
}
- fn subscribe(
+ fn subscribe_to_search_events(
&self,
cx: &mut MutableAppContext,
handler: Box<dyn Fn(SearchEvent, &mut MutableAppContext)>,
@@ -267,6 +267,14 @@ pub struct AppState {
pub initialize_workspace: fn(&mut Workspace, &Arc<AppState>, &mut ViewContext<Workspace>),
}
+#[derive(Eq, PartialEq, Hash)]
+pub enum ItemEvent {
+ CloseItem,
+ UpdateTab,
+ UpdateBreadcrumbs,
+ Edit,
+}
+
pub trait Item: View {
fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
fn workspace_deactivated(&mut self, _: &mut ViewContext<Self>) {}
@@ -311,15 +319,7 @@ pub trait Item: View {
project: ModelHandle<Project>,
cx: &mut ViewContext<Self>,
) -> Task<Result<()>>;
- fn should_close_item_on_event(_: &Self::Event) -> bool {
- false
- }
- fn should_update_tab_on_event(_: &Self::Event) -> bool {
- false
- }
- fn is_edit_event(_: &Self::Event) -> bool {
- false
- }
+ fn to_item_events(event: &Self::Event) -> Vec<ItemEvent>;
fn act_as_type(
&self,
type_id: TypeId,
@@ -335,6 +335,13 @@ pub trait Item: View {
fn as_searchable(&self, _: &ViewHandle<Self>) -> Option<Box<dyn SearchableItemHandle>> {
None
}
+
+ fn breadcrumb_location(&self) -> ToolbarItemLocation {
+ ToolbarItemLocation::Hidden
+ }
+ fn breadcrumbs(&self, _theme: &Theme, _cx: &AppContext) -> Option<Vec<ElementBox>> {
+ None
+ }
}
pub trait ProjectItem: Item {
@@ -430,6 +437,11 @@ impl<T: FollowableItem> FollowableItemHandle for ViewHandle<T> {
}
pub trait ItemHandle: 'static + fmt::Debug {
+ fn subscribe_to_item_events(
+ &self,
+ cx: &mut MutableAppContext,
+ handler: Box<dyn Fn(ItemEvent, &mut MutableAppContext)>,
+ ) -> gpui::Subscription;
fn tab_description<'a>(&self, detail: usize, cx: &'a AppContext) -> Option<Cow<'a, str>>;
fn tab_content(&self, detail: Option<usize>, style: &theme::Tab, cx: &AppContext)
-> ElementBox;
@@ -469,7 +481,9 @@ pub trait ItemHandle: 'static + fmt::Debug {
cx: &mut MutableAppContext,
callback: Box<dyn FnOnce(&mut MutableAppContext)>,
) -> gpui::Subscription;
- fn as_searchable(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
+ fn to_searchable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>>;
+ fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation;
+ fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option<Vec<ElementBox>>;
}
pub trait WeakItemHandle {
@@ -490,6 +504,18 @@ impl dyn ItemHandle {
}
impl<T: Item> ItemHandle for ViewHandle<T> {
+ fn subscribe_to_item_events(
+ &self,
+ cx: &mut MutableAppContext,
+ handler: Box<dyn Fn(ItemEvent, &mut MutableAppContext)>,
+ ) -> gpui::Subscription {
+ cx.subscribe(self, move |_, event, cx| {
+ for item_event in T::to_item_events(event) {
+ handler(item_event, cx)
+ }
+ })
+ }
+
fn tab_description<'a>(&self, detail: usize, cx: &'a AppContext) -> Option<Cow<'a, str>> {
self.read(cx).tab_description(detail, cx)
}
@@ -605,47 +631,53 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
}
}
- if T::should_close_item_on_event(event) {
- Pane::close_item(workspace, pane, item.id(), cx).detach_and_log_err(cx);
- return;
- }
-
- if T::should_update_tab_on_event(event) {
- pane.update(cx, |_, cx| {
- cx.emit(pane::Event::ChangeItemTitle);
- cx.notify();
- });
- }
-
- if T::is_edit_event(event) {
- if let Autosave::AfterDelay { milliseconds } =
- cx.global::<Settings>().autosave
- {
- let prev_autosave = pending_autosave
- .take()
- .unwrap_or_else(|| Task::ready(Some(())));
- let (cancel_tx, mut cancel_rx) = oneshot::channel::<()>();
- let prev_cancel_tx =
- mem::replace(&mut cancel_pending_autosave, cancel_tx);
- let project = workspace.project.downgrade();
- let _ = prev_cancel_tx.send(());
- pending_autosave = Some(cx.spawn_weak(|_, mut cx| async move {
- let mut timer = cx
- .background()
- .timer(Duration::from_millis(milliseconds))
- .fuse();
- prev_autosave.await;
- futures::select_biased! {
- _ = cancel_rx => return None,
- _ = timer => {}
+ for item_event in T::to_item_events(event).into_iter() {
+ match item_event {
+ ItemEvent::CloseItem => {
+ Pane::close_item(workspace, pane, item.id(), cx)
+ .detach_and_log_err(cx);
+ return;
+ }
+ ItemEvent::UpdateTab => {
+ pane.update(cx, |_, cx| {
+ cx.emit(pane::Event::ChangeItemTitle);
+ cx.notify();
+ });
+ }
+ ItemEvent::Edit => {
+ if let Autosave::AfterDelay { milliseconds } =
+ cx.global::<Settings>().autosave
+ {
+ let prev_autosave = pending_autosave
+ .take()
+ .unwrap_or_else(|| Task::ready(Some(())));
+ let (cancel_tx, mut cancel_rx) = oneshot::channel::<()>();
+ let prev_cancel_tx =
+ mem::replace(&mut cancel_pending_autosave, cancel_tx);
+ let project = workspace.project.downgrade();
+ let _ = prev_cancel_tx.send(());
+ let item = item.clone();
+ pending_autosave =
+ Some(cx.spawn_weak(|_, mut cx| async move {
+ let mut timer = cx
+ .background()
+ .timer(Duration::from_millis(milliseconds))
+ .fuse();
+ prev_autosave.await;
+ futures::select_biased! {
+ _ = cancel_rx => return None,
+ _ = timer => {}
+ }
+
+ let project = project.upgrade(&cx)?;
+ cx.update(|cx| Pane::autosave_item(&item, project, cx))
+ .await
+ .log_err();
+ None
+ }));
}
-
- let project = project.upgrade(&cx)?;
- cx.update(|cx| Pane::autosave_item(&item, project, cx))
- .await
- .log_err();
- None
- }));
+ }
+ _ => {}
}
}
}));
@@ -746,9 +778,17 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
cx.observe_release(self, move |_, cx| callback(cx))
}
- fn as_searchable(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
+ fn to_searchable_item_handle(&self, cx: &AppContext) -> Option<Box<dyn SearchableItemHandle>> {
self.read(cx).as_searchable(self)
}
+
+ fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation {
+ self.read(cx).breadcrumb_location()
+ }
+
+ fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option<Vec<ElementBox>> {
+ self.read(cx).breadcrumbs(theme, cx)
+ }
}
impl From<Box<dyn ItemHandle>> for AnyViewHandle {
@@ -3590,12 +3630,8 @@ mod tests {
Task::ready(Ok(()))
}
- fn should_update_tab_on_event(_: &Self::Event) -> bool {
- true
- }
-
- fn is_edit_event(event: &Self::Event) -> bool {
- matches!(event, TestItemEvent::Edit)
+ fn to_item_events(_: &Self::Event) -> Vec<ItemEvent> {
+ vec![ItemEvent::UpdateTab, ItemEvent::Edit]
}
}
}
@@ -225,12 +225,11 @@ pub fn initialize_workspace(
cx: &mut ViewContext<Workspace>,
) {
cx.subscribe(&cx.handle(), {
- let project = workspace.project().clone();
move |_, _, event, cx| {
if let workspace::Event::PaneAdded(pane) = event {
pane.update(cx, |pane, cx| {
pane.toolbar().update(cx, |toolbar, cx| {
- let breadcrumbs = cx.add_view(|_| Breadcrumbs::new(project.clone()));
+ let breadcrumbs = cx.add_view(|_| Breadcrumbs::new());
toolbar.add_item(breadcrumbs, cx);
let buffer_search_bar = cx.add_view(BufferSearchBar::new);
toolbar.add_item(buffer_search_bar, cx);