aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/rust-analyzer/src/config.rs4
-rw-r--r--crates/rust-analyzer/src/global_state.rs23
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs30
-rw-r--r--crates/rust-analyzer/src/main_loop.rs24
-rw-r--r--crates/rust-analyzer/src/op_queue.rs16
-rw-r--r--crates/rust-analyzer/src/reload.rs119
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/support.rs11
7 files changed, 119 insertions, 108 deletions
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index cda272fd4..e012b4452 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -445,8 +445,8 @@ impl Config {
445 pub fn hover_actions(&self) -> bool { 445 pub fn hover_actions(&self) -> bool {
446 self.experimental("hoverActions") 446 self.experimental("hoverActions")
447 } 447 }
448 pub fn status_notification(&self) -> bool { 448 pub fn server_status_notification(&self) -> bool {
449 self.experimental("statusNotification") 449 self.experimental("serverStatusNotification")
450 } 450 }
451 451
452 pub fn publish_diagnostics(&self) -> bool { 452 pub fn publish_diagnostics(&self) -> bool {
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 44a656e62..adeb7a97e 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -23,6 +23,7 @@ use crate::{
23 document::DocumentData, 23 document::DocumentData,
24 from_proto, 24 from_proto,
25 line_index::{LineEndings, LineIndex}, 25 line_index::{LineEndings, LineIndex},
26 lsp_ext,
26 main_loop::Task, 27 main_loop::Task,
27 op_queue::OpQueue, 28 op_queue::OpQueue,
28 reload::SourceRootConfig, 29 reload::SourceRootConfig,
@@ -32,20 +33,6 @@ use crate::{
32 Result, 33 Result,
33}; 34};
34 35
35#[derive(Eq, PartialEq, Copy, Clone)]
36pub(crate) enum Status {
37 Loading,
38 Ready { partial: bool },
39 Invalid,
40 NeedsReload,
41}
42
43impl Default for Status {
44 fn default() -> Self {
45 Status::Loading
46 }
47}
48
49// Enforces drop order 36// Enforces drop order
50pub(crate) struct Handle<H, C> { 37pub(crate) struct Handle<H, C> {
51 pub(crate) handle: H, 38 pub(crate) handle: H,
@@ -73,7 +60,7 @@ pub(crate) struct GlobalState {
73 pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>, 60 pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>,
74 pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>, 61 pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
75 pub(crate) shutdown_requested: bool, 62 pub(crate) shutdown_requested: bool,
76 pub(crate) status: Status, 63 pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
77 pub(crate) source_root_config: SourceRootConfig, 64 pub(crate) source_root_config: SourceRootConfig,
78 pub(crate) proc_macro_client: Option<ProcMacroClient>, 65 pub(crate) proc_macro_client: Option<ProcMacroClient>,
79 66
@@ -83,6 +70,7 @@ pub(crate) struct GlobalState {
83 70
84 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, 71 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
85 pub(crate) vfs_config_version: u32, 72 pub(crate) vfs_config_version: u32,
73 pub(crate) vfs_progress_config_version: u32,
86 pub(crate) vfs_progress_n_total: usize, 74 pub(crate) vfs_progress_n_total: usize,
87 pub(crate) vfs_progress_n_done: usize, 75 pub(crate) vfs_progress_n_done: usize,
88 76
@@ -141,7 +129,7 @@ impl GlobalState {
141 mem_docs: FxHashMap::default(), 129 mem_docs: FxHashMap::default(),
142 semantic_tokens_cache: Arc::new(Default::default()), 130 semantic_tokens_cache: Arc::new(Default::default()),
143 shutdown_requested: false, 131 shutdown_requested: false,
144 status: Status::default(), 132 last_reported_status: None,
145 source_root_config: SourceRootConfig::default(), 133 source_root_config: SourceRootConfig::default(),
146 proc_macro_client: None, 134 proc_macro_client: None,
147 135
@@ -151,14 +139,15 @@ impl GlobalState {
151 139
152 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), 140 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
153 vfs_config_version: 0, 141 vfs_config_version: 0,
142 vfs_progress_config_version: 0,
154 vfs_progress_n_total: 0, 143 vfs_progress_n_total: 0,
155 vfs_progress_n_done: 0, 144 vfs_progress_n_done: 0,
156 145
157 workspaces: Arc::new(Vec::new()), 146 workspaces: Arc::new(Vec::new()),
158 fetch_workspaces_queue: OpQueue::default(), 147 fetch_workspaces_queue: OpQueue::default(),
159 workspace_build_data: None, 148 workspace_build_data: None,
160 fetch_build_data_queue: OpQueue::default(),
161 149
150 fetch_build_data_queue: OpQueue::default(),
162 latest_requests: Default::default(), 151 latest_requests: Default::default(),
163 } 152 }
164 } 153 }
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 0e1fec209..81a6f22f1 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -241,26 +241,26 @@ pub struct SsrParams {
241 pub selections: Vec<lsp_types::Range>, 241 pub selections: Vec<lsp_types::Range>,
242} 242}
243 243
244pub enum StatusNotification {} 244pub enum ServerStatusNotification {}
245 245
246#[derive(Serialize, Deserialize)] 246impl Notification for ServerStatusNotification {
247#[serde(rename_all = "camelCase")] 247 type Params = ServerStatusParams;
248pub enum Status { 248 const METHOD: &'static str = "experimental/serverStatus";
249 Loading,
250 ReadyPartial,
251 Ready,
252 NeedsReload,
253 Invalid,
254} 249}
255 250
256#[derive(Deserialize, Serialize)] 251#[derive(Deserialize, Serialize, PartialEq, Eq, Clone)]
257pub struct StatusParams { 252pub struct ServerStatusParams {
258 pub status: Status, 253 pub health: Health,
254 pub quiescent: bool,
255 pub message: Option<String>,
259} 256}
260 257
261impl Notification for StatusNotification { 258#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
262 type Params = StatusParams; 259#[serde(rename_all = "camelCase")]
263 const METHOD: &'static str = "rust-analyzer/status"; 260pub enum Health {
261 Ok,
262 Warning,
263 Error,
264} 264}
265 265
266pub enum CodeActionRequest {} 266pub enum CodeActionRequest {}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 4a4705e1f..a5655116b 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -21,7 +21,7 @@ use crate::{
21 dispatch::{NotificationDispatcher, RequestDispatcher}, 21 dispatch::{NotificationDispatcher, RequestDispatcher},
22 document::DocumentData, 22 document::DocumentData,
23 from_proto, 23 from_proto,
24 global_state::{file_id_to_url, url_to_file_id, GlobalState, Status}, 24 global_state::{file_id_to_url, url_to_file_id, GlobalState},
25 handlers, lsp_ext, 25 handlers, lsp_ext,
26 lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress}, 26 lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress},
27 reload::{BuildDataProgress, ProjectWorkspaceProgress}, 27 reload::{BuildDataProgress, ProjectWorkspaceProgress},
@@ -189,7 +189,7 @@ impl GlobalState {
189 log::info!("task queue len: {}", task_queue_len); 189 log::info!("task queue len: {}", task_queue_len);
190 } 190 }
191 191
192 let mut new_status = self.status; 192 let was_quiescent = self.is_quiescent();
193 match event { 193 match event {
194 Event::Lsp(msg) => match msg { 194 Event::Lsp(msg) => match msg {
195 lsp_server::Message::Request(req) => self.on_request(loop_start, req)?, 195 lsp_server::Message::Request(req) => self.on_request(loop_start, req)?,
@@ -314,9 +314,12 @@ impl GlobalState {
314 } 314 }
315 } 315 }
316 vfs::loader::Message::Progress { n_total, n_done, config_version } => { 316 vfs::loader::Message::Progress { n_total, n_done, config_version } => {
317 always!(config_version <= self.vfs_config_version);
318
319 self.vfs_progress_config_version = config_version;
317 self.vfs_progress_n_total = n_total; 320 self.vfs_progress_n_total = n_total;
318 self.vfs_progress_n_done = n_done; 321 self.vfs_progress_n_done = n_done;
319 always!(config_version <= self.vfs_config_version); 322
320 let state = if n_done == 0 { 323 let state = if n_done == 0 {
321 Progress::Begin 324 Progress::Begin
322 } else if n_done < n_total { 325 } else if n_done < n_total {
@@ -406,18 +409,14 @@ impl GlobalState {
406 } 409 }
407 410
408 let state_changed = self.process_changes(); 411 let state_changed = self.process_changes();
409 let prev_status = self.status; 412
410 if prev_status != new_status { 413 if self.is_quiescent() && !was_quiescent {
411 self.transition(new_status);
412 }
413 let is_ready = matches!(self.status, Status::Ready { .. });
414 if prev_status == Status::Loading && is_ready {
415 for flycheck in &self.flycheck { 414 for flycheck in &self.flycheck {
416 flycheck.update(); 415 flycheck.update();
417 } 416 }
418 } 417 }
419 418
420 if is_ready && (state_changed || prev_status == Status::Loading) { 419 if self.is_quiescent() && (!was_quiescent || state_changed) {
421 self.update_file_notifications_on_threadpool(); 420 self.update_file_notifications_on_threadpool();
422 421
423 // Refresh semantic tokens if the client supports it. 422 // Refresh semantic tokens if the client supports it.
@@ -451,6 +450,8 @@ impl GlobalState {
451 } 450 }
452 self.fetch_build_data_if_needed(); 451 self.fetch_build_data_if_needed();
453 452
453 self.report_new_status_if_needed();
454
454 let loop_duration = loop_start.elapsed(); 455 let loop_duration = loop_start.elapsed();
455 if loop_duration > Duration::from_millis(100) { 456 if loop_duration > Duration::from_millis(100) {
456 log::warn!("overly long loop turn: {:?}", loop_duration); 457 log::warn!("overly long loop turn: {:?}", loop_duration);
@@ -477,7 +478,8 @@ impl GlobalState {
477 return Ok(()); 478 return Ok(());
478 } 479 }
479 480
480 if self.status == Status::Loading && req.method != "shutdown" { 481 // Avoid flashing a bunch of unresolved references during initial load.
482 if self.workspaces.is_empty() && !self.is_quiescent() {
481 self.respond(lsp_server::Response::new_err( 483 self.respond(lsp_server::Response::new_err(
482 req.id, 484 req.id,
483 // FIXME: i32 should impl From<ErrorCode> (from() guarantees lossless conversion) 485 // FIXME: i32 should impl From<ErrorCode> (from() guarantees lossless conversion)
diff --git a/crates/rust-analyzer/src/op_queue.rs b/crates/rust-analyzer/src/op_queue.rs
index f71b718bc..1d612a933 100644
--- a/crates/rust-analyzer/src/op_queue.rs
+++ b/crates/rust-analyzer/src/op_queue.rs
@@ -2,27 +2,27 @@
2//! at a time. 2//! at a time.
3 3
4pub(crate) struct OpQueue<Args, Output> { 4pub(crate) struct OpQueue<Args, Output> {
5 op_scheduled: Option<Args>, 5 op_requested: Option<Args>,
6 op_in_progress: bool, 6 op_in_progress: bool,
7 last_op_result: Output, 7 last_op_result: Output,
8} 8}
9 9
10impl<Args, Output: Default> Default for OpQueue<Args, Output> { 10impl<Args, Output: Default> Default for OpQueue<Args, Output> {
11 fn default() -> Self { 11 fn default() -> Self {
12 Self { op_scheduled: None, op_in_progress: false, last_op_result: Default::default() } 12 Self { op_requested: None, op_in_progress: false, last_op_result: Default::default() }
13 } 13 }
14} 14}
15 15
16impl<Args, Output> OpQueue<Args, Output> { 16impl<Args, Output> OpQueue<Args, Output> {
17 pub(crate) fn request_op(&mut self, data: Args) { 17 pub(crate) fn request_op(&mut self, data: Args) {
18 self.op_scheduled = Some(data); 18 self.op_requested = Some(data);
19 } 19 }
20 pub(crate) fn should_start_op(&mut self) -> Option<Args> { 20 pub(crate) fn should_start_op(&mut self) -> Option<Args> {
21 if self.op_in_progress { 21 if self.op_in_progress {
22 return None; 22 return None;
23 } 23 }
24 self.op_in_progress = self.op_scheduled.is_some(); 24 self.op_in_progress = self.op_requested.is_some();
25 self.op_scheduled.take() 25 self.op_requested.take()
26 } 26 }
27 pub(crate) fn op_completed(&mut self, result: Output) { 27 pub(crate) fn op_completed(&mut self, result: Output) {
28 assert!(self.op_in_progress); 28 assert!(self.op_in_progress);
@@ -34,4 +34,10 @@ impl<Args, Output> OpQueue<Args, Output> {
34 pub(crate) fn last_op_result(&self) -> &Output { 34 pub(crate) fn last_op_result(&self) -> &Output {
35 &self.last_op_result 35 &self.last_op_result
36 } 36 }
37 pub(crate) fn op_in_progress(&self) -> bool {
38 self.op_in_progress
39 }
40 pub(crate) fn op_requested(&self) -> bool {
41 self.op_requested.is_some()
42 }
37} 43}
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 5ff60c22a..301c7003b 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -9,11 +9,10 @@ use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};
9 9
10use crate::{ 10use crate::{
11 config::{Config, FilesWatcher, LinkedProject}, 11 config::{Config, FilesWatcher, LinkedProject},
12 global_state::{GlobalState, Status}, 12 global_state::GlobalState,
13 lsp_ext, 13 lsp_ext,
14 main_loop::Task, 14 main_loop::Task,
15}; 15};
16use lsp_ext::StatusParams;
17 16
18#[derive(Debug)] 17#[derive(Debug)]
19pub(crate) enum ProjectWorkspaceProgress { 18pub(crate) enum ProjectWorkspaceProgress {
@@ -30,6 +29,13 @@ pub(crate) enum BuildDataProgress {
30} 29}
31 30
32impl GlobalState { 31impl GlobalState {
32 pub(crate) fn is_quiescent(&self) -> bool {
33 !(self.fetch_workspaces_queue.op_in_progress()
34 || self.fetch_build_data_queue.op_in_progress()
35 || self.vfs_progress_config_version < self.vfs_config_version
36 || self.vfs_progress_n_done < self.vfs_progress_n_total)
37 }
38
33 pub(crate) fn update_configuration(&mut self, config: Config) { 39 pub(crate) fn update_configuration(&mut self, config: Config) {
34 let _p = profile::span("GlobalState::update_configuration"); 40 let _p = profile::span("GlobalState::update_configuration");
35 let old_config = mem::replace(&mut self.config, Arc::new(config)); 41 let old_config = mem::replace(&mut self.config, Arc::new(config));
@@ -46,17 +52,13 @@ impl GlobalState {
46 if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) { 52 if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) {
47 return; 53 return;
48 } 54 }
49 match self.status {
50 Status::Loading | Status::NeedsReload => return,
51 Status::Ready { .. } | Status::Invalid => (),
52 }
53 log::info!( 55 log::info!(
54 "Reloading workspace because of the following changes: {}", 56 "Requesting workspace reload because of the following changes: {}",
55 itertools::join( 57 itertools::join(
56 changes 58 changes
57 .iter() 59 .iter()
58 .filter(|(path, kind)| is_interesting(path, *kind)) 60 .filter(|(path, kind)| is_interesting(path, *kind))
59 .map(|(path, kind)| format!("{}/{:?}", path.display(), kind)), 61 .map(|(path, kind)| format!("{}: {:?}", path.display(), kind)),
60 ", " 62 ", "
61 ) 63 )
62 ); 64 );
@@ -97,19 +99,31 @@ impl GlobalState {
97 false 99 false
98 } 100 }
99 } 101 }
100 pub(crate) fn transition(&mut self, new_status: Status) { 102 pub(crate) fn report_new_status_if_needed(&mut self) {
101 self.status = new_status; 103 if !self.config.server_status_notification() {
102 if self.config.status_notification() { 104 return;
103 let lsp_status = match new_status { 105 }
104 Status::Loading => lsp_ext::Status::Loading, 106
105 Status::Ready { partial: true } => lsp_ext::Status::ReadyPartial, 107 let mut status = lsp_ext::ServerStatusParams {
106 Status::Ready { partial: false } => lsp_ext::Status::Ready, 108 health: lsp_ext::Health::Ok,
107 Status::Invalid => lsp_ext::Status::Invalid, 109 quiescent: self.is_quiescent(),
108 Status::NeedsReload => lsp_ext::Status::NeedsReload, 110 message: None,
109 }; 111 };
110 self.send_notification::<lsp_ext::StatusNotification>(StatusParams { 112 if !self.config.cargo_autoreload()
111 status: lsp_status, 113 && self.is_quiescent()
112 }); 114 && self.fetch_workspaces_queue.op_requested()
115 {
116 status.health = lsp_ext::Health::Warning;
117 status.message = Some("Workspace reload required".to_string())
118 }
119 if let Some(error) = self.loading_error() {
120 status.health = lsp_ext::Health::Error;
121 status.message = Some(format!("Workspace reload failed: {}", error))
122 }
123
124 if self.last_reported_status.as_ref() != Some(&status) {
125 self.last_reported_status = Some(status.clone());
126 self.send_notification::<lsp_ext::ServerStatusNotification>(status);
113 } 127 }
114 } 128 }
115 129
@@ -201,45 +215,28 @@ impl GlobalState {
201 215
202 pub(crate) fn switch_workspaces(&mut self) { 216 pub(crate) fn switch_workspaces(&mut self) {
203 let _p = profile::span("GlobalState::switch_workspaces"); 217 let _p = profile::span("GlobalState::switch_workspaces");
204 let workspaces = self.fetch_workspaces_queue.last_op_result(); 218 log::info!("will switch workspaces");
205 log::info!("will switch workspaces: {:?}", workspaces); 219
220 if let Some(error_message) = self.loading_error() {
221 log::error!("failed to switch workspaces: {}", error_message);
222 self.show_message(lsp_types::MessageType::Error, error_message);
223 if !self.workspaces.is_empty() {
224 return;
225 }
226 }
206 227
207 let mut error_message = None; 228 let workspaces = self
208 let workspaces = workspaces 229 .fetch_workspaces_queue
230 .last_op_result()
209 .iter() 231 .iter()
210 .filter_map(|res| match res { 232 .filter_map(|res| res.as_ref().ok().cloned())
211 Ok(it) => Some(it.clone()),
212 Err(err) => {
213 log::error!("failed to load workspace: {:#}", err);
214 let message = error_message.get_or_insert_with(String::new);
215 stdx::format_to!(
216 message,
217 "rust-analyzer failed to load workspace: {:#}\n",
218 err
219 );
220 None
221 }
222 })
223 .collect::<Vec<_>>(); 233 .collect::<Vec<_>>();
224 234
225 let workspace_build_data = match self.fetch_build_data_queue.last_op_result() { 235 let workspace_build_data = match self.fetch_build_data_queue.last_op_result() {
226 Some(Ok(it)) => Some(it.clone()), 236 Some(Ok(it)) => Some(it.clone()),
227 None => None, 237 None | Some(Err(_)) => None,
228 Some(Err(err)) => {
229 log::error!("failed to fetch build data: {:#}", err);
230 let message = error_message.get_or_insert_with(String::new);
231 stdx::format_to!(message, "rust-analyzer failed to fetch build data: {:#}\n", err);
232 None
233 }
234 }; 238 };
235 239
236 if let Some(error_message) = error_message {
237 self.show_message(lsp_types::MessageType::Error, error_message);
238 if !self.workspaces.is_empty() {
239 return;
240 }
241 }
242
243 if *self.workspaces == workspaces && self.workspace_build_data == workspace_build_data { 240 if *self.workspaces == workspaces && self.workspace_build_data == workspace_build_data {
244 return; 241 return;
245 } 242 }
@@ -346,6 +343,24 @@ impl GlobalState {
346 log::info!("did switch workspaces"); 343 log::info!("did switch workspaces");
347 } 344 }
348 345
346 fn loading_error(&self) -> Option<String> {
347 let mut message = None;
348
349 for ws in self.fetch_workspaces_queue.last_op_result() {
350 if let Err(err) = ws {
351 let message = message.get_or_insert_with(String::new);
352 stdx::format_to!(message, "rust-analyzer failed to load workspace: {:#}\n", err);
353 }
354 }
355
356 if let Some(Err(err)) = self.fetch_build_data_queue.last_op_result() {
357 let message = message.get_or_insert_with(String::new);
358 stdx::format_to!(message, "rust-analyzer failed to fetch build data: {:#}\n", err);
359 }
360
361 message
362 }
363
349 fn reload_flycheck(&mut self) { 364 fn reload_flycheck(&mut self) {
350 let _p = profile::span("GlobalState::reload_flycheck"); 365 let _p = profile::span("GlobalState::reload_flycheck");
351 let config = match self.config.flycheck() { 366 let config = match self.config.flycheck() {
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs
index 95bf26f01..8d68f1b7d 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/support.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs
@@ -103,7 +103,7 @@ impl<'a> Project<'a> {
103 ..Default::default() 103 ..Default::default()
104 }), 104 }),
105 experimental: Some(json!({ 105 experimental: Some(json!({
106 "statusNotification": true, 106 "serverStatusNotification": true,
107 })), 107 })),
108 ..Default::default() 108 ..Default::default()
109 }, 109 },
@@ -213,13 +213,12 @@ impl Server {
213 } 213 }
214 pub(crate) fn wait_until_workspace_is_loaded(self) -> Server { 214 pub(crate) fn wait_until_workspace_is_loaded(self) -> Server {
215 self.wait_for_message_cond(1, &|msg: &Message| match msg { 215 self.wait_for_message_cond(1, &|msg: &Message| match msg {
216 Message::Notification(n) if n.method == "rust-analyzer/status" => { 216 Message::Notification(n) if n.method == "experimental/serverStatus" => {
217 let status = n 217 let status = n
218 .clone() 218 .clone()
219 .extract::<lsp_ext::StatusParams>("rust-analyzer/status") 219 .extract::<lsp_ext::ServerStatusParams>("experimental/serverStatus")
220 .unwrap() 220 .unwrap();
221 .status; 221 status.quiescent
222 matches!(status, lsp_ext::Status::Ready)
223 } 222 }
224 _ => false, 223 _ => false,
225 }) 224 })