mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 01:17:27 +00:00
feat: show errors from cargo metadata
and initial cargo check
in the status bar
closes #3155
This commit is contained in:
parent
9ec5e6e4fd
commit
de33702784
3 changed files with 63 additions and 13 deletions
|
@ -13,7 +13,7 @@ use cargo_metadata::{BuildScript, Message};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use paths::{AbsPath, AbsPathBuf};
|
use paths::{AbsPath, AbsPathBuf};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use stdx::JodChild;
|
use stdx::{format_to, JodChild};
|
||||||
|
|
||||||
use crate::{cfg_flag::CfgFlag, CargoConfig};
|
use crate::{cfg_flag::CfgFlag, CargoConfig};
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ pub(crate) struct PackageBuildData {
|
||||||
#[derive(Debug, Default, PartialEq, Eq, Clone)]
|
#[derive(Debug, Default, PartialEq, Eq, Clone)]
|
||||||
pub(crate) struct WorkspaceBuildData {
|
pub(crate) struct WorkspaceBuildData {
|
||||||
per_package: FxHashMap<String, PackageBuildData>,
|
per_package: FxHashMap<String, PackageBuildData>,
|
||||||
|
error: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, Clone)]
|
#[derive(Debug, Default, PartialEq, Eq, Clone)]
|
||||||
|
@ -94,6 +95,19 @@ impl BuildDataResult {
|
||||||
pub(crate) fn get(&self, workspace_root: &AbsPath) -> Option<&WorkspaceBuildData> {
|
pub(crate) fn get(&self, workspace_root: &AbsPath) -> Option<&WorkspaceBuildData> {
|
||||||
self.per_workspace.get(workspace_root)
|
self.per_workspace.get(workspace_root)
|
||||||
}
|
}
|
||||||
|
pub fn error(&self) -> Option<String> {
|
||||||
|
let mut buf = String::new();
|
||||||
|
for (_workspace_root, build_data) in &self.per_workspace {
|
||||||
|
if let Some(err) = &build_data.error {
|
||||||
|
format_to!(buf, "cargo check failed:\n{}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if buf.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(buf)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BuildDataConfig {
|
impl BuildDataConfig {
|
||||||
|
@ -139,7 +153,7 @@ fn collect_from_workspace(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null());
|
cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
|
||||||
|
|
||||||
let mut child = cmd.spawn().map(JodChild)?;
|
let mut child = cmd.spawn().map(JodChild)?;
|
||||||
let child_stdout = child.stdout.take().unwrap();
|
let child_stdout = child.stdout.take().unwrap();
|
||||||
|
@ -209,6 +223,15 @@ fn collect_from_workspace(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let output = child.into_inner().wait_with_output()?;
|
||||||
|
if !output.status.success() {
|
||||||
|
let mut stderr = String::from_utf8(output.stderr).unwrap_or_default();
|
||||||
|
if stderr.is_empty() {
|
||||||
|
stderr = "cargo check failed".to_string();
|
||||||
|
}
|
||||||
|
res.error = Some(stderr)
|
||||||
|
}
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,11 @@ impl GlobalState {
|
||||||
quiescent: self.is_quiescent(),
|
quiescent: self.is_quiescent(),
|
||||||
message: None,
|
message: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(error) = self.build_data_error() {
|
||||||
|
status.health = lsp_ext::Health::Warning;
|
||||||
|
status.message = Some(error)
|
||||||
|
}
|
||||||
if !self.config.cargo_autoreload()
|
if !self.config.cargo_autoreload()
|
||||||
&& self.is_quiescent()
|
&& self.is_quiescent()
|
||||||
&& self.fetch_workspaces_queue.op_requested()
|
&& self.fetch_workspaces_queue.op_requested()
|
||||||
|
@ -116,9 +121,10 @@ impl GlobalState {
|
||||||
status.health = lsp_ext::Health::Warning;
|
status.health = lsp_ext::Health::Warning;
|
||||||
status.message = Some("Workspace reload required".to_string())
|
status.message = Some("Workspace reload required".to_string())
|
||||||
}
|
}
|
||||||
if let Some(error) = self.loading_error() {
|
|
||||||
|
if let Some(error) = self.fetch_workspace_error() {
|
||||||
status.health = lsp_ext::Health::Error;
|
status.health = lsp_ext::Health::Error;
|
||||||
status.message = Some(format!("Workspace reload failed: {}", error))
|
status.message = Some(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.last_reported_status.as_ref() != Some(&status) {
|
if self.last_reported_status.as_ref() != Some(&status) {
|
||||||
|
@ -217,7 +223,7 @@ impl GlobalState {
|
||||||
let _p = profile::span("GlobalState::switch_workspaces");
|
let _p = profile::span("GlobalState::switch_workspaces");
|
||||||
log::info!("will switch workspaces");
|
log::info!("will switch workspaces");
|
||||||
|
|
||||||
if let Some(error_message) = self.loading_error() {
|
if let Some(error_message) = self.fetch_workspace_error() {
|
||||||
log::error!("failed to switch workspaces: {}", error_message);
|
log::error!("failed to switch workspaces: {}", error_message);
|
||||||
self.show_message(lsp_types::MessageType::Error, error_message);
|
self.show_message(lsp_types::MessageType::Error, error_message);
|
||||||
if !self.workspaces.is_empty() {
|
if !self.workspaces.is_empty() {
|
||||||
|
@ -225,6 +231,11 @@ impl GlobalState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(error_message) = self.build_data_error() {
|
||||||
|
log::error!("failed to switch build data: {}", error_message);
|
||||||
|
self.show_message(lsp_types::MessageType::Error, error_message);
|
||||||
|
}
|
||||||
|
|
||||||
let workspaces = self
|
let workspaces = self
|
||||||
.fetch_workspaces_queue
|
.fetch_workspaces_queue
|
||||||
.last_op_result()
|
.last_op_result()
|
||||||
|
@ -343,22 +354,30 @@ impl GlobalState {
|
||||||
log::info!("did switch workspaces");
|
log::info!("did switch workspaces");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loading_error(&self) -> Option<String> {
|
fn fetch_workspace_error(&self) -> Option<String> {
|
||||||
let mut message = None;
|
let mut buf = String::new();
|
||||||
|
|
||||||
for ws in self.fetch_workspaces_queue.last_op_result() {
|
for ws in self.fetch_workspaces_queue.last_op_result() {
|
||||||
if let Err(err) = ws {
|
if let Err(err) = ws {
|
||||||
let message = message.get_or_insert_with(String::new);
|
stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err);
|
||||||
stdx::format_to!(message, "rust-analyzer failed to load workspace: {:#}\n", err);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(Err(err)) = self.fetch_build_data_queue.last_op_result() {
|
if buf.is_empty() {
|
||||||
let message = message.get_or_insert_with(String::new);
|
return None;
|
||||||
stdx::format_to!(message, "rust-analyzer failed to fetch build data: {:#}\n", err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message
|
Some(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_data_error(&self) -> Option<String> {
|
||||||
|
match self.fetch_build_data_queue.last_op_result() {
|
||||||
|
Some(Err(err)) => {
|
||||||
|
Some(format!("rust-analyzer failed to fetch build data: {:#}\n", err))
|
||||||
|
}
|
||||||
|
Some(Ok(data)) => data.error(),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reload_flycheck(&mut self) {
|
fn reload_flycheck(&mut self) {
|
||||||
|
|
|
@ -178,6 +178,7 @@ where
|
||||||
start..start + len
|
start..start + len
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
pub struct JodChild(pub process::Child);
|
pub struct JodChild(pub process::Child);
|
||||||
|
|
||||||
impl ops::Deref for JodChild {
|
impl ops::Deref for JodChild {
|
||||||
|
@ -200,6 +201,13 @@ impl Drop for JodChild {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl JodChild {
|
||||||
|
pub fn into_inner(self) -> process::Child {
|
||||||
|
// SAFETY: repr transparent
|
||||||
|
unsafe { std::mem::transmute::<JodChild, process::Child>(self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
Loading…
Reference in a new issue