Add settings to remote servers, use XDG paths on remote, and enable node LSPs (#19176)
Supersedes https://github.com/zed-industries/zed/pull/19166 TODO: - [x] Update basic zed paths - [x] update create_state_directory - [x] Use this with `NodeRuntime` - [x] Add server settings - [x] Add an 'open server settings command' - [x] Make sure it all works Release Notes: - Updated the actions `zed::OpenLocalSettings` and `zed::OpenLocalTasks` to `zed::OpenProjectSettings` and `zed::OpenProjectTasks`. --------- Co-authored-by: Conrad <conrad@zed.dev> Co-authored-by: Richard <richard@zed.dev>
This commit is contained in:
parent
1dda039f38
commit
f944ebc4cb
44 changed files with 804 additions and 218 deletions
|
@ -22,6 +22,7 @@ debug-embed = ["dep:rust-embed"]
|
|||
test-support = ["fs/test-support"]
|
||||
|
||||
[dependencies]
|
||||
async-watch.workspace = true
|
||||
anyhow.workspace = true
|
||||
backtrace = "0.3"
|
||||
clap.workspace = true
|
||||
|
@ -30,13 +31,16 @@ env_logger.workspace = true
|
|||
fs.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
http_client.workspace = true
|
||||
language.workspace = true
|
||||
languages.workspace = true
|
||||
log.workspace = true
|
||||
lsp.workspace = true
|
||||
node_runtime.workspace = true
|
||||
project.workspace = true
|
||||
paths = { workspace = true }
|
||||
remote.workspace = true
|
||||
reqwest_client.workspace = true
|
||||
rpc.workspace = true
|
||||
rust-embed = { workspace = true, optional = true, features = ["debug-embed"] }
|
||||
serde.workspace = true
|
||||
|
@ -66,4 +70,4 @@ cargo_toml.workspace = true
|
|||
toml.workspace = true
|
||||
|
||||
[package.metadata.cargo-machete]
|
||||
ignored = ["rust-embed"]
|
||||
ignored = ["rust-embed", "paths"]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use fs::Fs;
|
||||
use gpui::{AppContext, AsyncAppContext, Context, Model, ModelContext};
|
||||
use http_client::HttpClient;
|
||||
use language::{proto::serialize_operation, Buffer, BufferEvent, LanguageRegistry};
|
||||
use node_runtime::NodeRuntime;
|
||||
use project::{
|
||||
|
@ -16,6 +17,8 @@ use rpc::{
|
|||
proto::{self, SSH_PEER_ID, SSH_PROJECT_ID},
|
||||
AnyProtoClient, TypedEnvelope,
|
||||
};
|
||||
|
||||
use settings::initial_server_settings_content;
|
||||
use smol::stream::StreamExt;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
|
@ -36,6 +39,14 @@ pub struct HeadlessProject {
|
|||
pub languages: Arc<LanguageRegistry>,
|
||||
}
|
||||
|
||||
pub struct HeadlessAppState {
|
||||
pub session: Arc<ChannelClient>,
|
||||
pub fs: Arc<dyn Fs>,
|
||||
pub http_client: Arc<dyn HttpClient>,
|
||||
pub node_runtime: NodeRuntime,
|
||||
pub languages: Arc<LanguageRegistry>,
|
||||
}
|
||||
|
||||
impl HeadlessProject {
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
settings::init(cx);
|
||||
|
@ -43,11 +54,16 @@ impl HeadlessProject {
|
|||
project::Project::init_settings(cx);
|
||||
}
|
||||
|
||||
pub fn new(session: Arc<ChannelClient>, fs: Arc<dyn Fs>, cx: &mut ModelContext<Self>) -> Self {
|
||||
let languages = Arc::new(LanguageRegistry::new(cx.background_executor().clone()));
|
||||
|
||||
let node_runtime = NodeRuntime::unavailable();
|
||||
|
||||
pub fn new(
|
||||
HeadlessAppState {
|
||||
session,
|
||||
fs,
|
||||
http_client,
|
||||
node_runtime,
|
||||
languages,
|
||||
}: HeadlessAppState,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Self {
|
||||
languages::init(languages.clone(), node_runtime.clone(), cx);
|
||||
|
||||
let worktree_store = cx.new_model(|cx| {
|
||||
|
@ -99,7 +115,7 @@ impl HeadlessProject {
|
|||
prettier_store.clone(),
|
||||
environment,
|
||||
languages.clone(),
|
||||
None,
|
||||
http_client,
|
||||
fs.clone(),
|
||||
cx,
|
||||
);
|
||||
|
@ -139,6 +155,7 @@ impl HeadlessProject {
|
|||
|
||||
client.add_model_request_handler(Self::handle_open_buffer_by_path);
|
||||
client.add_model_request_handler(Self::handle_find_search_candidates);
|
||||
client.add_model_request_handler(Self::handle_open_server_settings);
|
||||
|
||||
client.add_model_request_handler(BufferStore::handle_update_buffer);
|
||||
client.add_model_message_handler(BufferStore::handle_close_buffer);
|
||||
|
@ -203,6 +220,15 @@ impl HeadlessProject {
|
|||
})
|
||||
.log_err();
|
||||
}
|
||||
LspStoreEvent::Notification(message) => {
|
||||
self.session
|
||||
.send(proto::Toast {
|
||||
project_id: SSH_PROJECT_ID,
|
||||
notification_id: "lsp".to_string(),
|
||||
message: message.clone(),
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
LspStoreEvent::LanguageServerLog(language_server_id, log_type, message) => {
|
||||
self.session
|
||||
.send(proto::LanguageServerLog {
|
||||
|
@ -336,6 +362,59 @@ impl HeadlessProject {
|
|||
})
|
||||
}
|
||||
|
||||
pub async fn handle_open_server_settings(
|
||||
this: Model<Self>,
|
||||
_: TypedEnvelope<proto::OpenServerSettings>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<proto::OpenBufferResponse> {
|
||||
let settings_path = paths::settings_file();
|
||||
let (worktree, path) = this
|
||||
.update(&mut cx, |this, cx| {
|
||||
this.worktree_store.update(cx, |worktree_store, cx| {
|
||||
worktree_store.find_or_create_worktree(settings_path, false, cx)
|
||||
})
|
||||
})?
|
||||
.await?;
|
||||
|
||||
let (buffer, buffer_store) = this.update(&mut cx, |this, cx| {
|
||||
let buffer = this.buffer_store.update(cx, |buffer_store, cx| {
|
||||
buffer_store.open_buffer(
|
||||
ProjectPath {
|
||||
worktree_id: worktree.read(cx).id(),
|
||||
path: path.into(),
|
||||
},
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
(buffer, this.buffer_store.clone())
|
||||
})?;
|
||||
|
||||
let buffer = buffer.await?;
|
||||
|
||||
let buffer_id = cx.update(|cx| {
|
||||
if buffer.read(cx).is_empty() {
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit([(0..0, initial_server_settings_content())], None, cx)
|
||||
});
|
||||
}
|
||||
|
||||
let buffer_id = buffer.read_with(cx, |b, _| b.remote_id());
|
||||
|
||||
buffer_store.update(cx, |buffer_store, cx| {
|
||||
buffer_store
|
||||
.create_buffer_for_peer(&buffer, SSH_PEER_ID, cx)
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
|
||||
buffer_id
|
||||
})?;
|
||||
|
||||
Ok(proto::OpenBufferResponse {
|
||||
buffer_id: buffer_id.to_proto(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn handle_find_search_candidates(
|
||||
this: Model<Self>,
|
||||
envelope: TypedEnvelope<proto::FindSearchCandidates>,
|
||||
|
|
|
@ -3,7 +3,7 @@ use client::{Client, UserStore};
|
|||
use clock::FakeSystemClock;
|
||||
use fs::{FakeFs, Fs};
|
||||
use gpui::{Context, Model, TestAppContext};
|
||||
use http_client::FakeHttpClient;
|
||||
use http_client::{BlockedHttpClient, FakeHttpClient};
|
||||
use language::{
|
||||
language_settings::{all_language_settings, AllLanguageSettings},
|
||||
Buffer, FakeLspAdapter, LanguageConfig, LanguageMatcher, LanguageRegistry, LanguageServerName,
|
||||
|
@ -17,7 +17,7 @@ use project::{
|
|||
};
|
||||
use remote::SshRemoteClient;
|
||||
use serde_json::json;
|
||||
use settings::{Settings, SettingsLocation, SettingsStore};
|
||||
use settings::{initial_server_settings_content, Settings, SettingsLocation, SettingsStore};
|
||||
use smol::stream::StreamExt;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
|
@ -197,7 +197,7 @@ async fn test_remote_settings(cx: &mut TestAppContext, server_cx: &mut TestAppCo
|
|||
|
||||
cx.update_global(|settings_store: &mut SettingsStore, cx| {
|
||||
settings_store.set_user_settings(
|
||||
r#"{"languages":{"Rust":{"language_servers":["custom-rust-analyzer"]}}}"#,
|
||||
r#"{"languages":{"Rust":{"language_servers":["from-local-settings"]}}}"#,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
|
@ -210,7 +210,27 @@ async fn test_remote_settings(cx: &mut TestAppContext, server_cx: &mut TestAppCo
|
|||
AllLanguageSettings::get_global(cx)
|
||||
.language(Some(&"Rust".into()))
|
||||
.language_servers,
|
||||
["custom-rust-analyzer".to_string()]
|
||||
["from-local-settings".to_string()]
|
||||
)
|
||||
});
|
||||
|
||||
server_cx
|
||||
.update_global(|settings_store: &mut SettingsStore, cx| {
|
||||
settings_store.set_server_settings(
|
||||
r#"{"languages":{"Rust":{"language_servers":["from-server-settings"]}}}"#,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
cx.run_until_parked();
|
||||
|
||||
server_cx.read(|cx| {
|
||||
assert_eq!(
|
||||
AllLanguageSettings::get_global(cx)
|
||||
.language(Some(&"Rust".into()))
|
||||
.language_servers,
|
||||
["from-server-settings".to_string()]
|
||||
)
|
||||
});
|
||||
|
||||
|
@ -606,6 +626,21 @@ async fn test_adding_then_removing_then_adding_worktrees(
|
|||
})
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_open_server_settings(cx: &mut TestAppContext, server_cx: &mut TestAppContext) {
|
||||
let (project, _headless, _fs) = init_test(cx, server_cx).await;
|
||||
let buffer = project.update(cx, |project, cx| project.open_server_settings(cx));
|
||||
cx.executor().run_until_parked();
|
||||
let buffer = buffer.await.unwrap();
|
||||
|
||||
cx.update(|cx| {
|
||||
assert_eq!(
|
||||
buffer.read(cx).text(),
|
||||
initial_server_settings_content().to_string()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn init_logger() {
|
||||
if std::env::var("RUST_LOG").is_ok() {
|
||||
env_logger::try_init().ok();
|
||||
|
@ -642,8 +677,23 @@ async fn init_test(
|
|||
);
|
||||
|
||||
server_cx.update(HeadlessProject::init);
|
||||
let headless =
|
||||
server_cx.new_model(|cx| HeadlessProject::new(ssh_server_client, fs.clone(), cx));
|
||||
let http_client = Arc::new(BlockedHttpClient);
|
||||
let node_runtime = NodeRuntime::unavailable();
|
||||
let languages = Arc::new(LanguageRegistry::new(cx.executor()));
|
||||
let headless = server_cx.new_model(|cx| {
|
||||
client::init_settings(cx);
|
||||
|
||||
HeadlessProject::new(
|
||||
crate::HeadlessAppState {
|
||||
session: ssh_server_client,
|
||||
fs: fs.clone(),
|
||||
http_client,
|
||||
node_runtime,
|
||||
languages,
|
||||
},
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let project = build_project(ssh_remote_client, cx);
|
||||
|
||||
project
|
||||
|
|
|
@ -6,4 +6,4 @@ pub mod unix;
|
|||
#[cfg(test)]
|
||||
mod remote_editing_tests;
|
||||
|
||||
pub use headless_project::HeadlessProject;
|
||||
pub use headless_project::{HeadlessAppState, HeadlessProject};
|
||||
|
|
|
@ -1,27 +1,37 @@
|
|||
use crate::headless_project::HeadlessAppState;
|
||||
use crate::HeadlessProject;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use fs::RealFs;
|
||||
use client::ProxySettings;
|
||||
use fs::{Fs, RealFs};
|
||||
use futures::channel::mpsc;
|
||||
use futures::{select, select_biased, AsyncRead, AsyncWrite, AsyncWriteExt, FutureExt, SinkExt};
|
||||
use gpui::{AppContext, Context as _};
|
||||
use gpui::{AppContext, Context as _, ModelContext, UpdateGlobal as _};
|
||||
use http_client::{read_proxy_from_env, Uri};
|
||||
use language::LanguageRegistry;
|
||||
use node_runtime::{NodeBinaryOptions, NodeRuntime};
|
||||
use paths::logs_dir;
|
||||
use project::project_settings::ProjectSettings;
|
||||
use remote::proxy::ProxyLaunchError;
|
||||
use remote::ssh_session::ChannelClient;
|
||||
use remote::{
|
||||
json_log::LogRecord,
|
||||
protocol::{read_message, write_message},
|
||||
};
|
||||
use rpc::proto::Envelope;
|
||||
use reqwest_client::ReqwestClient;
|
||||
use rpc::proto::{self, Envelope, SSH_PROJECT_ID};
|
||||
use settings::{watch_config_file, Settings, SettingsStore};
|
||||
use smol::channel::{Receiver, Sender};
|
||||
use smol::io::AsyncReadExt;
|
||||
|
||||
use smol::Async;
|
||||
use smol::{net::unix::UnixListener, stream::StreamExt as _};
|
||||
use std::{
|
||||
env,
|
||||
io::Write,
|
||||
mem,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::ResultExt;
|
||||
|
||||
fn init_logging_proxy() {
|
||||
env_logger::builder()
|
||||
|
@ -266,6 +276,22 @@ fn start_server(
|
|||
ChannelClient::new(incoming_rx, outgoing_tx, cx)
|
||||
}
|
||||
|
||||
fn init_paths() -> anyhow::Result<()> {
|
||||
for path in [
|
||||
paths::config_dir(),
|
||||
paths::extensions_dir(),
|
||||
paths::languages_dir(),
|
||||
paths::logs_dir(),
|
||||
paths::temp_dir(),
|
||||
]
|
||||
.iter()
|
||||
{
|
||||
std::fs::create_dir_all(path)
|
||||
.map_err(|e| anyhow!("Could not create directory {:?}: {}", path, e))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn execute_run(
|
||||
log_file: PathBuf,
|
||||
pid_file: PathBuf,
|
||||
|
@ -275,6 +301,7 @@ pub fn execute_run(
|
|||
) -> Result<()> {
|
||||
let log_rx = init_logging_server(log_file)?;
|
||||
init_panic_hook();
|
||||
init_paths()?;
|
||||
|
||||
log::info!(
|
||||
"starting up. pid_file: {:?}, stdin_socket: {:?}, stdout_socket: {:?}, stderr_socket: {:?}",
|
||||
|
@ -297,8 +324,43 @@ pub fn execute_run(
|
|||
log::info!("gpui app started, initializing server");
|
||||
let session = start_server(listeners, log_rx, cx);
|
||||
|
||||
client::init_settings(cx);
|
||||
|
||||
let project = cx.new_model(|cx| {
|
||||
HeadlessProject::new(session, Arc::new(RealFs::new(Default::default(), None)), cx)
|
||||
let fs = Arc::new(RealFs::new(Default::default(), None));
|
||||
let node_settings_rx = initialize_settings(session.clone(), fs.clone(), cx);
|
||||
|
||||
let proxy_url = read_proxy_settings(cx);
|
||||
|
||||
let http_client = Arc::new(
|
||||
ReqwestClient::proxy_and_user_agent(
|
||||
proxy_url,
|
||||
&format!(
|
||||
"Zed-Server/{} ({}; {})",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
std::env::consts::OS,
|
||||
std::env::consts::ARCH
|
||||
),
|
||||
)
|
||||
.expect("Could not start HTTP client"),
|
||||
);
|
||||
|
||||
let node_runtime = NodeRuntime::new(http_client.clone(), node_settings_rx);
|
||||
|
||||
let mut languages = LanguageRegistry::new(cx.background_executor().clone());
|
||||
languages.set_language_server_download_dir(paths::languages_dir().clone());
|
||||
let languages = Arc::new(languages);
|
||||
|
||||
HeadlessProject::new(
|
||||
HeadlessAppState {
|
||||
session,
|
||||
fs,
|
||||
http_client,
|
||||
node_runtime,
|
||||
languages,
|
||||
},
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
mem::forget(project);
|
||||
|
@ -318,13 +380,15 @@ struct ServerPaths {
|
|||
|
||||
impl ServerPaths {
|
||||
fn new(identifier: &str) -> Result<Self> {
|
||||
let project_dir = create_state_directory(identifier)?;
|
||||
let server_dir = paths::remote_server_state_dir().join(identifier);
|
||||
std::fs::create_dir_all(&server_dir)?;
|
||||
std::fs::create_dir_all(&logs_dir())?;
|
||||
|
||||
let pid_file = project_dir.join("server.pid");
|
||||
let stdin_socket = project_dir.join("stdin.sock");
|
||||
let stdout_socket = project_dir.join("stdout.sock");
|
||||
let stderr_socket = project_dir.join("stderr.sock");
|
||||
let log_file = project_dir.join("server.log");
|
||||
let pid_file = server_dir.join("server.pid");
|
||||
let stdin_socket = server_dir.join("stdin.sock");
|
||||
let stdout_socket = server_dir.join("stdout.sock");
|
||||
let stderr_socket = server_dir.join("stderr.sock");
|
||||
let log_file = logs_dir().join(format!("server-{}.log", identifier));
|
||||
|
||||
Ok(Self {
|
||||
pid_file,
|
||||
|
@ -358,7 +422,7 @@ pub fn execute_proxy(identifier: String, is_reconnecting: bool) -> Result<()> {
|
|||
}
|
||||
|
||||
spawn_server(&server_paths)?;
|
||||
}
|
||||
};
|
||||
|
||||
let stdin_task = smol::spawn(async move {
|
||||
let stdin = Async::new(std::io::stdin())?;
|
||||
|
@ -409,19 +473,6 @@ pub fn execute_proxy(identifier: String, is_reconnecting: bool) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn create_state_directory(identifier: &str) -> Result<PathBuf> {
|
||||
let home_dir = env::var("HOME").unwrap_or_else(|_| ".".to_string());
|
||||
let server_dir = PathBuf::from(home_dir)
|
||||
.join(".local")
|
||||
.join("state")
|
||||
.join("zed-remote-server")
|
||||
.join(identifier);
|
||||
|
||||
std::fs::create_dir_all(&server_dir)?;
|
||||
|
||||
Ok(server_dir)
|
||||
}
|
||||
|
||||
fn kill_running_server(pid: u32, paths: &ServerPaths) -> Result<()> {
|
||||
log::info!("killing existing server with PID {}", pid);
|
||||
std::process::Command::new("kill")
|
||||
|
@ -453,7 +504,7 @@ fn spawn_server(paths: &ServerPaths) -> Result<()> {
|
|||
}
|
||||
|
||||
let binary_name = std::env::current_exe()?;
|
||||
let server_process = std::process::Command::new(binary_name)
|
||||
let server_process = smol::process::Command::new(binary_name)
|
||||
.arg("run")
|
||||
.arg("--log-file")
|
||||
.arg(&paths.log_file)
|
||||
|
@ -484,6 +535,7 @@ fn spawn_server(paths: &ServerPaths) -> Result<()> {
|
|||
"server ready to accept connections. total time waited: {:?}",
|
||||
total_time_waited
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -556,3 +608,118 @@ async fn write_size_prefixed_buffer<S: AsyncWrite + Unpin>(
|
|||
stream.write_all(buffer).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn initialize_settings(
|
||||
session: Arc<ChannelClient>,
|
||||
fs: Arc<dyn Fs>,
|
||||
cx: &mut AppContext,
|
||||
) -> async_watch::Receiver<Option<NodeBinaryOptions>> {
|
||||
let user_settings_file_rx = watch_config_file(
|
||||
&cx.background_executor(),
|
||||
fs,
|
||||
paths::settings_file().clone(),
|
||||
);
|
||||
|
||||
handle_settings_file_changes(user_settings_file_rx, cx, {
|
||||
let session = session.clone();
|
||||
move |err, _cx| {
|
||||
if let Some(e) = err {
|
||||
log::info!("Server settings failed to change: {}", e);
|
||||
|
||||
session
|
||||
.send(proto::Toast {
|
||||
project_id: SSH_PROJECT_ID,
|
||||
notification_id: "server-settings-failed".to_string(),
|
||||
message: format!(
|
||||
"Error in settings on remote host {:?}: {}",
|
||||
paths::settings_file(),
|
||||
e
|
||||
),
|
||||
})
|
||||
.log_err();
|
||||
} else {
|
||||
session
|
||||
.send(proto::HideToast {
|
||||
project_id: SSH_PROJECT_ID,
|
||||
notification_id: "server-settings-failed".to_string(),
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let (tx, rx) = async_watch::channel(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.unwrap_or_default(),
|
||||
// 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();
|
||||
})
|
||||
.detach();
|
||||
|
||||
rx
|
||||
}
|
||||
|
||||
pub fn handle_settings_file_changes(
|
||||
mut server_settings_file: mpsc::UnboundedReceiver<String>,
|
||||
cx: &mut AppContext,
|
||||
settings_changed: impl Fn(Option<anyhow::Error>, &mut AppContext) + 'static,
|
||||
) {
|
||||
let server_settings_content = cx
|
||||
.background_executor()
|
||||
.block(server_settings_file.next())
|
||||
.unwrap();
|
||||
SettingsStore::update_global(cx, |store, cx| {
|
||||
store
|
||||
.set_server_settings(&server_settings_content, cx)
|
||||
.log_err();
|
||||
});
|
||||
cx.spawn(move |cx| async move {
|
||||
while let Some(server_settings_content) = server_settings_file.next().await {
|
||||
let result = cx.update_global(|store: &mut SettingsStore, cx| {
|
||||
let result = store.set_server_settings(&server_settings_content, cx);
|
||||
if let Err(err) = &result {
|
||||
log::error!("Failed to load server settings: {err}");
|
||||
}
|
||||
settings_changed(result.err(), cx);
|
||||
cx.refresh();
|
||||
});
|
||||
if result.is_err() {
|
||||
break; // App dropped
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn read_proxy_settings(cx: &mut ModelContext<'_, HeadlessProject>) -> Option<Uri> {
|
||||
let proxy_str = ProxySettings::get_global(cx).proxy.to_owned();
|
||||
let proxy_url = proxy_str
|
||||
.as_ref()
|
||||
.and_then(|input: &String| {
|
||||
input
|
||||
.parse::<Uri>()
|
||||
.inspect_err(|e| log::error!("Error parsing proxy settings: {}", e))
|
||||
.ok()
|
||||
})
|
||||
.or_else(read_proxy_from_env);
|
||||
proxy_url
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue