Show configuration in language server debug logs (#23084)

Michael Sloan created

Release Notes:

- Added configuration sent on initialization to the `Server Info`
section of the language server logs.

Change summary

crates/copilot/src/copilot.rs        |  9 ++++++++-
crates/language_tools/src/lsp_log.rs |  6 +++++-
crates/languages/src/json.rs         |  2 ++
crates/lsp/src/lsp.rs                | 25 ++++++++++++++++++++++++-
crates/prettier/src/prettier.rs      |  9 ++++++++-
crates/project/src/lsp_store.rs      | 15 ++++++++++-----
crates/settings/src/keymap_file.rs   |  2 ++
7 files changed, 59 insertions(+), 9 deletions(-)

Detailed changes

crates/copilot/src/copilot.rs 🔗

@@ -460,7 +460,14 @@ impl Copilot {
             server
                 .on_notification::<StatusNotification, _>(|_, _| { /* Silence the notification */ })
                 .detach();
-            let server = cx.update(|cx| server.initialize(None, cx))?.await?;
+
+            let initialize_params = None;
+            let configuration = lsp::DidChangeConfigurationParams {
+                settings: Default::default(),
+            };
+            let server = cx
+                .update(|cx| server.initialize(initialize_params, configuration.into(), cx))?
+                .await?;
 
             let status = server
                 .request::<request::CheckStatus>(request::CheckStatusParams {

crates/language_tools/src/lsp_log.rs 🔗

@@ -732,13 +732,17 @@ impl LspLogView {
 
 * Running in project: {PATH:?}
 
-* Capabilities: {CAPABILITIES}",
+* Capabilities: {CAPABILITIES}
+
+* Configuration: {CONFIGURATION}",
                 NAME = server.name(),
                 ID = server.server_id(),
                 BINARY = server.binary(),
                 PATH = server.root_path(),
                 CAPABILITIES = serde_json::to_string_pretty(&server.capabilities())
                     .unwrap_or_else(|e| format!("Failed to serialize capabilities: {e}")),
+                CONFIGURATION = serde_json::to_string_pretty(server.configuration())
+                    .unwrap_or_else(|e| format!("Failed to serialize configuration: {e}")),
             );
             editor.set_text(server_info, cx);
             editor.set_read_only(true);

crates/languages/src/json.rs 🔗

@@ -88,6 +88,8 @@ impl JsonLspAdapter {
         let tsconfig_schema = serde_json::Value::from_str(TSCONFIG_SCHEMA).unwrap();
         let package_json_schema = serde_json::Value::from_str(PACKAGE_JSON_SCHEMA).unwrap();
 
+        // This can be viewed via `debug: open language server logs` -> `json-language-server` ->
+        // `Server Info`
         serde_json::json!({
             "json": {
                 "format": {

crates/lsp/src/lsp.rs 🔗

@@ -84,6 +84,10 @@ pub struct LanguageServer {
     process_name: Arc<str>,
     binary: LanguageServerBinary,
     capabilities: RwLock<ServerCapabilities>,
+    /// Configuration sent to the server, stored for display in the language server logs
+    /// buffer. This is represented as the message sent to the LSP in order to avoid cloning it (can
+    /// be large in cases like sending schemas to the json server).
+    configuration: Arc<DidChangeConfigurationParams>,
     code_action_kinds: Option<Vec<CodeActionKind>>,
     notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,
     response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>,
@@ -459,6 +463,11 @@ impl LanguageServer {
             .log_err()
         });
 
+        let configuration = DidChangeConfigurationParams {
+            settings: Value::Null,
+        }
+        .into();
+
         Self {
             server_id,
             notification_handlers,
@@ -472,6 +481,7 @@ impl LanguageServer {
                 .unwrap_or_default(),
             binary,
             capabilities: Default::default(),
+            configuration,
             code_action_kinds,
             next_id: Default::default(),
             outbound_tx,
@@ -800,6 +810,7 @@ impl LanguageServer {
     pub fn initialize(
         mut self,
         initialize_params: Option<InitializeParams>,
+        configuration: Arc<DidChangeConfigurationParams>,
         cx: &AppContext,
     ) -> Task<Result<Arc<Self>>> {
         let params = if let Some(params) = initialize_params {
@@ -814,6 +825,7 @@ impl LanguageServer {
                 self.process_name = info.name.into();
             }
             self.capabilities = RwLock::new(response.capabilities);
+            self.configuration = configuration;
 
             self.notify::<notification::Initialized>(&InitializedParams {})?;
             Ok(Arc::new(self))
@@ -1049,6 +1061,10 @@ impl LanguageServer {
         update(self.capabilities.write().deref_mut());
     }
 
+    pub fn configuration(&self) -> &Value {
+        &self.configuration.settings
+    }
+
     /// Get the id of the running language server.
     pub fn server_id(&self) -> LanguageServerId {
         self.server_id
@@ -1538,7 +1554,14 @@ mod tests {
             })
             .detach();
 
-        let server = cx.update(|cx| server.initialize(None, cx)).await.unwrap();
+        let initialize_params = None;
+        let configuration = DidChangeConfigurationParams {
+            settings: Default::default(),
+        };
+        let server = cx
+            .update(|cx| server.initialize(initialize_params, configuration.into(), cx))
+            .await
+            .unwrap();
         server
             .notify::<notification::DidOpenTextDocument>(&DidOpenTextDocumentParams {
                 text_document: TextDocumentItem::new(

crates/prettier/src/prettier.rs 🔗

@@ -282,8 +282,15 @@ impl Prettier {
             cx.clone(),
         )
         .context("prettier server creation")?;
+
+        let initialize_params = None;
+        let configuration = lsp::DidChangeConfigurationParams {
+            settings: Default::default(),
+        };
         let server = cx
-            .update(|cx| executor.spawn(server.initialize(None, cx)))?
+            .update(|cx| {
+                executor.spawn(server.initialize(initialize_params, configuration.into(), cx))
+            })?
             .await
             .context("prettier server initialization")?;
         Ok(Self::Real(RealPrettier {

crates/project/src/lsp_store.rs 🔗

@@ -286,9 +286,17 @@ impl LocalLspStore {
 
                         Self::setup_lsp_messages(this.clone(), &language_server, delegate, adapter);
 
+                        let did_change_configuration_params =
+                            Arc::new(lsp::DidChangeConfigurationParams {
+                                settings: workspace_config,
+                            });
                         let language_server = cx
                             .update(|cx| {
-                                language_server.initialize(Some(initialization_params), cx)
+                                language_server.initialize(
+                                    Some(initialization_params),
+                                    did_change_configuration_params.clone(),
+                                    cx,
+                                )
                             })?
                             .await
                             .inspect_err(|_| {
@@ -302,9 +310,7 @@ impl LocalLspStore {
 
                         language_server
                             .notify::<lsp::notification::DidChangeConfiguration>(
-                                &lsp::DidChangeConfigurationParams {
-                                    settings: workspace_config,
-                                },
+                                &did_change_configuration_params,
                             )
                             .ok();
 
@@ -7636,7 +7642,6 @@ impl LspStore {
                             continue;
                         }
                     }
-
                     if progress.is_cancellable {
                         server
                             .notify::<lsp::notification::WorkDoneProgressCancel>(

crates/settings/src/keymap_file.rs 🔗

@@ -265,6 +265,8 @@ impl KeymapFile {
             .definitions
             .insert("KeymapAction".to_owned(), action_schema);
 
+        // This and other json schemas can be viewed via `debug: open language server logs` ->
+        // `json-language-server` -> `Server Info`.
         serde_json::to_value(root_schema).unwrap()
     }