Compare commits

...

3 Commits

Author SHA1 Message Date
Julia Ryan
8017d291a5 wip 2025-11-02 13:08:14 -08:00
Julia Ryan
f584509406 Add protos 2025-10-31 16:54:42 -07:00
Julia Ryan
b97a513265 Initial remote image viewing support 2025-10-31 00:51:17 -07:00
7 changed files with 164 additions and 14 deletions

View File

@@ -267,7 +267,12 @@ trait ImageStoreImpl {
fn as_local(&self) -> Option<Entity<LocalImageStore>>;
}
struct RemoteImageStore {}
struct RemoteImageStore {
image_ids_by_path: HashMap<ProjectPath, ImageId>,
image_ids_by_entry_id: HashMap<ProjectEntryId, ImageId>,
image_store: WeakEntity<ImageStore>,
_subscription: Subscription,
}
struct LocalImageStore {
local_image_ids_by_path: HashMap<ProjectPath, ImageId>,
@@ -320,8 +325,25 @@ impl ImageStore {
_remote_id: u64,
cx: &mut Context<Self>,
) -> Self {
let this = cx.weak_entity();
Self {
state: Box::new(cx.new(|_| RemoteImageStore {})),
state: Box::new(cx.new(|_| {
// let subscription = cx.subscribe(
// &worktree_store,
// |this: &mut RemoteImageStore, _, event, cx| {
// // if let WorktreeStoreEvent::WorktreeAdded(worktree) = event {
// // this.subscribe_to_worktree(worktree, cx);
// // }
// },
// );
let subscription = todo!();
RemoteImageStore {
image_ids_by_path: Default::default(),
image_ids_by_entry_id: Default::default(),
image_store: this,
_subscription: subscription,
}
})),
opened_images: Default::default(),
loading_images_by_path: Default::default(),
worktree_store,
@@ -697,13 +719,50 @@ fn create_gpui_image(content: Vec<u8>) -> anyhow::Result<Arc<gpui::Image>> {
impl ImageStoreImpl for Entity<RemoteImageStore> {
fn open_image(
&self,
_path: Arc<RelPath>,
_worktree: Entity<Worktree>,
_cx: &mut Context<ImageStore>,
path: Arc<RelPath>,
worktree: Entity<Worktree>,
cx: &mut Context<ImageStore>,
) -> Task<Result<Entity<ImageItem>>> {
Task::ready(Err(anyhow::anyhow!(
"Opening images from remote is not supported"
)))
let this = self.clone();
let load_file = worktree.update(cx, |worktree, cx| {
worktree.load_binary_file(path.as_ref(), cx)
});
cx.spawn(async move |image_store, cx| {
let LoadedBinaryFile { file, content } = load_file.await?;
let image = create_gpui_image(content)?;
let entity = cx.new(|cx| ImageItem {
id: cx.entity_id().as_non_zero_u64().into(),
file: file.clone(),
image,
image_metadata: None,
reload_task: None,
})?;
let image_id = cx.read_entity(&entity, |model, _| model.id)?;
this.update(cx, |this, cx| {
image_store.update(cx, |image_store, cx| {
image_store.add_image(entity.clone(), cx)
})??;
this.image_ids_by_path.insert(
ProjectPath {
worktree_id: file.worktree_id(cx),
path: file.path.clone(),
},
image_id,
);
if let Some(entry_id) = file.entry_id {
this.image_ids_by_entry_id.insert(entry_id, image_id);
}
anyhow::Ok(())
})??;
Ok(entity)
})
}
fn reload_images(

View File

@@ -1035,6 +1035,7 @@ impl Project {
client.add_entity_request_handler(Self::handle_open_new_buffer);
client.add_entity_message_handler(Self::handle_create_buffer_for_peer);
client.add_entity_message_handler(Self::handle_toggle_lsp_logs);
client.add_entity_request_handler(Self::handle_load_binary_file);
WorktreeStore::init(&client);
BufferStore::init(&client);
@@ -5169,6 +5170,25 @@ impl Project {
})
}
async fn handle_load_binary_file(
this: Entity<Self>,
envelope: TypedEnvelope<proto::LoadBinaryFile>,
mut cx: AsyncApp,
) -> Result<proto::BinaryFileResponse> {
let peer_id = envelope.original_sender_id()?;
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
let path = RelPath::from_proto(&envelope.payload.path)?;
let open_buffer = this
.update(&mut cx, |this, cx| {
this.image_store.update(&mut cx, |this, cx| {
this.open_image(project_path, cx)
})?
// this(ProjectPath { worktree_id, path }, cx)
})?
.await?;
Project::respond_to_open_buffer_request(this, open_buffer, peer_id, &mut cx)
}
fn set_worktrees_from_proto(
&mut self,
worktrees: Vec<proto::WorktreeMetadata>,

View File

@@ -158,3 +158,14 @@ message UpdateUserSettings {
uint64 project_id = 1;
string contents = 2;
}
message LoadBinaryFile{
uint64 project_id = 1;
uint64 worktree_id = 2;
string path = 3;
}
message BinaryFileResponse {
Entry entry = 1;
bytes content = 2;
}

View File

@@ -427,7 +427,10 @@ message Envelope {
GetTreeDiffResponse get_tree_diff_response = 385;
GetBlobContent get_blob_content = 386;
GetBlobContentResponse get_blob_content_response = 387; // current max
GetBlobContentResponse get_blob_content_response = 387;
LoadBinaryFile load_binary_file = 388;
BinaryFileResponse binary_file_response = 389; // current max
}
reserved 87 to 88;

View File

@@ -157,6 +157,8 @@ messages!(
(ListToolchainsResponse, Foreground),
(LoadCommitDiff, Foreground),
(LoadCommitDiffResponse, Foreground),
(LoadBinaryFile, Background),
(BinaryFileResponse, Background),
(LspExtExpandMacro, Background),
(LspExtExpandMacroResponse, Background),
(LspExtOpenDocs, Background),
@@ -389,6 +391,7 @@ request_messages!(
(LeaveChannelBuffer, Ack),
(LeaveRoom, Ack),
(LoadCommitDiff, LoadCommitDiffResponse),
(LoadBinaryFile, BinaryFileResponse),
(MarkNotificationRead, Ack),
(MoveChannel, Ack),
(OnTypeFormatting, OnTypeFormattingResponse),

View File

@@ -259,6 +259,7 @@ impl HeadlessProject {
session.add_entity_request_handler(Self::handle_open_server_settings);
session.add_entity_request_handler(Self::handle_get_directory_environment);
session.add_entity_message_handler(Self::handle_toggle_lsp_logs);
session.add_entity_request_handler(Self::handle_load_binary_file);
session.add_entity_request_handler(BufferStore::handle_update_buffer);
session.add_entity_message_handler(BufferStore::handle_close_buffer);
@@ -629,6 +630,34 @@ impl HeadlessProject {
})
}
pub async fn handle_load_binary_file(
this: Entity<Self>,
message: TypedEnvelope<proto::OpenBufferByPath>,
mut cx: AsyncApp,
) -> Result<proto::BinaryFileResposne> {
// let worktree_id = WorktreeId::from_proto(message.payload.worktree_id);
// let path = RelPath::from_proto(&message.payload.path)?;
// let (buffer_store, buffer) = this.update(&mut cx, |this, cx| {
// let buffer_store = this.buffer_store.clone();
// let buffer = this.buffer_store.update(cx, |buffer_store, cx| {
// buffer_store.open_buffer(ProjectPath { worktree_id, path }, cx)
// });
// anyhow::Ok((buffer_store, buffer))
// })??;
// let buffer = buffer.await?;
// let buffer_id = buffer.read_with(&cx, |b, _| b.remote_id())?;
// buffer_store.update(&mut cx, |buffer_store, cx| {
// buffer_store
// .create_buffer_for_peer(&buffer, REMOTE_SERVER_PEER_ID, cx)
// .detach_and_log_err(cx);
// })?;
// Ok(proto::OpenBufferResponse {
// buffer_id: buffer_id.to_proto(),
// })
}
async fn handle_find_search_candidates(
this: Entity<Self>,
envelope: TypedEnvelope<proto::FindSearchCandidates>,

View File

@@ -719,9 +719,7 @@ impl Worktree {
) -> Task<Result<LoadedBinaryFile>> {
match self {
Worktree::Local(this) => this.load_binary_file(path, cx),
Worktree::Remote(_) => {
Task::ready(Err(anyhow!("remote worktrees can't yet load binary files")))
}
Worktree::Remote(this) => this.load_binary_file(path, cx),
}
}
@@ -1970,7 +1968,7 @@ impl RemoteWorktree {
paths_to_copy: Vec<Arc<Path>>,
local_fs: Arc<dyn Fs>,
cx: &Context<Worktree>,
) -> Task<anyhow::Result<Vec<ProjectEntryId>>> {
) -> Task<Result<Vec<ProjectEntryId>>> {
let client = self.client.clone();
let worktree_id = self.id().to_proto();
let project_id = self.project_id;
@@ -2028,6 +2026,33 @@ impl RemoteWorktree {
Ok(copied_entry_ids)
})
}
fn load_binary_file(
&self,
path: &RelPath,
cx: &Context<Worktree>,
) -> Task<Result<LoadedBinaryFile>> {
let path = Arc::from(path);
let abs_path = self.absolutize(&path);
// let fs = self.fs.clone();
// let entry = self.refresh_entry(path.clone(), None, cx);
// let is_private = self.is_path_private(&path);
let response = self.client.request(proto::LoadBinaryFile{
path: path.to_proto(),
project_id: self.project_id(),
worktree_id: self.id().to_proto(),
});
let worktree = cx.weak_entity();
cx.background_spawn(async move {
let proto::BinaryFileResponse { entry, content } = response.await?;
let worktree = worktree.upgrade().context("worktree was dropped")?;
// TODO: check is_private
Ok(LoadedBinaryFile {
file: File::for_entry(entry.unwrap().into(), worktree),
content,
})
})
}
}
impl Snapshot {
@@ -5551,7 +5576,7 @@ impl CreatedEntry {
}
}
fn parse_gitfile(content: &str) -> anyhow::Result<&Path> {
fn parse_gitfile(content: &str) -> Result<&Path> {
let path = content
.strip_prefix("gitdir:")
.with_context(|| format!("parsing gitfile content {content:?}"))?;