mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-25 19:35:06 +00:00
Make config queries source root dependent for flycheck
This commit is contained in:
parent
110ec27dbe
commit
09a84bd717
5 changed files with 154 additions and 125 deletions
|
@ -1,20 +1,19 @@
|
|||
//! Flycheck provides the functionality needed to run `cargo check` to provide
|
||||
//! LSP diagnostics based on the output of the command.
|
||||
|
||||
use std::{fmt, io, process::Command, time::Duration};
|
||||
use std::{fmt, io, time::Duration};
|
||||
|
||||
use cargo_metadata::PackageId;
|
||||
use command::{FlycheckConfig, InvocationStrategy, Target};
|
||||
use command::{check_command, FlycheckConfig, Target};
|
||||
use crossbeam_channel::{select_biased, unbounded, Receiver, Sender};
|
||||
use ide_db::FxHashSet;
|
||||
use paths::{AbsPath, AbsPathBuf};
|
||||
use paths::AbsPathBuf;
|
||||
use serde::Deserialize as _;
|
||||
use serde_derive::Deserialize;
|
||||
|
||||
pub(crate) use cargo_metadata::diagnostic::{
|
||||
Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan,
|
||||
};
|
||||
use toolchain::Tool;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::command::{CommandHandle, ParseFromLine};
|
||||
|
@ -37,13 +36,11 @@ impl FlycheckHandle {
|
|||
pub(crate) fn spawn(
|
||||
id: usize,
|
||||
sender: Sender<FlycheckMessage>,
|
||||
config: FlycheckConfig,
|
||||
sysroot_root: Option<AbsPathBuf>,
|
||||
workspace_root: AbsPathBuf,
|
||||
manifest_path: Option<AbsPathBuf>,
|
||||
) -> FlycheckHandle {
|
||||
let actor =
|
||||
FlycheckActor::new(id, sender, config, sysroot_root, workspace_root, manifest_path);
|
||||
let actor = FlycheckActor::new(id, sender, sysroot_root, workspace_root, manifest_path);
|
||||
let (sender, receiver) = unbounded::<StateChange>();
|
||||
let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
|
||||
.name("Flycheck".to_owned())
|
||||
|
@ -53,14 +50,21 @@ impl FlycheckHandle {
|
|||
}
|
||||
|
||||
/// Schedule a re-start of the cargo check worker to do a workspace wide check.
|
||||
pub(crate) fn restart_workspace(&self, saved_file: Option<AbsPathBuf>) {
|
||||
self.sender.send(StateChange::Restart { package: None, saved_file, target: None }).unwrap();
|
||||
pub(crate) fn restart_workspace(&self, saved_file: Option<AbsPathBuf>, config: FlycheckConfig) {
|
||||
self.sender
|
||||
.send(StateChange::Restart { package: None, saved_file, target: None, config })
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Schedule a re-start of the cargo check worker to do a package wide check.
|
||||
pub(crate) fn restart_for_package(&self, package: String, target: Option<Target>) {
|
||||
pub(crate) fn restart_for_package(
|
||||
&self,
|
||||
package: String,
|
||||
target: Option<Target>,
|
||||
config: FlycheckConfig,
|
||||
) {
|
||||
self.sender
|
||||
.send(StateChange::Restart { package: Some(package), saved_file: None, target })
|
||||
.send(StateChange::Restart { package: Some(package), saved_file: None, target, config })
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
|
@ -130,7 +134,12 @@ pub(crate) enum Progress {
|
|||
}
|
||||
|
||||
enum StateChange {
|
||||
Restart { package: Option<String>, saved_file: Option<AbsPathBuf>, target: Option<Target> },
|
||||
Restart {
|
||||
package: Option<String>,
|
||||
saved_file: Option<AbsPathBuf>,
|
||||
target: Option<Target>,
|
||||
config: FlycheckConfig,
|
||||
},
|
||||
Cancel,
|
||||
}
|
||||
|
||||
|
@ -140,7 +149,6 @@ struct FlycheckActor {
|
|||
id: usize,
|
||||
|
||||
sender: Sender<FlycheckMessage>,
|
||||
config: FlycheckConfig,
|
||||
manifest_path: Option<AbsPathBuf>,
|
||||
/// Either the workspace root of the workspace we are flychecking,
|
||||
/// or the project root of the project.
|
||||
|
@ -165,13 +173,10 @@ enum Event {
|
|||
CheckEvent(Option<CargoCheckMessage>),
|
||||
}
|
||||
|
||||
pub(crate) const SAVED_FILE_PLACEHOLDER: &str = "$saved_file";
|
||||
|
||||
impl FlycheckActor {
|
||||
fn new(
|
||||
id: usize,
|
||||
sender: Sender<FlycheckMessage>,
|
||||
config: FlycheckConfig,
|
||||
sysroot_root: Option<AbsPathBuf>,
|
||||
workspace_root: AbsPathBuf,
|
||||
manifest_path: Option<AbsPathBuf>,
|
||||
|
@ -180,7 +185,6 @@ impl FlycheckActor {
|
|||
FlycheckActor {
|
||||
id,
|
||||
sender,
|
||||
config,
|
||||
sysroot_root,
|
||||
root: Arc::new(workspace_root),
|
||||
manifest_path,
|
||||
|
@ -215,7 +219,12 @@ impl FlycheckActor {
|
|||
tracing::debug!(flycheck_id = self.id, "flycheck cancelled");
|
||||
self.cancel_check_process();
|
||||
}
|
||||
Event::RequestStateChange(StateChange::Restart { package, saved_file, target }) => {
|
||||
Event::RequestStateChange(StateChange::Restart {
|
||||
package,
|
||||
saved_file,
|
||||
target,
|
||||
config,
|
||||
}) => {
|
||||
// Cancel the previously spawned process
|
||||
self.cancel_check_process();
|
||||
while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) {
|
||||
|
@ -225,9 +234,15 @@ impl FlycheckActor {
|
|||
}
|
||||
}
|
||||
|
||||
let Some(command) =
|
||||
self.check_command(package.as_deref(), saved_file.as_deref(), target)
|
||||
else {
|
||||
let Some(command) = check_command(
|
||||
&self.root,
|
||||
&self.sysroot_root,
|
||||
&self.manifest_path,
|
||||
config,
|
||||
package.as_deref(),
|
||||
saved_file.as_deref(),
|
||||
target,
|
||||
) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
|
@ -360,95 +375,6 @@ impl FlycheckActor {
|
|||
self.diagnostics_received = false;
|
||||
}
|
||||
|
||||
/// Construct a `Command` object for checking the user's code. If the user
|
||||
/// has specified a custom command with placeholders that we cannot fill,
|
||||
/// return None.
|
||||
fn check_command(
|
||||
&self,
|
||||
package: Option<&str>,
|
||||
saved_file: Option<&AbsPath>,
|
||||
target: Option<Target>,
|
||||
) -> Option<Command> {
|
||||
match &self.config {
|
||||
FlycheckConfig::CargoCommand { command, options, ansi_color_output } => {
|
||||
let mut cmd = toolchain::command(Tool::Cargo.path(), &*self.root);
|
||||
if let Some(sysroot_root) = &self.sysroot_root {
|
||||
cmd.env("RUSTUP_TOOLCHAIN", AsRef::<std::path::Path>::as_ref(sysroot_root));
|
||||
}
|
||||
cmd.arg(command);
|
||||
|
||||
match package {
|
||||
Some(pkg) => cmd.arg("-p").arg(pkg),
|
||||
None => cmd.arg("--workspace"),
|
||||
};
|
||||
|
||||
if let Some(tgt) = target {
|
||||
match tgt {
|
||||
Target::Bin(tgt) => cmd.arg("--bin").arg(tgt),
|
||||
Target::Example(tgt) => cmd.arg("--example").arg(tgt),
|
||||
Target::Test(tgt) => cmd.arg("--test").arg(tgt),
|
||||
Target::Benchmark(tgt) => cmd.arg("--bench").arg(tgt),
|
||||
};
|
||||
}
|
||||
|
||||
cmd.arg(if *ansi_color_output {
|
||||
"--message-format=json-diagnostic-rendered-ansi"
|
||||
} else {
|
||||
"--message-format=json"
|
||||
});
|
||||
|
||||
if let Some(manifest_path) = &self.manifest_path {
|
||||
cmd.arg("--manifest-path");
|
||||
cmd.arg(manifest_path);
|
||||
if manifest_path.extension() == Some("rs") {
|
||||
cmd.arg("-Zscript");
|
||||
}
|
||||
}
|
||||
|
||||
cmd.arg("--keep-going");
|
||||
|
||||
options.apply_on_command(&mut cmd);
|
||||
cmd.args(&options.extra_args);
|
||||
Some(cmd)
|
||||
}
|
||||
FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy } => {
|
||||
let root = match invocation_strategy {
|
||||
InvocationStrategy::Once => &*self.root,
|
||||
InvocationStrategy::PerWorkspace => {
|
||||
// FIXME: &affected_workspace
|
||||
&*self.root
|
||||
}
|
||||
};
|
||||
let mut cmd = toolchain::command(command, root);
|
||||
cmd.envs(extra_env);
|
||||
|
||||
// If the custom command has a $saved_file placeholder, and
|
||||
// we're saving a file, replace the placeholder in the arguments.
|
||||
if let Some(saved_file) = saved_file {
|
||||
for arg in args {
|
||||
if arg == SAVED_FILE_PLACEHOLDER {
|
||||
cmd.arg(saved_file);
|
||||
} else {
|
||||
cmd.arg(arg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for arg in args {
|
||||
if arg == SAVED_FILE_PLACEHOLDER {
|
||||
// The custom command has a $saved_file placeholder,
|
||||
// but we had an IDE event that wasn't a file save. Do nothing.
|
||||
return None;
|
||||
}
|
||||
|
||||
cmd.arg(arg);
|
||||
}
|
||||
}
|
||||
|
||||
Some(cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn send(&self, check_task: FlycheckMessage) {
|
||||
self.sender.send(check_task).unwrap();
|
||||
|
|
|
@ -2,6 +2,10 @@ use std::{fmt, process::Command};
|
|||
|
||||
use ide_db::FxHashMap;
|
||||
use paths::Utf8PathBuf;
|
||||
use toolchain::Tool;
|
||||
use vfs::{AbsPath, AbsPathBuf};
|
||||
|
||||
pub(crate) const SAVED_FILE_PLACEHOLDER: &str = "$saved_file";
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub(crate) enum InvocationStrategy {
|
||||
|
@ -82,3 +86,95 @@ impl fmt::Display for FlycheckConfig {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a `Command` object for checking the user's code. If the user
|
||||
/// has specified a custom command with placeholders that we cannot fill,
|
||||
/// return None.
|
||||
pub(super) fn check_command(
|
||||
root: &AbsPathBuf,
|
||||
sysroot_root: &Option<AbsPathBuf>,
|
||||
manifest_path: &Option<AbsPathBuf>,
|
||||
config: FlycheckConfig,
|
||||
package: Option<&str>,
|
||||
saved_file: Option<&AbsPath>,
|
||||
target: Option<Target>,
|
||||
) -> Option<Command> {
|
||||
match config {
|
||||
FlycheckConfig::CargoCommand { command, options, ansi_color_output } => {
|
||||
let mut cmd = toolchain::command(Tool::Cargo.path(), &*root);
|
||||
if let Some(sysroot_root) = &sysroot_root {
|
||||
cmd.env("RUSTUP_TOOLCHAIN", AsRef::<std::path::Path>::as_ref(sysroot_root));
|
||||
}
|
||||
cmd.arg(command);
|
||||
|
||||
match package {
|
||||
Some(pkg) => cmd.arg("-p").arg(pkg),
|
||||
None => cmd.arg("--workspace"),
|
||||
};
|
||||
|
||||
if let Some(tgt) = target {
|
||||
match tgt {
|
||||
Target::Bin(tgt) => cmd.arg("--bin").arg(tgt),
|
||||
Target::Example(tgt) => cmd.arg("--example").arg(tgt),
|
||||
Target::Test(tgt) => cmd.arg("--test").arg(tgt),
|
||||
Target::Benchmark(tgt) => cmd.arg("--bench").arg(tgt),
|
||||
};
|
||||
}
|
||||
|
||||
cmd.arg(if ansi_color_output {
|
||||
"--message-format=json-diagnostic-rendered-ansi"
|
||||
} else {
|
||||
"--message-format=json"
|
||||
});
|
||||
|
||||
if let Some(manifest_path) = &manifest_path {
|
||||
cmd.arg("--manifest-path");
|
||||
cmd.arg(manifest_path);
|
||||
if manifest_path.extension() == Some("rs") {
|
||||
cmd.arg("-Zscript");
|
||||
}
|
||||
}
|
||||
|
||||
cmd.arg("--keep-going");
|
||||
|
||||
options.apply_on_command(&mut cmd);
|
||||
cmd.args(&options.extra_args);
|
||||
Some(cmd)
|
||||
}
|
||||
FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy } => {
|
||||
let root = match invocation_strategy {
|
||||
InvocationStrategy::Once => &*root,
|
||||
InvocationStrategy::PerWorkspace => {
|
||||
// FIXME: &affected_workspace
|
||||
&*root
|
||||
}
|
||||
};
|
||||
let mut cmd = toolchain::command(command, root);
|
||||
cmd.envs(extra_env);
|
||||
|
||||
// If the custom command has a $saved_file placeholder, and
|
||||
// we're saving a file, replace the placeholder in the arguments.
|
||||
if let Some(saved_file) = saved_file {
|
||||
for arg in args {
|
||||
if arg == SAVED_FILE_PLACEHOLDER {
|
||||
cmd.arg(saved_file);
|
||||
} else {
|
||||
cmd.arg(arg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for arg in args {
|
||||
if arg == SAVED_FILE_PLACEHOLDER {
|
||||
// The custom command has a $saved_file placeholder,
|
||||
// but we had an IDE event that wasn't a file save. Do nothing.
|
||||
return None;
|
||||
}
|
||||
|
||||
cmd.arg(arg);
|
||||
}
|
||||
}
|
||||
|
||||
Some(cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -190,7 +190,9 @@ pub(crate) fn handle_did_save_text_document(
|
|||
} else if state.config.check_on_save(None) && state.config.flycheck_workspace(None) {
|
||||
// No specific flycheck was triggered, so let's trigger all of them.
|
||||
for flycheck in state.flycheck.iter() {
|
||||
flycheck.restart_workspace(None);
|
||||
let sr = state.workspace_source_root_map.get(&flycheck.id());
|
||||
let config = state.config.flycheck(sr.cloned());
|
||||
flycheck.restart_workspace(None, config);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -330,7 +332,10 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
|
|||
_ => false,
|
||||
});
|
||||
if let Some((idx, _)) = workspace {
|
||||
world.flycheck[idx].restart_for_package(package, target);
|
||||
let flycheck = &world.flycheck[idx];
|
||||
let source_root = world.workspace_source_root_map.get(&flycheck.id());
|
||||
let config = world.config.flycheck(source_root.cloned());
|
||||
flycheck.restart_for_package(package, target, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -391,7 +396,9 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
|
|||
for (id, _) in workspace_ids.clone() {
|
||||
if id == flycheck.id() {
|
||||
updated = true;
|
||||
flycheck.restart_workspace(saved_file.clone());
|
||||
let sr = world.workspace_source_root_map.get(&id);
|
||||
let config = world.config.flycheck(sr.cloned());
|
||||
flycheck.restart_workspace(saved_file.clone(), config);
|
||||
continue 'flychecks;
|
||||
}
|
||||
}
|
||||
|
@ -399,7 +406,9 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
|
|||
// No specific flycheck was triggered, so let's trigger all of them.
|
||||
if !updated {
|
||||
for flycheck in world.flycheck.iter() {
|
||||
flycheck.restart_workspace(saved_file.clone());
|
||||
let sr = world.workspace_source_root_map.get(&flycheck.id());
|
||||
let config = world.config.flycheck(sr.cloned());
|
||||
flycheck.restart_workspace(saved_file.clone(), config);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -442,7 +451,9 @@ pub(crate) fn handle_run_flycheck(
|
|||
// No specific flycheck was triggered, so let's trigger all of them.
|
||||
if state.config.flycheck_workspace(None) {
|
||||
for flycheck in state.flycheck.iter() {
|
||||
flycheck.restart_workspace(None);
|
||||
let sr = state.workspace_source_root_map.get(&flycheck.id());
|
||||
let config = state.config.flycheck(sr.cloned());
|
||||
flycheck.restart_workspace(None, config);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -413,7 +413,11 @@ impl GlobalState {
|
|||
&& !self.fetch_build_data_queue.op_requested()
|
||||
{
|
||||
// Project has loaded properly, kick off initial flycheck
|
||||
self.flycheck.iter().for_each(|flycheck| flycheck.restart_workspace(None));
|
||||
self.flycheck.iter().for_each(|flycheck| {
|
||||
let sr = self.workspace_source_root_map.get(&flycheck.id());
|
||||
let config = self.config.flycheck(sr.cloned());
|
||||
flycheck.restart_workspace(None, config)
|
||||
});
|
||||
}
|
||||
if self.config.prefill_caches() {
|
||||
self.prime_caches_queue.request_op("became quiescent".to_owned(), ());
|
||||
|
|
|
@ -811,14 +811,7 @@ impl GlobalState {
|
|||
|
||||
self.flycheck = match invocation_strategy {
|
||||
crate::flycheck::command::InvocationStrategy::Once => {
|
||||
vec![FlycheckHandle::spawn(
|
||||
0,
|
||||
sender,
|
||||
config,
|
||||
None,
|
||||
self.config.root_path().clone(),
|
||||
None,
|
||||
)]
|
||||
vec![FlycheckHandle::spawn(0, sender, None, self.config.root_path().clone(), None)]
|
||||
}
|
||||
crate::flycheck::command::InvocationStrategy::PerWorkspace => {
|
||||
self.workspaces
|
||||
|
@ -852,7 +845,6 @@ impl GlobalState {
|
|||
FlycheckHandle::spawn(
|
||||
id,
|
||||
sender.clone(),
|
||||
config.clone(),
|
||||
sysroot_root,
|
||||
root.to_path_buf(),
|
||||
manifest_path.map(|it| it.to_path_buf()),
|
||||
|
|
Loading…
Reference in a new issue