Detailed changes
@@ -1,46 +1,29 @@
-use editor::Editor;
use gpui::{
- elements::*, AppContext, Entity, ModelHandle, RenderContext, Subscription, View, ViewContext,
- ViewHandle,
+ elements::*, AppContext, Entity, RenderContext, Subscription, View, ViewContext, ViewHandle,
};
use itertools::Itertools;
-use project::Project;
use search::ProjectSearchView;
use settings::Settings;
-use workspace::{ItemHandle, ToolbarItemLocation, ToolbarItemView};
+use workspace::{ItemEvent, ItemHandle, ToolbarItemLocation, ToolbarItemView};
pub enum Event {
UpdateLocation,
}
pub struct Breadcrumbs {
- project: ModelHandle<Project>,
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,
active_item: Default::default(),
- subscriptions: 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.active_item.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 {
@@ -53,42 +36,16 @@ impl View for Breadcrumbs {
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
- // 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()
- // }
- // } else {
- // "untitled".to_string()
- // };
-
let theme = cx.global::<Settings>().theme.clone();
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()
}))
- // .with_child(Label::new(filename, theme.breadcrumbs.text.clone()).boxed())
- // .with_children(symbols.into_iter().flat_map(|symbol| {
- // [
- // Text::new(symbol.text, theme.breadcrumbs.text.clone())
- // .with_highlights(symbol.highlight_ranges)
- // .boxed(),
- // ]
- // }))
.contained()
.with_style(theme.breadcrumbs.container)
.aligned()
@@ -107,39 +64,25 @@ impl ToolbarItemView for Breadcrumbs {
cx: &mut ViewContext<Self>,
) -> ToolbarItemLocation {
cx.notify();
- self.subscriptions.clear();
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.active_item = 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
}
@@ -151,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
}
@@ -27,6 +27,7 @@ use util::TryFutureExt;
use workspace::{
searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle},
FollowableItem, Item, ItemEvent, ItemHandle, ItemNavHistory, ProjectItem, StatusItemView,
+ ToolbarItemLocation,
};
pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
@@ -476,17 +477,71 @@ impl Item for Editor {
}
fn to_item_events(event: &Self::Event) -> Vec<workspace::ItemEvent> {
+ let mut result = Vec::new();
match event {
- Event::Closed => vec![ItemEvent::CloseItem],
- Event::Saved | Event::DirtyChanged | Event::TitleChanged => vec![ItemEvent::UpdateTab],
- Event::BufferEdited => vec![ItemEvent::Edit],
- _ => Vec::new(),
+ 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 as_searchable(&self, handle: &ViewHandle<Self>) -> Option<Box<dyn SearchableItemHandle>> {
Some(Box::new(handle.clone()))
}
+
+ fn breadcrumb_location(&self) -> ToolbarItemLocation {
+ ToolbarItemLocation::PrimaryLeft { flex: None }
+ }
+
+ 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)
+ }
}
impl ProjectItem for Editor {
@@ -189,7 +189,9 @@ 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_to_search_events(
@@ -329,11 +329,23 @@ impl Item for ProjectSearchView {
fn to_item_events(event: &Self::Event) -> Vec<ItemEvent> {
match event {
- ViewEvent::UpdateTab => vec![ItemEvent::UpdateTab],
+ ViewEvent::UpdateTab => vec![ItemEvent::UpdateBreadcrumbs, ItemEvent::UpdateTab],
ViewEvent::EditorEvent(editor_event) => Editor::to_item_events(editor_event),
_ => Vec::new(),
}
}
+
+ fn breadcrumb_location(&self) -> ToolbarItemLocation {
+ if self.has_matches() {
+ ToolbarItemLocation::Secondary
+ } else {
+ 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, ItemEvent, Workspace};
+use workspace::{Item, ItemEvent, ToolbarItemLocation, Workspace};
use crate::TerminalSize;
use project::{LocalWorktree, Project, ProjectPath};
@@ -363,13 +363,37 @@ impl Item for TerminalContainer {
Some(Box::new(handle.clone()))
}
- fn to_item_events(event: &Self::Event) -> Vec<workspace::ItemEvent> {
+ 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 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()])
+ }
}
impl SearchableItem for TerminalContainer {
@@ -339,7 +339,7 @@ pub trait Item: View {
fn breadcrumb_location(&self) -> ToolbarItemLocation {
ToolbarItemLocation::Hidden
}
- fn breadcrumbs(&self, _theme: &Theme) -> Option<Vec<ElementBox>> {
+ fn breadcrumbs(&self, _theme: &Theme, _cx: &AppContext) -> Option<Vec<ElementBox>> {
None
}
}
@@ -437,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;
@@ -476,8 +481,7 @@ 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>>;
}
@@ -500,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)
}
@@ -762,7 +778,7 @@ 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)
}
@@ -771,7 +787,7 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
}
fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option<Vec<ElementBox>> {
- self.read(cx).breadcrumbs(theme)
+ self.read(cx).breadcrumbs(theme, cx)
}
}
@@ -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);