1 use std::sync::atomic::{AtomicUsize, Ordering};
2 
3 use crate::server::{Notification, Output};
4 use lazy_static::lazy_static;
5 use lsp_types::notification::{Progress, PublishDiagnostics, ShowMessage};
6 use lsp_types::{MessageType, ProgressParams, PublishDiagnosticsParams, ShowMessageParams};
7 
8 /// Communication of build progress back to the client.
9 pub trait ProgressNotifier: Send {
notify_begin_progress(&self)10     fn notify_begin_progress(&self);
notify_progress(&self, update: ProgressUpdate)11     fn notify_progress(&self, update: ProgressUpdate);
notify_end_progress(&self)12     fn notify_end_progress(&self);
13 }
14 
15 /// Kinds of progress updates.
16 pub enum ProgressUpdate {
17     Message(String),
18     Percentage(f64),
19 }
20 
21 /// Trait for communication of diagnostics (i.e., build results) back to the rest of
22 /// the RLS (and on to the client).
23 // This trait only really exists to work around the object safety rules (Output
24 // is not object-safe).
25 pub trait DiagnosticsNotifier: Send {
notify_begin_diagnostics(&self)26     fn notify_begin_diagnostics(&self);
notify_publish_diagnostics(&self, _: PublishDiagnosticsParams)27     fn notify_publish_diagnostics(&self, _: PublishDiagnosticsParams);
notify_error_diagnostics(&self, msg: String)28     fn notify_error_diagnostics(&self, msg: String);
notify_end_diagnostics(&self)29     fn notify_end_diagnostics(&self);
30 }
31 
32 /// Generates a new progress params with a unique ID and the given title.
new_progress_params(title: String) -> ProgressParams33 fn new_progress_params(title: String) -> ProgressParams {
34     // Counter to generate unique IDs for each chain-of-progress notification.
35     lazy_static! {
36         static ref PROGRESS_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
37     }
38 
39     ProgressParams {
40         id: format!("progress_{}", PROGRESS_ID_COUNTER.fetch_add(1, Ordering::SeqCst)),
41         title,
42         message: None,
43         percentage: None,
44         done: None,
45     }
46 }
47 
48 /// Notifier of progress for the build (window/progress notifications).
49 /// the same instance is used for the entirety of one single build.
50 pub struct BuildProgressNotifier<O: Output> {
51     out: O,
52     // These params are used as a template and are cloned for each
53     // message that is actually notified.
54     progress_params: ProgressParams,
55 }
56 
57 impl<O: Output> BuildProgressNotifier<O> {
new(out: O) -> BuildProgressNotifier<O>58     pub fn new(out: O) -> BuildProgressNotifier<O> {
59         BuildProgressNotifier { out, progress_params: new_progress_params("Building".into()) }
60     }
61 }
62 
63 impl<O: Output> ProgressNotifier for BuildProgressNotifier<O> {
notify_begin_progress(&self)64     fn notify_begin_progress(&self) {
65         let params = self.progress_params.clone();
66         self.out.notify(Notification::<Progress>::new(params));
67     }
notify_progress(&self, update: ProgressUpdate)68     fn notify_progress(&self, update: ProgressUpdate) {
69         let mut params = self.progress_params.clone();
70         match update {
71             ProgressUpdate::Message(s) => params.message = Some(s),
72             ProgressUpdate::Percentage(p) => params.percentage = Some(p),
73         }
74         self.out.notify(Notification::<Progress>::new(params));
75     }
notify_end_progress(&self)76     fn notify_end_progress(&self) {
77         let mut params = self.progress_params.clone();
78         params.done = Some(true);
79         self.out.notify(Notification::<Progress>::new(params));
80     }
81 }
82 
83 /// Notifier of diagnostics after the build has completed.
84 pub struct BuildDiagnosticsNotifier<O: Output> {
85     out: O,
86     // These params are used as a template, and are cloned for each
87     // message that is actually notified.
88     progress_params: ProgressParams,
89 }
90 
91 impl<O: Output> BuildDiagnosticsNotifier<O> {
new(out: O) -> BuildDiagnosticsNotifier<O>92     pub fn new(out: O) -> BuildDiagnosticsNotifier<O> {
93         BuildDiagnosticsNotifier {
94             out,
95             // We emit diagnostics then index, since emitting diagnostics is really
96             // quick and always has a message, "indexing" is usually a more useful
97             // title.
98             progress_params: new_progress_params("Indexing".into()),
99         }
100     }
101 }
102 
103 impl<O: Output> DiagnosticsNotifier for BuildDiagnosticsNotifier<O> {
notify_begin_diagnostics(&self)104     fn notify_begin_diagnostics(&self) {
105         let params = self.progress_params.clone();
106         self.out.notify(Notification::<Progress>::new(params));
107     }
notify_publish_diagnostics(&self, params: PublishDiagnosticsParams)108     fn notify_publish_diagnostics(&self, params: PublishDiagnosticsParams) {
109         self.out.notify(Notification::<PublishDiagnostics>::new(params));
110     }
notify_error_diagnostics(&self, message: String)111     fn notify_error_diagnostics(&self, message: String) {
112         self.out.notify(Notification::<ShowMessage>::new(ShowMessageParams {
113             typ: MessageType::Error,
114             message,
115         }));
116     }
notify_end_diagnostics(&self)117     fn notify_end_diagnostics(&self) {
118         let mut params = self.progress_params.clone();
119         params.done = Some(true);
120         self.out.notify(Notification::<Progress>::new(params));
121     }
122 }
123