Detailed changes
@@ -85,6 +85,8 @@ pub trait UpgradeModelHandle {
handle: &WeakModelHandle<T>,
) -> Option<ModelHandle<T>>;
+ fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool;
+
fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle>;
}
@@ -608,6 +610,10 @@ impl UpgradeModelHandle for AsyncAppContext {
self.0.borrow().upgrade_model_handle(handle)
}
+ fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool {
+ self.0.borrow().model_handle_is_upgradable(handle)
+ }
+
fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle> {
self.0.borrow().upgrade_any_model_handle(handle)
}
@@ -763,6 +769,7 @@ impl MutableAppContext {
models: Default::default(),
views: Default::default(),
windows: Default::default(),
+ app_states: Default::default(),
element_states: Default::default(),
ref_counts: Arc::new(Mutex::new(RefCounts::default())),
background,
@@ -1306,6 +1313,27 @@ impl MutableAppContext {
Ok(pending)
}
+ pub fn add_app_state<T: 'static>(&mut self, state: T) {
+ self.cx
+ .app_states
+ .insert(TypeId::of::<T>(), Box::new(state));
+ }
+
+ pub fn update_app_state<T: 'static, F, U>(&mut self, update: F) -> U
+ where
+ F: FnOnce(&mut T, &mut MutableAppContext) -> U,
+ {
+ let type_id = TypeId::of::<T>();
+ let mut state = self
+ .cx
+ .app_states
+ .remove(&type_id)
+ .expect("no app state has been added for this type");
+ let result = update(state.downcast_mut().unwrap(), self);
+ self.cx.app_states.insert(type_id, state);
+ result
+ }
+
pub fn add_model<T, F>(&mut self, build_model: F) -> ModelHandle<T>
where
T: Entity,
@@ -1828,6 +1856,10 @@ impl UpgradeModelHandle for MutableAppContext {
self.cx.upgrade_model_handle(handle)
}
+ fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool {
+ self.cx.model_handle_is_upgradable(handle)
+ }
+
fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle> {
self.cx.upgrade_any_model_handle(handle)
}
@@ -1898,6 +1930,7 @@ pub struct AppContext {
models: HashMap<usize, Box<dyn AnyModel>>,
views: HashMap<(usize, usize), Box<dyn AnyView>>,
windows: HashMap<usize, Window>,
+ app_states: HashMap<TypeId, Box<dyn Any>>,
element_states: HashMap<ElementStateId, Box<dyn Any>>,
background: Arc<executor::Background>,
ref_counts: Arc<Mutex<RefCounts>>,
@@ -1929,6 +1962,14 @@ impl AppContext {
pub fn platform(&self) -> &Arc<dyn Platform> {
&self.platform
}
+
+ pub fn app_state<T: 'static>(&self) -> &T {
+ self.app_states
+ .get(&TypeId::of::<T>())
+ .expect("no app state has been added for this type")
+ .downcast_ref()
+ .unwrap()
+ }
}
impl ReadModel for AppContext {
@@ -1956,6 +1997,10 @@ impl UpgradeModelHandle for AppContext {
}
}
+ fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool {
+ self.models.contains_key(&handle.model_id)
+ }
+
fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle> {
if self.models.contains_key(&handle.model_id) {
self.ref_counts.lock().inc_model(handle.model_id);
@@ -2361,6 +2406,10 @@ impl<M> UpgradeModelHandle for ModelContext<'_, M> {
self.cx.upgrade_model_handle(handle)
}
+ fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool {
+ self.cx.model_handle_is_upgradable(handle)
+ }
+
fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle> {
self.cx.upgrade_any_model_handle(handle)
}
@@ -2699,6 +2748,10 @@ impl<V> UpgradeModelHandle for ViewContext<'_, V> {
self.cx.upgrade_model_handle(handle)
}
+ fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool {
+ self.cx.model_handle_is_upgradable(handle)
+ }
+
fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle> {
self.cx.upgrade_any_model_handle(handle)
}
@@ -2941,6 +2994,12 @@ impl<T> PartialEq for ModelHandle<T> {
impl<T> Eq for ModelHandle<T> {}
+impl<T> PartialEq<WeakModelHandle<T>> for ModelHandle<T> {
+ fn eq(&self, other: &WeakModelHandle<T>) -> bool {
+ self.model_id == other.model_id
+ }
+}
+
impl<T> Hash for ModelHandle<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.model_id.hash(state);
@@ -3013,6 +3072,10 @@ impl<T: Entity> WeakModelHandle<T> {
self.model_id
}
+ pub fn is_upgradable(&self, cx: &impl UpgradeModelHandle) -> bool {
+ cx.model_handle_is_upgradable(self)
+ }
+
pub fn upgrade(&self, cx: &impl UpgradeModelHandle) -> Option<ModelHandle<T>> {
cx.upgrade_model_handle(self)
}
@@ -281,6 +281,10 @@ impl<'a> UpgradeModelHandle for LayoutContext<'a> {
self.app.upgrade_model_handle(handle)
}
+ fn model_handle_is_upgradable<T: Entity>(&self, handle: &WeakModelHandle<T>) -> bool {
+ self.app.model_handle_is_upgradable(handle)
+ }
+
fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option<AnyModelHandle> {
self.app.upgrade_any_model_handle(handle)
}
@@ -1,9 +1,10 @@
use crate::{Direction, SearchOption, SelectMatch, ToggleSearchOption};
+use collections::HashMap;
use editor::{Anchor, Autoscroll, Editor, MultiBuffer, SelectAll};
use gpui::{
action, elements::*, keymap::Binding, platform::CursorStyle, AppContext, ElementBox, Entity,
ModelContext, ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext,
- ViewHandle,
+ ViewHandle, WeakModelHandle,
};
use postage::watch;
use project::{search::SearchQuery, Project};
@@ -14,7 +15,9 @@ use std::{
path::PathBuf,
};
use util::ResultExt as _;
-use workspace::{Item, ItemHandle, ItemNavHistory, ItemView, Settings, Workspace};
+use workspace::{
+ Item, ItemHandle, ItemNavHistory, ItemView, Settings, WeakItemViewHandle, Workspace,
+};
action!(Deploy);
action!(Search);
@@ -23,7 +26,11 @@ action!(ToggleFocus);
const MAX_TAB_TITLE_LEN: usize = 24;
+#[derive(Default)]
+struct ActiveSearches(HashMap<WeakModelHandle<Project>, WeakModelHandle<ProjectSearch>>);
+
pub fn init(cx: &mut MutableAppContext) {
+ cx.add_app_state(ActiveSearches::default());
cx.add_bindings([
Binding::new("cmd-shift-F", ToggleFocus, Some("ProjectSearchView")),
Binding::new("cmd-f", ToggleFocus, Some("ProjectSearchView")),
@@ -194,6 +201,13 @@ impl View for ProjectSearchView {
}
fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
+ cx.update_app_state(|state: &mut ActiveSearches, cx| {
+ state.0.insert(
+ self.model.read(cx).project.downgrade(),
+ self.model.downgrade(),
+ )
+ });
+
if self.model.read(cx).match_ranges.is_empty() {
cx.focus(&self.query_editor);
} else {
@@ -383,11 +397,28 @@ impl ProjectSearchView {
this
}
+ // Re-activate the most recently activated search or the most recent if it has been closed.
+ // If no search exists in the workspace, create a new one.
fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext<Workspace>) {
- if let Some(existing) = workspace
- .items_of_type::<ProjectSearch>(cx)
- .max_by_key(|existing| existing.id())
- {
+ // Clean up entries for dropped projects
+ cx.update_app_state(|state: &mut ActiveSearches, cx| {
+ state.0.retain(|project, _| project.is_upgradable(cx))
+ });
+
+ let active_search = cx
+ .app_state::<ActiveSearches>()
+ .0
+ .get(&workspace.project().downgrade());
+
+ let existing = active_search
+ .and_then(|active_search| {
+ workspace
+ .items_of_type::<ProjectSearch>(cx)
+ .find(|search| search == active_search)
+ })
+ .or_else(|| workspace.item_of_type::<ProjectSearch>(cx));
+
+ if let Some(existing) = existing {
workspace.activate_item(&existing, cx);
} else {
let model = cx.add_model(|cx| ProjectSearch::new(workspace.project().clone(), cx));
@@ -515,10 +546,7 @@ impl ProjectSearchView {
self.focus_results_editor(cx);
}
} else {
- self.query_editor.update(cx, |query_editor, cx| {
- query_editor.select_all(&SelectAll, cx);
- });
- cx.focus(&self.query_editor);
+ self.focus_query_editor(cx);
}
}
@@ -532,6 +560,13 @@ impl ProjectSearchView {
}
}
+ fn focus_query_editor(&self, cx: &mut ViewContext<Self>) {
+ self.query_editor.update(cx, |query_editor, cx| {
+ query_editor.select_all(&SelectAll, cx);
+ });
+ cx.focus(&self.query_editor);
+ }
+
fn focus_results_editor(&self, cx: &mut ViewContext<Self>) {
self.query_editor.update(cx, |query_editor, cx| {
let cursor = query_editor.newest_anchor_selection().head();
@@ -9,7 +9,7 @@ mod status_bar;
use anyhow::{anyhow, Result};
use client::{Authenticate, ChannelList, Client, User, UserStore};
use clock::ReplicaId;
-use collections::HashSet;
+use collections::BTreeMap;
use gpui::{
action,
color::Color,
@@ -36,6 +36,7 @@ pub use status_bar::StatusItemView;
use std::{
any::{Any, TypeId},
cell::RefCell,
+ cmp::Reverse,
future::Future,
hash::{Hash, Hasher},
path::{Path, PathBuf},
@@ -569,7 +570,7 @@ pub struct Workspace {
status_bar: ViewHandle<StatusBar>,
project: ModelHandle<Project>,
path_openers: Arc<[Box<dyn PathOpener>]>,
- items: HashSet<Box<dyn WeakItemHandle>>,
+ items: BTreeMap<Reverse<usize>, Box<dyn WeakItemHandle>>,
_observe_current_user: Task<()>,
}
@@ -815,14 +816,14 @@ impl Workspace {
fn item_for_path(&self, path: &ProjectPath, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
self.items
- .iter()
+ .values()
.filter_map(|i| i.upgrade(cx))
.find(|i| i.project_path(cx).as_ref() == Some(path))
}
pub fn item_of_type<T: Item>(&self, cx: &AppContext) -> Option<ModelHandle<T>> {
self.items
- .iter()
+ .values()
.find_map(|i| i.upgrade(cx).and_then(|i| i.to_any().downcast()))
}
@@ -831,7 +832,7 @@ impl Workspace {
cx: &'a AppContext,
) -> impl 'a + Iterator<Item = ModelHandle<T>> {
self.items
- .iter()
+ .values()
.filter_map(|i| i.upgrade(cx).and_then(|i| i.to_any().downcast()))
}
@@ -974,7 +975,8 @@ impl Workspace {
where
T: 'static + ItemHandle,
{
- self.items.insert(item_handle.downgrade());
+ self.items
+ .insert(Reverse(item_handle.id()), item_handle.downgrade());
pane.update(cx, |pane, cx| pane.open_item(item_handle, self, cx))
}
@@ -1068,7 +1070,8 @@ impl Workspace {
if let Some(item) = pane.read(cx).active_item() {
let nav_history = new_pane.read(cx).nav_history().clone();
if let Some(clone) = item.clone_on_split(nav_history, cx.as_mut()) {
- self.items.insert(clone.item(cx).downgrade());
+ let item = clone.item(cx).downgrade();
+ self.items.insert(Reverse(item.id()), item);
new_pane.update(cx, |new_pane, cx| new_pane.add_item_view(clone, cx));
}
}