Skip to content

Commit 671926a

Browse files
Merge #3666
3666: Reload part of the server configuration without restarts r=matklad a=SomeoneToIgnore Partially addresses #2857 Closes #3751 Reloads all server configuration that's not related to VFS without restarts. The VFS-related parameters are not considered, since VFS is planned to be rewritten/replaced in the future and I have a suspicion that with the current code, swapping the VFS and the file watchers on the fly will cause big troubles. I have to store and process the config request id separately, since the `workspace/configuration` response returns `any[]` (https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_configuration), if there's a better way to handle those responses, let me know. Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
2 parents 9e12b9e + 4c897d8 commit 671926a

File tree

11 files changed

+205
-110
lines changed

11 files changed

+205
-110
lines changed

crates/ra_cargo_watch/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ impl CheckWatcherThread {
9595
}
9696

9797
fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
98+
// If we rerun the thread, we need to discard the previous check results first
99+
self.clean_previous_results(task_send);
100+
98101
loop {
99102
select! {
100103
recv(&cmd_recv) -> cmd => match cmd {
@@ -127,6 +130,13 @@ impl CheckWatcherThread {
127130
}
128131
}
129132

133+
fn clean_previous_results(&self, task_send: &Sender<CheckTask>) {
134+
task_send.send(CheckTask::ClearDiagnostics).unwrap();
135+
task_send
136+
.send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd { message: None })))
137+
.unwrap();
138+
}
139+
130140
fn should_recheck(&mut self) -> bool {
131141
if let Some(_last_update_req) = &self.last_update_req {
132142
// We currently only request an update on save, as we need up to

crates/ra_ide/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ impl AnalysisHost {
138138
pub fn new(lru_capacity: Option<usize>) -> AnalysisHost {
139139
AnalysisHost { db: RootDatabase::new(lru_capacity) }
140140
}
141+
142+
pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
143+
self.db.update_lru_capacity(lru_capacity);
144+
}
145+
141146
/// Returns a snapshot of the current state, which you can query for
142147
/// semantic information.
143148
pub fn analysis(&self) -> Analysis {

crates/ra_ide_db/src/lib.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,16 @@ impl RootDatabase {
115115
db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
116116
db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
117117
db.set_library_roots_with_durability(Default::default(), Durability::HIGH);
118-
let lru_capacity = lru_capacity.unwrap_or(ra_db::DEFAULT_LRU_CAP);
119-
db.query_mut(ra_db::ParseQuery).set_lru_capacity(lru_capacity);
120-
db.query_mut(hir::db::ParseMacroQuery).set_lru_capacity(lru_capacity);
121-
db.query_mut(hir::db::MacroExpandQuery).set_lru_capacity(lru_capacity);
118+
db.update_lru_capacity(lru_capacity);
122119
db
123120
}
121+
122+
pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
123+
let lru_capacity = lru_capacity.unwrap_or(ra_db::DEFAULT_LRU_CAP);
124+
self.query_mut(ra_db::ParseQuery).set_lru_capacity(lru_capacity);
125+
self.query_mut(hir::db::ParseMacroQuery).set_lru_capacity(lru_capacity);
126+
self.query_mut(hir::db::MacroExpandQuery).set_lru_capacity(lru_capacity);
127+
}
124128
}
125129

126130
impl salsa::ParallelDatabase for RootDatabase {

crates/rust-analyzer/src/main_loop.rs

Lines changed: 107 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ use std::{
1717
use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
1818
use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
1919
use lsp_types::{
20-
ClientCapabilities, NumberOrString, WorkDoneProgress, WorkDoneProgressBegin,
21-
WorkDoneProgressCreateParams, WorkDoneProgressEnd, WorkDoneProgressReport,
20+
ClientCapabilities, NumberOrString, TextDocumentClientCapabilities, WorkDoneProgress,
21+
WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd,
22+
WorkDoneProgressReport,
2223
};
2324
use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckTask};
2425
use ra_ide::{Canceled, FileId, InlayHintsOptions, LibraryData, SourceRootId};
@@ -40,6 +41,7 @@ use crate::{
4041
world::{Options, WorldSnapshot, WorldState},
4142
Result, ServerConfig,
4243
};
44+
use req::ConfigurationParams;
4345

4446
#[derive(Debug)]
4547
pub struct LspError {
@@ -63,6 +65,53 @@ impl fmt::Display for LspError {
6365

6466
impl Error for LspError {}
6567

68+
fn get_feature_flags(config: &ServerConfig, connection: &Connection) -> FeatureFlags {
69+
let mut ff = FeatureFlags::default();
70+
for (flag, &value) in &config.feature_flags {
71+
if ff.set(flag.as_str(), value).is_err() {
72+
log::error!("unknown feature flag: {:?}", flag);
73+
show_message(
74+
req::MessageType::Error,
75+
format!("unknown feature flag: {:?}", flag),
76+
&connection.sender,
77+
);
78+
}
79+
}
80+
log::info!("feature_flags: {:#?}", ff);
81+
ff
82+
}
83+
84+
fn get_options(
85+
config: &ServerConfig,
86+
text_document_caps: Option<&TextDocumentClientCapabilities>,
87+
) -> Options {
88+
Options {
89+
publish_decorations: config.publish_decorations,
90+
supports_location_link: text_document_caps
91+
.and_then(|it| it.definition)
92+
.and_then(|it| it.link_support)
93+
.unwrap_or(false),
94+
line_folding_only: text_document_caps
95+
.and_then(|it| it.folding_range.as_ref())
96+
.and_then(|it| it.line_folding_only)
97+
.unwrap_or(false),
98+
inlay_hints: InlayHintsOptions {
99+
type_hints: config.inlay_hints_type,
100+
parameter_hints: config.inlay_hints_parameter,
101+
chaining_hints: config.inlay_hints_chaining,
102+
max_length: config.inlay_hints_max_length,
103+
},
104+
cargo_watch: CheckOptions {
105+
enable: config.cargo_watch_enable,
106+
args: config.cargo_watch_args.clone(),
107+
command: config.cargo_watch_command.clone(),
108+
all_targets: config.cargo_watch_all_targets,
109+
},
110+
rustfmt_args: config.rustfmt_args.clone(),
111+
vscode_lldb: config.vscode_lldb,
112+
}
113+
}
114+
66115
pub fn main_loop(
67116
ws_roots: Vec<PathBuf>,
68117
client_caps: ClientCapabilities,
@@ -90,23 +139,10 @@ pub fn main_loop(
90139
SetThreadPriority(thread, thread_priority_above_normal);
91140
}
92141

142+
let text_document_caps = client_caps.text_document.as_ref();
93143
let mut loop_state = LoopState::default();
94144
let mut world_state = {
95-
let feature_flags = {
96-
let mut ff = FeatureFlags::default();
97-
for (flag, value) in config.feature_flags {
98-
if ff.set(flag.as_str(), value).is_err() {
99-
log::error!("unknown feature flag: {:?}", flag);
100-
show_message(
101-
req::MessageType::Error,
102-
format!("unknown feature flag: {:?}", flag),
103-
&connection.sender,
104-
);
105-
}
106-
}
107-
ff
108-
};
109-
log::info!("feature_flags: {:#?}", feature_flags);
145+
let feature_flags = get_feature_flags(&config, &connection);
110146

111147
// FIXME: support dynamic workspace loading.
112148
let workspaces = {
@@ -168,42 +204,13 @@ pub fn main_loop(
168204
connection.sender.send(request.into()).unwrap();
169205
}
170206

171-
let options = {
172-
let text_document_caps = client_caps.text_document.as_ref();
173-
Options {
174-
publish_decorations: config.publish_decorations,
175-
supports_location_link: text_document_caps
176-
.and_then(|it| it.definition)
177-
.and_then(|it| it.link_support)
178-
.unwrap_or(false),
179-
line_folding_only: text_document_caps
180-
.and_then(|it| it.folding_range.as_ref())
181-
.and_then(|it| it.line_folding_only)
182-
.unwrap_or(false),
183-
inlay_hints: InlayHintsOptions {
184-
type_hints: config.inlay_hints_type,
185-
parameter_hints: config.inlay_hints_parameter,
186-
chaining_hints: config.inlay_hints_chaining,
187-
max_length: config.inlay_hints_max_length,
188-
},
189-
cargo_watch: CheckOptions {
190-
enable: config.cargo_watch_enable,
191-
args: config.cargo_watch_args,
192-
command: config.cargo_watch_command,
193-
all_targets: config.cargo_watch_all_targets,
194-
},
195-
rustfmt_args: config.rustfmt_args,
196-
vscode_lldb: config.vscode_lldb,
197-
}
198-
};
199-
200207
WorldState::new(
201208
ws_roots,
202209
workspaces,
203210
config.lru_capacity,
204211
&globs,
205212
Watch(!config.use_client_watching),
206-
options,
213+
get_options(&config, text_document_caps),
207214
feature_flags,
208215
)
209216
};
@@ -247,6 +254,7 @@ pub fn main_loop(
247254
&task_sender,
248255
&libdata_sender,
249256
&connection,
257+
text_document_caps,
250258
&mut world_state,
251259
&mut loop_state,
252260
event,
@@ -336,10 +344,10 @@ struct LoopState {
336344
in_flight_libraries: usize,
337345
pending_libraries: Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>,
338346
workspace_loaded: bool,
339-
340347
roots_progress_reported: Option<usize>,
341348
roots_scanned: usize,
342349
roots_total: usize,
350+
configuration_request_id: Option<RequestId>,
343351
}
344352

345353
impl LoopState {
@@ -357,6 +365,7 @@ fn loop_turn(
357365
task_sender: &Sender<Task>,
358366
libdata_sender: &Sender<LibraryData>,
359367
connection: &Connection,
368+
text_document_caps: Option<&TextDocumentClientCapabilities>,
360369
world_state: &mut WorldState,
361370
loop_state: &mut LoopState,
362371
event: Event,
@@ -397,19 +406,47 @@ fn loop_turn(
397406
req,
398407
)?,
399408
Message::Notification(not) => {
400-
on_notification(
401-
&connection.sender,
402-
world_state,
403-
&mut loop_state.pending_requests,
404-
&mut loop_state.subscriptions,
405-
not,
406-
)?;
409+
on_notification(&connection.sender, world_state, loop_state, not)?;
407410
}
408411
Message::Response(resp) => {
409412
let removed = loop_state.pending_responses.remove(&resp.id);
410413
if !removed {
411414
log::error!("unexpected response: {:?}", resp)
412415
}
416+
417+
if Some(&resp.id) == loop_state.configuration_request_id.as_ref() {
418+
loop_state.configuration_request_id = None;
419+
log::debug!("config update response: '{:?}", resp);
420+
let Response { error, result, .. } = resp;
421+
422+
match (
423+
error,
424+
result.map(|result| serde_json::from_value::<Vec<ServerConfig>>(result)),
425+
) {
426+
(Some(err), _) => {
427+
log::error!("failed to fetch the server settings: {:?}", err)
428+
}
429+
(None, Some(Ok(new_config))) => {
430+
let new_config = new_config
431+
.first()
432+
.expect(
433+
"the client is expected to always send a non-empty config data",
434+
)
435+
.to_owned();
436+
world_state.update_configuration(
437+
new_config.lru_capacity,
438+
get_options(&new_config, text_document_caps),
439+
get_feature_flags(&new_config, connection),
440+
);
441+
}
442+
(None, Some(Err(e))) => {
443+
log::error!("failed to parse client config response: {}", e)
444+
}
445+
(None, None) => {
446+
log::error!("received empty server settings response from the client")
447+
}
448+
}
449+
}
413450
}
414451
},
415452
};
@@ -569,8 +606,7 @@ fn on_request(
569606
fn on_notification(
570607
msg_sender: &Sender<Message>,
571608
state: &mut WorldState,
572-
pending_requests: &mut PendingRequests,
573-
subs: &mut Subscriptions,
609+
loop_state: &mut LoopState,
574610
not: Notification,
575611
) -> Result<()> {
576612
let not = match notification_cast::<req::Cancel>(not) {
@@ -579,7 +615,7 @@ fn on_notification(
579615
NumberOrString::Number(id) => id.into(),
580616
NumberOrString::String(id) => id.into(),
581617
};
582-
if pending_requests.cancel(&id) {
618+
if loop_state.pending_requests.cancel(&id) {
583619
let response = Response::new_err(
584620
id,
585621
ErrorCode::RequestCanceled as i32,
@@ -598,7 +634,7 @@ fn on_notification(
598634
if let Some(file_id) =
599635
state.vfs.write().add_file_overlay(&path, params.text_document.text)
600636
{
601-
subs.add_sub(FileId(file_id.0));
637+
loop_state.subscriptions.add_sub(FileId(file_id.0));
602638
}
603639
return Ok(());
604640
}
@@ -629,7 +665,7 @@ fn on_notification(
629665
let uri = params.text_document.uri;
630666
let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
631667
if let Some(file_id) = state.vfs.write().remove_file_overlay(path.as_path()) {
632-
subs.remove_sub(FileId(file_id.0));
668+
loop_state.subscriptions.remove_sub(FileId(file_id.0));
633669
}
634670
let params =
635671
req::PublishDiagnosticsParams { uri, diagnostics: Vec::new(), version: None };
@@ -640,7 +676,17 @@ fn on_notification(
640676
Err(not) => not,
641677
};
642678
let not = match notification_cast::<req::DidChangeConfiguration>(not) {
643-
Ok(_params) => {
679+
Ok(_) => {
680+
// As stated in https://github.com/microsoft/language-server-protocol/issues/676,
681+
// this notification's parameters should be ignored and the actual config queried separately.
682+
let request_id = loop_state.next_request_id();
683+
let request = request_new::<req::WorkspaceConfiguration>(
684+
request_id.clone(),
685+
ConfigurationParams::default(),
686+
);
687+
msg_sender.send(request.into())?;
688+
loop_state.configuration_request_id = Some(request_id);
689+
644690
return Ok(());
645691
}
646692
Err(not) => not,

crates/rust-analyzer/src/req.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
66

77
pub use lsp_types::{
88
notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens,
9-
CodeLensParams, CompletionParams, CompletionResponse, DiagnosticTag,
9+
CodeLensParams, CompletionParams, CompletionResponse, ConfigurationParams, DiagnosticTag,
1010
DidChangeConfigurationParams, DidChangeWatchedFilesParams,
1111
DidChangeWatchedFilesRegistrationOptions, DocumentOnTypeFormattingParams, DocumentSymbolParams,
1212
DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType,

0 commit comments

Comments
 (0)