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

Commit e74304d

Browse files
committed
Use window/showMessage to communicate build errors to user
1 parent 5e990eb commit e74304d

File tree

8 files changed

+100
-10
lines changed

8 files changed

+100
-10
lines changed

src/actions/mod.rs

+39-2
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,17 @@ use span;
2222
use Span;
2323

2424
use actions::post_build::{BuildResults, PostBuildHandler, DiagnosticsNotifier};
25-
use actions::notifications::{PublishDiagnostics, Progress};
25+
use actions::notifications::{PublishDiagnostics, Progress, ShowMessage};
2626
use build::*;
2727
use lsp_data;
2828
use lsp_data::*;
29+
use ls_types::MessageType;
2930
use server::{Output, Notification};
3031

3132
use std::collections::HashMap;
3233
use std::path::{Path, PathBuf};
3334
use std::sync::{Arc, Mutex};
34-
use std::sync::atomic::{AtomicUsize, Ordering};
35+
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering};
3536
use std::thread;
3637

3738

@@ -119,6 +120,7 @@ pub struct InitActionContext {
119120
current_project: PathBuf,
120121

121122
previous_build_results: Arc<Mutex<BuildResults>>,
123+
previous_was_build_error: Arc<AtomicBool>,
122124
build_queue: BuildQueue,
123125
// Keep a record of builds/post-build tasks currently in flight so that
124126
// mutating actions can block until the data is ready.
@@ -165,6 +167,7 @@ impl InitActionContext {
165167
config,
166168
current_project,
167169
previous_build_results: Arc::new(Mutex::new(HashMap::new())),
170+
previous_was_build_error: Arc::new(AtomicBool::new(false)),
168171
build_queue,
169172
active_build_count: Arc::new(AtomicUsize::new(0)),
170173
client_capabilities: Arc::new(client_capabilities),
@@ -219,6 +222,10 @@ impl InitActionContext {
219222
out: O,
220223
active_build_count: Arc<AtomicUsize>,
221224
progress_params: ProgressParams,
225+
// shared bool in the entire context
226+
previous_was_build_error: Arc<AtomicBool>,
227+
// whether this current build encountered an error.
228+
current_is_build_error: AtomicBool,
222229
}
223230

224231
// base progress parameters for notification of the analysis.
@@ -235,11 +242,39 @@ impl InitActionContext {
235242
fn notify_publish_diagnostics(&self, params: PublishDiagnosticsParams) {
236243
self.out.notify(Notification::<PublishDiagnostics>::new(params));
237244
}
245+
fn notify_build_error(&self, message: String) {
246+
// This current build is definitely an error. Mark it and
247+
// propagate to the shrared state in notify_end_diagnostics()
248+
self.current_is_build_error.store(true, Ordering::SeqCst);
249+
250+
// We only want to notify a build error to the user one time,
251+
// not on every keystroke. As long as a previous build error
252+
// hasn't cleared, we stay silent.
253+
if !self.previous_was_build_error.load(Ordering::SeqCst) {
254+
let params = ShowMessageParams {
255+
typ: MessageType::Error,
256+
message,
257+
};
258+
self.out.notify(Notification::<ShowMessage>::new(params));
259+
}
260+
}
238261
fn notify_end_diagnostics(&self) {
239262
self.active_build_count.fetch_sub(1, Ordering::SeqCst);
240263
let mut params = self.progress_params.clone();
241264
params.done = Some(true);
242265
self.out.notify(Notification::<Progress>::new(params));
266+
267+
// If the diagnostics for this build were fully reported without
268+
// any current_is_build_error, we know the build is clear, and
269+
// that is propagated to the shared previous_was_build_error.
270+
//
271+
// This is not perfectly thread safe. There's a chance two threads
272+
// may be in contention propagating the local current_is_build_error
273+
// to the shared previous_was_build_error. Whether this happens
274+
// in practice is to be seen...
275+
let is_build_error = self.current_is_build_error.load(Ordering::SeqCst);
276+
self.previous_was_build_error
277+
.compare_and_swap(!is_build_error, is_build_error, Ordering::SeqCst);
243278
}
244279
}
245280

@@ -287,6 +322,8 @@ impl InitActionContext {
287322
out: out.clone(),
288323
active_build_count: self.active_build_count.clone(),
289324
progress_params: diganostics_progress_params,
325+
previous_was_build_error: self.previous_was_build_error.clone(),
326+
current_is_build_error: AtomicBool::new(false),
290327
}),
291328
blocked_threads: vec![],
292329
}

src/actions/post_build.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ pub struct PostBuildHandler {
4848
pub trait DiagnosticsNotifier: Send {
4949
fn notify_begin_diagnostics(&self);
5050
fn notify_publish_diagnostics(&self, PublishDiagnosticsParams);
51+
fn notify_build_error(&self, msg: String);
5152
fn notify_end_diagnostics(&self);
5253
}
5354

@@ -82,8 +83,11 @@ impl PostBuildHandler {
8283
BuildResult::Squashed => {
8384
trace!("build - Squashed");
8485
}
85-
BuildResult::Err => {
86+
BuildResult::Err(msg) => {
8687
trace!("build - Error");
88+
self.notifier.notify_begin_diagnostics();
89+
self.notifier.notify_build_error(msg);
90+
self.notifier.notify_end_diagnostics();
8791
}
8892
}
8993
}

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
@@ -384,7 +384,7 @@ impl JobQueue {
384384
analyses.append(&mut analysis);
385385
cwd = c;
386386
}
387-
BuildResult::Err => return BuildResult::Err,
387+
BuildResult::Err(msg) => return BuildResult::Err(msg),
388388
_ => {}
389389
}
390390
}

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)