diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_lsp_server/src/main_loop.rs | 92 |
1 files changed, 56 insertions, 36 deletions
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index 15bf519c9..9901fe931 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -5,11 +5,18 @@ mod handlers; | |||
5 | mod subscriptions; | 5 | mod subscriptions; |
6 | pub(crate) mod pending_requests; | 6 | pub(crate) mod pending_requests; |
7 | 7 | ||
8 | use std::{error::Error, fmt, panic, path::PathBuf, sync::Arc, time::Instant}; | 8 | use std::{ |
9 | env, | ||
10 | error::Error, | ||
11 | fmt, panic, | ||
12 | path::PathBuf, | ||
13 | sync::Arc, | ||
14 | time::{Duration, Instant}, | ||
15 | }; | ||
9 | 16 | ||
10 | use crossbeam_channel::{select, unbounded, RecvError, Sender}; | 17 | use crossbeam_channel::{select, unbounded, RecvError, Sender}; |
11 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; | 18 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; |
12 | use lsp_types::{ClientCapabilities, NumberOrString, Url}; | 19 | use lsp_types::{ClientCapabilities, NumberOrString}; |
13 | use ra_cargo_watch::{CheckOptions, CheckTask}; | 20 | use ra_cargo_watch::{CheckOptions, CheckTask}; |
14 | use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; | 21 | use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; |
15 | use ra_prof::profile; | 22 | use ra_prof::profile; |
@@ -352,7 +359,7 @@ fn loop_turn( | |||
352 | world_state.maybe_collect_garbage(); | 359 | world_state.maybe_collect_garbage(); |
353 | loop_state.in_flight_libraries -= 1; | 360 | loop_state.in_flight_libraries -= 1; |
354 | } | 361 | } |
355 | Event::CheckWatcher(task) => on_check_task(task, world_state, task_sender)?, | 362 | Event::CheckWatcher(task) => on_check_task(pool, task, world_state, task_sender)?, |
356 | Event::Msg(msg) => match msg { | 363 | Event::Msg(msg) => match msg { |
357 | Message::Request(req) => on_request( | 364 | Message::Request(req) => on_request( |
358 | world_state, | 365 | world_state, |
@@ -425,6 +432,19 @@ fn loop_turn( | |||
425 | loop_state.subscriptions.subscriptions(), | 432 | loop_state.subscriptions.subscriptions(), |
426 | ) | 433 | ) |
427 | } | 434 | } |
435 | |||
436 | let loop_duration = loop_start.elapsed(); | ||
437 | if loop_duration > Duration::from_millis(10) { | ||
438 | log::error!("overly long loop turn: {:?}", loop_duration); | ||
439 | if env::var("RA_PROFILE").is_ok() { | ||
440 | show_message( | ||
441 | req::MessageType::Error, | ||
442 | format!("overly long loop turn: {:?}", loop_duration), | ||
443 | &connection.sender, | ||
444 | ); | ||
445 | } | ||
446 | } | ||
447 | |||
428 | Ok(()) | 448 | Ok(()) |
429 | } | 449 | } |
430 | 450 | ||
@@ -452,7 +472,7 @@ fn on_request( | |||
452 | world: &mut WorldState, | 472 | world: &mut WorldState, |
453 | pending_requests: &mut PendingRequests, | 473 | pending_requests: &mut PendingRequests, |
454 | pool: &ThreadPool, | 474 | pool: &ThreadPool, |
455 | sender: &Sender<Task>, | 475 | task_sender: &Sender<Task>, |
456 | msg_sender: &Sender<Message>, | 476 | msg_sender: &Sender<Message>, |
457 | request_received: Instant, | 477 | request_received: Instant, |
458 | req: Request, | 478 | req: Request, |
@@ -461,7 +481,7 @@ fn on_request( | |||
461 | req: Some(req), | 481 | req: Some(req), |
462 | pool, | 482 | pool, |
463 | world, | 483 | world, |
464 | sender, | 484 | task_sender, |
465 | msg_sender, | 485 | msg_sender, |
466 | pending_requests, | 486 | pending_requests, |
467 | request_received, | 487 | request_received, |
@@ -602,31 +622,23 @@ fn on_notification( | |||
602 | } | 622 | } |
603 | 623 | ||
604 | fn on_check_task( | 624 | fn on_check_task( |
625 | pool: &ThreadPool, | ||
605 | task: CheckTask, | 626 | task: CheckTask, |
606 | world_state: &mut WorldState, | 627 | world_state: &mut WorldState, |
607 | task_sender: &Sender<Task>, | 628 | task_sender: &Sender<Task>, |
608 | ) -> Result<()> { | 629 | ) -> Result<()> { |
609 | match task { | 630 | let urls = match task { |
610 | CheckTask::ClearDiagnostics => { | 631 | CheckTask::ClearDiagnostics => { |
611 | let state = Arc::get_mut(&mut world_state.check_watcher.state) | 632 | let state = Arc::get_mut(&mut world_state.check_watcher.state) |
612 | .expect("couldn't get check watcher state as mutable"); | 633 | .expect("couldn't get check watcher state as mutable"); |
613 | let cleared_files = state.clear(); | 634 | state.clear() |
614 | |||
615 | // Send updated diagnostics for each cleared file | ||
616 | for url in cleared_files { | ||
617 | publish_diagnostics_for_url(&url, world_state, task_sender)?; | ||
618 | } | ||
619 | } | 635 | } |
620 | 636 | ||
621 | CheckTask::AddDiagnostic(url, diagnostic) => { | 637 | CheckTask::AddDiagnostic(url, diagnostic) => { |
622 | let state = Arc::get_mut(&mut world_state.check_watcher.state) | 638 | let state = Arc::get_mut(&mut world_state.check_watcher.state) |
623 | .expect("couldn't get check watcher state as mutable"); | 639 | .expect("couldn't get check watcher state as mutable"); |
624 | state.add_diagnostic_with_fixes(url.clone(), diagnostic); | 640 | state.add_diagnostic_with_fixes(url.clone(), diagnostic); |
625 | 641 | vec![url] | |
626 | // We manually send a diagnostic update when the watcher asks | ||
627 | // us to, to avoid the issue of having to change the file to | ||
628 | // receive updated diagnostics. | ||
629 | publish_diagnostics_for_url(&url, world_state, task_sender)?; | ||
630 | } | 642 | } |
631 | 643 | ||
632 | CheckTask::Status(progress) => { | 644 | CheckTask::Status(progress) => { |
@@ -636,22 +648,30 @@ fn on_check_task( | |||
636 | }; | 648 | }; |
637 | let not = notification_new::<req::Progress>(params); | 649 | let not = notification_new::<req::Progress>(params); |
638 | task_sender.send(Task::Notify(not)).unwrap(); | 650 | task_sender.send(Task::Notify(not)).unwrap(); |
651 | Vec::new() | ||
639 | } | 652 | } |
640 | } | 653 | }; |
641 | Ok(()) | 654 | |
642 | } | 655 | let subscriptions = urls |
656 | .into_iter() | ||
657 | .map(|url| { | ||
658 | let path = url.to_file_path().map_err(|()| format!("invalid uri: {}", url))?; | ||
659 | Ok(world_state.vfs.read().path2file(&path).map(|it| FileId(it.0))) | ||
660 | }) | ||
661 | .filter_map(|res| res.transpose()) | ||
662 | .collect::<Result<Vec<_>>>()?; | ||
663 | |||
664 | // We manually send a diagnostic update when the watcher asks | ||
665 | // us to, to avoid the issue of having to change the file to | ||
666 | // receive updated diagnostics. | ||
667 | update_file_notifications_on_threadpool( | ||
668 | pool, | ||
669 | world_state.snapshot(), | ||
670 | false, | ||
671 | task_sender.clone(), | ||
672 | subscriptions, | ||
673 | ); | ||
643 | 674 | ||
644 | fn publish_diagnostics_for_url( | ||
645 | url: &Url, | ||
646 | world_state: &WorldState, | ||
647 | task_sender: &Sender<Task>, | ||
648 | ) -> Result<()> { | ||
649 | let path = url.to_file_path().map_err(|()| format!("invalid uri: {}", url))?; | ||
650 | if let Some(file_id) = world_state.vfs.read().path2file(&path) { | ||
651 | let params = handlers::publish_diagnostics(&world_state.snapshot(), FileId(file_id.0))?; | ||
652 | let not = notification_new::<req::PublishDiagnostics>(params); | ||
653 | task_sender.send(Task::Notify(not)).unwrap(); | ||
654 | } | ||
655 | Ok(()) | 675 | Ok(()) |
656 | } | 676 | } |
657 | 677 | ||
@@ -661,7 +681,7 @@ struct PoolDispatcher<'a> { | |||
661 | world: &'a mut WorldState, | 681 | world: &'a mut WorldState, |
662 | pending_requests: &'a mut PendingRequests, | 682 | pending_requests: &'a mut PendingRequests, |
663 | msg_sender: &'a Sender<Message>, | 683 | msg_sender: &'a Sender<Message>, |
664 | sender: &'a Sender<Task>, | 684 | task_sender: &'a Sender<Task>, |
665 | request_received: Instant, | 685 | request_received: Instant, |
666 | } | 686 | } |
667 | 687 | ||
@@ -708,7 +728,7 @@ impl<'a> PoolDispatcher<'a> { | |||
708 | 728 | ||
709 | self.pool.execute({ | 729 | self.pool.execute({ |
710 | let world = self.world.snapshot(); | 730 | let world = self.world.snapshot(); |
711 | let sender = self.sender.clone(); | 731 | let sender = self.task_sender.clone(); |
712 | move || { | 732 | move || { |
713 | let result = f(world, params); | 733 | let result = f(world, params); |
714 | let task = result_to_task::<R>(id, result); | 734 | let task = result_to_task::<R>(id, result); |
@@ -786,7 +806,7 @@ fn update_file_notifications_on_threadpool( | |||
786 | pool: &ThreadPool, | 806 | pool: &ThreadPool, |
787 | world: WorldSnapshot, | 807 | world: WorldSnapshot, |
788 | publish_decorations: bool, | 808 | publish_decorations: bool, |
789 | sender: Sender<Task>, | 809 | task_sender: Sender<Task>, |
790 | subscriptions: Vec<FileId>, | 810 | subscriptions: Vec<FileId>, |
791 | ) { | 811 | ) { |
792 | log::trace!("updating notifications for {:?}", subscriptions); | 812 | log::trace!("updating notifications for {:?}", subscriptions); |
@@ -802,7 +822,7 @@ fn update_file_notifications_on_threadpool( | |||
802 | } | 822 | } |
803 | Ok(params) => { | 823 | Ok(params) => { |
804 | let not = notification_new::<req::PublishDiagnostics>(params); | 824 | let not = notification_new::<req::PublishDiagnostics>(params); |
805 | sender.send(Task::Notify(not)).unwrap(); | 825 | task_sender.send(Task::Notify(not)).unwrap(); |
806 | } | 826 | } |
807 | } | 827 | } |
808 | } | 828 | } |
@@ -815,7 +835,7 @@ fn update_file_notifications_on_threadpool( | |||
815 | } | 835 | } |
816 | Ok(params) => { | 836 | Ok(params) => { |
817 | let not = notification_new::<req::PublishDecorations>(params); | 837 | let not = notification_new::<req::PublishDecorations>(params); |
818 | sender.send(Task::Notify(not)).unwrap(); | 838 | task_sender.send(Task::Notify(not)).unwrap(); |
819 | } | 839 | } |
820 | } | 840 | } |
821 | } | 841 | } |