aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock4
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/global_state.rs13
-rw-r--r--crates/rust-analyzer/src/handlers.rs22
-rw-r--r--crates/rust-analyzer/src/main_loop.rs67
-rw-r--r--crates/rust-analyzer/src/to_proto.rs7
-rw-r--r--docs/dev/lsp-extensions.md8
-rw-r--r--docs/user/manual.adoc4
8 files changed, 87 insertions, 40 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 280d97432..af7062815 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -619,9 +619,9 @@ dependencies = [
619 619
620[[package]] 620[[package]]
621name = "lsp-types" 621name = "lsp-types"
622version = "0.77.0" 622version = "0.78.0"
623source = "registry+https://github.com/rust-lang/crates.io-index" 623source = "registry+https://github.com/rust-lang/crates.io-index"
624checksum = "897c6c8930fbf12b67deffc83729287bb379dd5e5a4bd0ae2d81eff8d6503db6" 624checksum = "d2e6cf68e3492cfa2035f0382c1da1b6ab045db0320feca505b86b4f13d66c27"
625dependencies = [ 625dependencies = [
626 "base64", 626 "base64",
627 "bitflags", 627 "bitflags",
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 023c104d1..758aa1c5d 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -20,7 +20,7 @@ env_logger = { version = "0.7.1", default-features = false }
20itertools = "0.9.0" 20itertools = "0.9.0"
21jod-thread = "0.1.0" 21jod-thread = "0.1.0"
22log = "0.4.8" 22log = "0.4.8"
23lsp-types = { version = "0.77.0", features = ["proposed"] } 23lsp-types = { version = "0.78.0", features = ["proposed"] }
24parking_lot = "0.11.0" 24parking_lot = "0.11.0"
25pico-args = "0.3.1" 25pico-args = "0.3.1"
26rand = { version = "0.7.3", features = ["small_rng"] } 26rand = { version = "0.7.3", features = ["small_rng"] }
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 94973b90a..80937dbc4 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -12,7 +12,7 @@ use parking_lot::RwLock;
12use ra_db::{CrateId, VfsPath}; 12use ra_db::{CrateId, VfsPath};
13use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId}; 13use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId};
14use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; 14use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target};
15use rustc_hash::{FxHashMap, FxHashSet}; 15use rustc_hash::FxHashMap;
16 16
17use crate::{ 17use crate::{
18 config::Config, 18 config::Config,
@@ -69,7 +69,7 @@ pub(crate) struct GlobalState {
69 pub(crate) config: Config, 69 pub(crate) config: Config,
70 pub(crate) analysis_host: AnalysisHost, 70 pub(crate) analysis_host: AnalysisHost,
71 pub(crate) diagnostics: DiagnosticCollection, 71 pub(crate) diagnostics: DiagnosticCollection,
72 pub(crate) mem_docs: FxHashSet<VfsPath>, 72 pub(crate) mem_docs: FxHashMap<VfsPath, Option<i64>>,
73 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, 73 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
74 pub(crate) status: Status, 74 pub(crate) status: Status,
75 pub(crate) source_root_config: SourceRootConfig, 75 pub(crate) source_root_config: SourceRootConfig,
@@ -84,6 +84,7 @@ pub(crate) struct GlobalStateSnapshot {
84 pub(crate) analysis: Analysis, 84 pub(crate) analysis: Analysis,
85 pub(crate) check_fixes: CheckFixes, 85 pub(crate) check_fixes: CheckFixes,
86 pub(crate) latest_requests: Arc<RwLock<LatestRequests>>, 86 pub(crate) latest_requests: Arc<RwLock<LatestRequests>>,
87 mem_docs: FxHashMap<VfsPath, Option<i64>>,
87 vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, 88 vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
88 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, 89 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
89} 90}
@@ -117,7 +118,7 @@ impl GlobalState {
117 config, 118 config,
118 analysis_host, 119 analysis_host,
119 diagnostics: Default::default(), 120 diagnostics: Default::default(),
120 mem_docs: FxHashSet::default(), 121 mem_docs: FxHashMap::default(),
121 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), 122 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
122 status: Status::default(), 123 status: Status::default(),
123 source_root_config: SourceRootConfig::default(), 124 source_root_config: SourceRootConfig::default(),
@@ -183,6 +184,7 @@ impl GlobalState {
183 vfs: Arc::clone(&self.vfs), 184 vfs: Arc::clone(&self.vfs),
184 latest_requests: Arc::clone(&self.latest_requests), 185 latest_requests: Arc::clone(&self.latest_requests),
185 check_fixes: Arc::clone(&self.diagnostics.check_fixes), 186 check_fixes: Arc::clone(&self.diagnostics.check_fixes),
187 mem_docs: self.mem_docs.clone(),
186 } 188 }
187 } 189 }
188 190
@@ -255,6 +257,11 @@ impl GlobalStateSnapshot {
255 self.vfs.read().1[&id] 257 self.vfs.read().1[&id]
256 } 258 }
257 259
260 pub(crate) fn url_file_version(&self, url: &Url) -> Option<i64> {
261 let path = from_proto::vfs_path(&url).ok()?;
262 self.mem_docs.get(&path).copied()?
263 }
264
258 pub(crate) fn anchored_path(&self, file_id: FileId, path: &str) -> Url { 265 pub(crate) fn anchored_path(&self, file_id: FileId, path: &str) -> Url {
259 let mut base = self.vfs.read().0.file_path(file_id); 266 let mut base = self.vfs.read().0.file_path(file_id);
260 base.pop(); 267 base.pop();
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index d880570d2..8d8c9442b 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -15,7 +15,7 @@ use lsp_types::{
15 DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location, 15 DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location,
16 Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensParams, 16 Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensParams,
17 SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, 17 SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
18 TextDocumentIdentifier, Url, WorkspaceEdit, 18 SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
19}; 19};
20use ra_ide::{ 20use ra_ide::{
21 FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, NavigationTarget, Query, 21 FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, NavigationTarget, Query,
@@ -253,10 +253,17 @@ pub(crate) fn handle_document_symbol(
253 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); 253 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new();
254 254
255 for symbol in snap.analysis.file_structure(file_id)? { 255 for symbol in snap.analysis.file_structure(file_id)? {
256 let mut tags = Vec::new();
257 if symbol.deprecated {
258 tags.push(SymbolTag::Deprecated)
259 };
260
261 #[allow(deprecated)]
256 let doc_symbol = DocumentSymbol { 262 let doc_symbol = DocumentSymbol {
257 name: symbol.label, 263 name: symbol.label,
258 detail: symbol.detail, 264 detail: symbol.detail,
259 kind: to_proto::symbol_kind(symbol.kind), 265 kind: to_proto::symbol_kind(symbol.kind),
266 tags: Some(tags),
260 deprecated: Some(symbol.deprecated), 267 deprecated: Some(symbol.deprecated),
261 range: to_proto::range(&line_index, symbol.node_range), 268 range: to_proto::range(&line_index, symbol.node_range),
262 selection_range: to_proto::range(&line_index, symbol.navigation_range), 269 selection_range: to_proto::range(&line_index, symbol.navigation_range),
@@ -296,9 +303,19 @@ pub(crate) fn handle_document_symbol(
296 url: &Url, 303 url: &Url,
297 res: &mut Vec<SymbolInformation>, 304 res: &mut Vec<SymbolInformation>,
298 ) { 305 ) {
306 let mut tags = Vec::new();
307
308 #[allow(deprecated)]
309 match symbol.deprecated {
310 Some(true) => tags.push(SymbolTag::Deprecated),
311 _ => {}
312 }
313
314 #[allow(deprecated)]
299 res.push(SymbolInformation { 315 res.push(SymbolInformation {
300 name: symbol.name.clone(), 316 name: symbol.name.clone(),
301 kind: symbol.kind, 317 kind: symbol.kind,
318 tags: Some(tags),
302 deprecated: symbol.deprecated, 319 deprecated: symbol.deprecated,
303 location: Location::new(url.clone(), symbol.range), 320 location: Location::new(url.clone(), symbol.range),
304 container_name, 321 container_name,
@@ -342,9 +359,12 @@ pub(crate) fn handle_workspace_symbol(
342 let mut res = Vec::new(); 359 let mut res = Vec::new();
343 for nav in snap.analysis.symbol_search(query)? { 360 for nav in snap.analysis.symbol_search(query)? {
344 let container_name = nav.container_name.as_ref().map(|v| v.to_string()); 361 let container_name = nav.container_name.as_ref().map(|v| v.to_string());
362
363 #[allow(deprecated)]
345 let info = SymbolInformation { 364 let info = SymbolInformation {
346 name: nav.name.to_string(), 365 name: nav.name.to_string(),
347 kind: to_proto::symbol_kind(nav.kind), 366 kind: to_proto::symbol_kind(nav.kind),
367 tags: None,
348 location: to_proto::location_from_nav(snap, nav)?, 368 location: to_proto::location_from_nav(snap, nav)?,
349 container_name, 369 container_name,
350 deprecated: None, 370 deprecated: None,
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index bb7c4c0c6..ac95e428e 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -97,22 +97,6 @@ impl fmt::Debug for Event {
97} 97}
98 98
99impl GlobalState { 99impl GlobalState {
100 fn next_event(&self, inbox: &Receiver<lsp_server::Message>) -> Option<Event> {
101 select! {
102 recv(inbox) -> msg =>
103 msg.ok().map(Event::Lsp),
104
105 recv(self.task_pool.receiver) -> task =>
106 Some(Event::Task(task.unwrap())),
107
108 recv(self.loader.receiver) -> task =>
109 Some(Event::Vfs(task.unwrap())),
110
111 recv(self.flycheck_receiver) -> task =>
112 Some(Event::Flycheck(task.unwrap())),
113 }
114 }
115
116 fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> { 100 fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> {
117 if self.config.linked_projects.is_empty() && self.config.notifications.cargo_toml_not_found 101 if self.config.linked_projects.is_empty() && self.config.notifications.cargo_toml_not_found
118 { 102 {
@@ -169,6 +153,22 @@ impl GlobalState {
169 Err("client exited without proper shutdown sequence")? 153 Err("client exited without proper shutdown sequence")?
170 } 154 }
171 155
156 fn next_event(&self, inbox: &Receiver<lsp_server::Message>) -> Option<Event> {
157 select! {
158 recv(inbox) -> msg =>
159 msg.ok().map(Event::Lsp),
160
161 recv(self.task_pool.receiver) -> task =>
162 Some(Event::Task(task.unwrap())),
163
164 recv(self.loader.receiver) -> task =>
165 Some(Event::Vfs(task.unwrap())),
166
167 recv(self.flycheck_receiver) -> task =>
168 Some(Event::Flycheck(task.unwrap())),
169 }
170 }
171
172 fn handle_event(&mut self, event: Event) -> Result<()> { 172 fn handle_event(&mut self, event: Event) -> Result<()> {
173 let loop_start = Instant::now(); 173 let loop_start = Instant::now();
174 // NOTE: don't count blocking select! call as a loop-turn time 174 // NOTE: don't count blocking select! call as a loop-turn time
@@ -210,7 +210,7 @@ impl GlobalState {
210 let vfs = &mut self.vfs.write().0; 210 let vfs = &mut self.vfs.write().0;
211 for (path, contents) in files { 211 for (path, contents) in files {
212 let path = VfsPath::from(path); 212 let path = VfsPath::from(path);
213 if !self.mem_docs.contains(&path) { 213 if !self.mem_docs.contains_key(&path) {
214 vfs.set_file_contents(path, contents) 214 vfs.set_file_contents(path, contents)
215 } 215 }
216 } 216 }
@@ -299,7 +299,7 @@ impl GlobalState {
299 if self.status == Status::Ready && (state_changed || prev_status == Status::Loading) { 299 if self.status == Status::Ready && (state_changed || prev_status == Status::Loading) {
300 let subscriptions = self 300 let subscriptions = self
301 .mem_docs 301 .mem_docs
302 .iter() 302 .keys()
303 .map(|path| self.vfs.read().0.file_id(&path).unwrap()) 303 .map(|path| self.vfs.read().0.file_id(&path).unwrap())
304 .collect::<Vec<_>>(); 304 .collect::<Vec<_>>();
305 305
@@ -310,8 +310,12 @@ impl GlobalState {
310 for file_id in diagnostic_changes { 310 for file_id in diagnostic_changes {
311 let url = file_id_to_url(&self.vfs.read().0, file_id); 311 let url = file_id_to_url(&self.vfs.read().0, file_id);
312 let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect(); 312 let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect();
313 let version = from_proto::vfs_path(&url)
314 .map(|path| self.mem_docs.get(&path).copied().flatten())
315 .unwrap_or_default();
316
313 self.send_notification::<lsp_types::notification::PublishDiagnostics>( 317 self.send_notification::<lsp_types::notification::PublishDiagnostics>(
314 lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None }, 318 lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version },
315 ); 319 );
316 } 320 }
317 } 321 }
@@ -400,7 +404,11 @@ impl GlobalState {
400 })? 404 })?
401 .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| { 405 .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| {
402 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) { 406 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
403 if !this.mem_docs.insert(path.clone()) { 407 if this
408 .mem_docs
409 .insert(path.clone(), Some(params.text_document.version))
410 .is_some()
411 {
404 log::error!("duplicate DidOpenTextDocument: {}", path) 412 log::error!("duplicate DidOpenTextDocument: {}", path)
405 } 413 }
406 this.vfs 414 this.vfs
@@ -412,29 +420,38 @@ impl GlobalState {
412 })? 420 })?
413 .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| { 421 .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| {
414 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) { 422 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
415 assert!(this.mem_docs.contains(&path)); 423 *this.mem_docs.get_mut(&path).unwrap() = params.text_document.version;
416 let vfs = &mut this.vfs.write().0; 424 let vfs = &mut this.vfs.write().0;
417 let file_id = vfs.file_id(&path).unwrap(); 425 let file_id = vfs.file_id(&path).unwrap();
418 let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap(); 426 let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap();
419 apply_document_changes(&mut text, params.content_changes); 427 apply_document_changes(&mut text, params.content_changes);
420 vfs.set_file_contents(path, Some(text.into_bytes())) 428 vfs.set_file_contents(path.clone(), Some(text.into_bytes()));
429
430 this.mem_docs.insert(path, params.text_document.version);
421 } 431 }
422 Ok(()) 432 Ok(())
423 })? 433 })?
424 .on::<lsp_types::notification::DidCloseTextDocument>(|this, params| { 434 .on::<lsp_types::notification::DidCloseTextDocument>(|this, params| {
435 let mut version = None;
425 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) { 436 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
426 if !this.mem_docs.remove(&path) { 437 match this.mem_docs.remove(&path) {
427 log::error!("orphan DidCloseTextDocument: {}", path) 438 Some(entry) => version = entry,
439 None => log::error!("orphan DidCloseTextDocument: {}", path),
428 } 440 }
441
429 if let Some(path) = path.as_path() { 442 if let Some(path) = path.as_path() {
430 this.loader.handle.invalidate(path.to_path_buf()); 443 this.loader.handle.invalidate(path.to_path_buf());
431 } 444 }
432 } 445 }
446
447 // Clear the diagnostics for the previously known version of the file.
448 // This prevents stale "cargo check" diagnostics if the file is
449 // closed, "cargo check" is run and then the file is reopened.
433 this.send_notification::<lsp_types::notification::PublishDiagnostics>( 450 this.send_notification::<lsp_types::notification::PublishDiagnostics>(
434 lsp_types::PublishDiagnosticsParams { 451 lsp_types::PublishDiagnosticsParams {
435 uri: params.text_document.uri, 452 uri: params.text_document.uri,
436 diagnostics: Vec::new(), 453 diagnostics: Vec::new(),
437 version: None, 454 version,
438 }, 455 },
439 ); 456 );
440 Ok(()) 457 Ok(())
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index fc94f28b9..c6935c029 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -480,9 +480,10 @@ pub(crate) fn url_from_abs_path(path: &Path) -> lsp_types::Url {
480pub(crate) fn versioned_text_document_identifier( 480pub(crate) fn versioned_text_document_identifier(
481 snap: &GlobalStateSnapshot, 481 snap: &GlobalStateSnapshot,
482 file_id: FileId, 482 file_id: FileId,
483 version: Option<i64>,
484) -> lsp_types::VersionedTextDocumentIdentifier { 483) -> lsp_types::VersionedTextDocumentIdentifier {
485 lsp_types::VersionedTextDocumentIdentifier { uri: url(snap, file_id), version } 484 let url = url(snap, file_id);
485 let version = snap.url_file_version(&url);
486 lsp_types::VersionedTextDocumentIdentifier { uri: url, version }
486} 487}
487 488
488pub(crate) fn location( 489pub(crate) fn location(
@@ -571,7 +572,7 @@ pub(crate) fn snippet_text_document_edit(
571 is_snippet: bool, 572 is_snippet: bool,
572 source_file_edit: SourceFileEdit, 573 source_file_edit: SourceFileEdit,
573) -> Result<lsp_ext::SnippetTextDocumentEdit> { 574) -> Result<lsp_ext::SnippetTextDocumentEdit> {
574 let text_document = versioned_text_document_identifier(snap, source_file_edit.file_id, None); 575 let text_document = versioned_text_document_identifier(snap, source_file_edit.file_id);
575 let line_index = snap.analysis.file_line_index(source_file_edit.file_id)?; 576 let line_index = snap.analysis.file_line_index(source_file_edit.file_id)?;
576 let line_endings = snap.file_line_endings(source_file_edit.file_id); 577 let line_endings = snap.file_line_endings(source_file_edit.file_id);
577 let edits = source_file_edit 578 let edits = source_file_edit
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 6d6bbac7c..98d14450b 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -11,11 +11,13 @@ If you want to be notified about the changes to this document, subscribe to [#46
11 11
12## `initializationOptions` 12## `initializationOptions`
13 13
14As `initializationOptions`, `rust-analyzer` expects `"rust-analyzer"` section of the configuration. 14For `initializationOptions`, `rust-analyzer` expects `"rust-analyzer"` section of the configuration.
15That is, `rust-analyzer` usually sends `"workspace/configuration"` request with `{ "items": ["rust-analyzer"] }` payload. 15That is, `rust-analyzer` usually sends `"workspace/configuration"` request with `{ "items": ["rust-analyzer"] }` payload.
16`initializationOptions` should contain the same data that would be in the first item of the result. 16`initializationOptions` should contain the same data that would be in the first item of the result.
17It's OK to not send anything, then all the settings would take their default values. 17If a language client does not know about `rust-analyzer`'s configuration options it can get sensible defaults by doing any of the following:
18However, some settings can not be changed after startup at the moment. 18 * Not sending `initializationOptions`
19 * Send `"initializationOptions": null`
20 * Send `"initializationOptions": {}`
19 21
20## Snippet `TextEdit` 22## Snippet `TextEdit`
21 23
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 978b463d5..57251b851 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -110,8 +110,8 @@ Here are some useful self-diagnostic commands:
110* **Rust Analyzer: Show RA Version** shows the version of `rust-analyzer` binary 110* **Rust Analyzer: Show RA Version** shows the version of `rust-analyzer` binary
111* **Rust Analyzer: Status** prints some statistics about the server, like the few latest LSP requests 111* **Rust Analyzer: Status** prints some statistics about the server, like the few latest LSP requests
112* To enable server-side logging, run with `env RA_LOG=info` and see `Output > Rust Analyzer Language Server` in VS Code's panel. 112* To enable server-side logging, run with `env RA_LOG=info` and see `Output > Rust Analyzer Language Server` in VS Code's panel.
113* To log all LSP requests, add `"rust-analyzer.trace.server": "verbose"` to the settings and look for `Server Trace` in the panel. 113* To log all LSP requests, add `"rust-analyzer.trace.server": "verbose"` to the settings and look for `Rust Analyzer Language Server Trace` in the panel.
114* To enable client-side logging, add `"rust-analyzer.trace.extension": true` to the settings and open the `Console` tab of VS Code developer tools. 114* To enable client-side logging, add `"rust-analyzer.trace.extension": true` to the settings and open `Output > Rust Analyzer Client` in the panel.
115 115
116=== rust-analyzer Language Server Binary 116=== rust-analyzer Language Server Binary
117 117