Detailed changes
@@ -44,7 +44,7 @@ use syntax_map::SyntaxSnapshot;
use theme::{SyntaxTheme, Theme};
use tree_sitter::{self, Query};
use unicase::UniCase;
-use util::{ResultExt, TryFutureExt as _, UnwrapFuture};
+use util::{merge_json_value_into, ResultExt, TryFutureExt as _, UnwrapFuture};
#[cfg(any(test, feature = "test-support"))]
use futures::channel::mpsc;
@@ -208,6 +208,13 @@ pub trait LspAdapter: 'static + Send + Sync {
None
}
+ fn workspace_configuration(
+ &self,
+ _: &mut MutableAppContext,
+ ) -> Option<BoxFuture<'static, Value>> {
+ None
+ }
+
async fn disk_based_diagnostic_sources(&self) -> Vec<String> {
Default::default()
}
@@ -541,6 +548,26 @@ impl LanguageRegistry {
result
}
+ pub fn workspace_configuration(&self, cx: &mut MutableAppContext) -> Task<serde_json::Value> {
+ let mut language_configs = Vec::new();
+ for language in self.available_languages.read().iter() {
+ if let Some(adapter) = language.lsp_adapter.as_ref() {
+ if let Some(language_config) = adapter.workspace_configuration(cx) {
+ language_configs.push(language_config);
+ }
+ }
+ }
+
+ cx.background().spawn(async move {
+ let mut config = serde_json::json!({});
+ let language_configs = futures::future::join_all(language_configs).await;
+ for language_config in language_configs {
+ merge_json_value_into(language_config, &mut config);
+ }
+ config
+ })
+ }
+
pub fn add(&self, language: Arc<Language>) {
if let Some(theme) = self.theme.read().clone() {
language.set_theme(&theme.editor.syntax);
@@ -64,7 +64,7 @@ use std::{
};
use terminals::Terminals;
-use util::{debug_panic, defer, post_inc, ResultExt, TryFutureExt as _};
+use util::{debug_panic, defer, merge_json_value_into, post_inc, ResultExt, TryFutureExt as _};
pub use fs::*;
pub use worktree::*;
@@ -125,6 +125,7 @@ pub struct Project {
buffers_being_formatted: HashSet<usize>,
nonce: u128,
_maintain_buffer_languages: Task<()>,
+ _maintain_workspace_config: Task<()>,
terminals: Terminals,
}
@@ -428,6 +429,7 @@ impl Project {
client_subscriptions: Vec::new(),
_subscriptions: vec![cx.observe_global::<Settings, _>(Self::on_settings_changed)],
_maintain_buffer_languages: Self::maintain_buffer_languages(&languages, cx),
+ _maintain_workspace_config: Self::maintain_workspace_config(languages.clone(), cx),
active_entry: None,
languages,
client,
@@ -486,6 +488,7 @@ impl Project {
active_entry: None,
collaborators: Default::default(),
_maintain_buffer_languages: Self::maintain_buffer_languages(&languages, cx),
+ _maintain_workspace_config: Self::maintain_workspace_config(languages.clone(), cx),
languages,
user_store: user_store.clone(),
fs,
@@ -1836,6 +1839,46 @@ impl Project {
})
}
+ fn maintain_workspace_config(
+ languages: Arc<LanguageRegistry>,
+ cx: &mut ModelContext<Project>,
+ ) -> Task<()> {
+ let mut languages_changed = languages.subscribe();
+ let (mut settings_changed_tx, mut settings_changed_rx) = watch::channel();
+ let settings_observation = cx.observe_global::<Settings, _>(move |_, _| {
+ *settings_changed_tx.borrow_mut() = ();
+ });
+ cx.spawn_weak(|this, mut cx| async move {
+ loop {
+ futures::select_biased! {
+ _ = languages_changed.next().fuse() => {},
+ _ = settings_changed_rx.next().fuse() => {}
+ }
+
+ let workspace_config = cx.update(|cx| languages.workspace_configuration(cx)).await;
+ if let Some(this) = this.upgrade(&cx) {
+ this.read_with(&cx, |this, _| {
+ for server_state in this.language_servers.values() {
+ if let LanguageServerState::Running { server, .. } = server_state {
+ server
+ .notify::<lsp::notification::DidChangeConfiguration>(
+ lsp::DidChangeConfigurationParams {
+ settings: workspace_config.clone(),
+ },
+ )
+ .ok();
+ }
+ }
+ })
+ } else {
+ break;
+ }
+ }
+
+ drop(settings_observation);
+ })
+ }
+
fn detect_language_for_buffer(
&mut self,
buffer: &ModelHandle<Buffer>,
@@ -1875,24 +1918,6 @@ impl Project {
}
}
- fn merge_json_value_into(source: serde_json::Value, target: &mut serde_json::Value) {
- use serde_json::Value;
-
- match (source, target) {
- (Value::Object(source), Value::Object(target)) => {
- for (key, value) in source {
- if let Some(target) = target.get_mut(&key) {
- Self::merge_json_value_into(value, target);
- } else {
- target.insert(key.clone(), value);
- }
- }
- }
-
- (source, target) => *target = source,
- }
- }
-
fn start_language_server(
&mut self,
worktree_id: WorktreeId,
@@ -1920,17 +1945,16 @@ impl Project {
let override_options = lsp.map(|s| s.initialization_options.clone()).flatten();
match (&mut initialization_options, override_options) {
(Some(initialization_options), Some(override_options)) => {
- Self::merge_json_value_into(override_options, initialization_options);
+ merge_json_value_into(override_options, initialization_options);
}
-
(None, override_options) => initialization_options = override_options,
-
_ => {}
}
self.language_server_ids
.entry(key.clone())
.or_insert_with(|| {
+ let languages = self.languages.clone();
let server_id = post_inc(&mut self.next_language_server_id);
let language_server = self.languages.start_language_server(
server_id,
@@ -1977,23 +2001,24 @@ impl Project {
language_server
.on_request::<lsp::request::WorkspaceConfiguration, _, _>({
- let settings = this.read_with(&cx, |this, _| {
- this.language_server_settings.clone()
- });
- move |params, _| {
- let settings = settings.lock().clone();
+ move |params, mut cx| {
+ let languages = languages.clone();
async move {
+ let workspace_config = cx
+ .update(|cx| languages.workspace_configuration(cx))
+ .await;
+
Ok(params
.items
.into_iter()
.map(|item| {
if let Some(section) = &item.section {
- settings
+ workspace_config
.get(section)
.cloned()
.unwrap_or(serde_json::Value::Null)
} else {
- settings.clone()
+ workspace_config.clone()
}
})
.collect())
@@ -2539,21 +2564,6 @@ impl Project {
}
}
- pub fn set_language_server_settings(&mut self, settings: serde_json::Value) {
- for server_state in self.language_servers.values() {
- if let LanguageServerState::Running { server, .. } = server_state {
- server
- .notify::<lsp::notification::DidChangeConfiguration>(
- lsp::DidChangeConfigurationParams {
- settings: settings.clone(),
- },
- )
- .ok();
- }
- }
- *self.language_server_settings.lock() = settings;
- }
-
pub fn language_server_statuses(
&self,
) -> impl DoubleEndedIterator<Item = &LanguageServerStatus> {
@@ -9,7 +9,7 @@ path = "src/util.rs"
doctest = false
[features]
-test-support = ["serde_json", "tempdir", "git2"]
+test-support = ["tempdir", "git2"]
[dependencies]
anyhow = "1.0.38"
@@ -19,11 +19,10 @@ log = { version = "0.4.16", features = ["kv_unstable_serde"] }
lazy_static = "1.4.0"
rand = { workspace = true }
tempdir = { version = "0.3.7", optional = true }
-serde_json = { version = "1.0", features = ["preserve_order"], optional = true }
+serde_json = { version = "1.0", features = ["preserve_order"] }
git2 = { version = "0.15", default-features = false, optional = true }
dirs = "3.0"
[dev-dependencies]
tempdir = { version = "0.3.7" }
-serde_json = { version = "1.0", features = ["preserve_order"] }
git2 = { version = "0.15", default-features = false }
@@ -83,6 +83,24 @@ where
}
}
+pub fn merge_json_value_into(source: serde_json::Value, target: &mut serde_json::Value) {
+ use serde_json::Value;
+
+ match (source, target) {
+ (Value::Object(source), Value::Object(target)) => {
+ for (key, value) in source {
+ if let Some(target) = target.get_mut(&key) {
+ merge_json_value_into(value, target);
+ } else {
+ target.insert(key.clone(), value);
+ }
+ }
+ }
+
+ (source, target) => *target = source,
+ }
+}
+
pub trait ResultExt {
type Ok;
@@ -2,6 +2,7 @@ use anyhow::Context;
pub use language::*;
use rust_embed::RustEmbed;
use std::{borrow::Cow, str, sync::Arc};
+use theme::ThemeRegistry;
mod c;
mod elixir;
@@ -31,7 +32,7 @@ mod yaml;
#[exclude = "*.rs"]
struct LanguageDir;
-pub fn init(languages: Arc<LanguageRegistry>) {
+pub fn init(languages: Arc<LanguageRegistry>, themes: Arc<ThemeRegistry>) {
for (name, grammar, lsp_adapter) in [
(
"c",
@@ -61,7 +62,10 @@ pub fn init(languages: Arc<LanguageRegistry>) {
(
"json",
tree_sitter_json::language(),
- Some(Box::new(json::JsonLspAdapter)),
+ Some(Box::new(json::JsonLspAdapter::new(
+ languages.clone(),
+ themes.clone(),
+ ))),
),
(
"markdown",
@@ -4,14 +4,32 @@ use async_compression::futures::bufread::GzipDecoder;
use async_trait::async_trait;
use client::http::HttpClient;
use collections::HashMap;
-use futures::{io::BufReader, StreamExt};
-use language::{LanguageServerName, LspAdapter};
+use futures::{future::BoxFuture, io::BufReader, FutureExt, StreamExt};
+use gpui::MutableAppContext;
+use language::{LanguageRegistry, LanguageServerName, LspAdapter};
use serde_json::json;
+use settings::{keymap_file_json_schema, settings_file_json_schema};
use smol::fs::{self, File};
-use std::{any::Any, env::consts, path::PathBuf, sync::Arc};
-use util::ResultExt;
+use std::{
+ any::Any,
+ env::consts,
+ future,
+ path::{Path, PathBuf},
+ sync::Arc,
+};
+use theme::ThemeRegistry;
+use util::{paths, ResultExt, StaffMode};
-pub struct JsonLspAdapter;
+pub struct JsonLspAdapter {
+ languages: Arc<LanguageRegistry>,
+ themes: Arc<ThemeRegistry>,
+}
+
+impl JsonLspAdapter {
+ pub fn new(languages: Arc<LanguageRegistry>, themes: Arc<ThemeRegistry>) -> Self {
+ Self { languages, themes }
+ }
+}
#[async_trait]
impl LspAdapter for JsonLspAdapter {
@@ -102,7 +120,45 @@ impl LspAdapter for JsonLspAdapter {
}))
}
+ fn workspace_configuration(
+ &self,
+ cx: &mut MutableAppContext,
+ ) -> Option<BoxFuture<'static, serde_json::Value>> {
+ let action_names = cx.all_action_names().collect::<Vec<_>>();
+ let theme_names = self
+ .themes
+ .list(**cx.default_global::<StaffMode>())
+ .map(|meta| meta.name)
+ .collect();
+ let language_names = self.languages.language_names();
+ Some(
+ future::ready(serde_json::json!({
+ "json": {
+ "format": {
+ "enable": true,
+ },
+ "schemas": [
+ {
+ "fileMatch": [schema_file_match(&paths::SETTINGS)],
+ "schema": settings_file_json_schema(theme_names, &language_names),
+ },
+ {
+ "fileMatch": [schema_file_match(&paths::KEYMAP)],
+ "schema": keymap_file_json_schema(&action_names),
+ }
+ ]
+ }
+ }))
+ .boxed(),
+ )
+ }
+
async fn language_ids(&self) -> HashMap<String, String> {
[("JSON".into(), "jsonc".into())].into_iter().collect()
}
}
+
+fn schema_file_match(path: &Path) -> &Path {
+ path.strip_prefix(path.parent().unwrap().parent().unwrap())
+ .unwrap()
+}
@@ -139,7 +139,7 @@ fn main() {
languages.set_executor(cx.background().clone());
languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone());
let languages = Arc::new(languages);
- languages::init(languages.clone());
+ languages::init(languages.clone(), themes.clone());
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx));
cx.set_global(client.clone());
@@ -29,10 +29,10 @@ use project_panel::ProjectPanel;
use search::{BufferSearchBar, ProjectSearchBar};
use serde::Deserialize;
use serde_json::to_string_pretty;
-use settings::{keymap_file_json_schema, settings_file_json_schema, Settings};
+use settings::Settings;
use std::{borrow::Cow, env, path::Path, str, sync::Arc};
use terminal_view::terminal_button::{self, TerminalButton};
-use util::{channel::ReleaseChannel, paths, ResultExt, StaffMode};
+use util::{channel::ReleaseChannel, paths, ResultExt};
use uuid::Uuid;
pub use workspace;
use workspace::{sidebar::SidebarSide, AppState, Restart, Workspace};
@@ -296,34 +296,6 @@ pub fn initialize_workspace(
cx.emit(workspace::Event::PaneAdded(workspace.active_pane().clone()));
cx.emit(workspace::Event::PaneAdded(workspace.dock_pane().clone()));
- let theme_names = app_state
- .themes
- .list(**cx.default_global::<StaffMode>())
- .map(|meta| meta.name)
- .collect();
- let language_names = app_state.languages.language_names();
-
- workspace.project().update(cx, |project, cx| {
- let action_names = cx.all_action_names().collect::<Vec<_>>();
- project.set_language_server_settings(serde_json::json!({
- "json": {
- "format": {
- "enable": true,
- },
- "schemas": [
- {
- "fileMatch": [schema_file_match(&paths::SETTINGS)],
- "schema": settings_file_json_schema(theme_names, &language_names),
- },
- {
- "fileMatch": [schema_file_match(&paths::KEYMAP)],
- "schema": keymap_file_json_schema(&action_names),
- }
- ]
- }
- }));
- });
-
let collab_titlebar_item =
cx.add_view(|cx| CollabTitlebarItem::new(&workspace_handle, &app_state.user_store, cx));
workspace.set_titlebar_item(collab_titlebar_item, cx);
@@ -676,11 +648,6 @@ fn open_bundled_file(
.detach();
}
-fn schema_file_match(path: &Path) -> &Path {
- path.strip_prefix(path.parent().unwrap().parent().unwrap())
- .unwrap()
-}
-
#[cfg(test)]
mod tests {
use super::*;
@@ -1882,7 +1849,8 @@ mod tests {
let mut languages = LanguageRegistry::new(Task::ready(()));
languages.set_executor(cx.background().clone());
let languages = Arc::new(languages);
- languages::init(languages.clone());
+ let themes = ThemeRegistry::new((), cx.font_cache().clone());
+ languages::init(languages.clone(), themes);
for name in languages.language_names() {
languages.language_for_name(&name);
}