Detailed changes
@@ -3037,15 +3037,11 @@ impl Project {
buffer: &Model<Buffer>,
cx: &mut ModelContext<Self>,
) -> Task<Option<ResolvedPath>> {
- // TODO: ssh based remoting.
- if self.ssh_session.is_some() {
- return Task::ready(None);
- }
-
- if self.is_local_or_ssh() {
- let expanded = PathBuf::from(shellexpand::tilde(&path).into_owned());
+ let path_buf = PathBuf::from(path);
+ if path_buf.is_absolute() || path.starts_with("~") {
+ if self.is_local() {
+ let expanded = PathBuf::from(shellexpand::tilde(&path).into_owned());
- if expanded.is_absolute() {
let fs = self.fs.clone();
cx.background_executor().spawn(async move {
let path = expanded.as_path();
@@ -3053,16 +3049,24 @@ impl Project {
exists.then(|| ResolvedPath::AbsPath(expanded))
})
+ } else if let Some(ssh_session) = self.ssh_session.as_ref() {
+ let request = ssh_session.request(proto::CheckFileExists {
+ project_id: SSH_PROJECT_ID,
+ path: path.to_string(),
+ });
+ cx.background_executor().spawn(async move {
+ let response = request.await.log_err()?;
+ if response.exists {
+ Some(ResolvedPath::AbsPath(PathBuf::from(response.path)))
+ } else {
+ None
+ }
+ })
} else {
- self.resolve_path_in_worktrees(expanded, buffer, cx)
- }
- } else {
- let path = PathBuf::from(path);
- if path.is_absolute() || path.starts_with("~") {
return Task::ready(None);
}
-
- self.resolve_path_in_worktrees(path, buffer, cx)
+ } else {
+ self.resolve_path_in_worktrees(path_buf, buffer, cx)
}
}
@@ -4016,17 +4020,7 @@ impl Project {
}
pub fn worktree_metadata_protos(&self, cx: &AppContext) -> Vec<proto::WorktreeMetadata> {
- self.worktrees(cx)
- .map(|worktree| {
- let worktree = worktree.read(cx);
- proto::WorktreeMetadata {
- id: worktree.id().to_proto(),
- root_name: worktree.root_name().into(),
- visible: worktree.is_visible(),
- abs_path: worktree.abs_path().to_string_lossy().into(),
- }
- })
- .collect()
+ self.worktree_store.read(cx).worktree_metadata_protos(cx)
}
fn set_worktrees_from_proto(
@@ -4035,10 +4029,9 @@ impl Project {
cx: &mut ModelContext<Project>,
) -> Result<()> {
cx.notify();
- let result = self.worktree_store.update(cx, |worktree_store, cx| {
+ self.worktree_store.update(cx, |worktree_store, cx| {
worktree_store.set_worktrees_from_proto(worktrees, self.replica_id(), cx)
- });
- result
+ })
}
fn set_collaborators_from_proto(
@@ -4547,6 +4540,22 @@ pub enum ResolvedPath {
AbsPath(PathBuf),
}
+impl ResolvedPath {
+ pub fn abs_path(&self) -> Option<&Path> {
+ match self {
+ Self::AbsPath(path) => Some(path.as_path()),
+ _ => None,
+ }
+ }
+
+ pub fn project_path(&self) -> Option<&ProjectPath> {
+ match self {
+ Self::ProjectPath(path) => Some(&path),
+ _ => None,
+ }
+ }
+}
+
impl Item for Buffer {
fn try_open(
project: &Model<Project>,
@@ -293,7 +293,10 @@ message Envelope {
TryExec try_exec = 252;
ReadTextFile read_text_file = 253;
- ReadTextFileResponse read_text_file_response = 254; // current max
+ ReadTextFileResponse read_text_file_response = 254;
+
+ CheckFileExists check_file_exists = 255;
+ CheckFileExistsResponse check_file_exists_response = 256; // current max
}
reserved 158 to 161;
@@ -2574,3 +2577,13 @@ message TryExec {
message TryExecResponse {
string text = 1;
}
+
+message CheckFileExists {
+ uint64 project_id = 1;
+ string path = 2;
+}
+
+message CheckFileExistsResponse {
+ bool exists = 1;
+ string path = 2;
+}
@@ -372,7 +372,9 @@ messages!(
(ShellEnvResponse, Foreground),
(TryExec, Foreground),
(ReadTextFile, Foreground),
- (ReadTextFileResponse, Foreground)
+ (ReadTextFileResponse, Foreground),
+ (CheckFileExists, Background),
+ (CheckFileExistsResponse, Background)
);
request_messages!(
@@ -501,6 +503,7 @@ request_messages!(
(ShellEnv, ShellEnvResponse),
(ReadTextFile, ReadTextFileResponse),
(TryExec, Ack),
+ (CheckFileExists, CheckFileExistsResponse)
);
entity_messages!(
@@ -578,7 +581,8 @@ entity_messages!(
WhichCommand,
ShellEnv,
TryExec,
- ReadTextFile
+ ReadTextFile,
+ CheckFileExists,
);
entity_messages!(
@@ -108,6 +108,7 @@ impl HeadlessProject {
session.subscribe_to_entity(SSH_PROJECT_ID, &settings_observer);
client.add_request_handler(cx.weak_model(), Self::handle_list_remote_directory);
+ client.add_request_handler(cx.weak_model(), Self::handle_check_file_exists);
client.add_model_request_handler(Self::handle_add_worktree);
client.add_model_request_handler(Self::handle_open_buffer_by_path);
@@ -298,4 +299,20 @@ impl HeadlessProject {
}
Ok(proto::ListRemoteDirectoryResponse { entries })
}
+
+ pub async fn handle_check_file_exists(
+ this: Model<Self>,
+ envelope: TypedEnvelope<proto::CheckFileExists>,
+ cx: AsyncAppContext,
+ ) -> Result<proto::CheckFileExistsResponse> {
+ let fs = cx.read_model(&this, |this, _| this.fs.clone())?;
+ let expanded = shellexpand::tilde(&envelope.payload.path).to_string();
+
+ let exists = fs.is_file(&PathBuf::from(expanded.clone())).await;
+
+ Ok(proto::CheckFileExistsResponse {
+ exists,
+ path: expanded,
+ })
+ }
}
@@ -12,7 +12,7 @@ use lsp::{CompletionContext, CompletionResponse, CompletionTriggerKind};
use node_runtime::NodeRuntime;
use project::{
search::{SearchQuery, SearchResult},
- Project,
+ Project, ProjectPath,
};
use remote::SshSession;
use serde_json::json;
@@ -440,6 +440,49 @@ async fn test_remote_lsp(cx: &mut TestAppContext, server_cx: &mut TestAppContext
})
}
+#[gpui::test]
+async fn test_remote_resolve_file_path(cx: &mut TestAppContext, server_cx: &mut TestAppContext) {
+ let (project, _headless, _fs) = init_test(cx, server_cx).await;
+ let (worktree, _) = project
+ .update(cx, |project, cx| {
+ project.find_or_create_worktree("/code/project1", true, cx)
+ })
+ .await
+ .unwrap();
+
+ let worktree_id = cx.update(|cx| worktree.read(cx).id());
+
+ let buffer = project
+ .update(cx, |project, cx| {
+ project.open_buffer((worktree_id, Path::new("src/lib.rs")), cx)
+ })
+ .await
+ .unwrap();
+
+ let path = project
+ .update(cx, |project, cx| {
+ project.resolve_existing_file_path("/code/project1/README.md", &buffer, cx)
+ })
+ .await
+ .unwrap();
+ assert_eq!(
+ path.abs_path().unwrap().to_string_lossy(),
+ "/code/project1/README.md"
+ );
+
+ let path = project
+ .update(cx, |project, cx| {
+ project.resolve_existing_file_path("../README.md", &buffer, cx)
+ })
+ .await
+ .unwrap();
+
+ assert_eq!(
+ path.project_path().unwrap().clone(),
+ ProjectPath::from((worktree_id, "README.md"))
+ );
+}
+
fn init_logger() {
if std::env::var("RUST_LOG").is_ok() {
env_logger::try_init().ok();