diff options
author | Ryan Cumming <[email protected]> | 2019-06-26 22:52:19 +0100 |
---|---|---|
committer | Ryan Cumming <[email protected]> | 2019-06-26 23:08:26 +0100 |
commit | e052ca9d614e946a6cea4875ae50c68d77088257 (patch) | |
tree | c71ae4c1757f53786150f6ea638d3641ea82fdb2 | |
parent | 5536a249145fc67c415ad48738e62fc2a9e848ff (diff) |
Swallow expected `rustfmt` errors
My workflow in Visual Studio Code + Rust Analyzer has become:
1. Make a change to Rust source code using all the analysis magic
2. Save the file to trigger `cargo watch`. I have format on save enabled
for all file types so this also runs `rustfmt`
3. Fix any diagnostics that `cargo watch` finds
Unfortunately if the Rust source has any syntax errors the act of saving
will pop up a scary "command has failed" message and will switch to the
"Output" tab to show the `rustfmt` error and exit code.
I did a quick survey of what other Language Servers do in this case.
Both the JSON and TypeScript servers will swallow the error and return
success. This is consistent with how I remember my workflow in those
languages. The syntax error will show up as a diagnostic so it should
be clear why the file isn't formatting.
I checked the `rustfmt` source code and while it does distinguish "parse
errors" from "operational errors" internally they both result in exit
status of 1. However, more catastrophic errors (missing `rustfmt`,
SIGSEGV, etc) will return 127+ error codes which we can distinguish from
a normal failure.
This changes our handler to log an info message and feign success if
`rustfmt` exits with status 1.
Another option I considered was only swallowing the error if the
formatting request came from format-on-save. However, the Language
Server Protocol doesn't seem to distinguish those cases.
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 35 |
1 files changed, 25 insertions, 10 deletions
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 6373240d5..47222cd0a 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -621,17 +621,32 @@ pub fn handle_formatting( | |||
621 | 621 | ||
622 | let output = rustfmt.wait_with_output()?; | 622 | let output = rustfmt.wait_with_output()?; |
623 | let captured_stdout = String::from_utf8(output.stdout)?; | 623 | let captured_stdout = String::from_utf8(output.stdout)?; |
624 | |||
624 | if !output.status.success() { | 625 | if !output.status.success() { |
625 | return Err(LspError::new( | 626 | match output.status.code() { |
626 | -32900, | 627 | Some(1) => { |
627 | format!( | 628 | // While `rustfmt` doesn't have a specific exit code for parse errors this is the |
628 | r#"rustfmt exited with: | 629 | // likely cause exiting with 1. Most Language Servers swallow parse errors on |
629 | Status: {} | 630 | // formatting because otherwise an error is surfaced to the user on top of the |
630 | stdout: {}"#, | 631 | // syntax error diagnostics they're already receiving. This is especially jarring |
631 | output.status, captured_stdout, | 632 | // if they have format on save enabled. |
632 | ), | 633 | log::info!("rustfmt exited with status 1, assuming parse error and ignoring"); |
633 | ) | 634 | return Ok(None); |
634 | .into()); | 635 | } |
636 | _ => { | ||
637 | // Something else happened - e.g. `rustfmt` is missing or caught a signal | ||
638 | return Err(LspError::new( | ||
639 | -32900, | ||
640 | format!( | ||
641 | r#"rustfmt exited with: | ||
642 | Status: {} | ||
643 | stdout: {}"#, | ||
644 | output.status, captured_stdout, | ||
645 | ), | ||
646 | ) | ||
647 | .into()); | ||
648 | } | ||
649 | } | ||
635 | } | 650 | } |
636 | 651 | ||
637 | Ok(Some(vec![TextEdit { | 652 | Ok(Some(vec![TextEdit { |