mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Replicate project-specific settings when collaborating
This commit is contained in:
parent
ed0fa2404c
commit
8f95435548
@ -112,6 +112,16 @@ CREATE INDEX "index_worktree_repository_statuses_on_project_id" ON "worktree_rep
|
||||
CREATE INDEX "index_worktree_repository_statuses_on_project_id_and_worktree_id" ON "worktree_repository_statuses" ("project_id", "worktree_id");
|
||||
CREATE INDEX "index_worktree_repository_statuses_on_project_id_and_worktree_id_and_work_directory_id" ON "worktree_repository_statuses" ("project_id", "worktree_id", "work_directory_id");
|
||||
|
||||
CREATE TABLE "worktree_settings_files" (
|
||||
"project_id" INTEGER NOT NULL,
|
||||
"worktree_id" INTEGER NOT NULL,
|
||||
"path" VARCHAR NOT NULL,
|
||||
"content" TEXT,
|
||||
PRIMARY KEY(project_id, worktree_id, path),
|
||||
FOREIGN KEY(project_id, worktree_id) REFERENCES worktrees (project_id, id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX "index_worktree_settings_files_on_project_id" ON "worktree_settings_files" ("project_id");
|
||||
CREATE INDEX "index_worktree_settings_files_on_project_id_and_worktree_id" ON "worktree_settings_files" ("project_id", "worktree_id");
|
||||
|
||||
CREATE TABLE "worktree_diagnostic_summaries" (
|
||||
"project_id" INTEGER NOT NULL,
|
||||
|
@ -0,0 +1,10 @@
|
||||
CREATE TABLE "worktree_settings_files" (
|
||||
"project_id" INTEGER NOT NULL,
|
||||
"worktree_id" INT8 NOT NULL,
|
||||
"path" VARCHAR NOT NULL,
|
||||
"content" TEXT NOT NULL,
|
||||
PRIMARY KEY(project_id, worktree_id, path),
|
||||
FOREIGN KEY(project_id, worktree_id) REFERENCES worktrees (project_id, id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE INDEX "index_settings_files_on_project_id" ON "worktree_settings_files" ("project_id");
|
||||
CREATE INDEX "index_settings_files_on_project_id_and_wt_id" ON "worktree_settings_files" ("project_id", "worktree_id");
|
@ -16,6 +16,7 @@ mod worktree_diagnostic_summary;
|
||||
mod worktree_entry;
|
||||
mod worktree_repository;
|
||||
mod worktree_repository_statuses;
|
||||
mod worktree_settings_file;
|
||||
|
||||
use crate::executor::Executor;
|
||||
use crate::{Error, Result};
|
||||
@ -1494,6 +1495,7 @@ impl Database {
|
||||
updated_repositories: Default::default(),
|
||||
removed_repositories: Default::default(),
|
||||
diagnostic_summaries: Default::default(),
|
||||
settings_files: Default::default(),
|
||||
scan_id: db_worktree.scan_id as u64,
|
||||
completed_scan_id: db_worktree.completed_scan_id as u64,
|
||||
};
|
||||
@ -1638,6 +1640,25 @@ impl Database {
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
{
|
||||
let mut db_settings_files = worktree_settings_file::Entity::find()
|
||||
.filter(worktree_settings_file::Column::ProjectId.eq(project_id))
|
||||
.stream(&*tx)
|
||||
.await?;
|
||||
while let Some(db_settings_file) = db_settings_files.next().await {
|
||||
let db_settings_file = db_settings_file?;
|
||||
if let Some(worktree) = worktrees
|
||||
.iter_mut()
|
||||
.find(|w| w.id == db_settings_file.worktree_id as u64)
|
||||
{
|
||||
worktree.settings_files.push(WorktreeSettingsFile {
|
||||
path: db_settings_file.path,
|
||||
content: db_settings_file.content,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut collaborators = project
|
||||
.find_related(project_collaborator::Entity)
|
||||
.all(&*tx)
|
||||
@ -2637,6 +2658,58 @@ impl Database {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn update_worktree_settings(
|
||||
&self,
|
||||
update: &proto::UpdateWorktreeSettings,
|
||||
connection: ConnectionId,
|
||||
) -> Result<RoomGuard<Vec<ConnectionId>>> {
|
||||
let project_id = ProjectId::from_proto(update.project_id);
|
||||
let room_id = self.room_id_for_project(project_id).await?;
|
||||
self.room_transaction(room_id, |tx| async move {
|
||||
// Ensure the update comes from the host.
|
||||
let project = project::Entity::find_by_id(project_id)
|
||||
.one(&*tx)
|
||||
.await?
|
||||
.ok_or_else(|| anyhow!("no such project"))?;
|
||||
if project.host_connection()? != connection {
|
||||
return Err(anyhow!("can't update a project hosted by someone else"))?;
|
||||
}
|
||||
|
||||
if let Some(content) = &update.content {
|
||||
worktree_settings_file::Entity::insert(worktree_settings_file::ActiveModel {
|
||||
project_id: ActiveValue::Set(project_id),
|
||||
worktree_id: ActiveValue::Set(update.worktree_id as i64),
|
||||
path: ActiveValue::Set(update.path.clone()),
|
||||
content: ActiveValue::Set(content.clone()),
|
||||
})
|
||||
.on_conflict(
|
||||
OnConflict::columns([
|
||||
worktree_settings_file::Column::ProjectId,
|
||||
worktree_settings_file::Column::WorktreeId,
|
||||
worktree_settings_file::Column::Path,
|
||||
])
|
||||
.update_column(worktree_settings_file::Column::Content)
|
||||
.to_owned(),
|
||||
)
|
||||
.exec(&*tx)
|
||||
.await?;
|
||||
} else {
|
||||
worktree_settings_file::Entity::delete(worktree_settings_file::ActiveModel {
|
||||
project_id: ActiveValue::Set(project_id),
|
||||
worktree_id: ActiveValue::Set(update.worktree_id as i64),
|
||||
path: ActiveValue::Set(update.path.clone()),
|
||||
..Default::default()
|
||||
})
|
||||
.exec(&*tx)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let connection_ids = self.project_guest_connection_ids(project_id, &tx).await?;
|
||||
Ok(connection_ids)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn join_project(
|
||||
&self,
|
||||
project_id: ProjectId,
|
||||
@ -2707,6 +2780,7 @@ impl Database {
|
||||
entries: Default::default(),
|
||||
repository_entries: Default::default(),
|
||||
diagnostic_summaries: Default::default(),
|
||||
settings_files: Default::default(),
|
||||
scan_id: db_worktree.scan_id as u64,
|
||||
completed_scan_id: db_worktree.completed_scan_id as u64,
|
||||
},
|
||||
@ -2819,6 +2893,25 @@ impl Database {
|
||||
}
|
||||
}
|
||||
|
||||
// Populate worktree settings files
|
||||
{
|
||||
let mut db_settings_files = worktree_settings_file::Entity::find()
|
||||
.filter(worktree_settings_file::Column::ProjectId.eq(project_id))
|
||||
.stream(&*tx)
|
||||
.await?;
|
||||
while let Some(db_settings_file) = db_settings_files.next().await {
|
||||
let db_settings_file = db_settings_file?;
|
||||
if let Some(worktree) =
|
||||
worktrees.get_mut(&(db_settings_file.worktree_id as u64))
|
||||
{
|
||||
worktree.settings_files.push(WorktreeSettingsFile {
|
||||
path: db_settings_file.path,
|
||||
content: db_settings_file.content,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Populate language servers.
|
||||
let language_servers = project
|
||||
.find_related(language_server::Entity)
|
||||
@ -3482,6 +3575,7 @@ pub struct RejoinedWorktree {
|
||||
pub updated_repositories: Vec<proto::RepositoryEntry>,
|
||||
pub removed_repositories: Vec<u64>,
|
||||
pub diagnostic_summaries: Vec<proto::DiagnosticSummary>,
|
||||
pub settings_files: Vec<WorktreeSettingsFile>,
|
||||
pub scan_id: u64,
|
||||
pub completed_scan_id: u64,
|
||||
}
|
||||
@ -3537,10 +3631,17 @@ pub struct Worktree {
|
||||
pub entries: Vec<proto::Entry>,
|
||||
pub repository_entries: BTreeMap<u64, proto::RepositoryEntry>,
|
||||
pub diagnostic_summaries: Vec<proto::DiagnosticSummary>,
|
||||
pub settings_files: Vec<WorktreeSettingsFile>,
|
||||
pub scan_id: u64,
|
||||
pub completed_scan_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WorktreeSettingsFile {
|
||||
pub path: String,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub use test::*;
|
||||
|
||||
|
19
crates/collab/src/db/worktree_settings_file.rs
Normal file
19
crates/collab/src/db/worktree_settings_file.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use super::ProjectId;
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "worktree_settings_files")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub project_id: ProjectId,
|
||||
#[sea_orm(primary_key)]
|
||||
pub worktree_id: i64,
|
||||
#[sea_orm(primary_key)]
|
||||
pub path: String,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
@ -200,6 +200,7 @@ impl Server {
|
||||
.add_message_handler(start_language_server)
|
||||
.add_message_handler(update_language_server)
|
||||
.add_message_handler(update_diagnostic_summary)
|
||||
.add_message_handler(update_worktree_settings)
|
||||
.add_request_handler(forward_project_request::<proto::GetHover>)
|
||||
.add_request_handler(forward_project_request::<proto::GetDefinition>)
|
||||
.add_request_handler(forward_project_request::<proto::GetTypeDefinition>)
|
||||
@ -1088,6 +1089,18 @@ async fn rejoin_room(
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
for settings_file in worktree.settings_files {
|
||||
session.peer.send(
|
||||
session.connection_id,
|
||||
proto::UpdateWorktreeSettings {
|
||||
project_id: project.id.to_proto(),
|
||||
worktree_id: worktree.id,
|
||||
path: settings_file.path,
|
||||
content: Some(settings_file.content),
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
for language_server in &project.language_servers {
|
||||
@ -1410,6 +1423,18 @@ async fn join_project(
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
for settings_file in dbg!(worktree.settings_files) {
|
||||
session.peer.send(
|
||||
session.connection_id,
|
||||
proto::UpdateWorktreeSettings {
|
||||
project_id: project_id.to_proto(),
|
||||
worktree_id: worktree.id,
|
||||
path: settings_file.path,
|
||||
content: Some(settings_file.content),
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
for language_server in &project.language_servers {
|
||||
@ -1525,6 +1550,31 @@ async fn update_diagnostic_summary(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_worktree_settings(
|
||||
message: proto::UpdateWorktreeSettings,
|
||||
session: Session,
|
||||
) -> Result<()> {
|
||||
dbg!(&message);
|
||||
|
||||
let guest_connection_ids = session
|
||||
.db()
|
||||
.await
|
||||
.update_worktree_settings(&message, session.connection_id)
|
||||
.await?;
|
||||
|
||||
broadcast(
|
||||
Some(session.connection_id),
|
||||
guest_connection_ids.iter().copied(),
|
||||
|connection_id| {
|
||||
session
|
||||
.peer
|
||||
.forward_send(session.connection_id, connection_id, message.clone())
|
||||
},
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn start_language_server(
|
||||
request: proto::StartLanguageServer,
|
||||
session: Session,
|
||||
|
@ -3114,6 +3114,135 @@ async fn test_fs_operations(
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_local_settings(
|
||||
deterministic: Arc<Deterministic>,
|
||||
cx_a: &mut TestAppContext,
|
||||
cx_b: &mut TestAppContext,
|
||||
) {
|
||||
deterministic.forbid_parking();
|
||||
let mut server = TestServer::start(&deterministic).await;
|
||||
let client_a = server.create_client(cx_a, "user_a").await;
|
||||
let client_b = server.create_client(cx_b, "user_b").await;
|
||||
server
|
||||
.create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||
.await;
|
||||
let active_call_a = cx_a.read(ActiveCall::global);
|
||||
|
||||
// As client A, open a project that contains some local settings files
|
||||
client_a
|
||||
.fs
|
||||
.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
".zed": {
|
||||
"settings.json": r#"{ "tab_size": 2 }"#
|
||||
},
|
||||
"a": {
|
||||
".zed": {
|
||||
"settings.json": r#"{ "tab_size": 8 }"#
|
||||
},
|
||||
"a.txt": "a-contents",
|
||||
},
|
||||
"b": {
|
||||
"b.txt": "b-contents",
|
||||
}
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
let (project_a, _) = client_a.build_local_project("/dir", cx_a).await;
|
||||
let project_id = active_call_a
|
||||
.update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// As client B, join that project and observe the local settings.
|
||||
let project_b = client_b.build_remote_project(project_id, cx_b).await;
|
||||
let worktree_b = project_b.read_with(cx_b, |project, cx| project.worktrees(cx).next().unwrap());
|
||||
deterministic.run_until_parked();
|
||||
cx_b.read(|cx| {
|
||||
let store = cx.global::<SettingsStore>();
|
||||
assert_eq!(
|
||||
store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
|
||||
&[
|
||||
(Path::new("").into(), r#"{"tab_size":2}"#.to_string()),
|
||||
(Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
|
||||
]
|
||||
)
|
||||
});
|
||||
|
||||
// As client A, update a settings file. As Client B, see the changed settings.
|
||||
client_a
|
||||
.fs
|
||||
.insert_file("/dir/.zed/settings.json", r#"{}"#.into())
|
||||
.await;
|
||||
deterministic.run_until_parked();
|
||||
cx_b.read(|cx| {
|
||||
let store = cx.global::<SettingsStore>();
|
||||
assert_eq!(
|
||||
store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
|
||||
&[
|
||||
(Path::new("").into(), r#"{}"#.to_string()),
|
||||
(Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
|
||||
]
|
||||
)
|
||||
});
|
||||
|
||||
// As client A, create and remove some settings files. As client B, see the changed settings.
|
||||
client_a
|
||||
.fs
|
||||
.remove_file("/dir/.zed/settings.json".as_ref(), Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
client_a
|
||||
.fs
|
||||
.create_dir("/dir/b/.zed".as_ref())
|
||||
.await
|
||||
.unwrap();
|
||||
client_a
|
||||
.fs
|
||||
.insert_file("/dir/b/.zed/settings.json", r#"{"tab_size": 4}"#.into())
|
||||
.await;
|
||||
deterministic.run_until_parked();
|
||||
cx_b.read(|cx| {
|
||||
let store = cx.global::<SettingsStore>();
|
||||
assert_eq!(
|
||||
store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
|
||||
&[
|
||||
(Path::new("a").into(), r#"{"tab_size":8}"#.to_string()),
|
||||
(Path::new("b").into(), r#"{"tab_size":4}"#.to_string()),
|
||||
]
|
||||
)
|
||||
});
|
||||
|
||||
// As client B, disconnect.
|
||||
server.forbid_connections();
|
||||
server.disconnect_client(client_b.peer_id().unwrap());
|
||||
|
||||
// As client A, change and remove settings files while client B is disconnected.
|
||||
client_a
|
||||
.fs
|
||||
.insert_file("/dir/a/.zed/settings.json", r#"{"hard_tabs":true}"#.into())
|
||||
.await;
|
||||
client_a
|
||||
.fs
|
||||
.remove_file("/dir/b/.zed/settings.json".as_ref(), Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
deterministic.run_until_parked();
|
||||
|
||||
// As client B, reconnect and see the changed settings.
|
||||
server.allow_connections();
|
||||
deterministic.advance_clock(RECEIVE_TIMEOUT);
|
||||
cx_b.read(|cx| {
|
||||
let store = cx.global::<SettingsStore>();
|
||||
assert_eq!(
|
||||
store.local_settings(worktree_b.id()).collect::<Vec<_>>(),
|
||||
&[(Path::new("a").into(), r#"{"hard_tabs":true}"#.to_string()),]
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_buffer_conflict_after_save(
|
||||
deterministic: Arc<Deterministic>,
|
||||
|
@ -462,6 +462,7 @@ impl Project {
|
||||
client.add_model_request_handler(Self::handle_update_buffer);
|
||||
client.add_model_message_handler(Self::handle_update_diagnostic_summary);
|
||||
client.add_model_message_handler(Self::handle_update_worktree);
|
||||
client.add_model_message_handler(Self::handle_update_worktree_settings);
|
||||
client.add_model_request_handler(Self::handle_create_project_entry);
|
||||
client.add_model_request_handler(Self::handle_rename_project_entry);
|
||||
client.add_model_request_handler(Self::handle_copy_project_entry);
|
||||
@ -1105,6 +1106,21 @@ impl Project {
|
||||
.log_err();
|
||||
}
|
||||
|
||||
let store = cx.global::<SettingsStore>();
|
||||
for worktree in self.worktrees(cx) {
|
||||
let worktree_id = worktree.read(cx).id().to_proto();
|
||||
for (path, content) in store.local_settings(worktree.id()) {
|
||||
self.client
|
||||
.send(proto::UpdateWorktreeSettings {
|
||||
project_id,
|
||||
worktree_id,
|
||||
path: path.to_string_lossy().into(),
|
||||
content: Some(content),
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
|
||||
let (updates_tx, mut updates_rx) = mpsc::unbounded();
|
||||
let client = self.client.clone();
|
||||
self.client_state = Some(ProjectClientState::Local {
|
||||
@ -1217,6 +1233,14 @@ impl Project {
|
||||
message_id: u32,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Result<()> {
|
||||
cx.update_global::<SettingsStore, _, _>(|store, cx| {
|
||||
for worktree in &self.worktrees {
|
||||
store
|
||||
.clear_local_settings(worktree.handle_id(), cx)
|
||||
.log_err();
|
||||
}
|
||||
});
|
||||
|
||||
self.join_project_response_message_id = message_id;
|
||||
self.set_worktrees_from_proto(message.worktrees, cx)?;
|
||||
self.set_collaborators_from_proto(message.collaborators, cx)?;
|
||||
@ -4888,8 +4912,12 @@ impl Project {
|
||||
.push(WorktreeHandle::Weak(worktree.downgrade()));
|
||||
}
|
||||
|
||||
cx.observe_release(worktree, |this, worktree, cx| {
|
||||
let handle_id = worktree.id();
|
||||
cx.observe_release(worktree, move |this, worktree, cx| {
|
||||
let _ = this.remove_worktree(worktree.id(), cx);
|
||||
cx.update_global::<SettingsStore, _, _>(|store, cx| {
|
||||
store.clear_local_settings(handle_id, cx).log_err()
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
|
||||
@ -5174,14 +5202,16 @@ impl Project {
|
||||
.detach();
|
||||
}
|
||||
|
||||
pub fn update_local_worktree_settings(
|
||||
fn update_local_worktree_settings(
|
||||
&mut self,
|
||||
worktree: &ModelHandle<Worktree>,
|
||||
changes: &UpdatedEntriesSet,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
let project_id = self.remote_id();
|
||||
let worktree_id = worktree.id();
|
||||
let worktree = worktree.read(cx).as_local().unwrap();
|
||||
let remote_worktree_id = worktree.id();
|
||||
|
||||
let mut settings_contents = Vec::new();
|
||||
for (path, _, change) in changes.iter() {
|
||||
@ -5195,10 +5225,7 @@ impl Project {
|
||||
let removed = *change == PathChange::Removed;
|
||||
let abs_path = worktree.absolutize(path);
|
||||
settings_contents.push(async move {
|
||||
anyhow::Ok((
|
||||
settings_dir,
|
||||
(!removed).then_some(fs.load(&abs_path).await?),
|
||||
))
|
||||
(settings_dir, (!removed).then_some(fs.load(&abs_path).await))
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -5207,19 +5234,30 @@ impl Project {
|
||||
return;
|
||||
}
|
||||
|
||||
let client = self.client.clone();
|
||||
cx.spawn_weak(move |_, mut cx| async move {
|
||||
let settings_contents = futures::future::join_all(settings_contents).await;
|
||||
let settings_contents: Vec<(Arc<Path>, _)> =
|
||||
futures::future::join_all(settings_contents).await;
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<SettingsStore, _, _>(|store, cx| {
|
||||
for entry in settings_contents {
|
||||
if let Some((directory, file_content)) = entry.log_err() {
|
||||
store
|
||||
.set_local_settings(
|
||||
worktree_id,
|
||||
directory,
|
||||
file_content.as_ref().map(String::as_str),
|
||||
cx,
|
||||
)
|
||||
for (directory, file_content) in settings_contents {
|
||||
let file_content = file_content.and_then(|content| content.log_err());
|
||||
store
|
||||
.set_local_settings(
|
||||
worktree_id,
|
||||
directory.clone(),
|
||||
file_content.as_ref().map(String::as_str),
|
||||
cx,
|
||||
)
|
||||
.log_err();
|
||||
if let Some(remote_id) = project_id {
|
||||
client
|
||||
.send(proto::UpdateWorktreeSettings {
|
||||
project_id: remote_id,
|
||||
worktree_id: remote_worktree_id.to_proto(),
|
||||
path: directory.to_string_lossy().into_owned(),
|
||||
content: file_content,
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
@ -5467,6 +5505,30 @@ impl Project {
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_update_worktree_settings(
|
||||
this: ModelHandle<Self>,
|
||||
envelope: TypedEnvelope<proto::UpdateWorktreeSettings>,
|
||||
_: Arc<Client>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<()> {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
let worktree_id = WorktreeId::from_proto(envelope.payload.worktree_id);
|
||||
if let Some(worktree) = this.worktree_for_id(worktree_id, cx) {
|
||||
cx.update_global::<SettingsStore, _, _>(|store, cx| {
|
||||
store
|
||||
.set_local_settings(
|
||||
worktree.id(),
|
||||
PathBuf::from(&envelope.payload.path).into(),
|
||||
envelope.payload.content.as_ref().map(String::as_str),
|
||||
cx,
|
||||
)
|
||||
.log_err();
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_create_project_entry(
|
||||
this: ModelHandle<Self>,
|
||||
envelope: TypedEnvelope<proto::CreateProjectEntry>,
|
||||
@ -6557,8 +6619,8 @@ impl Project {
|
||||
}
|
||||
|
||||
self.metadata_changed(cx);
|
||||
for (id, _) in old_worktrees_by_id {
|
||||
cx.emit(Event::WorktreeRemoved(id));
|
||||
for id in old_worktrees_by_id.keys() {
|
||||
cx.emit(Event::WorktreeRemoved(*id));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -6928,6 +6990,13 @@ impl WorktreeHandle {
|
||||
WorktreeHandle::Weak(handle) => handle.upgrade(cx),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_id(&self) -> usize {
|
||||
match self {
|
||||
WorktreeHandle::Strong(handle) => handle.id(),
|
||||
WorktreeHandle::Weak(handle) => handle.id(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenBuffer {
|
||||
|
@ -132,6 +132,8 @@ message Envelope {
|
||||
|
||||
OnTypeFormatting on_type_formatting = 111;
|
||||
OnTypeFormattingResponse on_type_formatting_response = 112;
|
||||
|
||||
UpdateWorktreeSettings update_worktree_settings = 113;
|
||||
}
|
||||
}
|
||||
|
||||
@ -339,6 +341,13 @@ message UpdateWorktree {
|
||||
string abs_path = 10;
|
||||
}
|
||||
|
||||
message UpdateWorktreeSettings {
|
||||
uint64 project_id = 1;
|
||||
uint64 worktree_id = 2;
|
||||
string path = 3;
|
||||
optional string content = 4;
|
||||
}
|
||||
|
||||
message CreateProjectEntry {
|
||||
uint64 project_id = 1;
|
||||
uint64 worktree_id = 2;
|
||||
|
@ -236,6 +236,7 @@ messages!(
|
||||
(UpdateProject, Foreground),
|
||||
(UpdateProjectCollaborator, Foreground),
|
||||
(UpdateWorktree, Foreground),
|
||||
(UpdateWorktreeSettings, Foreground),
|
||||
(UpdateDiffBase, Foreground),
|
||||
(GetPrivateUserInfo, Foreground),
|
||||
(GetPrivateUserInfoResponse, Foreground),
|
||||
@ -345,6 +346,7 @@ entity_messages!(
|
||||
UpdateProject,
|
||||
UpdateProjectCollaborator,
|
||||
UpdateWorktree,
|
||||
UpdateWorktreeSettings,
|
||||
UpdateDiffBase
|
||||
);
|
||||
|
||||
|
@ -359,6 +359,21 @@ impl SettingsStore {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add or remove a set of local settings via a JSON string.
|
||||
pub fn clear_local_settings(&mut self, root_id: usize, cx: &AppContext) -> Result<()> {
|
||||
eprintln!("clearing local settings {root_id}");
|
||||
self.local_deserialized_settings
|
||||
.retain(|k, _| k.0 != root_id);
|
||||
self.recompute_values(Some((root_id, "".as_ref())), cx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn local_settings(&self, root_id: usize) -> impl '_ + Iterator<Item = (Arc<Path>, String)> {
|
||||
self.local_deserialized_settings
|
||||
.range((root_id, Path::new("").into())..(root_id + 1, Path::new("").into()))
|
||||
.map(|((_, path), content)| (path.clone(), serde_json::to_string(content).unwrap()))
|
||||
}
|
||||
|
||||
pub fn json_schema(
|
||||
&self,
|
||||
schema_params: &SettingsJsonSchemaParams,
|
||||
|
Loading…
Reference in New Issue
Block a user