Start work on relaying settings to language servers

Max Brunsfeld created

Change summary

crates/language/src/language.rs  |  5 --
crates/lsp/src/lsp.rs            |  7 ++++
crates/project/src/project.rs    | 48 ++++++++++++++++++++++++++++++++++
crates/workspace/src/settings.rs |  5 +--
crates/zed/src/language.rs       | 11 -------
crates/zed/src/zed.rs            | 14 +++++++++
6 files changed, 72 insertions(+), 18 deletions(-)

Detailed changes

crates/language/src/language.rs 🔗

@@ -95,8 +95,6 @@ pub trait LspAdapter: 'static + Send + Sync {
     fn initialization_options(&self) -> Option<Value> {
         None
     }
-
-    fn register_handlers(&self, _: &mut lsp::LanguageServer) {}
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -321,14 +319,13 @@ impl LanguageRegistry {
 
             let server_binary_path = server_binary_path.await?;
             let server_args = adapter.server_args();
-            let mut server = lsp::LanguageServer::new(
+            let server = lsp::LanguageServer::new(
                 &server_binary_path,
                 server_args,
                 &root_path,
                 adapter.initialization_options(),
                 background,
             )?;
-            adapter.register_handlers(&mut server);
             Ok(server)
         }))
     }

crates/lsp/src/lsp.rs 🔗

@@ -265,6 +265,13 @@ impl LanguageServer {
             root_uri: Some(root_uri),
             initialization_options: options,
             capabilities: ClientCapabilities {
+                workspace: Some(WorkspaceClientCapabilities {
+                    configuration: Some(true),
+                    did_change_configuration: Some(DynamicRegistrationClientCapabilities {
+                        dynamic_registration: Some(true),
+                    }),
+                    ..Default::default()
+                }),
                 text_document: Some(TextDocumentClientCapabilities {
                     definition: Some(GotoCapability {
                         link_support: Some(true),

crates/project/src/project.rs 🔗

@@ -23,6 +23,7 @@ use language::{
 };
 use lsp::{DiagnosticSeverity, DocumentHighlightKind, LanguageServer};
 use lsp_command::*;
+use parking_lot::Mutex;
 use postage::watch;
 use rand::prelude::*;
 use search::SearchQuery;
@@ -52,6 +53,7 @@ pub struct Project {
     language_servers: HashMap<(WorktreeId, Arc<str>), Arc<LanguageServer>>,
     started_language_servers: HashMap<(WorktreeId, Arc<str>), Task<Option<Arc<LanguageServer>>>>,
     language_server_statuses: BTreeMap<usize, LanguageServerStatus>,
+    language_server_settings: Arc<Mutex<serde_json::Value>>,
     next_language_server_id: usize,
     client: Arc<client::Client>,
     user_store: ModelHandle<UserStore>,
@@ -333,6 +335,7 @@ impl Project {
                 language_servers: Default::default(),
                 started_language_servers: Default::default(),
                 language_server_statuses: Default::default(),
+                language_server_settings: Default::default(),
                 next_language_server_id: 0,
                 nonce: StdRng::from_entropy().gen(),
             }
@@ -403,6 +406,7 @@ impl Project {
                 language_servers_with_diagnostics_running: 0,
                 language_servers: Default::default(),
                 started_language_servers: Default::default(),
+                language_server_settings: Default::default(),
                 language_server_statuses: response
                     .language_servers
                     .into_iter()
@@ -1228,6 +1232,30 @@ impl Project {
                         })
                         .detach();
 
+                    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();
+                                Ok(params
+                                    .items
+                                    .into_iter()
+                                    .map(|item| {
+                                        if let Some(section) = &item.section {
+                                            settings
+                                                .get(section)
+                                                .cloned()
+                                                .unwrap_or(serde_json::Value::Null)
+                                        } else {
+                                            settings.clone()
+                                        }
+                                    })
+                                    .collect())
+                            }
+                        })
+                        .detach();
+
                     language_server
                         .on_notification::<lsp::notification::Progress, _>(move |params| {
                             let token = match params.token {
@@ -1299,6 +1327,13 @@ impl Project {
                                 pending_diagnostic_updates: 0,
                             },
                         );
+                        language_server
+                            .notify::<lsp::notification::DidChangeConfiguration>(
+                                lsp::DidChangeConfigurationParams {
+                                    settings: this.language_server_settings.lock().clone(),
+                                },
+                            )
+                            .ok();
 
                         if let Some(project_id) = this.remote_id() {
                             this.client
@@ -1547,6 +1582,19 @@ impl Project {
         }
     }
 
+    pub fn set_language_server_settings(&mut self, settings: serde_json::Value) {
+        for server in self.language_servers.values() {
+            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> {

crates/workspace/src/settings.rs 🔗

@@ -93,9 +93,8 @@ impl SettingsFile {
 }
 
 impl Settings {
-    pub fn file_json_schema() -> String {
-        let schema = schema_for!(SettingsFileContent);
-        serde_json::to_string(&schema).unwrap()
+    pub fn file_json_schema() -> serde_json::Value {
+        serde_json::to_value(schema_for!(SettingsFileContent)).unwrap()
     }
 
     pub fn from_files(

crates/zed/src/language.rs 🔗

@@ -530,17 +530,6 @@ impl LspAdapter for JsonLspAdapter {
             "provideFormatter": true
         }))
     }
-
-    fn register_handlers(&self, lsp: &mut lsp::LanguageServer) {
-        lsp.on_custom_request::<Vec<String>, Option<String>, _>("vscode/content", |schema| {
-            if schema.get(0).map(String::as_str) == Some("zed://settings") {
-                Ok(Some(workspace::Settings::file_json_schema()))
-            } else {
-                Ok(None)
-            }
-        })
-        .detach();
-    }
 }
 
 pub fn build_language_registry(login_shell_env_loaded: Task<()>) -> LanguageRegistry {

crates/zed/src/zed.rs 🔗

@@ -96,6 +96,20 @@ pub fn build_workspace(
     };
     let mut workspace = Workspace::new(&workspace_params, cx);
     let project = workspace.project().clone();
+
+    project.update(cx, |project, _| {
+        project.set_language_server_settings(serde_json::json!({
+            "json": {
+                "schemas": [
+                    {
+                        "fileMatch": "**/.zed/settings.json",
+                        "schema": Settings::file_json_schema(),
+                    }
+                ]
+            }
+        }));
+    });
+
     workspace.left_sidebar_mut().add_item(
         "icons/folder-tree-16.svg",
         ProjectPanel::new(project, cx).into(),