From e41023ce46b36d57fd920a4520555d1067170e30 Mon Sep 17 00:00:00 2001
From: Lukas Wirth <lukastw97@gmail.com>
Date: Mon, 17 Oct 2022 16:32:47 +0200
Subject: [PATCH] Make flycheck workdone progress reports cancellable

---
 crates/rust-analyzer/src/lsp_utils.rs | 10 +++++++---
 crates/rust-analyzer/src/main_loop.rs | 26 +++++++++++++++++++++-----
 2 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 5a37cbe2e33..b3cea64d417 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -87,6 +87,7 @@ impl GlobalState {
         state: Progress,
         message: Option<String>,
         fraction: Option<f64>,
+        cancel_token: Option<String>,
     ) {
         if !self.config.work_done_progress() {
             return;
@@ -95,7 +96,10 @@ impl GlobalState {
             assert!((0.0..=1.0).contains(&f));
             (f * 100.0) as u32
         });
-        let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title));
+        let cancellable = Some(cancel_token.is_some());
+        let token = lsp_types::ProgressToken::String(
+            cancel_token.unwrap_or_else(|| format!("rustAnalyzer/{}", title)),
+        );
         let work_done_progress = match state {
             Progress::Begin => {
                 self.send_request::<lsp_types::request::WorkDoneProgressCreate>(
@@ -105,14 +109,14 @@ impl GlobalState {
 
                 lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
                     title: title.into(),
-                    cancellable: None,
+                    cancellable,
                     message,
                     percentage,
                 })
             }
             Progress::Report => {
                 lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport {
-                    cancellable: None,
+                    cancellable,
                     message,
                     percentage,
                 })
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 15922dac651..3a379993ad6 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -257,7 +257,7 @@ impl GlobalState {
                         }
                     };
 
-                    self.report_progress("Indexing", state, message, Some(fraction));
+                    self.report_progress("Indexing", state, message, Some(fraction), None);
                 }
             }
             Event::Vfs(message) => {
@@ -465,7 +465,7 @@ impl GlobalState {
                     }
                 };
 
-                self.report_progress("Fetching", state, msg, None);
+                self.report_progress("Fetching", state, msg, None, None);
             }
             Task::FetchBuildData(progress) => {
                 let (state, msg) = match progress {
@@ -481,7 +481,7 @@ impl GlobalState {
                 };
 
                 if let Some(state) = state {
-                    self.report_progress("Loading", state, msg, None);
+                    self.report_progress("Loading", state, msg, None, None);
                 }
             }
         }
@@ -518,6 +518,7 @@ impl GlobalState {
                     state,
                     Some(format!("{}/{}", n_done, n_total)),
                     Some(Progress::fraction(n_done, n_total)),
+                    None,
                 )
             }
         }
@@ -584,7 +585,13 @@ impl GlobalState {
                 } else {
                     format!("cargo check (#{})", id + 1)
                 };
-                self.report_progress(&title, state, message, None);
+                self.report_progress(
+                    &title,
+                    state,
+                    message,
+                    None,
+                    Some(format!("rust-analyzer/checkOnSave/{}", id)),
+                );
             }
         }
     }
@@ -698,7 +705,16 @@ impl GlobalState {
                 this.cancel(id);
                 Ok(())
             })?
-            .on::<lsp_types::notification::WorkDoneProgressCancel>(|_this, _params| {
+            .on::<lsp_types::notification::WorkDoneProgressCancel>(|this, params| {
+                if let lsp_types::NumberOrString::String(s) = &params.token {
+                    if let Some(id) = s.strip_prefix("rust-analyzer/checkOnSave/") {
+                        if let Ok(id) = u32::from_str_radix(id, 10) {
+                            if let Some(flycheck) = this.flycheck.get(id as usize) {
+                                flycheck.cancel();
+                            }
+                        }
+                    }
+                }
                 // Just ignore this. It is OK to continue sending progress
                 // notifications for this token, as the client can't know when
                 // we accepted notification.