diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 58be8a4dc3e9ce337ba2af8a8cd9f297905d6c33..2725122990d9fe4ded4f1f4b03714edb1265fa43 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -30,7 +30,10 @@ use gpui::{AppContext, AsyncAppContext, Model, SharedString, Task}; pub use highlight_map::HighlightMap; use http_client::HttpClient; pub use language_registry::{LanguageName, LoadedLanguage}; -use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerBinaryOptions, LanguageServerName}; +use lsp::{ + CodeActionKind, InitializeParams, LanguageServerBinary, LanguageServerBinaryOptions, + LanguageServerName, +}; use parking_lot::Mutex; use regex::Regex; use schemars::{ @@ -484,6 +487,11 @@ pub trait LspAdapter: 'static + Send + Sync { fn language_ids(&self) -> HashMap { Default::default() } + + /// Support custom initialize params. + fn prepare_initialize_params(&self, original: InitializeParams) -> Result { + Ok(original) + } } async fn try_fetch_server_binary( diff --git a/crates/languages/src/c.rs b/crates/languages/src/c.rs index 8d0369f0e019545fc4dfa193636ae79b8f9fdf70..c50a16b3e467d8fd1ca970cf192a18577dab0490 100644 --- a/crates/languages/src/c.rs +++ b/crates/languages/src/c.rs @@ -4,10 +4,11 @@ use futures::StreamExt; use gpui::AsyncAppContext; use http_client::github::{latest_github_release, GitHubLspBinaryVersion}; pub use language::*; -use lsp::{LanguageServerBinary, LanguageServerName}; +use lsp::{InitializeParams, LanguageServerBinary, LanguageServerName}; +use serde_json::json; use smol::fs::{self, File}; use std::{any::Any, env::consts, path::PathBuf, sync::Arc}; -use util::{fs::remove_matching, maybe, ResultExt}; +use util::{fs::remove_matching, maybe, merge_json_value_into, ResultExt}; pub struct CLspAdapter; @@ -257,6 +258,26 @@ impl super::LspAdapter for CLspAdapter { filter_range, }) } + + fn prepare_initialize_params( + &self, + mut original: InitializeParams, + ) -> Result { + // enable clangd's dot-to-arrow feature. + let experimental = json!({ + "textDocument": { + "completion" : { + "editsNearCursor": true + } + } + }); + if let Some(ref mut original_experimental) = original.capabilities.experimental { + merge_json_value_into(experimental, original_experimental); + } else { + original.capabilities.experimental = Some(experimental); + } + Ok(original) + } } async fn get_cached_server_binary(container_dir: PathBuf) -> Option { diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 98755583e3ce574eb5a5b7e4a76944aab785c6de..8789f5f2521179764260509202752f589ab8d3c2 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -599,22 +599,14 @@ impl LanguageServer { Ok(()) } - /// Initializes a language server by sending the `Initialize` request. - /// Note that `options` is used directly to construct [`InitializeParams`], which is why it is owned. - /// - /// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize) - pub fn initialize( - mut self, - options: Option, - cx: &AppContext, - ) -> Task>> { + pub fn default_initialize_params(&self, cx: &AppContext) -> InitializeParams { let root_uri = Url::from_file_path(&self.working_dir).unwrap(); #[allow(deprecated)] - let params = InitializeParams { + InitializeParams { process_id: None, root_path: None, root_uri: Some(root_uri.clone()), - initialization_options: options, + initialization_options: None, capabilities: ClientCapabilities { workspace: Some(WorkspaceClientCapabilities { configuration: Some(true), @@ -779,6 +771,22 @@ impl LanguageServer { }), locale: None, ..Default::default() + } + } + + /// Initializes a language server by sending the `Initialize` request. + /// Note that `options` is used directly to construct [`InitializeParams`], which is why it is owned. + /// + /// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize) + pub fn initialize( + mut self, + initialize_params: Option, + cx: &AppContext, + ) -> Task>> { + let params = if let Some(params) = initialize_params { + params + } else { + self.default_initialize_params(cx) }; cx.spawn(|_| async move { diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index 6f4d23fa7617dbb0429f9bec7d2125d905108457..7d75347cf0e0c2e9fbfe0de85c8760d716e1bf1f 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -5673,8 +5673,6 @@ impl LspStore { .initialization_options(&(delegate)) .await?; - Self::setup_lsp_messages(this.clone(), &language_server, delegate, adapter); - match (&mut initialization_options, override_options) { (Some(initialization_options), Some(override_options)) => { merge_json_value_into(override_options, initialization_options); @@ -5683,8 +5681,18 @@ impl LspStore { _ => {} } + let initialization_params = cx.update(|cx| { + let mut params = language_server.default_initialize_params(cx); + params.initialization_options = initialization_options; + adapter.adapter.prepare_initialize_params(params) + })??; + + Self::setup_lsp_messages(this.clone(), &language_server, delegate, adapter); + let language_server = cx - .update(|cx| language_server.initialize(initialization_options, cx))? + .update(|cx| { + language_server.initialize(Some(initialization_params), cx) + })? .await .inspect_err(|_| { if let Some(this) = this.upgrade() {