aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_flycheck/src/lib.rs12
-rw-r--r--crates/rust-analyzer/src/global_state.rs2
-rw-r--r--crates/rust-analyzer/src/lib.rs6
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs44
-rw-r--r--crates/rust-analyzer/src/main_loop.rs187
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
5use std::{ 5use 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
35impl 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
30fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> Option<Flycheck> { 30fn 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;
29mod diagnostics; 29mod diagnostics;
30mod line_endings; 30mod line_endings;
31mod request_metrics; 31mod request_metrics;
32mod lsp_utils;
32pub mod lsp_ext; 33pub mod lsp_ext;
33pub mod config; 34pub mod config;
34 35
35use serde::de::DeserializeOwned; 36use serde::de::DeserializeOwned;
36 37
37pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>; 38pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>;
38pub use crate::{ 39pub 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};
42use std::fmt; 40use std::fmt;
43 41
44pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> { 42pub 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.
2use std::error::Error;
3
4use crossbeam_channel::Sender;
5use lsp_server::{Message, Notification};
6use ra_db::Canceled;
7use serde::{de::DeserializeOwned, Serialize};
8
9pub 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
20pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool {
21 e.downcast_ref::<Canceled>().is_some()
22}
23
24pub(crate) fn notification_is<N: lsp_types::notification::Notification>(
25 notification: &Notification,
26) -> bool {
27 notification.method == N::METHOD
28}
29
30pub(crate) fn notification_cast<N>(notification: Notification) -> Result<N::Params, Notification>
31where
32 N: lsp_types::notification::Notification,
33 N::Params: DeserializeOwned,
34{
35 notification.extract(N::METHOD)
36}
37
38pub(crate) fn notification_new<N>(params: N::Params) -> Notification
39where
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)]
169enum Task { 170enum 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)]
657enum ProgressState {
658 Start,
659 Report,
660 End,
661}
662
663fn percentage(done: usize, total: usize) -> f64 {
664 (done as f64 / total.max(1) as f64) * 100.0
665}
666
674fn report_progress( 667fn 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
869pub 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
880fn is_canceled(e: &Box<dyn std::error::Error + Send + Sync>) -> bool {
881 e.downcast_ref::<Canceled>().is_some()
882}
883
884fn notification_is<N: lsp_types::notification::Notification>(notification: &Notification) -> bool {
885 notification.method == N::METHOD
886}
887
888fn notification_cast<N>(notification: Notification) -> std::result::Result<N::Params, Notification>
889where
890 N: lsp_types::notification::Notification,
891 N::Params: DeserializeOwned,
892{
893 notification.extract(N::METHOD)
894}
895
896fn notification_new<N>(params: N::Params) -> Notification
897where
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)]
905mod tests { 868mod tests {
906 use lsp_types::{Position, Range, TextDocumentContentChangeEvent}; 869 use lsp_types::{Position, Range, TextDocumentContentChangeEvent};