Make remote projects to sync in local user settings (#37560)
Closes https://github.com/zed-industries/zed/issues/20024 Closes https://github.com/zed-industries/zed/issues/23489 https://github.com/user-attachments/assets/6466e0c1-4188-4980-8bb6-52ef6e7591c9 Release Notes: - Made remote projects to sync in local user settings
This commit is contained in:
@@ -1271,6 +1271,7 @@ impl Project {
|
||||
fs.clone(),
|
||||
worktree_store.clone(),
|
||||
task_store.clone(),
|
||||
Some(remote_proto.clone()),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
@@ -1521,7 +1522,13 @@ impl Project {
|
||||
})?;
|
||||
|
||||
let settings_observer = cx.new(|cx| {
|
||||
SettingsObserver::new_remote(fs.clone(), worktree_store.clone(), task_store.clone(), cx)
|
||||
SettingsObserver::new_remote(
|
||||
fs.clone(),
|
||||
worktree_store.clone(),
|
||||
task_store.clone(),
|
||||
None,
|
||||
cx,
|
||||
)
|
||||
})?;
|
||||
|
||||
let git_store = cx.new(|cx| {
|
||||
|
||||
@@ -4,7 +4,7 @@ use context_server::ContextServerCommand;
|
||||
use dap::adapters::DebugAdapterName;
|
||||
use fs::Fs;
|
||||
use futures::StreamExt as _;
|
||||
use gpui::{App, AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, Task};
|
||||
use gpui::{App, AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, Subscription, Task};
|
||||
use lsp::LanguageServerName;
|
||||
use paths::{
|
||||
EDITORCONFIG_NAME, local_debug_file_relative_path, local_settings_file_relative_path,
|
||||
@@ -13,7 +13,7 @@ use paths::{
|
||||
};
|
||||
use rpc::{
|
||||
AnyProtoClient, TypedEnvelope,
|
||||
proto::{self, FromProto, ToProto},
|
||||
proto::{self, FromProto, REMOTE_SERVER_PROJECT_ID, ToProto},
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -658,6 +658,7 @@ pub struct SettingsObserver {
|
||||
worktree_store: Entity<WorktreeStore>,
|
||||
project_id: u64,
|
||||
task_store: Entity<TaskStore>,
|
||||
_user_settings_watcher: Option<Subscription>,
|
||||
_global_task_config_watcher: Task<()>,
|
||||
_global_debug_config_watcher: Task<()>,
|
||||
}
|
||||
@@ -670,6 +671,7 @@ pub struct SettingsObserver {
|
||||
impl SettingsObserver {
|
||||
pub fn init(client: &AnyProtoClient) {
|
||||
client.add_entity_message_handler(Self::handle_update_worktree_settings);
|
||||
client.add_entity_message_handler(Self::handle_update_user_settings);
|
||||
}
|
||||
|
||||
pub fn new_local(
|
||||
@@ -686,7 +688,8 @@ impl SettingsObserver {
|
||||
task_store,
|
||||
mode: SettingsObserverMode::Local(fs.clone()),
|
||||
downstream_client: None,
|
||||
project_id: 0,
|
||||
_user_settings_watcher: None,
|
||||
project_id: REMOTE_SERVER_PROJECT_ID,
|
||||
_global_task_config_watcher: Self::subscribe_to_global_task_file_changes(
|
||||
fs.clone(),
|
||||
paths::tasks_file().clone(),
|
||||
@@ -704,14 +707,38 @@ impl SettingsObserver {
|
||||
fs: Arc<dyn Fs>,
|
||||
worktree_store: Entity<WorktreeStore>,
|
||||
task_store: Entity<TaskStore>,
|
||||
upstream_client: Option<AnyProtoClient>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let mut user_settings_watcher = None;
|
||||
if cx.try_global::<SettingsStore>().is_some() {
|
||||
if let Some(upstream_client) = upstream_client {
|
||||
let mut user_settings = None;
|
||||
user_settings_watcher = Some(cx.observe_global::<SettingsStore>(move |_, cx| {
|
||||
let new_settings = cx.global::<SettingsStore>().raw_user_settings();
|
||||
if Some(new_settings) != user_settings.as_ref() {
|
||||
if let Some(new_settings_string) = serde_json::to_string(new_settings).ok()
|
||||
{
|
||||
user_settings = Some(new_settings.clone());
|
||||
upstream_client
|
||||
.send(proto::UpdateUserSettings {
|
||||
project_id: REMOTE_SERVER_PROJECT_ID,
|
||||
contents: new_settings_string,
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
Self {
|
||||
worktree_store,
|
||||
task_store,
|
||||
mode: SettingsObserverMode::Remote,
|
||||
downstream_client: None,
|
||||
project_id: 0,
|
||||
project_id: REMOTE_SERVER_PROJECT_ID,
|
||||
_user_settings_watcher: user_settings_watcher,
|
||||
_global_task_config_watcher: Self::subscribe_to_global_task_file_changes(
|
||||
fs.clone(),
|
||||
paths::tasks_file().clone(),
|
||||
@@ -803,6 +830,24 @@ impl SettingsObserver {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_update_user_settings(
|
||||
_: Entity<Self>,
|
||||
envelope: TypedEnvelope<proto::UpdateUserSettings>,
|
||||
cx: AsyncApp,
|
||||
) -> anyhow::Result<()> {
|
||||
let new_settings = serde_json::from_str::<serde_json::Value>(&envelope.payload.contents)
|
||||
.with_context(|| {
|
||||
format!("deserializing {} user settings", envelope.payload.contents)
|
||||
})?;
|
||||
cx.update_global(|settings_store: &mut SettingsStore, cx| {
|
||||
settings_store
|
||||
.set_raw_user_settings(new_settings, cx)
|
||||
.context("setting new user settings")?;
|
||||
anyhow::Ok(())
|
||||
})??;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_worktree_store_event(
|
||||
&mut self,
|
||||
_: Entity<WorktreeStore>,
|
||||
@@ -1089,7 +1134,7 @@ impl SettingsObserver {
|
||||
project_id: self.project_id,
|
||||
worktree_id: remote_worktree_id.to_proto(),
|
||||
path: directory.to_proto(),
|
||||
content: file_content,
|
||||
content: file_content.clone(),
|
||||
kind: Some(local_settings_kind_to_proto(kind).into()),
|
||||
})
|
||||
.log_err();
|
||||
|
||||
@@ -150,3 +150,8 @@ enum LocalSettingsKind {
|
||||
Editorconfig = 2;
|
||||
Debug = 3;
|
||||
}
|
||||
|
||||
message UpdateUserSettings {
|
||||
uint64 project_id = 1;
|
||||
string contents = 2;
|
||||
}
|
||||
|
||||
@@ -397,7 +397,9 @@ message Envelope {
|
||||
|
||||
LspQuery lsp_query = 365;
|
||||
LspQueryResponse lsp_query_response = 366;
|
||||
ToggleLspLogs toggle_lsp_logs = 367; // current max
|
||||
ToggleLspLogs toggle_lsp_logs = 367;
|
||||
|
||||
UpdateUserSettings update_user_settings = 368; // current max
|
||||
}
|
||||
|
||||
reserved 87 to 88;
|
||||
|
||||
@@ -278,6 +278,7 @@ messages!(
|
||||
(UpdateUserChannels, Foreground),
|
||||
(UpdateWorktree, Foreground),
|
||||
(UpdateWorktreeSettings, Foreground),
|
||||
(UpdateUserSettings, Background),
|
||||
(UpdateRepository, Foreground),
|
||||
(RemoveRepository, Foreground),
|
||||
(UsersResponse, Foreground),
|
||||
@@ -583,6 +584,7 @@ entity_messages!(
|
||||
UpdateRepository,
|
||||
RemoveRepository,
|
||||
UpdateWorktreeSettings,
|
||||
UpdateUserSettings,
|
||||
LspExtExpandMacro,
|
||||
LspExtOpenDocs,
|
||||
LspExtRunnables,
|
||||
|
||||
@@ -280,7 +280,8 @@ async fn test_remote_settings(cx: &mut TestAppContext, server_cx: &mut TestAppCo
|
||||
AllLanguageSettings::get_global(cx)
|
||||
.language(None, Some(&"Rust".into()), cx)
|
||||
.language_servers,
|
||||
["..."] // local settings are ignored
|
||||
["from-local-settings"],
|
||||
"User language settings should be synchronized with the server settings"
|
||||
)
|
||||
});
|
||||
|
||||
@@ -300,7 +301,8 @@ async fn test_remote_settings(cx: &mut TestAppContext, server_cx: &mut TestAppCo
|
||||
AllLanguageSettings::get_global(cx)
|
||||
.language(None, Some(&"Rust".into()), cx)
|
||||
.language_servers,
|
||||
["from-server-settings".to_string()]
|
||||
["from-server-settings".to_string()],
|
||||
"Server language settings should take precedence over the user settings"
|
||||
)
|
||||
});
|
||||
|
||||
|
||||
@@ -918,29 +918,33 @@ fn initialize_settings(
|
||||
});
|
||||
|
||||
let (mut tx, rx) = watch::channel(None);
|
||||
let mut node_settings = None;
|
||||
cx.observe_global::<SettingsStore>(move |cx| {
|
||||
let settings = &ProjectSettings::get_global(cx).node;
|
||||
log::info!("Got new node settings: {:?}", settings);
|
||||
let options = NodeBinaryOptions {
|
||||
allow_path_lookup: !settings.ignore_system_version,
|
||||
// TODO: Implement this setting
|
||||
allow_binary_download: true,
|
||||
use_paths: settings.path.as_ref().map(|node_path| {
|
||||
let node_path = PathBuf::from(shellexpand::tilde(node_path).as_ref());
|
||||
let npm_path = settings
|
||||
.npm_path
|
||||
.as_ref()
|
||||
.map(|path| PathBuf::from(shellexpand::tilde(&path).as_ref()));
|
||||
(
|
||||
node_path.clone(),
|
||||
npm_path.unwrap_or_else(|| {
|
||||
let base_path = PathBuf::new();
|
||||
node_path.parent().unwrap_or(&base_path).join("npm")
|
||||
}),
|
||||
)
|
||||
}),
|
||||
};
|
||||
tx.send(Some(options)).log_err();
|
||||
let new_node_settings = &ProjectSettings::get_global(cx).node;
|
||||
if Some(new_node_settings) != node_settings.as_ref() {
|
||||
log::info!("Got new node settings: {new_node_settings:?}");
|
||||
let options = NodeBinaryOptions {
|
||||
allow_path_lookup: !new_node_settings.ignore_system_version,
|
||||
// TODO: Implement this setting
|
||||
allow_binary_download: true,
|
||||
use_paths: new_node_settings.path.as_ref().map(|node_path| {
|
||||
let node_path = PathBuf::from(shellexpand::tilde(node_path).as_ref());
|
||||
let npm_path = new_node_settings
|
||||
.npm_path
|
||||
.as_ref()
|
||||
.map(|path| PathBuf::from(shellexpand::tilde(&path).as_ref()));
|
||||
(
|
||||
node_path.clone(),
|
||||
npm_path.unwrap_or_else(|| {
|
||||
let base_path = PathBuf::new();
|
||||
node_path.parent().unwrap_or(&base_path).join("npm")
|
||||
}),
|
||||
)
|
||||
}),
|
||||
};
|
||||
node_settings = Some(new_node_settings.clone());
|
||||
tx.send(Some(options)).ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
|
||||
@@ -467,6 +467,13 @@ impl SettingsStore {
|
||||
&self.raw_user_settings
|
||||
}
|
||||
|
||||
/// Replaces current settings with the values from the given JSON.
|
||||
pub fn set_raw_user_settings(&mut self, new_settings: Value, cx: &mut App) -> Result<()> {
|
||||
self.raw_user_settings = new_settings;
|
||||
self.recompute_values(None, cx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the configured settings profile names.
|
||||
pub fn configured_settings_profiles(&self) -> impl Iterator<Item = &str> {
|
||||
self.raw_user_settings
|
||||
@@ -525,20 +532,6 @@ impl SettingsStore {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn load_global_settings(fs: &Arc<dyn Fs>) -> Result<String> {
|
||||
match fs.load(paths::global_settings_file()).await {
|
||||
result @ Ok(_) => result,
|
||||
Err(err) => {
|
||||
if let Some(e) = err.downcast_ref::<std::io::Error>()
|
||||
&& e.kind() == std::io::ErrorKind::NotFound
|
||||
{
|
||||
return Ok("{}".to_string());
|
||||
}
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_settings_file_inner(
|
||||
&self,
|
||||
fs: Arc<dyn Fs>,
|
||||
|
||||
Reference in New Issue
Block a user