1 //! The main loop of `rust-analyzer` responsible for dispatching LSP
2 //! requests/replies and notifications back to the client.
7 time
::{Duration, Instant}
,
10 use always_assert
::always
;
11 use crossbeam_channel
::{select, Receiver}
;
12 use flycheck
::FlycheckHandle
;
13 use ide_db
::base_db
::{SourceDatabaseExt, VfsPath}
;
14 use itertools
::Itertools
;
15 use lsp_server
::{Connection, Notification, Request}
;
16 use lsp_types
::notification
::Notification
as _
;
17 use vfs
::{ChangeKind, FileId}
;
21 dispatch
::{NotificationDispatcher, RequestDispatcher}
,
23 global_state
::{file_id_to_url, url_to_file_id, GlobalState}
,
25 lsp_utils
::{apply_document_changes, notification_is, Progress}
,
26 mem_docs
::DocumentData
,
27 reload
::{self, BuildDataProgress, ProjectWorkspaceProgress}
,
31 pub fn main_loop(config
: Config
, connection
: Connection
) -> Result
<()> {
32 tracing
::info
!("initial config: {:#?}", config
);
34 // Windows scheduler implements priority boosts: if thread waits for an
35 // event (like a condvar), and event fires, priority of the thread is
36 // temporary bumped. This optimization backfires in our case: each time the
37 // `main_loop` schedules a task to run on a threadpool, the worker threads
38 // gets a higher priority, and (on a machine with fewer cores) displaces the
39 // main loop! We work-around this by marking the main loop as a
40 // higher-priority thread.
42 // https://docs.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities
43 // https://docs.microsoft.com/en-us/windows/win32/procthread/priority-boosts
44 // https://github.com/rust-lang/rust-analyzer/issues/2835
47 use winapi
::um
::processthreadsapi
::*;
48 let thread
= GetCurrentThread();
49 let thread_priority_above_normal
= 1;
50 SetThreadPriority(thread
, thread_priority_above_normal
);
53 GlobalState
::new(connection
.sender
, config
).run(connection
.receiver
)
57 Lsp(lsp_server
::Message
),
59 Vfs(vfs
::loader
::Message
),
60 Flycheck(flycheck
::Message
),
64 pub(crate) enum Task
{
65 Response(lsp_server
::Response
),
66 Retry(lsp_server
::Request
),
67 Diagnostics(Vec
<(FileId
, Vec
<lsp_types
::Diagnostic
>)>),
68 PrimeCaches(PrimeCachesProgress
),
69 FetchWorkspace(ProjectWorkspaceProgress
),
70 FetchBuildData(BuildDataProgress
),
74 pub(crate) enum PrimeCachesProgress
{
76 Report(ide
::ParallelPrimeCachesProgress
),
77 End { cancelled: bool }
,
80 impl fmt
::Debug
for Event
{
81 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
82 let debug_verbose_not
= |not
: &Notification
, f
: &mut fmt
::Formatter
<'_
>| {
83 f
.debug_struct("Notification").field("method", ¬
.method
).finish()
87 Event
::Lsp(lsp_server
::Message
::Notification(not
)) => {
88 if notification_is
::<lsp_types
::notification
::DidOpenTextDocument
>(not
)
89 || notification_is
::<lsp_types
::notification
::DidChangeTextDocument
>(not
)
91 return debug_verbose_not(not
, f
);
94 Event
::Task(Task
::Response(resp
)) => {
96 .debug_struct("Response")
97 .field("id", &resp
.id
)
98 .field("error", &resp
.error
)
104 Event
::Lsp(it
) => fmt
::Debug
::fmt(it
, f
),
105 Event
::Task(it
) => fmt
::Debug
::fmt(it
, f
),
106 Event
::Vfs(it
) => fmt
::Debug
::fmt(it
, f
),
107 Event
::Flycheck(it
) => fmt
::Debug
::fmt(it
, f
),
113 fn run(mut self, inbox
: Receiver
<lsp_server
::Message
>) -> Result
<()> {
114 if self.config
.linked_projects().is_empty()
115 && self.config
.detached_files().is_empty()
116 && self.config
.notifications().cargo_toml_not_found
118 self.show_and_log_error("rust-analyzer failed to discover workspace".to_string(), None
);
121 if self.config
.did_save_text_document_dynamic_registration() {
122 let save_registration_options
= lsp_types
::TextDocumentSaveRegistrationOptions
{
123 include_text
: Some(false),
124 text_document_registration_options
: lsp_types
::TextDocumentRegistrationOptions
{
125 document_selector
: Some(vec
![
126 lsp_types
::DocumentFilter
{
129 pattern
: Some("**/*.rs".into()),
131 lsp_types
::DocumentFilter
{
134 pattern
: Some("**/Cargo.toml".into()),
136 lsp_types
::DocumentFilter
{
139 pattern
: Some("**/Cargo.lock".into()),
145 let registration
= lsp_types
::Registration
{
146 id
: "textDocument/didSave".to_string(),
147 method
: "textDocument/didSave".to_string(),
148 register_options
: Some(serde_json
::to_value(save_registration_options
).unwrap()),
150 self.send_request
::<lsp_types
::request
::RegisterCapability
>(
151 lsp_types
::RegistrationParams { registrations: vec![registration] }
,
156 self.fetch_workspaces_queue
.request_op("startup".to_string());
157 if let Some(cause
) = self.fetch_workspaces_queue
.should_start_op() {
158 self.fetch_workspaces(cause
);
161 while let Some(event
) = self.next_event(&inbox
) {
162 if let Event
::Lsp(lsp_server
::Message
::Notification(not
)) = &event
{
163 if not
.method
== lsp_types
::notification
::Exit
::METHOD
{
167 self.handle_event(event
)?
170 Err("client exited without proper shutdown sequence".into())
173 fn next_event(&self, inbox
: &Receiver
<lsp_server
::Message
>) -> Option
<Event
> {
175 recv(inbox
) -> msg
=>
176 msg
.ok().map(Event
::Lsp
),
178 recv(self.task_pool
.receiver
) -> task
=>
179 Some(Event
::Task(task
.unwrap())),
181 recv(self.loader
.receiver
) -> task
=>
182 Some(Event
::Vfs(task
.unwrap())),
184 recv(self.flycheck_receiver
) -> task
=>
185 Some(Event
::Flycheck(task
.unwrap())),
189 fn handle_event(&mut self, event
: Event
) -> Result
<()> {
190 let loop_start
= Instant
::now();
191 // NOTE: don't count blocking select! call as a loop-turn time
192 let _p
= profile
::span("GlobalState::handle_event");
194 tracing
::debug
!("{:?} handle_event({:?})", loop_start
, event
);
195 let task_queue_len
= self.task_pool
.handle
.len();
196 if task_queue_len
> 0 {
197 tracing
::info
!("task queue len: {}", task_queue_len
);
200 let was_quiescent
= self.is_quiescent();
202 Event
::Lsp(msg
) => match msg
{
203 lsp_server
::Message
::Request(req
) => self.on_new_request(loop_start
, req
),
204 lsp_server
::Message
::Notification(not
) => {
205 self.on_notification(not
)?
;
207 lsp_server
::Message
::Response(resp
) => self.complete_request(resp
),
209 Event
::Task(task
) => {
210 let _p
= profile
::span("GlobalState::handle_event/task");
211 let mut prime_caches_progress
= Vec
::new();
213 self.handle_task(&mut prime_caches_progress
, task
);
214 // Coalesce multiple task events into one loop turn
215 while let Ok(task
) = self.task_pool
.receiver
.try_recv() {
216 self.handle_task(&mut prime_caches_progress
, task
);
219 for progress
in prime_caches_progress
{
220 let (state
, message
, fraction
);
222 PrimeCachesProgress
::Begin
=> {
223 state
= Progress
::Begin
;
227 PrimeCachesProgress
::Report(report
) => {
228 state
= Progress
::Report
;
230 message
= match &report
.crates_currently_indexing
[..] {
231 [crate_name
] => Some(format
!(
233 report
.crates_done
, report
.crates_total
, crate_name
235 [crate_name
, rest @
..] => Some(format
!(
236 "{}/{} ({} + {} more)",
245 fraction
= Progress
::fraction(report
.crates_done
, report
.crates_total
);
247 PrimeCachesProgress
::End { cancelled }
=> {
248 state
= Progress
::End
;
252 self.prime_caches_queue
.op_completed(());
254 self.prime_caches_queue
255 .request_op("restart after cancellation".to_string());
260 self.report_progress("Indexing", state
, message
, Some(fraction
), None
);
263 Event
::Vfs(message
) => {
264 let _p
= profile
::span("GlobalState::handle_event/vfs");
265 self.handle_vfs_msg(message
);
266 // Coalesce many VFS event into a single loop turn
267 while let Ok(message
) = self.loader
.receiver
.try_recv() {
268 self.handle_vfs_msg(message
);
271 Event
::Flycheck(message
) => {
272 let _p
= profile
::span("GlobalState::handle_event/flycheck");
273 self.handle_flycheck_msg(message
);
274 // Coalesce many flycheck updates into a single loop turn
275 while let Ok(message
) = self.flycheck_receiver
.try_recv() {
276 self.handle_flycheck_msg(message
);
281 let state_changed
= self.process_changes();
282 let memdocs_added_or_removed
= self.mem_docs
.take_changes();
284 if self.is_quiescent() {
285 let became_quiescent
= !(was_quiescent
286 || self.fetch_workspaces_queue
.op_requested()
287 || self.fetch_build_data_queue
.op_requested());
289 if became_quiescent
{
290 // Project has loaded properly, kick off initial flycheck
291 self.flycheck
.iter().for_each(FlycheckHandle
::restart
);
292 if self.config
.prefill_caches() {
293 self.prime_caches_queue
.request_op("became quiescent".to_string());
297 if !was_quiescent
|| state_changed
{
298 // Refresh semantic tokens if the client supports it.
299 if self.config
.semantic_tokens_refresh() {
300 self.semantic_tokens_cache
.lock().clear();
301 self.send_request
::<lsp_types
::request
::SemanticTokensRefresh
>((), |_
, _
| ());
304 // Refresh code lens if the client supports it.
305 if self.config
.code_lens_refresh() {
306 self.send_request
::<lsp_types
::request
::CodeLensRefresh
>((), |_
, _
| ());
310 if !was_quiescent
|| state_changed
|| memdocs_added_or_removed
{
311 if self.config
.publish_diagnostics() {
312 self.update_diagnostics()
317 if let Some(diagnostic_changes
) = self.diagnostics
.take_changes() {
318 for file_id
in diagnostic_changes
{
319 let db
= self.analysis_host
.raw_database();
320 let source_root
= db
.file_source_root(file_id
);
321 if db
.source_root(source_root
).is_library
{
322 // Only publish diagnostics for files in the workspace, not from crates.io deps
324 // While theoretically these should never have errors, we have quite a few false
325 // positives particularly in the stdlib, and those diagnostics would stay around
326 // forever if we emitted them here.
330 let uri
= file_id_to_url(&self.vfs
.read().0, file_id
);
331 let mut diagnostics
=
332 self.diagnostics
.diagnostics_for(file_id
).cloned().collect
::<Vec
<_
>>();
334 // VSCode assumes diagnostic messages to be non-empty strings, so we need to patch
335 // empty diagnostics. Neither the docs of VSCode nor the LSP spec say whether
336 // diagnostic messages are actually allowed to be empty or not and patching this
337 // in the VSCode client does not work as the assertion happens in the protocol
338 // conversion. So this hack is here to stay, and will be considered a hack
339 // until the LSP decides to state that empty messages are allowed.
341 // See https://github.com/rust-lang/rust-analyzer/issues/11404
342 // See https://github.com/rust-lang/rust-analyzer/issues/13130
343 let patch_empty
= |message
: &mut String
| {
344 if message
.is_empty() {
345 *message
= " ".to_string();
349 for d
in &mut diagnostics
{
350 patch_empty(&mut d
.message
);
351 if let Some(dri
) = &mut d
.related_information
{
353 patch_empty(&mut dri
.message
);
358 let version
= from_proto
::vfs_path(&uri
)
359 .map(|path
| self.mem_docs
.get(&path
).map(|it
| it
.version
))
360 .unwrap_or_default();
362 self.send_notification
::<lsp_types
::notification
::PublishDiagnostics
>(
363 lsp_types
::PublishDiagnosticsParams { uri, diagnostics, version }
,
368 if self.config
.cargo_autoreload() {
369 if let Some(cause
) = self.fetch_workspaces_queue
.should_start_op() {
370 self.fetch_workspaces(cause
);
374 if !self.fetch_workspaces_queue
.op_in_progress() {
375 if let Some(cause
) = self.fetch_build_data_queue
.should_start_op() {
376 self.fetch_build_data(cause
);
380 if let Some(cause
) = self.prime_caches_queue
.should_start_op() {
381 tracing
::debug
!(%cause
, "will prime caches");
382 let num_worker_threads
= self.config
.prime_caches_num_threads();
384 self.task_pool
.handle
.spawn_with_sender({
385 let analysis
= self.snapshot().analysis
;
387 sender
.send(Task
::PrimeCaches(PrimeCachesProgress
::Begin
)).unwrap();
388 let res
= analysis
.parallel_prime_caches(num_worker_threads
, |progress
| {
389 let report
= PrimeCachesProgress
::Report(progress
);
390 sender
.send(Task
::PrimeCaches(report
)).unwrap();
393 .send(Task
::PrimeCaches(PrimeCachesProgress
::End
{
394 cancelled
: res
.is_err(),
401 let status
= self.current_status();
402 if self.last_reported_status
.as_ref() != Some(&status
) {
403 self.last_reported_status
= Some(status
.clone());
405 if let (lsp_ext
::Health
::Error
, Some(message
)) = (status
.health
, &status
.message
) {
406 self.show_message(lsp_types
::MessageType
::ERROR
, message
.clone());
409 if self.config
.server_status_notification() {
410 self.send_notification
::<lsp_ext
::ServerStatusNotification
>(status
);
414 let loop_duration
= loop_start
.elapsed();
415 if loop_duration
> Duration
::from_millis(100) && was_quiescent
{
416 tracing
::warn
!("overly long loop turn: {:?}", loop_duration
);
417 self.poke_rust_analyzer_developer(format
!(
418 "overly long loop turn: {:?}",
425 fn handle_task(&mut self, prime_caches_progress
: &mut Vec
<PrimeCachesProgress
>, task
: Task
) {
427 Task
::Response(response
) => self.respond(response
),
428 // Only retry requests that haven't been cancelled. Otherwise we do unnecessary work.
429 Task
::Retry(req
) if !self.is_completed(&req
) => self.on_request(req
),
430 Task
::Retry(_
) => (),
431 Task
::Diagnostics(diagnostics_per_file
) => {
432 for (file_id
, diagnostics
) in diagnostics_per_file
{
433 self.diagnostics
.set_native_diagnostics(file_id
, diagnostics
)
436 Task
::PrimeCaches(progress
) => match progress
{
437 PrimeCachesProgress
::Begin
=> prime_caches_progress
.push(progress
),
438 PrimeCachesProgress
::Report(_
) => {
439 match prime_caches_progress
.last_mut() {
440 Some(last @ PrimeCachesProgress
::Report(_
)) => {
441 // Coalesce subsequent update events.
444 _
=> prime_caches_progress
.push(progress
),
447 PrimeCachesProgress
::End { .. }
=> prime_caches_progress
.push(progress
),
449 Task
::FetchWorkspace(progress
) => {
450 let (state
, msg
) = match progress
{
451 ProjectWorkspaceProgress
::Begin
=> (Progress
::Begin
, None
),
452 ProjectWorkspaceProgress
::Report(msg
) => (Progress
::Report
, Some(msg
)),
453 ProjectWorkspaceProgress
::End(workspaces
) => {
454 self.fetch_workspaces_queue
.op_completed(Some(workspaces
));
456 let old
= Arc
::clone(&self.workspaces
);
457 self.switch_workspaces("fetched workspace".to_string());
458 let workspaces_updated
= !Arc
::ptr_eq(&old
, &self.workspaces
);
460 if self.config
.run_build_scripts() && workspaces_updated
{
461 self.fetch_build_data_queue
.request_op(format
!("workspace updated"));
464 (Progress
::End
, None
)
468 self.report_progress("Fetching", state
, msg
, None
, None
);
470 Task
::FetchBuildData(progress
) => {
471 let (state
, msg
) = match progress
{
472 BuildDataProgress
::Begin
=> (Some(Progress
::Begin
), None
),
473 BuildDataProgress
::Report(msg
) => (Some(Progress
::Report
), Some(msg
)),
474 BuildDataProgress
::End(build_data_result
) => {
475 self.fetch_build_data_queue
.op_completed(build_data_result
);
477 self.switch_workspaces("fetched build data".to_string());
479 (Some(Progress
::End
), None
)
483 if let Some(state
) = state
{
484 self.report_progress("Loading", state
, msg
, None
, None
);
490 fn handle_vfs_msg(&mut self, message
: vfs
::loader
::Message
) {
492 vfs
::loader
::Message
::Loaded { files }
=> {
493 let vfs
= &mut self.vfs
.write().0;
494 for (path
, contents
) in files
{
495 let path
= VfsPath
::from(path
);
496 if !self.mem_docs
.contains(&path
) {
497 vfs
.set_file_contents(path
, contents
);
501 vfs
::loader
::Message
::Progress { n_total, n_done, config_version }
=> {
502 always
!(config_version
<= self.vfs_config_version
);
504 self.vfs_progress_config_version
= config_version
;
505 self.vfs_progress_n_total
= n_total
;
506 self.vfs_progress_n_done
= n_done
;
508 let state
= if n_done
== 0 {
510 } else if n_done
< n_total
{
513 assert_eq
!(n_done
, n_total
);
516 self.report_progress(
519 Some(format
!("{}/{}", n_done
, n_total
)),
520 Some(Progress
::fraction(n_done
, n_total
)),
527 fn handle_flycheck_msg(&mut self, message
: flycheck
::Message
) {
529 flycheck
::Message
::AddDiagnostic { id, workspace_root, diagnostic }
=> {
530 let snap
= self.snapshot();
531 let diagnostics
= crate::diagnostics
::to_proto
::map_rust_diagnostic_to_lsp(
532 &self.config
.diagnostics_map(),
537 for diag
in diagnostics
{
538 match url_to_file_id(&self.vfs
.read().0, &diag
.url
) {
539 Ok(file_id
) => self.diagnostics
.add_check_diagnostic(
547 "flycheck {id}: File with cargo diagnostic not found in VFS: {}",
555 flycheck
::Message
::Progress { id, progress }
=> {
556 let (state
, message
) = match progress
{
557 flycheck
::Progress
::DidStart
=> {
558 self.diagnostics
.clear_check(id
);
559 (Progress
::Begin
, None
)
561 flycheck
::Progress
::DidCheckCrate(target
) => (Progress
::Report
, Some(target
)),
562 flycheck
::Progress
::DidCancel
=> (Progress
::End
, None
),
563 flycheck
::Progress
::DidFailToRestart(err
) => {
564 self.show_and_log_error(
565 "cargo check failed".to_string(),
566 Some(err
.to_string()),
570 flycheck
::Progress
::DidFinish(result
) => {
571 if let Err(err
) = result
{
572 self.show_and_log_error(
573 "cargo check failed".to_string(),
574 Some(err
.to_string()),
577 (Progress
::End
, None
)
581 // When we're running multiple flychecks, we have to include a disambiguator in
582 // the title, or the editor complains. Note that this is a user-facing string.
583 let title
= if self.flycheck
.len() == 1 {
584 match self.config
.flycheck() {
585 Some(config
) => format
!("{}", config
),
586 None
=> "cargo check".to_string(),
589 format
!("cargo check (#{})", id
+ 1)
591 self.report_progress(
596 Some(format
!("rust-analyzer/checkOnSave/{}", id
)),
602 /// Registers and handles a request. This should only be called once per incoming request.
603 fn on_new_request(&mut self, request_received
: Instant
, req
: Request
) {
604 self.register_request(&req
, request_received
);
605 self.on_request(req
);
608 /// Handles a request.
609 fn on_request(&mut self, req
: Request
) {
610 let mut dispatcher
= RequestDispatcher { req: Some(req), global_state: self }
;
611 dispatcher
.on_sync_mut
::<lsp_types
::request
::Shutdown
>(|s
, ()| {
612 s
.shutdown_requested
= true;
616 if let RequestDispatcher { req: Some(req), global_state: this }
= &mut dispatcher
{
617 if this
.shutdown_requested
{
618 this
.respond(lsp_server
::Response
::new_err(
620 lsp_server
::ErrorCode
::InvalidRequest
as i32,
621 "Shutdown already requested.".to_owned(),
626 // Avoid flashing a bunch of unresolved references during initial load.
627 if this
.workspaces
.is_empty() && !this
.is_quiescent() {
628 this
.respond(lsp_server
::Response
::new_err(
630 lsp_server
::ErrorCode
::ContentModified
as i32,
631 "waiting for cargo metadata or cargo check".to_owned(),
638 .on_sync_mut
::<lsp_ext
::ReloadWorkspace
>(handlers
::handle_workspace_reload
)
639 .on_sync_mut
::<lsp_ext
::MemoryUsage
>(handlers
::handle_memory_usage
)
640 .on_sync_mut
::<lsp_ext
::ShuffleCrateGraph
>(handlers
::handle_shuffle_crate_graph
)
641 .on_sync_mut
::<lsp_ext
::CancelFlycheck
>(handlers
::handle_cancel_flycheck
)
642 .on_sync
::<lsp_ext
::JoinLines
>(handlers
::handle_join_lines
)
643 .on_sync
::<lsp_ext
::OnEnter
>(handlers
::handle_on_enter
)
644 .on_sync
::<lsp_types
::request
::SelectionRangeRequest
>(handlers
::handle_selection_range
)
645 .on_sync
::<lsp_ext
::MatchingBrace
>(handlers
::handle_matching_brace
)
646 .on
::<lsp_ext
::AnalyzerStatus
>(handlers
::handle_analyzer_status
)
647 .on
::<lsp_ext
::SyntaxTree
>(handlers
::handle_syntax_tree
)
648 .on
::<lsp_ext
::ViewHir
>(handlers
::handle_view_hir
)
649 .on
::<lsp_ext
::ViewFileText
>(handlers
::handle_view_file_text
)
650 .on
::<lsp_ext
::ViewCrateGraph
>(handlers
::handle_view_crate_graph
)
651 .on
::<lsp_ext
::ViewItemTree
>(handlers
::handle_view_item_tree
)
652 .on
::<lsp_ext
::ExpandMacro
>(handlers
::handle_expand_macro
)
653 .on
::<lsp_ext
::ParentModule
>(handlers
::handle_parent_module
)
654 .on
::<lsp_ext
::Runnables
>(handlers
::handle_runnables
)
655 .on
::<lsp_ext
::RelatedTests
>(handlers
::handle_related_tests
)
656 .on
::<lsp_ext
::CodeActionRequest
>(handlers
::handle_code_action
)
657 .on
::<lsp_ext
::CodeActionResolveRequest
>(handlers
::handle_code_action_resolve
)
658 .on
::<lsp_ext
::HoverRequest
>(handlers
::handle_hover
)
659 .on
::<lsp_ext
::ExternalDocs
>(handlers
::handle_open_docs
)
660 .on
::<lsp_ext
::OpenCargoToml
>(handlers
::handle_open_cargo_toml
)
661 .on
::<lsp_ext
::MoveItem
>(handlers
::handle_move_item
)
662 .on
::<lsp_ext
::WorkspaceSymbol
>(handlers
::handle_workspace_symbol
)
663 .on
::<lsp_ext
::OnTypeFormatting
>(handlers
::handle_on_type_formatting
)
664 .on
::<lsp_types
::request
::DocumentSymbolRequest
>(handlers
::handle_document_symbol
)
665 .on
::<lsp_types
::request
::GotoDefinition
>(handlers
::handle_goto_definition
)
666 .on
::<lsp_types
::request
::GotoDeclaration
>(handlers
::handle_goto_declaration
)
667 .on
::<lsp_types
::request
::GotoImplementation
>(handlers
::handle_goto_implementation
)
668 .on
::<lsp_types
::request
::GotoTypeDefinition
>(handlers
::handle_goto_type_definition
)
669 .on
::<lsp_types
::request
::InlayHintRequest
>(handlers
::handle_inlay_hints
)
670 .on
::<lsp_types
::request
::InlayHintResolveRequest
>(handlers
::handle_inlay_hints_resolve
)
671 .on
::<lsp_types
::request
::Completion
>(handlers
::handle_completion
)
672 .on
::<lsp_types
::request
::ResolveCompletionItem
>(handlers
::handle_completion_resolve
)
673 .on
::<lsp_types
::request
::CodeLensRequest
>(handlers
::handle_code_lens
)
674 .on
::<lsp_types
::request
::CodeLensResolve
>(handlers
::handle_code_lens_resolve
)
675 .on
::<lsp_types
::request
::FoldingRangeRequest
>(handlers
::handle_folding_range
)
676 .on
::<lsp_types
::request
::SignatureHelpRequest
>(handlers
::handle_signature_help
)
677 .on
::<lsp_types
::request
::PrepareRenameRequest
>(handlers
::handle_prepare_rename
)
678 .on
::<lsp_types
::request
::Rename
>(handlers
::handle_rename
)
679 .on
::<lsp_types
::request
::References
>(handlers
::handle_references
)
680 .on
::<lsp_types
::request
::Formatting
>(handlers
::handle_formatting
)
681 .on
::<lsp_types
::request
::RangeFormatting
>(handlers
::handle_range_formatting
)
682 .on
::<lsp_types
::request
::DocumentHighlightRequest
>(handlers
::handle_document_highlight
)
683 .on
::<lsp_types
::request
::CallHierarchyPrepare
>(handlers
::handle_call_hierarchy_prepare
)
684 .on
::<lsp_types
::request
::CallHierarchyIncomingCalls
>(
685 handlers
::handle_call_hierarchy_incoming
,
687 .on
::<lsp_types
::request
::CallHierarchyOutgoingCalls
>(
688 handlers
::handle_call_hierarchy_outgoing
,
690 .on
::<lsp_types
::request
::SemanticTokensFullRequest
>(
691 handlers
::handle_semantic_tokens_full
,
693 .on
::<lsp_types
::request
::SemanticTokensFullDeltaRequest
>(
694 handlers
::handle_semantic_tokens_full_delta
,
696 .on
::<lsp_types
::request
::SemanticTokensRangeRequest
>(
697 handlers
::handle_semantic_tokens_range
,
699 .on
::<lsp_types
::request
::WillRenameFiles
>(handlers
::handle_will_rename_files
)
700 .on
::<lsp_ext
::Ssr
>(handlers
::handle_ssr
)
704 /// Handles an incoming notification.
705 fn on_notification(&mut self, not
: Notification
) -> Result
<()> {
706 NotificationDispatcher { not: Some(not), global_state: self }
707 .on
::<lsp_types
::notification
::Cancel
>(|this
, params
| {
708 let id
: lsp_server
::RequestId
= match params
.id
{
709 lsp_types
::NumberOrString
::Number(id
) => id
.into(),
710 lsp_types
::NumberOrString
::String(id
) => id
.into(),
715 .on
::<lsp_types
::notification
::WorkDoneProgressCancel
>(|this
, params
| {
716 if let lsp_types
::NumberOrString
::String(s
) = ¶ms
.token
{
717 if let Some(id
) = s
.strip_prefix("rust-analyzer/checkOnSave/") {
718 if let Ok(id
) = u32::from_str_radix(id
, 10) {
719 if let Some(flycheck
) = this
.flycheck
.get(id
as usize) {
725 // Just ignore this. It is OK to continue sending progress
726 // notifications for this token, as the client can't know when
727 // we accepted notification.
730 .on
::<lsp_types
::notification
::DidOpenTextDocument
>(|this
, params
| {
731 if let Ok(path
) = from_proto
::vfs_path(¶ms
.text_document
.uri
) {
732 let already_exists
= this
734 .insert(path
.clone(), DocumentData
::new(params
.text_document
.version
))
737 tracing
::error
!("duplicate DidOpenTextDocument: {}", path
);
742 .set_file_contents(path
, Some(params
.text_document
.text
.into_bytes()));
746 .on
::<lsp_types
::notification
::DidChangeTextDocument
>(|this
, params
| {
747 if let Ok(path
) = from_proto
::vfs_path(¶ms
.text_document
.uri
) {
748 match this
.mem_docs
.get_mut(&path
) {
750 // The version passed in DidChangeTextDocument is the version after all edits are applied
751 // so we should apply it before the vfs is notified.
752 doc
.version
= params
.text_document
.version
;
755 tracing
::error
!("unexpected DidChangeTextDocument: {}", path
);
760 let vfs
= &mut this
.vfs
.write().0;
761 let file_id
= vfs
.file_id(&path
).unwrap();
762 let text
= apply_document_changes(
763 || std
::str::from_utf8(vfs
.file_contents(file_id
)).unwrap().into(),
764 params
.content_changes
,
767 vfs
.set_file_contents(path
, Some(text
.into_bytes()));
771 .on
::<lsp_types
::notification
::DidCloseTextDocument
>(|this
, params
| {
772 if let Ok(path
) = from_proto
::vfs_path(¶ms
.text_document
.uri
) {
773 if this
.mem_docs
.remove(&path
).is_err() {
774 tracing
::error
!("orphan DidCloseTextDocument: {}", path
);
777 this
.semantic_tokens_cache
.lock().remove(¶ms
.text_document
.uri
);
779 if let Some(path
) = path
.as_path() {
780 this
.loader
.handle
.invalidate(path
.to_path_buf());
785 .on
::<lsp_types
::notification
::DidSaveTextDocument
>(|this
, params
| {
786 if let Ok(vfs_path
) = from_proto
::vfs_path(¶ms
.text_document
.uri
) {
787 // Re-fetch workspaces if a workspace related file has changed
788 if let Some(abs_path
) = vfs_path
.as_path() {
789 if reload
::should_refresh_for_change(&abs_path
, ChangeKind
::Modify
) {
790 this
.fetch_workspaces_queue
791 .request_op(format
!("DidSaveTextDocument {}", abs_path
.display()));
795 let file_id
= this
.vfs
.read().0.file_id
(&vfs_path
);
796 if let Some(file_id
) = file_id
{
797 let world
= this
.snapshot();
798 let mut updated
= false;
799 let task
= move || -> std
::result
::Result
<(), ide
::Cancelled
> {
800 // Trigger flychecks for all workspaces that depend on the saved file
801 // Crates containing or depending on the saved file
802 let crate_ids
: Vec
<_
> = world
804 .crates_for(file_id
)?
806 .flat_map(|id
| world
.analysis
.transitive_rev_deps(id
))
812 let crate_root_paths
: Vec
<_
> = crate_ids
814 .filter_map(|&crate_id
| {
817 .crate_root(crate_id
)
820 .file_id_to_file_path(file_id
)
822 .map(ToOwned
::to_owned
)
826 .collect
::<ide
::Cancellable
<_
>>()?
;
827 let crate_root_paths
: Vec
<_
> =
828 crate_root_paths
.iter().map(Deref
::deref
).collect();
830 // Find all workspaces that have at least one target containing the saved file
832 world
.workspaces
.iter().enumerate().filter(|(_
, ws
)| match ws
{
833 project_model
::ProjectWorkspace
::Cargo { cargo, .. }
=> {
834 cargo
.packages().any(|pkg
| {
835 cargo
[pkg
].targets
.iter().any(|&it
| {
836 crate_root_paths
.contains(&cargo
[it
].root
.as_path())
840 project_model
::ProjectWorkspace
::Json { project, .. }
=> {
841 project
.crates().any(|(c
, _
)| {
842 crate_ids
.iter().any(|&crate_id
| crate_id
== c
)
845 project_model
::ProjectWorkspace
::DetachedFiles { .. }
=> false,
848 // Find and trigger corresponding flychecks
849 for flycheck
in world
.flycheck
.iter() {
850 for (id
, _
) in workspace_ids
.clone() {
851 if id
== flycheck
.id() {
858 // No specific flycheck was triggered, so let's trigger all of them.
860 for flycheck
in world
.flycheck
.iter() {
866 this
.task_pool
.handle
.spawn_with_sender(move |_
| {
867 if let Err(e
) = std
::panic
::catch_unwind(task
) {
868 tracing
::error
!("DidSaveTextDocument flycheck task panicked: {e:?}")
875 // No specific flycheck was triggered, so let's trigger all of them.
876 for flycheck
in this
.flycheck
.iter() {
881 .on
::<lsp_types
::notification
::DidChangeConfiguration
>(|this
, _params
| {
882 // As stated in https://github.com/microsoft/language-server-protocol/issues/676,
883 // this notification's parameters should be ignored and the actual config queried separately.
884 this
.send_request
::<lsp_types
::request
::WorkspaceConfiguration
>(
885 lsp_types
::ConfigurationParams
{
886 items
: vec
![lsp_types
::ConfigurationItem
{
888 section
: Some("rust-analyzer".to_string()),
892 tracing
::debug
!("config update response: '{:?}", resp
);
893 let lsp_server
::Response { error, result, .. }
= resp
;
895 match (error
, result
) {
897 tracing
::error
!("failed to fetch the server settings: {:?}", err
)
899 (None
, Some(mut configs
)) => {
900 if let Some(json
) = configs
.get_mut(0) {
901 // Note that json can be null according to the spec if the client can't
902 // provide a configuration. This is handled in Config::update below.
903 let mut config
= Config
::clone(&*this
.config
);
904 if let Err(error
) = config
.update(json
.take()) {
906 lsp_types
::MessageType
::WARNING
,
910 this
.update_configuration(config
);
913 (None
, None
) => tracing
::error
!(
914 "received empty server settings response from the client"
922 .on
::<lsp_types
::notification
::DidChangeWatchedFiles
>(|this
, params
| {
923 for change
in params
.changes
{
924 if let Ok(path
) = from_proto
::abs_path(&change
.uri
) {
925 this
.loader
.handle
.invalidate(path
);
934 fn update_diagnostics(&mut self) {
935 let subscriptions
= self
938 .map(|path
| self.vfs
.read().0.file_id
(path
).unwrap())
939 .collect
::<Vec
<_
>>();
941 tracing
::trace
!("updating notifications for {:?}", subscriptions
);
943 let snapshot
= self.snapshot();
944 self.task_pool
.handle
.spawn(move || {
945 let diagnostics
= subscriptions
947 .filter_map(|file_id
| {
948 handlers
::publish_diagnostics(&snapshot
, file_id
)
950 .map(|diags
| (file_id
, diags
))
952 .collect
::<Vec
<_
>>();
953 Task
::Diagnostics(diagnostics
)