Skip to content
This repository was archived by the owner on Dec 29, 2022. It is now read-only.

Commit 9527a0b

Browse files
committed
Use window/showMessage to communicate build errors to user
1 parent 3719158 commit 9527a0b

File tree

9 files changed

+107
-14
lines changed

9 files changed

+107
-14
lines changed

src/actions/mod.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use server::Output;
3131
use std::collections::HashMap;
3232
use std::path::{Path, PathBuf};
3333
use std::sync::{Arc, Mutex};
34-
use std::sync::atomic::{AtomicUsize, Ordering};
34+
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering};
3535
use std::thread;
3636

3737

@@ -120,6 +120,7 @@ pub struct InitActionContext {
120120
current_project: PathBuf,
121121

122122
previous_build_results: Arc<Mutex<BuildResults>>,
123+
previous_was_build_error: Arc<AtomicBool>,
123124
build_queue: BuildQueue,
124125
// Keep a record of builds/post-build tasks currently in flight so that
125126
// mutating actions can block until the data is ready.
@@ -166,6 +167,7 @@ impl InitActionContext {
166167
config,
167168
current_project,
168169
previous_build_results: Arc::new(Mutex::new(HashMap::new())),
170+
previous_was_build_error: Arc::new(AtomicBool::new(false)),
169171
build_queue,
170172
active_build_count: Arc::new(AtomicUsize::new(0)),
171173
client_capabilities: Arc::new(client_capabilities),
@@ -208,7 +210,10 @@ impl InitActionContext {
208210
show_warnings: config.show_warnings,
209211
use_black_list: config.use_crate_blacklist,
210212
active_build_count: self.active_build_count.clone(),
211-
notifier: Box::new(BuildDiagnosticsNotifier::new(out.clone())),
213+
notifier: Box::new(BuildDiagnosticsNotifier::new(
214+
out.clone(),
215+
self.previous_was_build_error.clone(),
216+
)),
212217
blocked_threads: vec![],
213218
}
214219
};

src/actions/post_build.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,11 @@ impl PostBuildHandler {
7979
trace!("build - Squashed");
8080
self.active_build_count.fetch_sub(1, Ordering::SeqCst);
8181
}
82-
BuildResult::Err => {
82+
BuildResult::Err(msg) => {
8383
trace!("build - Error");
84+
self.notifier.notify_begin_diagnostics();
85+
self.notifier.notify_build_error(msg);
86+
self.notifier.notify_end_diagnostics();
8487
self.active_build_count.fetch_sub(1, Ordering::SeqCst);
8588
}
8689
}

src/actions/progress.rs

+40-4
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use std::sync::atomic::{AtomicUsize, Ordering};
11+
use std::sync::Arc;
12+
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
1213

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};
1516
use server::{Output, Notification};
1617

1718
/// Trait for communication of build progress back to the client.
@@ -34,6 +35,7 @@ pub enum ProgressUpdate {
3435
pub trait DiagnosticsNotifier: Send {
3536
fn notify_begin_diagnostics(&self);
3637
fn notify_publish_diagnostics(&self, PublishDiagnosticsParams);
38+
fn notify_build_error(&self, msg: String);
3739
fn notify_end_diagnostics(&self);
3840
}
3941

@@ -101,13 +103,19 @@ pub struct BuildDiagnosticsNotifier<O: Output> {
101103
// these params are used as a template and are cloned for each
102104
// message that is actually notified.
103105
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,
104110
}
105111

106112
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> {
108114
BuildDiagnosticsNotifier {
109115
out,
110116
progress_params: new_progress_params("Diagnostics".into()),
117+
previous_was_build_error,
118+
current_is_build_error: AtomicBool::new(false),
111119
}
112120
}
113121
}
@@ -120,9 +128,37 @@ impl<O: Output> DiagnosticsNotifier for BuildDiagnosticsNotifier<O> {
120128
fn notify_publish_diagnostics(&self, params: PublishDiagnosticsParams) {
121129
self.out.notify(Notification::<PublishDiagnostics>::new(params));
122130
}
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+
}
123147
fn notify_end_diagnostics(&self) {
124148
let mut params = self.progress_params.clone();
125149
params.done = Some(true);
126150
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);
127163
}
128164
}

src/build/cargo.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,14 @@ pub(super) fn cargo(internals: &Internals, progress_sender: Sender<ProgressUpdat
8484
Ok(cwd) => BuildResult::Success(cwd, vec![], vec![]),
8585
Err(err) => {
8686
let stdout = String::from_utf8(out_clone.lock().unwrap().to_owned()).unwrap();
87-
info!("cargo failed\ncause: {}\nstdout: {}", err, stdout);
88-
BuildResult::Err
87+
let stdout_msg = if stdout.is_empty() {
88+
"".to_string()
89+
} else {
90+
format!("({})", stdout)
91+
};
92+
let msg = format!("Cargo failed: {}{}", err, stdout_msg);
93+
info!("{}", msg);
94+
BuildResult::Err(msg)
8995
}
9096
}
9197
}

src/build/mod.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,9 @@ pub enum BuildResult {
103103
Success(PathBuf, Vec<String>, Vec<Analysis>),
104104
/// Build was coalesced with another build.
105105
Squashed,
106-
/// There was an error attempting to build.
107-
Err,
106+
/// There was an error attempting to build. The argument is a message
107+
/// to be propagated to the user.
108+
Err(String),
108109
}
109110

110111
/// Priority for a build request.
@@ -545,8 +546,8 @@ impl Internals {
545546
// In single package mode Cargo needs to be run to cache args/envs for
546547
// future rustc calls
547548
} else if needs_to_run_cargo {
548-
if let BuildResult::Err = cargo::cargo(self, progress_sender) {
549-
return BuildResult::Err;
549+
if let BuildResult::Err(s) = cargo::cargo(self, progress_sender) {
550+
return BuildResult::Err(s);
550551
}
551552
}
552553

src/build/plan.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ impl JobQueue {
385385
analyses.append(&mut analysis);
386386
cwd = c;
387387
}
388-
BuildResult::Err => return BuildResult::Err,
388+
BuildResult::Err(msg) => return BuildResult::Err(msg),
389389
_ => {}
390390
}
391391
}

src/test/mod.rs

+28
Original file line numberDiff line numberDiff line change
@@ -1386,3 +1386,31 @@ fn test_deglob() {
13861386
],
13871387
);
13881388
}
1389+
1390+
#[test]
1391+
fn test_cargo_fail() {
1392+
let mut env = Environment::new("cargo_fail");
1393+
1394+
let root_path = env.cache.abs_path(Path::new("."));
1395+
let messages = vec![
1396+
initialize(0, root_path.as_os_str().to_str().map(|x| x.to_owned())).to_string(),
1397+
];
1398+
1399+
let (mut server, results) = env.mock_server(messages);
1400+
// Initialize and build.
1401+
assert_eq!(
1402+
ls_server::LsService::handle_message(&mut server),
1403+
ls_server::ServerStateChange::Continue
1404+
);
1405+
expect_messages(
1406+
results.clone(),
1407+
&[
1408+
ExpectedMessage::new(Some(0)).expect_contains("capabilities"),
1409+
ExpectedMessage::new(None).expect_contains("progress").expect_contains("Build"),
1410+
ExpectedMessage::new(None).expect_contains("progress").expect_contains(r#""done":true"#),
1411+
ExpectedMessage::new(None).expect_contains("progress").expect_contains("Diagnostics"),
1412+
ExpectedMessage::new(None).expect_contains("showMessage").expect_contains("Cargo failed: no matching package named `some_non_existant_crate`"),
1413+
ExpectedMessage::new(None).expect_contains("progress").expect_contains(r#""done":true"#),
1414+
],
1415+
);
1416+
}

test_data/cargo_fail/Cargo.toml

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "cargo_fail"
3+
version = "0.1.0"
4+
authors = ["Martin Algesten <[email protected]>"]
5+
6+
[dependencies]
7+
some_non_existant_crate = "*"

test_data/cargo_fail/src/main.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
extern crate some_none_existant_crate;
3+
4+
fn main() {
5+
// deliberate bad formatting
6+
println!("Hello, world!");
7+
}

0 commit comments

Comments
 (0)