8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
10
11
- use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
11
+ use std:: sync:: Arc ;
12
+ use std:: sync:: atomic:: { AtomicBool , AtomicUsize , Ordering } ;
12
13
13
- use actions:: notifications:: { PublishDiagnostics , Progress } ;
14
- use lsp_data:: { ProgressParams , PublishDiagnosticsParams } ;
14
+ use actions:: notifications:: { PublishDiagnostics , Progress , ShowMessage } ;
15
+ use lsp_data:: { ProgressParams , PublishDiagnosticsParams , ShowMessageParams , MessageType } ;
15
16
use server:: { Output , Notification } ;
16
17
17
18
/// Trait for communication of build progress back to the client.
@@ -34,6 +35,7 @@ pub enum ProgressUpdate {
34
35
pub trait DiagnosticsNotifier : Send {
35
36
fn notify_begin_diagnostics ( & self ) ;
36
37
fn notify_publish_diagnostics ( & self , PublishDiagnosticsParams ) ;
38
+ fn notify_build_error ( & self , msg : String ) ;
37
39
fn notify_end_diagnostics ( & self ) ;
38
40
}
39
41
@@ -101,13 +103,19 @@ pub struct BuildDiagnosticsNotifier<O: Output> {
101
103
// these params are used as a template and are cloned for each
102
104
// message that is actually notified.
103
105
progress_params : ProgressParams ,
106
+ // shared bool in the entire context
107
+ previous_was_build_error : Arc < AtomicBool > ,
108
+ // whether this current build encountered an error.
109
+ current_is_build_error : AtomicBool ,
104
110
}
105
111
106
112
impl < O : Output > BuildDiagnosticsNotifier < O > {
107
- pub fn new ( out : O ) -> BuildDiagnosticsNotifier < O > {
113
+ pub fn new ( out : O , previous_was_build_error : Arc < AtomicBool > ) -> BuildDiagnosticsNotifier < O > {
108
114
BuildDiagnosticsNotifier {
109
115
out,
110
116
progress_params : new_progress_params ( "Diagnostics" . into ( ) ) ,
117
+ previous_was_build_error,
118
+ current_is_build_error : AtomicBool :: new ( false ) ,
111
119
}
112
120
}
113
121
}
@@ -120,9 +128,37 @@ impl<O: Output> DiagnosticsNotifier for BuildDiagnosticsNotifier<O> {
120
128
fn notify_publish_diagnostics ( & self , params : PublishDiagnosticsParams ) {
121
129
self . out . notify ( Notification :: < PublishDiagnostics > :: new ( params) ) ;
122
130
}
131
+ fn notify_build_error ( & self , message : String ) {
132
+ // This current build is definitely an error. Mark it and
133
+ // propagate to the shrared state in notify_end_diagnostics()
134
+ self . current_is_build_error . store ( true , Ordering :: SeqCst ) ;
135
+
136
+ // We only want to notify a build error to the user one time,
137
+ // not on every keystroke. As long as a previous build error
138
+ // hasn't cleared, we stay silent.
139
+ if !self . previous_was_build_error . load ( Ordering :: SeqCst ) {
140
+ let params = ShowMessageParams {
141
+ typ : MessageType :: Error ,
142
+ message,
143
+ } ;
144
+ self . out . notify ( Notification :: < ShowMessage > :: new ( params) ) ;
145
+ }
146
+ }
123
147
fn notify_end_diagnostics ( & self ) {
124
148
let mut params = self . progress_params . clone ( ) ;
125
149
params. done = Some ( true ) ;
126
150
self . out . notify ( Notification :: < Progress > :: new ( params) ) ;
151
+
152
+ // If the diagnostics for this build were fully reported without
153
+ // any current_is_build_error, we know the build is clear, and
154
+ // that is propagated to the shared previous_was_build_error.
155
+ //
156
+ // This is not perfectly thread safe. There's a chance two threads
157
+ // may be in contention propagating the local current_is_build_error
158
+ // to the shared previous_was_build_error. Whether this happens
159
+ // in practice is to be seen...
160
+ let is_build_error = self . current_is_build_error . load ( Ordering :: SeqCst ) ;
161
+ self . previous_was_build_error
162
+ . compare_and_swap ( !is_build_error, is_build_error, Ordering :: SeqCst ) ;
127
163
}
128
164
}
0 commit comments