* Perform grouping even for repositories that have no main worktree * Enable grouping for remote projects * Delete entire project groups when deleting via the recent project picker Release Notes: - Fixed a bug where each linked worktree appeared as its own entry in recent projects for repositories without main worktrees - Fixed a bug where deleting projects from the recent projects sometimes appeared to have no effect.
145 lines
4.4 KiB
Rust
145 lines
4.4 KiB
Rust
use std::{path::PathBuf, sync::Arc};
|
|
|
|
use fs::Fs;
|
|
use gpui::{AppContext, Entity, Global, MenuItem};
|
|
use smallvec::SmallVec;
|
|
use ui::{App, Context};
|
|
use util::{ResultExt, paths::PathExt};
|
|
|
|
use crate::{
|
|
NewWindow, SerializedWorkspaceLocation, WorkspaceId, path_list::PathList,
|
|
persistence::WorkspaceDb,
|
|
};
|
|
|
|
pub fn init(fs: Arc<dyn Fs>, cx: &mut App) {
|
|
let manager = cx.new(|_| HistoryManager::new());
|
|
HistoryManager::set_global(manager.clone(), cx);
|
|
HistoryManager::init(manager, fs, cx);
|
|
}
|
|
|
|
pub struct HistoryManager {
|
|
/// The history of workspaces that have been opened in the past, in reverse order.
|
|
/// The most recent workspace is at the end of the vector.
|
|
history: Vec<HistoryManagerEntry>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct HistoryManagerEntry {
|
|
pub id: WorkspaceId,
|
|
pub path: SmallVec<[PathBuf; 2]>,
|
|
}
|
|
|
|
struct GlobalHistoryManager(Entity<HistoryManager>);
|
|
|
|
impl Global for GlobalHistoryManager {}
|
|
|
|
impl HistoryManager {
|
|
fn new() -> Self {
|
|
Self {
|
|
history: Vec::new(),
|
|
}
|
|
}
|
|
|
|
fn init(this: Entity<HistoryManager>, fs: Arc<dyn Fs>, cx: &App) {
|
|
let db = WorkspaceDb::global(cx);
|
|
cx.spawn(async move |cx| {
|
|
let recent_folders = db
|
|
.recent_project_workspaces(fs.as_ref())
|
|
.await
|
|
.unwrap_or_default()
|
|
.into_iter()
|
|
.rev()
|
|
.filter_map(|workspace| {
|
|
if matches!(workspace.location, SerializedWorkspaceLocation::Local) {
|
|
Some(HistoryManagerEntry::new(
|
|
workspace.workspace_id,
|
|
&workspace.paths,
|
|
))
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect::<Vec<_>>();
|
|
this.update(cx, |this, cx| {
|
|
this.history = recent_folders;
|
|
this.update_jump_list(cx);
|
|
})
|
|
})
|
|
.detach();
|
|
}
|
|
|
|
pub fn global(cx: &App) -> Option<Entity<Self>> {
|
|
cx.try_global::<GlobalHistoryManager>()
|
|
.map(|model| model.0.clone())
|
|
}
|
|
|
|
fn set_global(history_manager: Entity<Self>, cx: &mut App) {
|
|
cx.set_global(GlobalHistoryManager(history_manager));
|
|
}
|
|
|
|
pub fn update_history(
|
|
&mut self,
|
|
id: WorkspaceId,
|
|
entry: HistoryManagerEntry,
|
|
cx: &mut Context<'_, HistoryManager>,
|
|
) {
|
|
if let Some(pos) = self.history.iter().position(|e| e.id == id) {
|
|
self.history.remove(pos);
|
|
}
|
|
self.history.push(entry);
|
|
self.update_jump_list(cx);
|
|
}
|
|
|
|
pub fn delete_history(&mut self, id: WorkspaceId, cx: &mut Context<'_, HistoryManager>) {
|
|
let Some(pos) = self.history.iter().position(|e| e.id == id) else {
|
|
return;
|
|
};
|
|
self.history.remove(pos);
|
|
self.update_jump_list(cx);
|
|
}
|
|
|
|
fn update_jump_list(&mut self, cx: &mut Context<'_, HistoryManager>) {
|
|
let menus = vec![MenuItem::action("New Window", NewWindow)];
|
|
let entries = self
|
|
.history
|
|
.iter()
|
|
.rev()
|
|
.map(|entry| entry.path.clone())
|
|
.collect::<Vec<_>>();
|
|
let user_removed = cx.update_jump_list(menus, entries);
|
|
let db = WorkspaceDb::global(cx);
|
|
cx.spawn(async move |this, cx| {
|
|
let user_removed = user_removed.await;
|
|
if user_removed.is_empty() {
|
|
return;
|
|
}
|
|
let mut deleted_ids = Vec::new();
|
|
if let Ok(()) = this.update(cx, |this, _| {
|
|
for idx in (0..this.history.len()).rev() {
|
|
if let Some(entry) = this.history.get(idx)
|
|
&& user_removed.contains(&entry.path)
|
|
{
|
|
deleted_ids.push(entry.id);
|
|
this.history.remove(idx);
|
|
}
|
|
}
|
|
}) {
|
|
for id in deleted_ids.iter() {
|
|
db.delete_workspace_by_id(*id).await.log_err();
|
|
}
|
|
}
|
|
})
|
|
.detach();
|
|
}
|
|
}
|
|
|
|
impl HistoryManagerEntry {
|
|
pub fn new(id: WorkspaceId, paths: &PathList) -> Self {
|
|
let path = paths
|
|
.ordered_paths()
|
|
.map(|path| path.compact())
|
|
.collect::<SmallVec<[PathBuf; 2]>>();
|
|
Self { id, path }
|
|
}
|
|
}
|