remote project settings

Ben Kunkle and Mikayla created

Co-Authored-By: Mikayla <mikayla@zed.dev>

Change summary

crates/language/src/buffer.rs         |  5 ++++
crates/project/src/project.rs         | 36 ++++++++++++++++------------
crates/proto/proto/worktree.proto     | 22 +++++++++++++++++
crates/settings_ui/src/settings_ui.rs | 23 ------------------
crates/worktree/src/worktree.rs       |  1 
5 files changed, 48 insertions(+), 39 deletions(-)

Detailed changes

crates/language/src/buffer.rs 🔗

@@ -385,6 +385,8 @@ pub enum DiskState {
     Present { mtime: MTime },
     /// Deleted file that was previously present.
     Deleted,
+    /// File stored remotely.
+    Remote,
 }
 
 impl DiskState {
@@ -394,6 +396,7 @@ impl DiskState {
             DiskState::New => None,
             DiskState::Present { mtime } => Some(mtime),
             DiskState::Deleted => None,
+            DiskState::Remote => None,
         }
     }
 
@@ -402,6 +405,7 @@ impl DiskState {
             DiskState::New => false,
             DiskState::Present { .. } => true,
             DiskState::Deleted => false,
+            DiskState::Remote => true,
         }
     }
 }
@@ -2073,6 +2077,7 @@ impl Buffer {
                 None => true,
             },
             DiskState::Deleted => false,
+            DiskState::Remote => false,
         }
     }
 

crates/project/src/project.rs 🔗

@@ -5275,32 +5275,36 @@ impl Project {
     }
 
     pub fn update_local_settings_file(
-        &self,
+        &mut self,
         worktree_id: WorktreeId,
         rel_path: Arc<RelPath>,
-        cx: &mut App,
+        cx: &mut Context<Project>,
         update: impl 'static + Send + FnOnce(&mut settings::SettingsContent, &App),
     ) {
-        let Some(worktree) = self.worktree_for_id(worktree_id, cx) else {
-            // todo(settings_ui) error?
-            return;
-        };
-        cx.spawn(async move |cx| {
-            let file = worktree
-                .update(cx, |worktree, cx| worktree.load_file(&rel_path, cx))?
+        let open_buffer_task = self.open_buffer(
+            ProjectPath {
+                worktree_id,
+                path: rel_path,
+            },
+            cx,
+        );
+        cx.spawn(async move |this, cx| {
+            let buffer = open_buffer_task
                 .await
                 .context("Failed to load settings file")?;
 
+            let buffer_text = buffer.read_with(cx, |buffer, _| buffer.text())?;
             let new_text = cx.read_global::<SettingsStore, _>(|store, cx| {
-                store.new_text_for_update(file.text, move |settings| update(settings, cx))
+                store.new_text_for_update(buffer_text, move |settings| update(settings, cx))
             })?;
-            worktree
-                .update(cx, |worktree, cx| {
-                    let line_ending = text::LineEnding::detect(&new_text);
-                    worktree.write_file(rel_path.clone(), new_text.into(), line_ending, cx)
-                })?
+
+            buffer.update(cx, |buffer, cx| {
+                buffer.set_text(new_text, cx);
+            })?;
+
+            this.update(cx, |this, cx| this.save_buffer(buffer, cx))?
                 .await
-                .context("Failed to write settings file")?;
+                .context("Failed to save settings file")?;
 
             anyhow::Ok(())
         })

crates/proto/proto/worktree.proto 🔗

@@ -158,3 +158,25 @@ message UpdateUserSettings {
     uint64 project_id = 1;
     string contents = 2;
 }
+
+message LoadProjectFile {
+    uint64 project_id = 1;
+    uint64 worktree_id = 2;
+    string rel_path = 3;
+}
+
+message LoadProjectFileResponse {
+    message LoadProjectFileError {
+        string message = 1;
+        uint64 code = 2;
+    }
+
+    message LoadProjectFileSuccess {
+        string contents = 1;
+    }
+
+    oneof result {
+        LoadProjectFileError error = 1;
+        LoadProjectFileSuccess success = 2;
+    }
+}

crates/settings_ui/src/settings_ui.rs 🔗

@@ -2116,29 +2116,6 @@ impl SettingsWindow {
         }
     }
 
-    // TODO:
-    //  Reconsider this after preview launch
-    // fn file_location_str(&self) -> String {
-    //     match &self.current_file {
-    //         SettingsUiFile::User => "settings.json".to_string(),
-    //         SettingsUiFile::Project((worktree_id, path)) => self
-    //             .worktree_root_dirs
-    //             .get(&worktree_id)
-    //             .map(|directory_name| {
-    //                 let path_style = PathStyle::local();
-    //                 let file_path = path.join(paths::local_settings_file_relative_path());
-    //                 format!(
-    //                     "{}{}{}",
-    //                     directory_name,
-    //                     path_style.separator(),
-    //                     file_path.display(path_style)
-    //                 )
-    //             })
-    //             .expect("Current file should always be present in root dir map"),
-    //         SettingsUiFile::Server(file) => file.to_string(),
-    //     }
-    // }
-
     fn render_search(&self, _window: &mut Window, cx: &mut App) -> Div {
         h_flex()
             .py_1()

crates/worktree/src/worktree.rs 🔗

@@ -706,6 +706,7 @@ impl Worktree {
         match self {
             Worktree::Local(this) => this.load_file(path, cx),
             Worktree::Remote(_) => {
+                // This needs to respect the private files configuration
                 Task::ready(Err(anyhow!("remote worktrees can't yet load files")))
             }
         }