Skip to content

Commit 8cba87e

Browse files
authored
Add support for server specific handlers (#1177)
1 parent a0eb77b commit 8cba87e

File tree

6 files changed

+77
-8
lines changed

6 files changed

+77
-8
lines changed

.vim/settings.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,16 @@
44
"all_targets": false,
55
"build_on_save": true,
66
"wait_to_build": 0
7+
},
8+
"rust-analyzer": {
9+
"diagnostics": {
10+
"disabled": ["macro-error"]
11+
},
12+
"procMacro": {
13+
"enable": true
14+
},
15+
"cargo": {
16+
"loadOutDirsFromCheck": true
17+
}
718
}
819
}

doc/LanguageClient.txt

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@ To use the language server with Vim's formatting operator |gq|, set 'formatexpr'
7878
Dictionary to specify the servers to be used for each filetype. The keys are
7979
strings representing the filetypes and the values are either
8080
1 - a list of strings that form a command
81-
2 - a dictionary with keys name, command and initializationOptions, where:
82-
- name: is the name of the server, which should match the root node of the
81+
2 - a dictionary with the following keys:
82+
- name: the name of the server, which should match the root node of the
8383
configuration options specified in the settings.json file for this server
84-
- command: is the same list of strings in option 1
85-
- initializationOptions: is an optional dictionary with options to be passed
84+
- command: the same list of strings in option 1
85+
- initializationOptions: an optional dictionary with options to be passed
8686
to the server. The values to be set here are highly dependent on each
8787
server so you should see the documentation for each server to find out
8888
what to configure in here (if anything). The options set in
@@ -93,6 +93,13 @@ strings representing the filetypes and the values are either
9393
then the server settings configured in this section and lastly the
9494
contents of the files in the `LanguageClient_settingsPath` variable in
9595
the order in which they were listed.
96+
- handlers: an optional dictionary of custom handlers to register for this
97+
server. The keys for this dictionary should be (LSP) method names, and
98+
the values should be vim functions to be executed upon receiving an event
99+
with that method. The function will be called with a single param which
100+
is the JSON-RPC message itself. Note that any events will only be handled
101+
once, so if you register a handler for an event, the function you specify
102+
will dictate all that happens with that event.
96103

97104
For example: >
98105
@@ -108,14 +115,21 @@ For example: >
108115
\ 'test': v:true,
109116
\ },
110117
\ },
118+
\ 'handlers': {
119+
\ 'window/logMessage': 'LogMessage'
120+
\ }
111121
\ },
112122
\}
113123
114124
In the configuration for the rust filetype above, 'run', 'stable', and 'rls'
115-
are arguments to the 'rustup' command line tool. And in the configuration for
116-
the go filetype the server is configured to run the command `gopls` with no
117-
additional arguments, and with the initialization options set in the
118-
`initializationOptions` key.
125+
are arguments to the 'rustup' command line tool.
126+
127+
In the configuration for the go filetype the server is configured to run the
128+
command `gopls` with no additional arguments, and with the initialization
129+
options set in the `initializationOptions` key. Also, the go server is
130+
configured to run the vim function LogMessage upon receiving an event with
131+
method `window/logMessage`. Remember that both initializationOptions and
132+
handlers are optional, so you can omit specifying those if you wish.
119133

120134
You can also use a tcp connection to the server, for example: >
121135
let g:LanguageClient_serverCommands = {

src/config/server_command.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::collections::HashMap;
2+
13
use crate::utils::expand_json_path;
24
use jsonrpc_core::Value;
35
use serde::{Deserialize, Serialize};
@@ -8,6 +10,7 @@ pub struct ServerDetails {
810
pub command: Vec<String>,
911
pub name: String,
1012
pub initialization_options: Option<Value>,
13+
pub handlers: Option<HashMap<String, String>>,
1114
}
1215

1316
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@@ -48,6 +51,13 @@ impl ServerCommand {
4851
}
4952
}
5053

54+
pub fn handlers(&self) -> HashMap<String, String> {
55+
match self {
56+
ServerCommand::Simple(_) => HashMap::new(),
57+
ServerCommand::Detailed(cmd) => cmd.handlers.clone().unwrap_or_default(),
58+
}
59+
}
60+
5161
#[cfg(not(target_os = "windows"))]
5262
fn name_from_command(cmd: &[String]) -> String {
5363
// it's safe to assume there is at least one item in cmd, otherwise

src/language_server_protocol.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,17 @@ impl LanguageClient {
872872
}
873873
let command = command.unwrap();
874874

875+
let handlers = command.handlers();
876+
if !handlers.is_empty() {
877+
self.update_state(|s| {
878+
s.custom_handlers
879+
.entry(language_id.clone())
880+
.or_insert_with(HashMap::new)
881+
.extend(handlers);
882+
Ok(())
883+
})?;
884+
}
885+
875886
let settings = self.get_workspace_settings(&root).unwrap_or_default();
876887
// warn the user that they are using a deprecated workspace settings
877888
// file format and direct them to the documentation about the new one
@@ -4008,6 +4019,7 @@ mod test {
40084019
initialization_options: Some(json!({
40094020
"inlayHints.enable": true,
40104021
})),
4022+
handlers: None,
40114023
});
40124024

40134025
let options = merged_initialization_options(&command, &settings)
@@ -4040,6 +4052,7 @@ mod test {
40404052
name: "gopls".into(),
40414053
command: vec!["gopls".into()],
40424054
initialization_options: None,
4055+
handlers: None,
40434056
});
40444057

40454058
let options = merged_initialization_options(&command, &settings)
@@ -4066,6 +4079,7 @@ mod test {
40664079
initialization_options: Some(json!({
40674080
"usePlaceholders": true,
40684081
})),
4082+
handlers: None,
40694083
});
40704084

40714085
let options = merged_initialization_options(&command, &settings)

src/rpchandler.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,19 @@ impl LanguageClient {
158158
) -> Result<()> {
159159
let params = serde_json::to_value(notification.params.clone())?;
160160

161+
// handle custom, server specific handlers
162+
let custom_handler = self.get_state(|state| {
163+
state
164+
.custom_handlers
165+
.get(language_id.unwrap_or_default())
166+
.map(|h| h.get(&notification.method).cloned())
167+
.unwrap_or_default()
168+
})?;
169+
if let Some(handler) = custom_handler {
170+
return self.vim()?.rpcclient.notify(&handler, params);
171+
}
172+
173+
// handle custom, server agnostic handlers
161174
let user_handler =
162175
self.get_state(|state| state.user_handlers.get(&notification.method).cloned())?;
163176
if let Some(user_handler) = user_handler {

src/types.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,12 @@ pub struct State {
175175
// TODO: make file specific.
176176
pub highlight_match_ids: Vec<u32>,
177177
pub user_handlers: HashMap<String, String>,
178+
/// server_handlers stores a hashmap keyed by filetype with values being another hashmap, keyed
179+
/// by method and values being a vim function to call when an LSP event with that method is
180+
/// received.
181+
/// The difference between user_handlers and server_handlers is that this one is filetype
182+
/// specific, and user_handlers is global, the handlers are ran for all language servers.
183+
pub custom_handlers: HashMap<String, HashMap<String, String>>,
178184
#[serde(skip_serializing)]
179185
pub watchers: HashMap<String, FSWatch>,
180186
#[serde(skip_serializing)]
@@ -233,6 +239,7 @@ impl State {
233239
highlights_placed: HashMap::new(),
234240
highlight_match_ids: Vec::new(),
235241
user_handlers: HashMap::new(),
242+
custom_handlers: HashMap::new(),
236243
watchers: HashMap::new(),
237244
watcher_rxs: HashMap::new(),
238245
last_cursor_line: 0,

0 commit comments

Comments
 (0)