Show both local and remote checkouts in Recent Projects (#53953)
This fixes #53917. Recent Projects was treating local and remote workspaces as the same entry whenever they resolved to the same checkout path. That meant one could hide the other, and if one of them was already open, the picker could end up hiding both. This change keeps the deduplication and picker filtering keyed on both the workspace location and the path list, so local and remote copies of the same repo can both appear when they should. ## Test plan - `rustfmt --edition 2024 crates/recent_projects/src/recent_projects.rs crates/workspace/src/persistence.rs` - `git diff --check -- crates/recent_projects/src/recent_projects.rs crates/workspace/src/persistence.rs` Release Notes: - Fixed Recent Projects hiding either the local or remote workspace when both used the same checkout path. Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
This commit is contained in:
1 parent
75ac36d92a
commit
2838ea3f59
1 file changed
+65
-8
@@ -17,7 +17,7 @@ use fs::Fs;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod wsl_picker;
|
||||
|
||||
use remote::RemoteConnectionOptions;
|
||||
use remote::{RemoteConnectionOptions, same_remote_connection_identity};
|
||||
pub use remote_connection::{RemoteConnectionModal, connect, connect_with_modal};
|
||||
pub use remote_connections::{navigate_to_positions, open_remote_project};
|
||||
|
||||
@@ -45,8 +45,8 @@ use ui::{
|
||||
};
|
||||
use util::{ResultExt, paths::PathExt};
|
||||
use workspace::{
|
||||
HistoryManager, ModalView, MultiWorkspace, OpenMode, OpenOptions, OpenVisible, PathList,
|
||||
RecentWorkspace, SerializedWorkspaceLocation, Workspace, WorkspaceDb, WorkspaceId,
|
||||
HistoryManager, ModalView, MultiWorkspace, OpenMode, OpenOptions, OpenVisible, RecentWorkspace,
|
||||
SerializedWorkspaceLocation, Workspace, WorkspaceDb, WorkspaceId,
|
||||
notifications::DetachAndPromptErr, with_active_or_new_workspace,
|
||||
};
|
||||
use zed_actions::{OpenDevContainer, OpenRecent, OpenRemote};
|
||||
@@ -2433,14 +2433,24 @@ impl RecentProjectsDelegate {
|
||||
.any(|key| key.matches(&workspace.project_group_key()))
|
||||
}
|
||||
|
||||
fn is_open_folder(&self, paths: &PathList) -> bool {
|
||||
fn is_open_folder(&self, workspace: &RecentWorkspace) -> bool {
|
||||
if self.open_folders.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
for workspace_path in paths.paths() {
|
||||
let workspace_host = match &workspace.location {
|
||||
SerializedWorkspaceLocation::Local => None,
|
||||
SerializedWorkspaceLocation::Remote(options) => Some(options),
|
||||
};
|
||||
|
||||
for workspace_path in workspace.paths.paths() {
|
||||
for open_folder in &self.open_folders {
|
||||
if workspace_path == &open_folder.path {
|
||||
if workspace_path == &open_folder.path
|
||||
&& same_remote_connection_identity(
|
||||
workspace_host,
|
||||
open_folder.connection_options.as_ref(),
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2456,7 +2466,7 @@ impl RecentProjectsDelegate {
|
||||
) -> bool {
|
||||
!self.is_current_workspace(workspace.workspace_id, cx)
|
||||
&& !self.is_in_current_window_groups(workspace)
|
||||
&& !self.is_open_folder(&workspace.paths)
|
||||
&& !self.is_open_folder(workspace)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2467,7 +2477,7 @@ mod tests {
|
||||
use serde_json::json;
|
||||
use settings::SettingsStore;
|
||||
use util::path;
|
||||
use workspace::{AppState, open_paths};
|
||||
use workspace::{AppState, PathList, open_paths};
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -2679,6 +2689,53 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn is_open_folder_distinguishes_local_and_remote(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let shared_path = PathBuf::from("/repo");
|
||||
let local_open_folder = OpenFolderEntry {
|
||||
worktree_id: WorktreeId::from_usize(0),
|
||||
name: "repo".into(),
|
||||
path: shared_path.clone(),
|
||||
branch: None,
|
||||
is_active: false,
|
||||
connection_options: None,
|
||||
};
|
||||
|
||||
let delegate = RecentProjectsDelegate::new(
|
||||
WeakEntity::new_invalid(),
|
||||
false,
|
||||
cx.update(|cx| cx.focus_handle()),
|
||||
vec![local_open_folder],
|
||||
Vec::new(),
|
||||
ProjectPickerStyle::Modal,
|
||||
);
|
||||
|
||||
let paths = PathList::new(&[shared_path]);
|
||||
let local_workspace = RecentWorkspace {
|
||||
workspace_id: WorkspaceId::from_i64(1),
|
||||
location: SerializedWorkspaceLocation::Local,
|
||||
paths: paths.clone(),
|
||||
identity_paths: paths.clone(),
|
||||
timestamp: Utc::now(),
|
||||
};
|
||||
let remote_workspace = RecentWorkspace {
|
||||
workspace_id: WorkspaceId::from_i64(2),
|
||||
location: SerializedWorkspaceLocation::Remote(RemoteConnectionOptions::Mock(
|
||||
remote::MockConnectionOptions { id: 0 },
|
||||
)),
|
||||
paths: paths.clone(),
|
||||
identity_paths: paths,
|
||||
timestamp: Utc::now(),
|
||||
};
|
||||
|
||||
// A local open folder should hide only the matching local recent
|
||||
// project, not a remote checkout that shares the same path.
|
||||
assert!(delegate.is_open_folder(&local_workspace));
|
||||
assert!(!delegate.is_open_folder(&remote_workspace));
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn deleting_top_recent_project_preserves_scroll_position(cx: &mut TestAppContext) {
|
||||
let target = FIRST_RECENT_PROJECT;
|
||||
|
||||
Reference in new issue
Block a user