Merge pull request #1468 from zed-industries/lsp-initialization-options

Julia created

Add ability to provide custom LSP server initialization options

Change summary

assets/settings/default.json    |  3 ++-
crates/project/src/project.rs   | 34 +++++++++++++++++++++++++++++++++-
crates/settings/src/settings.rs | 12 ++++++++++++
3 files changed, 47 insertions(+), 2 deletions(-)

Detailed changes

crates/project/src/project.rs 🔗

@@ -2071,6 +2071,24 @@ impl Project {
         None
     }
 
+    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,
@@ -2092,6 +2110,20 @@ impl Project {
         };
         let key = (worktree_id, adapter.name.clone());
 
+        let mut initialization_options = adapter.initialization_options.clone();
+
+        let lsp = &cx.global::<Settings>().lsp.get(&adapter.name.0);
+        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);
+            }
+
+            (None, override_options) => initialization_options = override_options,
+
+            _ => {}
+        }
+
         self.language_server_ids
             .entry(key.clone())
             .or_insert_with(|| {
@@ -2108,7 +2140,7 @@ impl Project {
                     LanguageServerState::Starting(cx.spawn_weak(|this, mut cx| async move {
                         let language_server = language_server?.await.log_err()?;
                         let language_server = language_server
-                            .initialize(adapter.initialization_options.clone())
+                            .initialize(initialization_options)
                             .await
                             .log_err()?;
                         let this = this.upgrade(&cx)?;

crates/settings/src/settings.rs 🔗

@@ -34,6 +34,7 @@ pub struct Settings {
     pub terminal_overrides: TerminalSettings,
     pub language_defaults: HashMap<Arc<str>, EditorSettings>,
     pub language_overrides: HashMap<Arc<str>, EditorSettings>,
+    pub lsp: HashMap<Arc<str>, LspSettings>,
     pub theme: Arc<Theme>,
 }
 
@@ -131,9 +132,17 @@ pub struct SettingsFileContent {
     #[serde(alias = "language_overrides")]
     pub languages: HashMap<Arc<str>, EditorSettings>,
     #[serde(default)]
+    pub lsp: HashMap<Arc<str>, LspSettings>,
+    #[serde(default)]
     pub theme: Option<String>,
 }
 
+#[derive(Clone, Debug, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct LspSettings {
+    pub initialization_options: Option<Value>,
+}
+
 impl Settings {
     pub fn defaults(
         assets: impl AssetSource,
@@ -174,6 +183,7 @@ impl Settings {
             terminal_overrides: Default::default(),
             language_defaults: defaults.languages,
             language_overrides: Default::default(),
+            lsp: defaults.lsp.clone(),
             theme: themes.get(&defaults.theme.unwrap()).unwrap(),
         }
     }
@@ -218,6 +228,7 @@ impl Settings {
         self.terminal_defaults.font_size = data.terminal.font_size;
         self.terminal_overrides = data.terminal;
         self.language_overrides = data.languages;
+        self.lsp = data.lsp;
     }
 
     pub fn with_language_defaults(
@@ -288,6 +299,7 @@ impl Settings {
             terminal_overrides: Default::default(),
             language_defaults: Default::default(),
             language_overrides: Default::default(),
+            lsp: Default::default(),
             projects_online_by_default: true,
             theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), || Default::default()),
         }