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