diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_flycheck/src/lib.rs | 12 | ||||
-rw-r--r-- | crates/rust-analyzer/src/global_state.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lib.rs | 6 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_utils.rs | 44 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 187 |
5 files changed, 134 insertions, 117 deletions
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs index 6c4170529..0e2ee8698 100644 --- a/crates/ra_flycheck/src/lib.rs +++ b/crates/ra_flycheck/src/lib.rs | |||
@@ -3,6 +3,7 @@ | |||
3 | //! LSP diagnostics based on the output of the command. | 3 | //! LSP diagnostics based on the output of the command. |
4 | 4 | ||
5 | use std::{ | 5 | use std::{ |
6 | fmt, | ||
6 | io::{self, BufReader}, | 7 | io::{self, BufReader}, |
7 | path::PathBuf, | 8 | path::PathBuf, |
8 | process::{Command, Stdio}, | 9 | process::{Command, Stdio}, |
@@ -31,6 +32,17 @@ pub enum FlycheckConfig { | |||
31 | }, | 32 | }, |
32 | } | 33 | } |
33 | 34 | ||
35 | impl fmt::Display for FlycheckConfig { | ||
36 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
37 | match self { | ||
38 | FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command), | ||
39 | FlycheckConfig::CustomCommand { command, args } => { | ||
40 | write!(f, "{} {}", command, args.join(" ")) | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | } | ||
45 | |||
34 | /// Flycheck wraps the shared state and communication machinery used for | 46 | /// Flycheck wraps the shared state and communication machinery used for |
35 | /// running `cargo check` (or other compatible command) and providing | 47 | /// running `cargo check` (or other compatible command) and providing |
36 | /// diagnostics based on the output. | 48 | /// diagnostics based on the output. |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 87f3fe4db..64d4e2787 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -29,7 +29,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; | |||
29 | 29 | ||
30 | fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> Option<Flycheck> { | 30 | fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> Option<Flycheck> { |
31 | // FIXME: Figure out the multi-workspace situation | 31 | // FIXME: Figure out the multi-workspace situation |
32 | workspaces.iter().find_map(|w| match w { | 32 | workspaces.iter().find_map(move |w| match w { |
33 | ProjectWorkspace::Cargo { cargo, .. } => { | 33 | ProjectWorkspace::Cargo { cargo, .. } => { |
34 | let cargo_project_root = cargo.workspace_root().to_path_buf(); | 34 | let cargo_project_root = cargo.workspace_root().to_path_buf(); |
35 | Some(Flycheck::new(config.clone(), cargo_project_root.into())) | 35 | Some(Flycheck::new(config.clone(), cargo_project_root.into())) |
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index d6cd04303..794286672 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs | |||
@@ -29,16 +29,14 @@ mod markdown; | |||
29 | mod diagnostics; | 29 | mod diagnostics; |
30 | mod line_endings; | 30 | mod line_endings; |
31 | mod request_metrics; | 31 | mod request_metrics; |
32 | mod lsp_utils; | ||
32 | pub mod lsp_ext; | 33 | pub mod lsp_ext; |
33 | pub mod config; | 34 | pub mod config; |
34 | 35 | ||
35 | use serde::de::DeserializeOwned; | 36 | use serde::de::DeserializeOwned; |
36 | 37 | ||
37 | pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>; | 38 | pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>; |
38 | pub use crate::{ | 39 | pub use crate::{caps::server_capabilities, lsp_utils::show_message, main_loop::main_loop}; |
39 | caps::server_capabilities, | ||
40 | main_loop::{main_loop, show_message}, | ||
41 | }; | ||
42 | use std::fmt; | 40 | use std::fmt; |
43 | 41 | ||
44 | pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> { | 42 | pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> { |
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs new file mode 100644 index 000000000..078f8778e --- /dev/null +++ b/crates/rust-analyzer/src/lsp_utils.rs | |||
@@ -0,0 +1,44 @@ | |||
1 | //! Utilities for LSP-related boilerplate code. | ||
2 | use std::error::Error; | ||
3 | |||
4 | use crossbeam_channel::Sender; | ||
5 | use lsp_server::{Message, Notification}; | ||
6 | use ra_db::Canceled; | ||
7 | use serde::{de::DeserializeOwned, Serialize}; | ||
8 | |||
9 | pub fn show_message( | ||
10 | typ: lsp_types::MessageType, | ||
11 | message: impl Into<String>, | ||
12 | sender: &Sender<Message>, | ||
13 | ) { | ||
14 | let message = message.into(); | ||
15 | let params = lsp_types::ShowMessageParams { typ, message }; | ||
16 | let not = notification_new::<lsp_types::notification::ShowMessage>(params); | ||
17 | sender.send(not.into()).unwrap(); | ||
18 | } | ||
19 | |||
20 | pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool { | ||
21 | e.downcast_ref::<Canceled>().is_some() | ||
22 | } | ||
23 | |||
24 | pub(crate) fn notification_is<N: lsp_types::notification::Notification>( | ||
25 | notification: &Notification, | ||
26 | ) -> bool { | ||
27 | notification.method == N::METHOD | ||
28 | } | ||
29 | |||
30 | pub(crate) fn notification_cast<N>(notification: Notification) -> Result<N::Params, Notification> | ||
31 | where | ||
32 | N: lsp_types::notification::Notification, | ||
33 | N::Params: DeserializeOwned, | ||
34 | { | ||
35 | notification.extract(N::METHOD) | ||
36 | } | ||
37 | |||
38 | pub(crate) fn notification_new<N>(params: N::Params) -> Notification | ||
39 | where | ||
40 | N: lsp_types::notification::Notification, | ||
41 | N::Params: Serialize, | ||
42 | { | ||
43 | Notification::new(N::METHOD.to_string(), params) | ||
44 | } | ||
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index eb9e7f913..03569086a 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -25,6 +25,7 @@ use crate::{ | |||
25 | from_proto, | 25 | from_proto, |
26 | global_state::{file_id_to_url, GlobalState, GlobalStateSnapshot, Status}, | 26 | global_state::{file_id_to_url, GlobalState, GlobalStateSnapshot, Status}, |
27 | handlers, lsp_ext, | 27 | handlers, lsp_ext, |
28 | lsp_utils::{is_canceled, notification_cast, notification_is, notification_new, show_message}, | ||
28 | request_metrics::RequestMetrics, | 29 | request_metrics::RequestMetrics, |
29 | LspError, Result, | 30 | LspError, Result, |
30 | }; | 31 | }; |
@@ -138,7 +139,7 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { | |||
138 | recv(global_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task { | 139 | recv(global_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task { |
139 | Ok(task) => Event::CheckWatcher(task), | 140 | Ok(task) => Event::CheckWatcher(task), |
140 | Err(RecvError) => return Err("check watcher died".into()), | 141 | Err(RecvError) => return Err("check watcher died".into()), |
141 | } | 142 | }, |
142 | }; | 143 | }; |
143 | if let Event::Msg(Message::Request(req)) = &event { | 144 | if let Event::Msg(Message::Request(req)) = &event { |
144 | if connection.handle_shutdown(&req)? { | 145 | if connection.handle_shutdown(&req)? { |
@@ -168,7 +169,6 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { | |||
168 | #[derive(Debug)] | 169 | #[derive(Debug)] |
169 | enum Task { | 170 | enum Task { |
170 | Respond(Response), | 171 | Respond(Response), |
171 | Notify(Notification), | ||
172 | Diagnostic(DiagnosticTask), | 172 | Diagnostic(DiagnosticTask), |
173 | } | 173 | } |
174 | 174 | ||
@@ -193,11 +193,6 @@ impl fmt::Debug for Event { | |||
193 | return debug_verbose_not(not, f); | 193 | return debug_verbose_not(not, f); |
194 | } | 194 | } |
195 | } | 195 | } |
196 | Event::Task(Task::Notify(not)) => { | ||
197 | if notification_is::<lsp_types::notification::PublishDiagnostics>(not) { | ||
198 | return debug_verbose_not(not, f); | ||
199 | } | ||
200 | } | ||
201 | Event::Task(Task::Respond(resp)) => { | 196 | Event::Task(Task::Respond(resp)) => { |
202 | return f | 197 | return f |
203 | .debug_struct("Response") | 198 | .debug_struct("Response") |
@@ -254,14 +249,29 @@ fn loop_turn( | |||
254 | } | 249 | } |
255 | } | 250 | } |
256 | vfs::loader::Message::Progress { n_total, n_done } => { | 251 | vfs::loader::Message::Progress { n_total, n_done } => { |
257 | if n_done == n_total { | 252 | let state = if n_done == 0 { |
253 | ProgressState::Start | ||
254 | } else if n_done < n_total { | ||
255 | ProgressState::Report | ||
256 | } else { | ||
257 | assert_eq!(n_done, n_total); | ||
258 | global_state.status = Status::Ready; | 258 | global_state.status = Status::Ready; |
259 | became_ready = true; | 259 | became_ready = true; |
260 | } | 260 | ProgressState::End |
261 | report_progress(global_state, &connection.sender, n_done, n_total, "roots scanned") | 261 | }; |
262 | report_progress( | ||
263 | global_state, | ||
264 | &connection.sender, | ||
265 | "roots scanned", | ||
266 | state, | ||
267 | Some(format!("{}/{}", n_done, n_total)), | ||
268 | Some(percentage(n_done, n_total)), | ||
269 | ) | ||
262 | } | 270 | } |
263 | }, | 271 | }, |
264 | Event::CheckWatcher(task) => on_check_task(task, global_state, task_sender)?, | 272 | Event::CheckWatcher(task) => { |
273 | on_check_task(task, global_state, task_sender, &connection.sender)? | ||
274 | } | ||
265 | Event::Msg(msg) => match msg { | 275 | Event::Msg(msg) => match msg { |
266 | Message::Request(req) => { | 276 | Message::Request(req) => { |
267 | on_request(global_state, pool, task_sender, &connection.sender, loop_start, req)? | 277 | on_request(global_state, pool, task_sender, &connection.sender, loop_start, req)? |
@@ -335,9 +345,6 @@ fn on_task(task: Task, msg_sender: &Sender<Message>, global_state: &mut GlobalSt | |||
335 | msg_sender.send(response.into()).unwrap(); | 345 | msg_sender.send(response.into()).unwrap(); |
336 | } | 346 | } |
337 | } | 347 | } |
338 | Task::Notify(n) => { | ||
339 | msg_sender.send(n.into()).unwrap(); | ||
340 | } | ||
341 | Task::Diagnostic(task) => on_diagnostic_task(task, msg_sender, global_state), | 348 | Task::Diagnostic(task) => on_diagnostic_task(task, msg_sender, global_state), |
342 | } | 349 | } |
343 | } | 350 | } |
@@ -589,6 +596,7 @@ fn on_check_task( | |||
589 | task: CheckTask, | 596 | task: CheckTask, |
590 | global_state: &mut GlobalState, | 597 | global_state: &mut GlobalState, |
591 | task_sender: &Sender<Task>, | 598 | task_sender: &Sender<Task>, |
599 | msg_sender: &Sender<Message>, | ||
592 | ) -> Result<()> { | 600 | ) -> Result<()> { |
593 | match task { | 601 | match task { |
594 | CheckTask::ClearDiagnostics => { | 602 | CheckTask::ClearDiagnostics => { |
@@ -620,39 +628,13 @@ fn on_check_task( | |||
620 | } | 628 | } |
621 | 629 | ||
622 | CheckTask::Status(status) => { | 630 | CheckTask::Status(status) => { |
623 | if global_state.config.client_caps.work_done_progress { | 631 | let (state, message) = match status { |
624 | let progress = match status { | 632 | ra_flycheck::Status::Being => (ProgressState::Start, None), |
625 | ra_flycheck::Status::Being => { | 633 | ra_flycheck::Status::Progress(target) => (ProgressState::Report, Some(target)), |
626 | lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { | 634 | ra_flycheck::Status::End => (ProgressState::End, None), |
627 | title: "Running `cargo check`".to_string(), | 635 | }; |
628 | cancellable: Some(false), | ||
629 | message: None, | ||
630 | percentage: None, | ||
631 | }) | ||
632 | } | ||
633 | ra_flycheck::Status::Progress(target) => { | ||
634 | lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport { | ||
635 | cancellable: Some(false), | ||
636 | message: Some(target), | ||
637 | percentage: None, | ||
638 | }) | ||
639 | } | ||
640 | ra_flycheck::Status::End => { | ||
641 | lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { | ||
642 | message: None, | ||
643 | }) | ||
644 | } | ||
645 | }; | ||
646 | 636 | ||
647 | let params = lsp_types::ProgressParams { | 637 | report_progress(global_state, msg_sender, "cargo check", state, message, None); |
648 | token: lsp_types::ProgressToken::String( | ||
649 | "rustAnalyzer/cargoWatcher".to_string(), | ||
650 | ), | ||
651 | value: lsp_types::ProgressParamsValue::WorkDone(progress), | ||
652 | }; | ||
653 | let not = notification_new::<lsp_types::notification::Progress>(params); | ||
654 | task_sender.send(Task::Notify(not)).unwrap(); | ||
655 | } | ||
656 | } | 638 | } |
657 | }; | 639 | }; |
658 | 640 | ||
@@ -671,39 +653,55 @@ fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state: | |||
671 | } | 653 | } |
672 | } | 654 | } |
673 | 655 | ||
656 | #[derive(Eq, PartialEq)] | ||
657 | enum ProgressState { | ||
658 | Start, | ||
659 | Report, | ||
660 | End, | ||
661 | } | ||
662 | |||
663 | fn percentage(done: usize, total: usize) -> f64 { | ||
664 | (done as f64 / total.max(1) as f64) * 100.0 | ||
665 | } | ||
666 | |||
674 | fn report_progress( | 667 | fn report_progress( |
675 | global_state: &mut GlobalState, | 668 | global_state: &mut GlobalState, |
676 | sender: &Sender<Message>, | 669 | sender: &Sender<Message>, |
677 | done: usize, | 670 | title: &str, |
678 | total: usize, | 671 | state: ProgressState, |
679 | message: &str, | 672 | message: Option<String>, |
673 | percentage: Option<f64>, | ||
680 | ) { | 674 | ) { |
681 | let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", message)); | 675 | if !global_state.config.client_caps.work_done_progress { |
682 | let message = Some(format!("{}/{} {}", done, total, message)); | 676 | return; |
683 | let percentage = Some(100.0 * done as f64 / total.max(1) as f64); | 677 | } |
684 | let work_done_progress = if done == 0 { | 678 | let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title)); |
685 | let work_done_progress_create = global_state.req_queue.outgoing.register( | 679 | let work_done_progress = match state { |
686 | lsp_types::request::WorkDoneProgressCreate::METHOD.to_string(), | 680 | ProgressState::Start => { |
687 | lsp_types::WorkDoneProgressCreateParams { token: token.clone() }, | 681 | let work_done_progress_create = global_state.req_queue.outgoing.register( |
688 | DO_NOTHING, | 682 | lsp_types::request::WorkDoneProgressCreate::METHOD.to_string(), |
689 | ); | 683 | lsp_types::WorkDoneProgressCreateParams { token: token.clone() }, |
690 | sender.send(work_done_progress_create.into()).unwrap(); | 684 | DO_NOTHING, |
691 | 685 | ); | |
692 | lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { | 686 | sender.send(work_done_progress_create.into()).unwrap(); |
693 | title: "rust-analyzer".into(), | 687 | |
694 | cancellable: None, | 688 | lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { |
695 | message, | 689 | title: title.into(), |
696 | percentage, | 690 | cancellable: None, |
697 | }) | 691 | message, |
698 | } else if done < total { | 692 | percentage, |
699 | lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport { | 693 | }) |
700 | cancellable: None, | 694 | } |
701 | message, | 695 | ProgressState::Report => { |
702 | percentage, | 696 | lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport { |
703 | }) | 697 | cancellable: None, |
704 | } else { | 698 | message, |
705 | assert!(done == total); | 699 | percentage, |
706 | lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message }) | 700 | }) |
701 | } | ||
702 | ProgressState::End => { | ||
703 | lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message }) | ||
704 | } | ||
707 | }; | 705 | }; |
708 | let notification = | 706 | let notification = |
709 | notification_new::<lsp_types::notification::Progress>(lsp_types::ProgressParams { | 707 | notification_new::<lsp_types::notification::Progress>(lsp_types::ProgressParams { |
@@ -826,7 +824,7 @@ where | |||
826 | Err(e) => match e.downcast::<LspError>() { | 824 | Err(e) => match e.downcast::<LspError>() { |
827 | Ok(lsp_error) => Response::new_err(id, lsp_error.code, lsp_error.message), | 825 | Ok(lsp_error) => Response::new_err(id, lsp_error.code, lsp_error.message), |
828 | Err(e) => { | 826 | Err(e) => { |
829 | if is_canceled(&e) { | 827 | if is_canceled(&*e) { |
830 | Response::new_err( | 828 | Response::new_err( |
831 | id, | 829 | id, |
832 | ErrorCode::ContentModified as i32, | 830 | ErrorCode::ContentModified as i32, |
@@ -853,7 +851,7 @@ fn update_file_notifications_on_threadpool( | |||
853 | for file_id in subscriptions { | 851 | for file_id in subscriptions { |
854 | match handlers::publish_diagnostics(&world, file_id) { | 852 | match handlers::publish_diagnostics(&world, file_id) { |
855 | Err(e) => { | 853 | Err(e) => { |
856 | if !is_canceled(&e) { | 854 | if !is_canceled(&*e) { |
857 | log::error!("failed to compute diagnostics: {:?}", e); | 855 | log::error!("failed to compute diagnostics: {:?}", e); |
858 | } | 856 | } |
859 | } | 857 | } |
@@ -866,41 +864,6 @@ fn update_file_notifications_on_threadpool( | |||
866 | } | 864 | } |
867 | } | 865 | } |
868 | 866 | ||
869 | pub fn show_message( | ||
870 | typ: lsp_types::MessageType, | ||
871 | message: impl Into<String>, | ||
872 | sender: &Sender<Message>, | ||
873 | ) { | ||
874 | let message = message.into(); | ||
875 | let params = lsp_types::ShowMessageParams { typ, message }; | ||
876 | let not = notification_new::<lsp_types::notification::ShowMessage>(params); | ||
877 | sender.send(not.into()).unwrap(); | ||
878 | } | ||
879 | |||
880 | fn is_canceled(e: &Box<dyn std::error::Error + Send + Sync>) -> bool { | ||
881 | e.downcast_ref::<Canceled>().is_some() | ||
882 | } | ||
883 | |||
884 | fn notification_is<N: lsp_types::notification::Notification>(notification: &Notification) -> bool { | ||
885 | notification.method == N::METHOD | ||
886 | } | ||
887 | |||
888 | fn notification_cast<N>(notification: Notification) -> std::result::Result<N::Params, Notification> | ||
889 | where | ||
890 | N: lsp_types::notification::Notification, | ||
891 | N::Params: DeserializeOwned, | ||
892 | { | ||
893 | notification.extract(N::METHOD) | ||
894 | } | ||
895 | |||
896 | fn notification_new<N>(params: N::Params) -> Notification | ||
897 | where | ||
898 | N: lsp_types::notification::Notification, | ||
899 | N::Params: Serialize, | ||
900 | { | ||
901 | Notification::new(N::METHOD.to_string(), params) | ||
902 | } | ||
903 | |||
904 | #[cfg(test)] | 867 | #[cfg(test)] |
905 | mod tests { | 868 | mod tests { |
906 | use lsp_types::{Position, Range, TextDocumentContentChangeEvent}; | 869 | use lsp_types::{Position, Range, TextDocumentContentChangeEvent}; |