mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-02 06:41:48 +00:00
Merge #5046
5046: Fix progress reporting for flycheck r=matklad a=matklad bors r+ Co-authored-by: veetaha <veetaha2@gmail.com> Co-authored-by: Veetaha <veetaha2@gmail.com> Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
659b16981a
7 changed files with 133 additions and 219 deletions
|
@ -3,6 +3,7 @@
|
||||||
//! LSP diagnostics based on the output of the command.
|
//! LSP diagnostics based on the output of the command.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
fmt,
|
||||||
io::{self, BufReader},
|
io::{self, BufReader},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
|
@ -31,6 +32,17 @@ pub enum FlycheckConfig {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FlycheckConfig {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command),
|
||||||
|
FlycheckConfig::CustomCommand { command, args } => {
|
||||||
|
write!(f, "{} {}", command, args.join(" "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Flycheck wraps the shared state and communication machinery used for
|
/// Flycheck wraps the shared state and communication machinery used for
|
||||||
/// running `cargo check` (or other compatible command) and providing
|
/// running `cargo check` (or other compatible command) and providing
|
||||||
/// diagnostics based on the output.
|
/// diagnostics based on the output.
|
||||||
|
|
|
@ -29,7 +29,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
|
||||||
fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> Option<Flycheck> {
|
fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> Option<Flycheck> {
|
||||||
// FIXME: Figure out the multi-workspace situation
|
// FIXME: Figure out the multi-workspace situation
|
||||||
workspaces.iter().find_map(|w| match w {
|
workspaces.iter().find_map(move |w| match w {
|
||||||
ProjectWorkspace::Cargo { cargo, .. } => {
|
ProjectWorkspace::Cargo { cargo, .. } => {
|
||||||
let cargo_project_root = cargo.workspace_root().to_path_buf();
|
let cargo_project_root = cargo.workspace_root().to_path_buf();
|
||||||
Some(Flycheck::new(config.clone(), cargo_project_root.into()))
|
Some(Flycheck::new(config.clone(), cargo_project_root.into()))
|
||||||
|
|
|
@ -29,16 +29,14 @@ mod markdown;
|
||||||
mod diagnostics;
|
mod diagnostics;
|
||||||
mod line_endings;
|
mod line_endings;
|
||||||
mod request_metrics;
|
mod request_metrics;
|
||||||
|
mod lsp_utils;
|
||||||
pub mod lsp_ext;
|
pub mod lsp_ext;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
|
|
||||||
pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>;
|
pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>;
|
||||||
pub use crate::{
|
pub use crate::{caps::server_capabilities, lsp_utils::show_message, main_loop::main_loop};
|
||||||
caps::server_capabilities,
|
|
||||||
main_loop::{main_loop, show_message},
|
|
||||||
};
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> {
|
pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> {
|
||||||
|
|
44
crates/rust-analyzer/src/lsp_utils.rs
Normal file
44
crates/rust-analyzer/src/lsp_utils.rs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
//! Utilities for LSP-related boilerplate code.
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use crossbeam_channel::Sender;
|
||||||
|
use lsp_server::{Message, Notification};
|
||||||
|
use ra_db::Canceled;
|
||||||
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
|
|
||||||
|
pub fn show_message(
|
||||||
|
typ: lsp_types::MessageType,
|
||||||
|
message: impl Into<String>,
|
||||||
|
sender: &Sender<Message>,
|
||||||
|
) {
|
||||||
|
let message = message.into();
|
||||||
|
let params = lsp_types::ShowMessageParams { typ, message };
|
||||||
|
let not = notification_new::<lsp_types::notification::ShowMessage>(params);
|
||||||
|
sender.send(not.into()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool {
|
||||||
|
e.downcast_ref::<Canceled>().is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn notification_is<N: lsp_types::notification::Notification>(
|
||||||
|
notification: &Notification,
|
||||||
|
) -> bool {
|
||||||
|
notification.method == N::METHOD
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn notification_cast<N>(notification: Notification) -> Result<N::Params, Notification>
|
||||||
|
where
|
||||||
|
N: lsp_types::notification::Notification,
|
||||||
|
N::Params: DeserializeOwned,
|
||||||
|
{
|
||||||
|
notification.extract(N::METHOD)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn notification_new<N>(params: N::Params) -> Notification
|
||||||
|
where
|
||||||
|
N: lsp_types::notification::Notification,
|
||||||
|
N::Params: Serialize,
|
||||||
|
{
|
||||||
|
Notification::new(N::METHOD.to_string(), params)
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ use crate::{
|
||||||
from_proto,
|
from_proto,
|
||||||
global_state::{file_id_to_url, GlobalState, GlobalStateSnapshot, Status},
|
global_state::{file_id_to_url, GlobalState, GlobalStateSnapshot, Status},
|
||||||
handlers, lsp_ext,
|
handlers, lsp_ext,
|
||||||
|
lsp_utils::{is_canceled, notification_cast, notification_is, notification_new, show_message},
|
||||||
request_metrics::RequestMetrics,
|
request_metrics::RequestMetrics,
|
||||||
LspError, Result,
|
LspError, Result,
|
||||||
};
|
};
|
||||||
|
@ -138,7 +139,7 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
|
||||||
recv(global_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task {
|
recv(global_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task {
|
||||||
Ok(task) => Event::CheckWatcher(task),
|
Ok(task) => Event::CheckWatcher(task),
|
||||||
Err(RecvError) => return Err("check watcher died".into()),
|
Err(RecvError) => return Err("check watcher died".into()),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
if let Event::Msg(Message::Request(req)) = &event {
|
if let Event::Msg(Message::Request(req)) = &event {
|
||||||
if connection.handle_shutdown(&req)? {
|
if connection.handle_shutdown(&req)? {
|
||||||
|
@ -168,7 +169,6 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Task {
|
enum Task {
|
||||||
Respond(Response),
|
Respond(Response),
|
||||||
Notify(Notification),
|
|
||||||
Diagnostic(DiagnosticTask),
|
Diagnostic(DiagnosticTask),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,11 +193,6 @@ impl fmt::Debug for Event {
|
||||||
return debug_verbose_not(not, f);
|
return debug_verbose_not(not, f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::Task(Task::Notify(not)) => {
|
|
||||||
if notification_is::<lsp_types::notification::PublishDiagnostics>(not) {
|
|
||||||
return debug_verbose_not(not, f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::Task(Task::Respond(resp)) => {
|
Event::Task(Task::Respond(resp)) => {
|
||||||
return f
|
return f
|
||||||
.debug_struct("Response")
|
.debug_struct("Response")
|
||||||
|
@ -254,14 +249,29 @@ fn loop_turn(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vfs::loader::Message::Progress { n_total, n_done } => {
|
vfs::loader::Message::Progress { n_total, n_done } => {
|
||||||
if n_done == n_total {
|
let state = if n_done == 0 {
|
||||||
|
ProgressState::Start
|
||||||
|
} else if n_done < n_total {
|
||||||
|
ProgressState::Report
|
||||||
|
} else {
|
||||||
|
assert_eq!(n_done, n_total);
|
||||||
global_state.status = Status::Ready;
|
global_state.status = Status::Ready;
|
||||||
became_ready = true;
|
became_ready = true;
|
||||||
}
|
ProgressState::End
|
||||||
report_progress(global_state, &connection.sender, n_done, n_total, "roots scanned")
|
};
|
||||||
|
report_progress(
|
||||||
|
global_state,
|
||||||
|
&connection.sender,
|
||||||
|
"roots scanned",
|
||||||
|
state,
|
||||||
|
Some(format!("{}/{}", n_done, n_total)),
|
||||||
|
Some(percentage(n_done, n_total)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Event::CheckWatcher(task) => on_check_task(task, global_state, task_sender)?,
|
Event::CheckWatcher(task) => {
|
||||||
|
on_check_task(task, global_state, task_sender, &connection.sender)?
|
||||||
|
}
|
||||||
Event::Msg(msg) => match msg {
|
Event::Msg(msg) => match msg {
|
||||||
Message::Request(req) => {
|
Message::Request(req) => {
|
||||||
on_request(global_state, pool, task_sender, &connection.sender, loop_start, req)?
|
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
|
||||||
msg_sender.send(response.into()).unwrap();
|
msg_sender.send(response.into()).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Task::Notify(n) => {
|
|
||||||
msg_sender.send(n.into()).unwrap();
|
|
||||||
}
|
|
||||||
Task::Diagnostic(task) => on_diagnostic_task(task, msg_sender, global_state),
|
Task::Diagnostic(task) => on_diagnostic_task(task, msg_sender, global_state),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -589,6 +596,7 @@ fn on_check_task(
|
||||||
task: CheckTask,
|
task: CheckTask,
|
||||||
global_state: &mut GlobalState,
|
global_state: &mut GlobalState,
|
||||||
task_sender: &Sender<Task>,
|
task_sender: &Sender<Task>,
|
||||||
|
msg_sender: &Sender<Message>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match task {
|
match task {
|
||||||
CheckTask::ClearDiagnostics => {
|
CheckTask::ClearDiagnostics => {
|
||||||
|
@ -620,39 +628,13 @@ fn on_check_task(
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckTask::Status(status) => {
|
CheckTask::Status(status) => {
|
||||||
if global_state.config.client_caps.work_done_progress {
|
let (state, message) = match status {
|
||||||
let progress = match status {
|
ra_flycheck::Status::Being => (ProgressState::Start, None),
|
||||||
ra_flycheck::Status::Being => {
|
ra_flycheck::Status::Progress(target) => (ProgressState::Report, Some(target)),
|
||||||
lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
|
ra_flycheck::Status::End => (ProgressState::End, None),
|
||||||
title: "Running `cargo check`".to_string(),
|
};
|
||||||
cancellable: Some(false),
|
|
||||||
message: None,
|
|
||||||
percentage: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
ra_flycheck::Status::Progress(target) => {
|
|
||||||
lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport {
|
|
||||||
cancellable: Some(false),
|
|
||||||
message: Some(target),
|
|
||||||
percentage: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
ra_flycheck::Status::End => {
|
|
||||||
lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd {
|
|
||||||
message: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let params = lsp_types::ProgressParams {
|
report_progress(global_state, msg_sender, "cargo check", state, message, None);
|
||||||
token: lsp_types::ProgressToken::String(
|
|
||||||
"rustAnalyzer/cargoWatcher".to_string(),
|
|
||||||
),
|
|
||||||
value: lsp_types::ProgressParamsValue::WorkDone(progress),
|
|
||||||
};
|
|
||||||
let not = notification_new::<lsp_types::notification::Progress>(params);
|
|
||||||
task_sender.send(Task::Notify(not)).unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -671,39 +653,55 @@ fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq)]
|
||||||
|
enum ProgressState {
|
||||||
|
Start,
|
||||||
|
Report,
|
||||||
|
End,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn percentage(done: usize, total: usize) -> f64 {
|
||||||
|
(done as f64 / total.max(1) as f64) * 100.0
|
||||||
|
}
|
||||||
|
|
||||||
fn report_progress(
|
fn report_progress(
|
||||||
global_state: &mut GlobalState,
|
global_state: &mut GlobalState,
|
||||||
sender: &Sender<Message>,
|
sender: &Sender<Message>,
|
||||||
done: usize,
|
title: &str,
|
||||||
total: usize,
|
state: ProgressState,
|
||||||
message: &str,
|
message: Option<String>,
|
||||||
|
percentage: Option<f64>,
|
||||||
) {
|
) {
|
||||||
let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", message));
|
if !global_state.config.client_caps.work_done_progress {
|
||||||
let message = Some(format!("{}/{} {}", done, total, message));
|
return;
|
||||||
let percentage = Some(100.0 * done as f64 / total.max(1) as f64);
|
}
|
||||||
let work_done_progress = if done == 0 {
|
let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title));
|
||||||
let work_done_progress_create = global_state.req_queue.outgoing.register(
|
let work_done_progress = match state {
|
||||||
lsp_types::request::WorkDoneProgressCreate::METHOD.to_string(),
|
ProgressState::Start => {
|
||||||
lsp_types::WorkDoneProgressCreateParams { token: token.clone() },
|
let work_done_progress_create = global_state.req_queue.outgoing.register(
|
||||||
DO_NOTHING,
|
lsp_types::request::WorkDoneProgressCreate::METHOD.to_string(),
|
||||||
);
|
lsp_types::WorkDoneProgressCreateParams { token: token.clone() },
|
||||||
sender.send(work_done_progress_create.into()).unwrap();
|
DO_NOTHING,
|
||||||
|
);
|
||||||
|
sender.send(work_done_progress_create.into()).unwrap();
|
||||||
|
|
||||||
lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
|
lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
|
||||||
title: "rust-analyzer".into(),
|
title: title.into(),
|
||||||
cancellable: None,
|
cancellable: None,
|
||||||
message,
|
message,
|
||||||
percentage,
|
percentage,
|
||||||
})
|
})
|
||||||
} else if done < total {
|
}
|
||||||
lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport {
|
ProgressState::Report => {
|
||||||
cancellable: None,
|
lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport {
|
||||||
message,
|
cancellable: None,
|
||||||
percentage,
|
message,
|
||||||
})
|
percentage,
|
||||||
} else {
|
})
|
||||||
assert!(done == total);
|
}
|
||||||
lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message })
|
ProgressState::End => {
|
||||||
|
lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message })
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let notification =
|
let notification =
|
||||||
notification_new::<lsp_types::notification::Progress>(lsp_types::ProgressParams {
|
notification_new::<lsp_types::notification::Progress>(lsp_types::ProgressParams {
|
||||||
|
@ -826,7 +824,7 @@ where
|
||||||
Err(e) => match e.downcast::<LspError>() {
|
Err(e) => match e.downcast::<LspError>() {
|
||||||
Ok(lsp_error) => Response::new_err(id, lsp_error.code, lsp_error.message),
|
Ok(lsp_error) => Response::new_err(id, lsp_error.code, lsp_error.message),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if is_canceled(&e) {
|
if is_canceled(&*e) {
|
||||||
Response::new_err(
|
Response::new_err(
|
||||||
id,
|
id,
|
||||||
ErrorCode::ContentModified as i32,
|
ErrorCode::ContentModified as i32,
|
||||||
|
@ -853,7 +851,7 @@ fn update_file_notifications_on_threadpool(
|
||||||
for file_id in subscriptions {
|
for file_id in subscriptions {
|
||||||
match handlers::publish_diagnostics(&world, file_id) {
|
match handlers::publish_diagnostics(&world, file_id) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if !is_canceled(&e) {
|
if !is_canceled(&*e) {
|
||||||
log::error!("failed to compute diagnostics: {:?}", e);
|
log::error!("failed to compute diagnostics: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -866,41 +864,6 @@ fn update_file_notifications_on_threadpool(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_message(
|
|
||||||
typ: lsp_types::MessageType,
|
|
||||||
message: impl Into<String>,
|
|
||||||
sender: &Sender<Message>,
|
|
||||||
) {
|
|
||||||
let message = message.into();
|
|
||||||
let params = lsp_types::ShowMessageParams { typ, message };
|
|
||||||
let not = notification_new::<lsp_types::notification::ShowMessage>(params);
|
|
||||||
sender.send(not.into()).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_canceled(e: &Box<dyn std::error::Error + Send + Sync>) -> bool {
|
|
||||||
e.downcast_ref::<Canceled>().is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn notification_is<N: lsp_types::notification::Notification>(notification: &Notification) -> bool {
|
|
||||||
notification.method == N::METHOD
|
|
||||||
}
|
|
||||||
|
|
||||||
fn notification_cast<N>(notification: Notification) -> std::result::Result<N::Params, Notification>
|
|
||||||
where
|
|
||||||
N: lsp_types::notification::Notification,
|
|
||||||
N::Params: DeserializeOwned,
|
|
||||||
{
|
|
||||||
notification.extract(N::METHOD)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn notification_new<N>(params: N::Params) -> Notification
|
|
||||||
where
|
|
||||||
N: lsp_types::notification::Notification,
|
|
||||||
N::Params: Serialize,
|
|
||||||
{
|
|
||||||
Notification::new(N::METHOD.to_string(), params)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use lsp_types::{Position, Range, TextDocumentContentChangeEvent};
|
use lsp_types::{Position, Range, TextDocumentContentChangeEvent};
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { promises as fs, PathLike } from "fs";
|
||||||
|
|
||||||
import * as commands from './commands';
|
import * as commands from './commands';
|
||||||
import { activateInlayHints } from './inlay_hints';
|
import { activateInlayHints } from './inlay_hints';
|
||||||
import { activateStatusDisplay } from './status_display';
|
|
||||||
import { Ctx } from './ctx';
|
import { Ctx } from './ctx';
|
||||||
import { Config, NIGHTLY_TAG } from './config';
|
import { Config, NIGHTLY_TAG } from './config';
|
||||||
import { log, assert, isValidExecutable } from './util';
|
import { log, assert, isValidExecutable } from './util';
|
||||||
|
@ -117,8 +116,6 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||||
|
|
||||||
ctx.pushCleanup(activateTaskProvider(workspaceFolder));
|
ctx.pushCleanup(activateTaskProvider(workspaceFolder));
|
||||||
|
|
||||||
activateStatusDisplay(ctx);
|
|
||||||
|
|
||||||
activateInlayHints(ctx);
|
activateInlayHints(ctx);
|
||||||
|
|
||||||
vscode.workspace.onDidChangeConfiguration(
|
vscode.workspace.onDidChangeConfiguration(
|
||||||
|
|
|
@ -1,100 +0,0 @@
|
||||||
import * as vscode from 'vscode';
|
|
||||||
|
|
||||||
import { WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressReport, WorkDoneProgressEnd, Disposable } from 'vscode-languageclient';
|
|
||||||
|
|
||||||
import { Ctx } from './ctx';
|
|
||||||
|
|
||||||
const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
||||||
|
|
||||||
export function activateStatusDisplay(ctx: Ctx) {
|
|
||||||
const statusDisplay = new StatusDisplay(ctx.config.checkOnSave.command);
|
|
||||||
ctx.pushCleanup(statusDisplay);
|
|
||||||
const client = ctx.client;
|
|
||||||
if (client != null) {
|
|
||||||
ctx.pushCleanup(client.onProgress(
|
|
||||||
WorkDoneProgress.type,
|
|
||||||
'rustAnalyzer/cargoWatcher',
|
|
||||||
params => statusDisplay.handleProgressNotification(params)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StatusDisplay implements Disposable {
|
|
||||||
packageName?: string;
|
|
||||||
|
|
||||||
private i: number = 0;
|
|
||||||
private statusBarItem: vscode.StatusBarItem;
|
|
||||||
private command: string;
|
|
||||||
private timer?: NodeJS.Timeout;
|
|
||||||
|
|
||||||
constructor(command: string) {
|
|
||||||
this.statusBarItem = vscode.window.createStatusBarItem(
|
|
||||||
vscode.StatusBarAlignment.Left,
|
|
||||||
10,
|
|
||||||
);
|
|
||||||
this.command = command;
|
|
||||||
this.statusBarItem.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
show() {
|
|
||||||
this.packageName = undefined;
|
|
||||||
|
|
||||||
this.timer =
|
|
||||||
this.timer ||
|
|
||||||
setInterval(() => {
|
|
||||||
this.tick();
|
|
||||||
this.refreshLabel();
|
|
||||||
}, 300);
|
|
||||||
|
|
||||||
this.statusBarItem.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
hide() {
|
|
||||||
if (this.timer) {
|
|
||||||
clearInterval(this.timer);
|
|
||||||
this.timer = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.statusBarItem.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose() {
|
|
||||||
if (this.timer) {
|
|
||||||
clearInterval(this.timer);
|
|
||||||
this.timer = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.statusBarItem.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshLabel() {
|
|
||||||
if (this.packageName) {
|
|
||||||
this.statusBarItem.text = `${spinnerFrames[this.i]} cargo ${this.command} [${this.packageName}]`;
|
|
||||||
} else {
|
|
||||||
this.statusBarItem.text = `${spinnerFrames[this.i]} cargo ${this.command}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleProgressNotification(params: WorkDoneProgressBegin | WorkDoneProgressReport | WorkDoneProgressEnd) {
|
|
||||||
switch (params.kind) {
|
|
||||||
case 'begin':
|
|
||||||
this.show();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'report':
|
|
||||||
if (params.message) {
|
|
||||||
this.packageName = params.message;
|
|
||||||
this.refreshLabel();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'end':
|
|
||||||
this.hide();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private tick() {
|
|
||||||
this.i = (this.i + 1) % spinnerFrames.length;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue