Fix ssh project history (#19683)
Use `Fs` instead of `std::fs` and do entry existence checks better: * first, check the worktree entry existence without any FS checks * then, only for local cases, use `Fs` to check for abs_path existence of items, in case those came from single-filed worktrees that got closed and removed. Remote entries do not get file existence checks, so might try opening previously removed buffers for now. Release Notes: - N/A
This commit is contained in:
parent
3ec015b325
commit
454d3dd52b
5 changed files with 80 additions and 52 deletions
|
@ -95,7 +95,9 @@ async fn test_channel_guest_promotion(cx_a: &mut TestAppContext, cx_b: &mut Test
|
||||||
let room_b = cx_b
|
let room_b = cx_b
|
||||||
.read(ActiveCall::global)
|
.read(ActiveCall::global)
|
||||||
.update(cx_b, |call, _| call.room().unwrap().clone());
|
.update(cx_b, |call, _| call.room().unwrap().clone());
|
||||||
cx_b.simulate_keystrokes("cmd-p 1 enter");
|
cx_b.simulate_keystrokes("cmd-p");
|
||||||
|
cx_a.run_until_parked();
|
||||||
|
cx_b.simulate_keystrokes("1 enter");
|
||||||
|
|
||||||
let (project_b, editor_b) = workspace_b.update(cx_b, |workspace, cx| {
|
let (project_b, editor_b) = workspace_b.update(cx_b, |workspace, cx| {
|
||||||
(
|
(
|
||||||
|
|
|
@ -1589,8 +1589,9 @@ async fn test_following_stops_on_unshare(cx_a: &mut TestAppContext, cx_b: &mut T
|
||||||
.await;
|
.await;
|
||||||
let (workspace_b, cx_b) = client_b.join_workspace(channel_id, cx_b).await;
|
let (workspace_b, cx_b) = client_b.join_workspace(channel_id, cx_b).await;
|
||||||
|
|
||||||
cx_a.simulate_keystrokes("cmd-p 2 enter");
|
cx_a.simulate_keystrokes("cmd-p");
|
||||||
cx_a.run_until_parked();
|
cx_a.run_until_parked();
|
||||||
|
cx_a.simulate_keystrokes("2 enter");
|
||||||
|
|
||||||
let editor_a = workspace_a.update(cx_a, |workspace, cx| {
|
let editor_a = workspace_a.update(cx_a, |workspace, cx| {
|
||||||
workspace.active_item_as::<Editor>(cx).unwrap()
|
workspace.active_item_as::<Editor>(cx).unwrap()
|
||||||
|
@ -2041,7 +2042,9 @@ async fn test_following_to_channel_notes_other_workspace(
|
||||||
share_workspace(&workspace_a, cx_a).await.unwrap();
|
share_workspace(&workspace_a, cx_a).await.unwrap();
|
||||||
|
|
||||||
// a opens 1.txt
|
// a opens 1.txt
|
||||||
cx_a.simulate_keystrokes("cmd-p 1 enter");
|
cx_a.simulate_keystrokes("cmd-p");
|
||||||
|
cx_a.run_until_parked();
|
||||||
|
cx_a.simulate_keystrokes("1 enter");
|
||||||
cx_a.run_until_parked();
|
cx_a.run_until_parked();
|
||||||
workspace_a.update(cx_a, |workspace, cx| {
|
workspace_a.update(cx_a, |workspace, cx| {
|
||||||
let editor = workspace.active_item(cx).unwrap();
|
let editor = workspace.active_item(cx).unwrap();
|
||||||
|
@ -2098,7 +2101,9 @@ async fn test_following_while_deactivated(cx_a: &mut TestAppContext, cx_b: &mut
|
||||||
share_workspace(&workspace_a, cx_a).await.unwrap();
|
share_workspace(&workspace_a, cx_a).await.unwrap();
|
||||||
|
|
||||||
// a opens 1.txt
|
// a opens 1.txt
|
||||||
cx_a.simulate_keystrokes("cmd-p 1 enter");
|
cx_a.simulate_keystrokes("cmd-p");
|
||||||
|
cx_a.run_until_parked();
|
||||||
|
cx_a.simulate_keystrokes("1 enter");
|
||||||
cx_a.run_until_parked();
|
cx_a.run_until_parked();
|
||||||
workspace_a.update(cx_a, |workspace, cx| {
|
workspace_a.update(cx_a, |workspace, cx| {
|
||||||
let editor = workspace.active_item(cx).unwrap();
|
let editor = workspace.active_item(cx).unwrap();
|
||||||
|
@ -2118,7 +2123,9 @@ async fn test_following_while_deactivated(cx_a: &mut TestAppContext, cx_b: &mut
|
||||||
cx_b.simulate_keystrokes("down");
|
cx_b.simulate_keystrokes("down");
|
||||||
|
|
||||||
// a opens a different file while not followed
|
// a opens a different file while not followed
|
||||||
cx_a.simulate_keystrokes("cmd-p 2 enter");
|
cx_a.simulate_keystrokes("cmd-p");
|
||||||
|
cx_a.run_until_parked();
|
||||||
|
cx_a.simulate_keystrokes("2 enter");
|
||||||
|
|
||||||
workspace_b.update(cx_b, |workspace, cx| {
|
workspace_b.update(cx_b, |workspace, cx| {
|
||||||
let editor = workspace.active_item_as::<Editor>(cx).unwrap();
|
let editor = workspace.active_item_as::<Editor>(cx).unwrap();
|
||||||
|
@ -2128,7 +2135,9 @@ async fn test_following_while_deactivated(cx_a: &mut TestAppContext, cx_b: &mut
|
||||||
// a opens a file in a new window
|
// a opens a file in a new window
|
||||||
let (_, cx_a2) = client_a.build_test_workspace(&mut cx_a2).await;
|
let (_, cx_a2) = client_a.build_test_workspace(&mut cx_a2).await;
|
||||||
cx_a2.update(|cx| cx.activate_window());
|
cx_a2.update(|cx| cx.activate_window());
|
||||||
cx_a2.simulate_keystrokes("cmd-p 3 enter");
|
cx_a2.simulate_keystrokes("cmd-p");
|
||||||
|
cx_a2.run_until_parked();
|
||||||
|
cx_a2.simulate_keystrokes("3 enter");
|
||||||
cx_a2.run_until_parked();
|
cx_a2.run_until_parked();
|
||||||
|
|
||||||
// b starts following a again
|
// b starts following a again
|
||||||
|
|
|
@ -5,6 +5,7 @@ mod file_finder_settings;
|
||||||
mod new_path_prompt;
|
mod new_path_prompt;
|
||||||
mod open_path_prompt;
|
mod open_path_prompt;
|
||||||
|
|
||||||
|
use futures::future::join_all;
|
||||||
pub use open_path_prompt::OpenPathDelegate;
|
pub use open_path_prompt::OpenPathDelegate;
|
||||||
|
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
|
@ -59,7 +60,7 @@ impl FileFinder {
|
||||||
fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
|
fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
|
||||||
workspace.register_action(|workspace, action: &workspace::ToggleFileFinder, cx| {
|
workspace.register_action(|workspace, action: &workspace::ToggleFileFinder, cx| {
|
||||||
let Some(file_finder) = workspace.active_modal::<Self>(cx) else {
|
let Some(file_finder) = workspace.active_modal::<Self>(cx) else {
|
||||||
Self::open(workspace, action.separate_history, cx);
|
Self::open(workspace, action.separate_history, cx).detach();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -72,8 +73,13 @@ impl FileFinder {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open(workspace: &mut Workspace, separate_history: bool, cx: &mut ViewContext<Workspace>) {
|
fn open(
|
||||||
|
workspace: &mut Workspace,
|
||||||
|
separate_history: bool,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
|
) -> Task<()> {
|
||||||
let project = workspace.project().read(cx);
|
let project = workspace.project().read(cx);
|
||||||
|
let fs = project.fs();
|
||||||
|
|
||||||
let currently_opened_path = workspace
|
let currently_opened_path = workspace
|
||||||
.active_item(cx)
|
.active_item(cx)
|
||||||
|
@ -88,13 +94,33 @@ impl FileFinder {
|
||||||
let history_items = workspace
|
let history_items = workspace
|
||||||
.recent_navigation_history(Some(MAX_RECENT_SELECTIONS), cx)
|
.recent_navigation_history(Some(MAX_RECENT_SELECTIONS), cx)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|(_, history_abs_path)| match history_abs_path {
|
.filter_map(|(project_path, abs_path)| {
|
||||||
Some(abs_path) => history_file_exists(abs_path),
|
if project.entry_for_path(&project_path, cx).is_some() {
|
||||||
None => true,
|
return Some(Task::ready(Some(FoundPath::new(project_path, abs_path))));
|
||||||
|
}
|
||||||
|
let abs_path = abs_path?;
|
||||||
|
if project.is_local() {
|
||||||
|
let fs = fs.clone();
|
||||||
|
Some(cx.background_executor().spawn(async move {
|
||||||
|
if fs.is_file(&abs_path).await {
|
||||||
|
Some(FoundPath::new(project_path, Some(abs_path)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Some(Task::ready(Some(FoundPath::new(
|
||||||
|
project_path,
|
||||||
|
Some(abs_path),
|
||||||
|
))))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.map(|(history_path, abs_path)| FoundPath::new(history_path, abs_path))
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
cx.spawn(move |workspace, mut cx| async move {
|
||||||
|
let history_items = join_all(history_items).await.into_iter().flatten();
|
||||||
|
|
||||||
|
workspace
|
||||||
|
.update(&mut cx, |workspace, cx| {
|
||||||
let project = workspace.project().clone();
|
let project = workspace.project().clone();
|
||||||
let weak_workspace = cx.view().downgrade();
|
let weak_workspace = cx.view().downgrade();
|
||||||
workspace.toggle_modal(cx, |cx| {
|
workspace.toggle_modal(cx, |cx| {
|
||||||
|
@ -103,13 +129,16 @@ impl FileFinder {
|
||||||
weak_workspace,
|
weak_workspace,
|
||||||
project,
|
project,
|
||||||
currently_opened_path,
|
currently_opened_path,
|
||||||
history_items,
|
history_items.collect(),
|
||||||
separate_history,
|
separate_history,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
FileFinder::new(delegate, cx)
|
FileFinder::new(delegate, cx)
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(delegate: FileFinderDelegate, cx: &mut ViewContext<Self>) -> Self {
|
fn new(delegate: FileFinderDelegate, cx: &mut ViewContext<Self>) -> Self {
|
||||||
|
@ -456,16 +485,6 @@ impl FoundPath {
|
||||||
|
|
||||||
const MAX_RECENT_SELECTIONS: usize = 20;
|
const MAX_RECENT_SELECTIONS: usize = 20;
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
fn history_file_exists(abs_path: &PathBuf) -> bool {
|
|
||||||
abs_path.exists()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
fn history_file_exists(abs_path: &Path) -> bool {
|
|
||||||
!abs_path.ends_with("nonexistent.rs")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Selected(ProjectPath),
|
Selected(ProjectPath),
|
||||||
Dismissed,
|
Dismissed,
|
||||||
|
|
|
@ -4,7 +4,7 @@ use super::*;
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
use gpui::{Entity, TestAppContext, VisualTestContext};
|
use gpui::{Entity, TestAppContext, VisualTestContext};
|
||||||
use menu::{Confirm, SelectNext, SelectPrev};
|
use menu::{Confirm, SelectNext, SelectPrev};
|
||||||
use project::FS_WATCH_LATENCY;
|
use project::{RemoveOptions, FS_WATCH_LATENCY};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use workspace::{AppState, ToggleFileFinder, Workspace};
|
use workspace::{AppState, ToggleFileFinder, Workspace};
|
||||||
|
|
||||||
|
@ -1450,6 +1450,15 @@ async fn test_nonexistent_history_items_not_shown(cx: &mut gpui::TestAppContext)
|
||||||
open_close_queried_buffer("non", 1, "nonexistent.rs", &workspace, cx).await;
|
open_close_queried_buffer("non", 1, "nonexistent.rs", &workspace, cx).await;
|
||||||
open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await;
|
open_close_queried_buffer("thi", 1, "third.rs", &workspace, cx).await;
|
||||||
open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await;
|
open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await;
|
||||||
|
app_state
|
||||||
|
.fs
|
||||||
|
.remove_file(
|
||||||
|
Path::new("/src/test/nonexistent.rs"),
|
||||||
|
RemoveOptions::default(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
cx.run_until_parked();
|
||||||
|
|
||||||
let picker = open_file_picker(&workspace, cx);
|
let picker = open_file_picker(&workspace, cx);
|
||||||
cx.simulate_input("rs");
|
cx.simulate_input("rs");
|
||||||
|
|
|
@ -1875,11 +1875,7 @@ impl Project {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_open_buffer(
|
pub fn get_open_buffer(&self, path: &ProjectPath, cx: &AppContext) -> Option<Model<Buffer>> {
|
||||||
&mut self,
|
|
||||||
path: &ProjectPath,
|
|
||||||
cx: &mut ModelContext<Self>,
|
|
||||||
) -> Option<Model<Buffer>> {
|
|
||||||
self.buffer_store.read(cx).get_by_path(path, cx)
|
self.buffer_store.read(cx).get_by_path(path, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3295,17 +3291,10 @@ impl Project {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn absolute_path(&self, project_path: &ProjectPath, cx: &AppContext) -> Option<PathBuf> {
|
pub fn absolute_path(&self, project_path: &ProjectPath, cx: &AppContext) -> Option<PathBuf> {
|
||||||
let workspace_root = self
|
self.worktree_for_id(project_path.worktree_id, cx)?
|
||||||
.worktree_for_id(project_path.worktree_id, cx)?
|
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.abs_path();
|
.absolutize(&project_path.path)
|
||||||
let project_path = project_path.path.as_ref();
|
.ok()
|
||||||
|
|
||||||
Some(if project_path == Path::new("") {
|
|
||||||
workspace_root.to_path_buf()
|
|
||||||
} else {
|
|
||||||
workspace_root.join(project_path)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to find a `ProjectPath` corresponding to the given path. If the path
|
/// Attempts to find a `ProjectPath` corresponding to the given path. If the path
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue