vtsls: Move all default configuration to workspace_configuration (#18259)

Thorsten Ball created

This fixes https://github.com/zed-industries/zed/issues/18014 by fixing
the regression that was introduced in
https://github.com/zed-industries/zed/pull/17757.

In short: after digging into the `vtsls` code, it looks like it
essentially doesn't need any `initialization_options`, it's all
workspace configuration, since it tries to use the built-in settings
from VS Code.

I tested the completions, the inlay hints, the max memory - all of it
now works after moving to `workspace_configuration`.

Closes #18014.

Release Notes:

- Fixed `vtsls` being initialized the wrong way, which would mean the
wrong options were used to enable completions or inlay hints.

Change summary

crates/languages/src/vtsls.rs    | 43 +++++---------
docs/src/languages/typescript.md | 94 ++++++++++++++++++++++++++++-----
2 files changed, 96 insertions(+), 41 deletions(-)

Detailed changes

crates/languages/src/vtsls.rs 🔗

@@ -6,14 +6,14 @@ use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
 use lsp::{CodeActionKind, LanguageServerBinary};
 use node_runtime::NodeRuntime;
 use project::{lsp_store::language_server_settings, project_settings::BinarySettings};
-use serde_json::{json, Value};
+use serde_json::Value;
 use std::{
     any::Any,
     ffi::OsString,
     path::{Path, PathBuf},
     sync::Arc,
 };
-use util::{maybe, ResultExt};
+use util::{maybe, merge_json_value_into, ResultExt};
 
 fn typescript_server_binary_arguments(server_path: &Path) -> Vec<OsString> {
     vec![server_path.into(), "--stdio".into()]
@@ -212,11 +212,12 @@ impl LspAdapter for VtslsLspAdapter {
         })
     }
 
-    async fn initialization_options(
+    async fn workspace_configuration(
         self: Arc<Self>,
-        adapter: &Arc<dyn LspAdapterDelegate>,
-    ) -> Result<Option<serde_json::Value>> {
-        let tsdk_path = Self::tsdk_path(adapter).await;
+        delegate: &Arc<dyn LspAdapterDelegate>,
+        cx: &mut AsyncAppContext,
+    ) -> Result<Value> {
+        let tsdk_path = Self::tsdk_path(delegate).await;
         let config = serde_json::json!({
             "tsdk": tsdk_path,
             "suggest": {
@@ -243,10 +244,13 @@ impl LspAdapter for VtslsLspAdapter {
                 "enumMemberValues": {
                     "enabled": true
                 }
-            }
+            },
+            "tsserver": {
+                "maxTsServerMemory": 8092
+            },
         });
 
-        Ok(Some(json!({
+        let mut default_workspace_configuration = serde_json::json!({
             "typescript": config,
             "javascript": config,
             "vtsls": {
@@ -258,33 +262,18 @@ impl LspAdapter for VtslsLspAdapter {
                 },
                "autoUseWorkspaceTsdk": true
             }
-        })))
-    }
+        });
 
-    async fn workspace_configuration(
-        self: Arc<Self>,
-        delegate: &Arc<dyn LspAdapterDelegate>,
-        cx: &mut AsyncAppContext,
-    ) -> Result<Value> {
         let override_options = cx.update(|cx| {
             language_server_settings(delegate.as_ref(), SERVER_NAME, cx)
                 .and_then(|s| s.settings.clone())
         })?;
 
-        if let Some(options) = override_options {
-            return Ok(options);
+        if let Some(override_options) = override_options {
+            merge_json_value_into(override_options, &mut default_workspace_configuration)
         }
 
-        let config = serde_json::json!({
-            "tsserver": {
-                "maxTsServerMemory": 8092
-            },
-        });
-
-        Ok(serde_json::json!({
-            "typescript": config,
-            "javascript": config
-        }))
+        Ok(default_workspace_configuration)
     }
 
     fn language_ids(&self) -> HashMap<String, String> {

docs/src/languages/typescript.md 🔗

@@ -68,29 +68,95 @@ Prettier will also be used for TypeScript files by default. To disable this:
 Zed sets the following initialization options to make the language server send back inlay hints
 (that is, when Zed has inlay hints enabled in the settings).
 
-You can override these settings in your configuration file:
+You can override these settings in your Zed settings file.
+
+When using `typescript-language-server`:
 
 ```json
-"lsp": {
-    "$LANGUAGE_SERVER_NAME": {
-        "initialization_options": {
-            "preferences": {
-              "includeInlayParameterNameHints": "all",
-              "includeInlayParameterNameHintsWhenArgumentMatchesName": true,
-              "includeInlayFunctionParameterTypeHints": true,
-              "includeInlayVariableTypeHints": true,
-              "includeInlayVariableTypeHintsWhenTypeMatchesName": true,
-              "includeInlayPropertyDeclarationTypeHints": true,
-              "includeInlayFunctionLikeReturnTypeHints": true,
-              "includeInlayEnumMemberValueHints": true,
-            }
+{
+  "lsp": {
+    "typescript-language-server": {
+      "initialization_options": {
+        "preferences": {
+          "includeInlayParameterNameHints": "all",
+          "includeInlayParameterNameHintsWhenArgumentMatchesName": true,
+          "includeInlayFunctionParameterTypeHints": true,
+          "includeInlayVariableTypeHints": true,
+          "includeInlayVariableTypeHintsWhenTypeMatchesName": true,
+          "includeInlayPropertyDeclarationTypeHints": true,
+          "includeInlayFunctionLikeReturnTypeHints": true,
+          "includeInlayEnumMemberValueHints": true
         }
+      }
     }
 }
 ```
 
 See [typescript-language-server inlayhints documentation](https://github.com/typescript-language-server/typescript-language-server?tab=readme-ov-file#inlay-hints-textdocumentinlayhint) for more information.
 
+When using `vtsls`:
+
+```json
+{
+  "lsp": {
+    "vtsls": {
+      "settings": {
+        // For JavaScript:
+        "javascript": {
+          "inlayHints": {
+            "parameterNames": {
+              "enabled": "all",
+              "suppressWhenArgumentMatchesName": false
+            },
+            "parameterTypes": {
+              "enabled": true
+            },
+            "variableTypes": {
+              "enabled": true,
+              "suppressWhenTypeMatchesName": true
+            },
+            "propertyDeclarationTypes": {
+              "enabled": true
+            },
+            "functionLikeReturnTypes": {
+              "enabled": true
+            },
+            "enumMemberValues": {
+              "enabled": true
+            }
+          }
+        },
+        // For TypeScript:
+        "typescript": {
+          "inlayHints": {
+            "parameterNames": {
+              "enabled": "all",
+              "suppressWhenArgumentMatchesName": false
+            },
+            "parameterTypes": {
+              "enabled": true
+            },
+            "variableTypes": {
+              "enabled": true,
+              "suppressWhenTypeMatchesName": true
+            },
+            "propertyDeclarationTypes": {
+              "enabled": true
+            },
+            "functionLikeReturnTypes": {
+              "enabled": true
+            },
+            "enumMemberValues": {
+              "enabled": true
+            }
+          }
+        }
+      }
+    }
+  }
+}
+```
+
 ## See also
 
 - [Zed Yarn documentation](./yarn.md) for a walkthrough of configuring your project to use Yarn.