collab: Fix project sharing between Windows and Unix (#23680)
Closes #14258 Windows user(host) sharing a project to a guest(using macOS), and host follows guest: https://github.com/user-attachments/assets/ba306b6b-23f7-48b1-8ba8-fdc5992d8f00 macOS user(host) sharing a project to a guest(using Windows), and host follows guest: https://github.com/user-attachments/assets/c5ee5e78-870d-49e5-907d-8565977a01ae macOS user edits files in a windows project through collab: https://github.com/user-attachments/assets/581057cf-e7df-4e56-a0ce-ced74339906a Release Notes: - N/A
This commit is contained in:
parent
929c5e76b4
commit
c1f162abc6
14 changed files with 226 additions and 117 deletions
|
@ -23,7 +23,10 @@ use language::{
|
|||
},
|
||||
Buffer, BufferEvent, Capability, DiskState, File as _, Language, LanguageRegistry, Operation,
|
||||
};
|
||||
use rpc::{proto, AnyProtoClient, ErrorExt as _, TypedEnvelope};
|
||||
use rpc::{
|
||||
proto::{self, ToProto},
|
||||
AnyProtoClient, ErrorExt as _, TypedEnvelope,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use smol::channel::Receiver;
|
||||
use std::{
|
||||
|
@ -580,13 +583,12 @@ impl RemoteBufferStore {
|
|||
let worktree_id = worktree.read(cx).id().to_proto();
|
||||
let project_id = self.project_id;
|
||||
let client = self.upstream_client.clone();
|
||||
let path_string = path.clone().to_string_lossy().to_string();
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let response = client
|
||||
.request(proto::OpenBufferByPath {
|
||||
project_id,
|
||||
worktree_id,
|
||||
path: path_string,
|
||||
path: path.to_proto(),
|
||||
})
|
||||
.await?;
|
||||
let buffer_id = BufferId::new(response.buffer_id)?;
|
||||
|
|
|
@ -13,6 +13,7 @@ use gpui::{
|
|||
App, AppContext, Context, Entity, EventEmitter, SharedString, Subscription, Task, WeakEntity,
|
||||
};
|
||||
use language::{Buffer, LanguageRegistry};
|
||||
use rpc::proto::ToProto;
|
||||
use rpc::{proto, AnyProtoClient};
|
||||
use settings::WorktreeId;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
@ -222,7 +223,7 @@ impl GitState {
|
|||
work_directory_id: work_directory_id.to_proto(),
|
||||
paths: paths
|
||||
.into_iter()
|
||||
.map(|repo_path| repo_path.to_proto())
|
||||
.map(|repo_path| repo_path.as_ref().to_proto())
|
||||
.collect(),
|
||||
})
|
||||
.await
|
||||
|
@ -247,7 +248,7 @@ impl GitState {
|
|||
work_directory_id: work_directory_id.to_proto(),
|
||||
paths: paths
|
||||
.into_iter()
|
||||
.map(|repo_path| repo_path.to_proto())
|
||||
.map(|repo_path| repo_path.as_ref().to_proto())
|
||||
.collect(),
|
||||
})
|
||||
.await
|
||||
|
|
|
@ -55,7 +55,10 @@ use parking_lot::Mutex;
|
|||
use postage::watch;
|
||||
use rand::prelude::*;
|
||||
|
||||
use rpc::AnyProtoClient;
|
||||
use rpc::{
|
||||
proto::{FromProto, ToProto},
|
||||
AnyProtoClient,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use settings::{Settings, SettingsLocation, SettingsStore};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
@ -5360,7 +5363,7 @@ impl LspStore {
|
|||
project_id: *project_id,
|
||||
worktree_id: worktree_id.to_proto(),
|
||||
summary: Some(proto::DiagnosticSummary {
|
||||
path: worktree_path.to_string_lossy().to_string(),
|
||||
path: worktree_path.to_proto(),
|
||||
language_server_id: server_id.0 as u64,
|
||||
error_count: new_summary.error_count as u32,
|
||||
warning_count: new_summary.warning_count as u32,
|
||||
|
@ -5848,10 +5851,8 @@ impl LspStore {
|
|||
.ok_or_else(|| anyhow!("worktree not found"))?;
|
||||
let (old_abs_path, new_abs_path) = {
|
||||
let root_path = worktree.update(&mut cx, |this, _| this.abs_path())?;
|
||||
(
|
||||
root_path.join(&old_path),
|
||||
root_path.join(&envelope.payload.new_path),
|
||||
)
|
||||
let new_path = PathBuf::from_proto(envelope.payload.new_path.clone());
|
||||
(root_path.join(&old_path), root_path.join(&new_path))
|
||||
};
|
||||
|
||||
Self::will_rename_entry(
|
||||
|
@ -5881,7 +5882,7 @@ impl LspStore {
|
|||
if let Some(message) = envelope.payload.summary {
|
||||
let project_path = ProjectPath {
|
||||
worktree_id,
|
||||
path: Path::new(&message.path).into(),
|
||||
path: Arc::<Path>::from_proto(message.path),
|
||||
};
|
||||
let path = project_path.path.clone();
|
||||
let server_id = LanguageServerId(message.language_server_id as usize);
|
||||
|
@ -5915,7 +5916,7 @@ impl LspStore {
|
|||
project_id: *project_id,
|
||||
worktree_id: worktree_id.to_proto(),
|
||||
summary: Some(proto::DiagnosticSummary {
|
||||
path: project_path.path.to_string_lossy().to_string(),
|
||||
path: project_path.path.as_ref().to_proto(),
|
||||
language_server_id: server_id.0 as u64,
|
||||
error_count: summary.error_count as u32,
|
||||
warning_count: summary.warning_count as u32,
|
||||
|
@ -7114,7 +7115,7 @@ impl LspStore {
|
|||
project_id,
|
||||
worktree_id: worktree_id.to_proto(),
|
||||
summary: Some(proto::DiagnosticSummary {
|
||||
path: path.to_string_lossy().to_string(),
|
||||
path: path.as_ref().to_proto(),
|
||||
language_server_id: server_id.0 as u64,
|
||||
error_count: 0,
|
||||
warning_count: 0,
|
||||
|
@ -7768,7 +7769,7 @@ impl LspStore {
|
|||
language_server_name: symbol.language_server_name.0.to_string(),
|
||||
source_worktree_id: symbol.source_worktree_id.to_proto(),
|
||||
worktree_id: symbol.path.worktree_id.to_proto(),
|
||||
path: symbol.path.path.to_string_lossy().to_string(),
|
||||
path: symbol.path.path.as_ref().to_proto(),
|
||||
name: symbol.name.clone(),
|
||||
kind: unsafe { mem::transmute::<lsp::SymbolKind, i32>(symbol.kind) },
|
||||
start: Some(proto::PointUtf16 {
|
||||
|
@ -7789,7 +7790,7 @@ impl LspStore {
|
|||
let kind = unsafe { mem::transmute::<i32, lsp::SymbolKind>(serialized_symbol.kind) };
|
||||
let path = ProjectPath {
|
||||
worktree_id,
|
||||
path: PathBuf::from(serialized_symbol.path).into(),
|
||||
path: Arc::<Path>::from_proto(serialized_symbol.path),
|
||||
};
|
||||
|
||||
let start = serialized_symbol
|
||||
|
@ -8263,7 +8264,7 @@ impl DiagnosticSummary {
|
|||
path: &Path,
|
||||
) -> proto::DiagnosticSummary {
|
||||
proto::DiagnosticSummary {
|
||||
path: path.to_string_lossy().to_string(),
|
||||
path: path.to_proto(),
|
||||
language_server_id: language_server_id.0 as u64,
|
||||
error_count: self.error_count as u32,
|
||||
warning_count: self.warning_count as u32,
|
||||
|
|
|
@ -73,7 +73,7 @@ pub use prettier_store::PrettierStore;
|
|||
use project_settings::{ProjectSettings, SettingsObserver, SettingsObserverEvent};
|
||||
use remote::{SshConnectionOptions, SshRemoteClient};
|
||||
use rpc::{
|
||||
proto::{LanguageServerPromptResponse, SSH_PROJECT_ID},
|
||||
proto::{FromProto, LanguageServerPromptResponse, ToProto, SSH_PROJECT_ID},
|
||||
AnyProtoClient, ErrorCode,
|
||||
};
|
||||
use search::{SearchInputKind, SearchQuery, SearchResult};
|
||||
|
@ -297,14 +297,14 @@ impl ProjectPath {
|
|||
pub fn from_proto(p: proto::ProjectPath) -> Self {
|
||||
Self {
|
||||
worktree_id: WorktreeId::from_proto(p.worktree_id),
|
||||
path: Arc::from(PathBuf::from(p.path)),
|
||||
path: Arc::<Path>::from_proto(p.path),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_proto(&self) -> proto::ProjectPath {
|
||||
proto::ProjectPath {
|
||||
worktree_id: self.worktree_id.to_proto(),
|
||||
path: self.path.to_string_lossy().to_string(),
|
||||
path: self.path.as_ref().to_proto(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3360,18 +3360,19 @@ impl Project {
|
|||
})
|
||||
})
|
||||
} else if let Some(ssh_client) = self.ssh_client.as_ref() {
|
||||
let request_path = Path::new(path);
|
||||
let request = ssh_client
|
||||
.read(cx)
|
||||
.proto_client()
|
||||
.request(proto::GetPathMetadata {
|
||||
project_id: SSH_PROJECT_ID,
|
||||
path: path.to_string(),
|
||||
path: request_path.to_proto(),
|
||||
});
|
||||
cx.background_executor().spawn(async move {
|
||||
let response = request.await.log_err()?;
|
||||
if response.exists {
|
||||
Some(ResolvedPath::AbsPath {
|
||||
path: PathBuf::from(response.path),
|
||||
path: PathBuf::from_proto(response.path),
|
||||
is_dir: response.is_dir,
|
||||
})
|
||||
} else {
|
||||
|
@ -3441,9 +3442,10 @@ impl Project {
|
|||
if self.is_local() {
|
||||
DirectoryLister::Local(self.fs.clone()).list_directory(query, cx)
|
||||
} else if let Some(session) = self.ssh_client.as_ref() {
|
||||
let path_buf = PathBuf::from(query);
|
||||
let request = proto::ListRemoteDirectory {
|
||||
dev_server_id: SSH_PROJECT_ID,
|
||||
path: query,
|
||||
path: path_buf.to_proto(),
|
||||
};
|
||||
|
||||
let response = session.read(cx).proto_client().request(request);
|
||||
|
@ -3994,7 +3996,7 @@ impl Project {
|
|||
this.open_buffer(
|
||||
ProjectPath {
|
||||
worktree_id,
|
||||
path: PathBuf::from(envelope.payload.path).into(),
|
||||
path: Arc::<Path>::from_proto(envelope.payload.path),
|
||||
},
|
||||
cx,
|
||||
)
|
||||
|
|
|
@ -7,18 +7,17 @@ use paths::{
|
|||
local_settings_file_relative_path, local_tasks_file_relative_path,
|
||||
local_vscode_tasks_file_relative_path, EDITORCONFIG_NAME,
|
||||
};
|
||||
use rpc::{proto, AnyProtoClient, TypedEnvelope};
|
||||
use rpc::{
|
||||
proto::{self, FromProto, ToProto},
|
||||
AnyProtoClient, TypedEnvelope,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{
|
||||
parse_json_with_comments, InvalidSettingsError, LocalSettingsKind, Settings, SettingsLocation,
|
||||
SettingsSources, SettingsStore,
|
||||
};
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
use std::{path::Path, sync::Arc, time::Duration};
|
||||
use task::{TaskTemplates, VsCodeTaskFile};
|
||||
use util::ResultExt;
|
||||
use worktree::{PathChange, UpdatedEntriesSet, Worktree, WorktreeId};
|
||||
|
@ -292,7 +291,7 @@ impl SettingsObserver {
|
|||
.send(proto::UpdateWorktreeSettings {
|
||||
project_id,
|
||||
worktree_id,
|
||||
path: path.to_string_lossy().into(),
|
||||
path: path.to_proto(),
|
||||
content: Some(content),
|
||||
kind: Some(
|
||||
local_settings_kind_to_proto(LocalSettingsKind::Settings).into(),
|
||||
|
@ -305,7 +304,7 @@ impl SettingsObserver {
|
|||
.send(proto::UpdateWorktreeSettings {
|
||||
project_id,
|
||||
worktree_id,
|
||||
path: path.to_string_lossy().into(),
|
||||
path: path.to_proto(),
|
||||
content: Some(content),
|
||||
kind: Some(
|
||||
local_settings_kind_to_proto(LocalSettingsKind::Editorconfig).into(),
|
||||
|
@ -343,7 +342,7 @@ impl SettingsObserver {
|
|||
this.update_settings(
|
||||
worktree,
|
||||
[(
|
||||
PathBuf::from(&envelope.payload.path).into(),
|
||||
Arc::<Path>::from_proto(envelope.payload.path.clone()),
|
||||
local_settings_kind_from_proto(kind),
|
||||
envelope.payload.content,
|
||||
)],
|
||||
|
@ -551,7 +550,7 @@ impl SettingsObserver {
|
|||
.send(proto::UpdateWorktreeSettings {
|
||||
project_id: self.project_id,
|
||||
worktree_id: remote_worktree_id.to_proto(),
|
||||
path: directory.to_string_lossy().into_owned(),
|
||||
path: directory.to_proto(),
|
||||
content: file_content,
|
||||
kind: Some(local_settings_kind_to_proto(kind).into()),
|
||||
})
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{str::FromStr, sync::Arc};
|
||||
use std::{path::PathBuf, str::FromStr, sync::Arc};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
|
@ -8,7 +8,10 @@ use gpui::{
|
|||
App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Subscription, Task, WeakEntity,
|
||||
};
|
||||
use language::{LanguageName, LanguageRegistry, LanguageToolchainStore, Toolchain, ToolchainList};
|
||||
use rpc::{proto, AnyProtoClient, TypedEnvelope};
|
||||
use rpc::{
|
||||
proto::{self, FromProto, ToProto},
|
||||
AnyProtoClient, TypedEnvelope,
|
||||
};
|
||||
use settings::WorktreeId;
|
||||
use util::ResultExt as _;
|
||||
|
||||
|
@ -120,7 +123,9 @@ impl ToolchainStore {
|
|||
};
|
||||
let toolchain = Toolchain {
|
||||
name: toolchain.name.into(),
|
||||
path: toolchain.path.into(),
|
||||
// todo(windows)
|
||||
// Do we need to convert path to native string?
|
||||
path: PathBuf::from(toolchain.path).to_proto().into(),
|
||||
as_json: serde_json::Value::from_str(&toolchain.raw_json)?,
|
||||
language_name,
|
||||
};
|
||||
|
@ -144,10 +149,13 @@ impl ToolchainStore {
|
|||
.await;
|
||||
|
||||
Ok(proto::ActiveToolchainResponse {
|
||||
toolchain: toolchain.map(|toolchain| proto::Toolchain {
|
||||
name: toolchain.name.into(),
|
||||
path: toolchain.path.into(),
|
||||
raw_json: toolchain.as_json.to_string(),
|
||||
toolchain: toolchain.map(|toolchain| {
|
||||
let path = PathBuf::from(toolchain.path.to_string());
|
||||
proto::Toolchain {
|
||||
name: toolchain.name.into(),
|
||||
path: path.to_proto(),
|
||||
raw_json: toolchain.as_json.to_string(),
|
||||
}
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
@ -183,10 +191,13 @@ impl ToolchainStore {
|
|||
toolchains
|
||||
.toolchains
|
||||
.into_iter()
|
||||
.map(|toolchain| proto::Toolchain {
|
||||
name: toolchain.name.to_string(),
|
||||
path: toolchain.path.to_string(),
|
||||
raw_json: toolchain.as_json.to_string(),
|
||||
.map(|toolchain| {
|
||||
let path = PathBuf::from(toolchain.path.to_string());
|
||||
proto::Toolchain {
|
||||
name: toolchain.name.to_string(),
|
||||
path: path.to_proto(),
|
||||
raw_json: toolchain.as_json.to_string(),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
|
@ -354,6 +365,7 @@ impl RemoteToolchainStore {
|
|||
let project_id = self.project_id;
|
||||
let client = self.client.clone();
|
||||
cx.spawn(move |_| async move {
|
||||
let path = PathBuf::from(toolchain.path.to_string());
|
||||
let _ = client
|
||||
.request(proto::ActivateToolchain {
|
||||
project_id,
|
||||
|
@ -361,7 +373,7 @@ impl RemoteToolchainStore {
|
|||
language_name: toolchain.language_name.into(),
|
||||
toolchain: Some(proto::Toolchain {
|
||||
name: toolchain.name.into(),
|
||||
path: toolchain.path.into(),
|
||||
path: path.to_proto(),
|
||||
raw_json: toolchain.as_json.to_string(),
|
||||
}),
|
||||
})
|
||||
|
@ -398,7 +410,12 @@ impl RemoteToolchainStore {
|
|||
Some(Toolchain {
|
||||
language_name: language_name.clone(),
|
||||
name: toolchain.name.into(),
|
||||
path: toolchain.path.into(),
|
||||
// todo(windows)
|
||||
// Do we need to convert path to native string?
|
||||
path: PathBuf::from_proto(toolchain.path)
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
.into(),
|
||||
as_json: serde_json::Value::from_str(&toolchain.raw_json).ok()?,
|
||||
})
|
||||
})
|
||||
|
@ -439,7 +456,12 @@ impl RemoteToolchainStore {
|
|||
Some(Toolchain {
|
||||
language_name: language_name.clone(),
|
||||
name: toolchain.name.into(),
|
||||
path: toolchain.path.into(),
|
||||
// todo(windows)
|
||||
// Do we need to convert path to native string?
|
||||
path: PathBuf::from_proto(toolchain.path)
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
.into(),
|
||||
as_json: serde_json::Value::from_str(&toolchain.raw_json).ok()?,
|
||||
})
|
||||
})
|
||||
|
|
|
@ -15,7 +15,7 @@ use futures::{
|
|||
use gpui::{App, AsyncApp, Context, Entity, EntityId, EventEmitter, Task, WeakEntity};
|
||||
use postage::oneshot;
|
||||
use rpc::{
|
||||
proto::{self, SSH_PROJECT_ID},
|
||||
proto::{self, FromProto, ToProto, SSH_PROJECT_ID},
|
||||
AnyProtoClient, ErrorExt, TypedEnvelope,
|
||||
};
|
||||
use smol::{
|
||||
|
@ -268,10 +268,11 @@ impl WorktreeStore {
|
|||
cx.spawn(|this, mut cx| async move {
|
||||
let this = this.upgrade().context("Dropped worktree store")?;
|
||||
|
||||
let path = Path::new(abs_path.as_str());
|
||||
let response = client
|
||||
.request(proto::AddWorktree {
|
||||
project_id: SSH_PROJECT_ID,
|
||||
path: abs_path.clone(),
|
||||
path: path.to_proto(),
|
||||
visible,
|
||||
})
|
||||
.await?;
|
||||
|
@ -282,10 +283,11 @@ impl WorktreeStore {
|
|||
return Ok(existing_worktree);
|
||||
}
|
||||
|
||||
let root_name = PathBuf::from(&response.canonicalized_path)
|
||||
let root_path_buf = PathBuf::from_proto(response.canonicalized_path.clone());
|
||||
let root_name = root_path_buf
|
||||
.file_name()
|
||||
.map(|n| n.to_string_lossy().to_string())
|
||||
.unwrap_or(response.canonicalized_path.to_string());
|
||||
.unwrap_or(root_path_buf.to_string_lossy().to_string());
|
||||
|
||||
let worktree = cx.update(|cx| {
|
||||
Worktree::remote(
|
||||
|
@ -596,7 +598,7 @@ impl WorktreeStore {
|
|||
id: worktree.id().to_proto(),
|
||||
root_name: worktree.root_name().into(),
|
||||
visible: worktree.is_visible(),
|
||||
abs_path: worktree.abs_path().to_string_lossy().into(),
|
||||
abs_path: worktree.abs_path().to_proto(),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
|
@ -923,7 +925,7 @@ impl WorktreeStore {
|
|||
project_id: remote_worktree.project_id(),
|
||||
repository: Some(proto::ProjectPath {
|
||||
worktree_id: project_path.worktree_id.to_proto(),
|
||||
path: project_path.path.to_string_lossy().to_string(), // Root path
|
||||
path: project_path.path.to_proto(), // Root path
|
||||
}),
|
||||
});
|
||||
|
||||
|
@ -994,7 +996,7 @@ impl WorktreeStore {
|
|||
project_id: remote_worktree.project_id(),
|
||||
repository: Some(proto::ProjectPath {
|
||||
worktree_id: repository.worktree_id.to_proto(),
|
||||
path: repository.path.to_string_lossy().to_string(), // Root path
|
||||
path: repository.path.to_proto(), // Root path
|
||||
}),
|
||||
branch_name: new_branch,
|
||||
});
|
||||
|
@ -1116,7 +1118,7 @@ impl WorktreeStore {
|
|||
.context("Invalid GitBranches call")?;
|
||||
let project_path = ProjectPath {
|
||||
worktree_id: WorktreeId::from_proto(project_path.worktree_id),
|
||||
path: Path::new(&project_path.path).into(),
|
||||
path: Arc::<Path>::from_proto(project_path.path),
|
||||
};
|
||||
|
||||
let branches = this
|
||||
|
@ -1147,7 +1149,7 @@ impl WorktreeStore {
|
|||
.context("Invalid GitBranches call")?;
|
||||
let project_path = ProjectPath {
|
||||
worktree_id: WorktreeId::from_proto(project_path.worktree_id),
|
||||
path: Path::new(&project_path.path).into(),
|
||||
path: Arc::<Path>::from_proto(project_path.path),
|
||||
};
|
||||
let new_branch = update_branch.payload.branch_name;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue