Auto merge of #17831 - Veykril:flycheck-move-to-rust-analyzer, r=Veykril

internal: Move and split flycheck crate into rust-analyzer main crate

The crate no longer is about flychecking, it mainly hosts common command process handling shared by flycheck, test explorer and now project discovery. This re-organizes that into the main crate.
This commit is contained in:
bors 2024-08-08 11:10:28 +00:00
commit 34d9409211
16 changed files with 252 additions and 303 deletions

20
Cargo.lock generated
View file

@ -423,23 +423,6 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "flycheck"
version = "0.0.0"
dependencies = [
"cargo_metadata",
"crossbeam-channel",
"paths",
"process-wrap",
"project-model",
"rustc-hash",
"serde",
"serde_json",
"stdx",
"toolchain",
"tracing",
]
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.2.1" version = "1.2.1"
@ -1650,12 +1633,12 @@ version = "0.0.0"
dependencies = [ dependencies = [
"always-assert", "always-assert",
"anyhow", "anyhow",
"cargo_metadata",
"cfg", "cfg",
"crossbeam-channel", "crossbeam-channel",
"dirs", "dirs",
"dissimilar", "dissimilar",
"expect-test", "expect-test",
"flycheck",
"hir", "hir",
"hir-def", "hir-def",
"hir-ty", "hir-ty",
@ -1676,6 +1659,7 @@ dependencies = [
"parser", "parser",
"paths", "paths",
"proc-macro-api", "proc-macro-api",
"process-wrap",
"profile", "profile",
"project-model", "project-model",
"rayon", "rayon",

View file

@ -52,7 +52,6 @@ debug = 2
# local crates # local crates
base-db = { path = "./crates/base-db", version = "0.0.0" } base-db = { path = "./crates/base-db", version = "0.0.0" }
cfg = { path = "./crates/cfg", version = "0.0.0", features = ["tt"] } cfg = { path = "./crates/cfg", version = "0.0.0", features = ["tt"] }
flycheck = { path = "./crates/flycheck", version = "0.0.0" }
hir = { path = "./crates/hir", version = "0.0.0" } hir = { path = "./crates/hir", version = "0.0.0" }
hir-def = { path = "./crates/hir-def", version = "0.0.0" } hir-def = { path = "./crates/hir-def", version = "0.0.0" }
hir-expand = { path = "./crates/hir-expand", version = "0.0.0" } hir-expand = { path = "./crates/hir-expand", version = "0.0.0" }

View file

@ -1,31 +0,0 @@
[package]
name = "flycheck"
version = "0.0.0"
repository.workspace = true
description = "Functionality needed for rust-analyzer to run `cargo` commands in a background thread."
authors.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
[lib]
doctest = false
[dependencies]
cargo_metadata.workspace = true
crossbeam-channel.workspace = true
tracing.workspace = true
rustc-hash.workspace = true
serde_json.workspace = true
serde.workspace = true
process-wrap.workspace = true
# local deps
paths.workspace = true
stdx.workspace = true
toolchain.workspace = true
project-model.workspace = true
[lints]
workspace = true

View file

@ -74,106 +74,6 @@ pub struct ProjectJson {
runnables: Vec<Runnable>, runnables: Vec<Runnable>,
} }
/// A crate points to the root module of a crate and lists the dependencies of the crate. This is
/// useful in creating the crate graph.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Crate {
pub(crate) display_name: Option<CrateDisplayName>,
pub root_module: AbsPathBuf,
pub(crate) edition: Edition,
pub(crate) version: Option<String>,
pub(crate) deps: Vec<Dep>,
pub(crate) cfg: Vec<CfgAtom>,
pub(crate) target: Option<String>,
pub(crate) env: FxHashMap<String, String>,
pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
pub(crate) is_workspace_member: bool,
pub(crate) include: Vec<AbsPathBuf>,
pub(crate) exclude: Vec<AbsPathBuf>,
pub(crate) is_proc_macro: bool,
pub(crate) repository: Option<String>,
pub build: Option<Build>,
}
/// Additional, build-specific data about a crate.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Build {
/// The name associated with this crate.
///
/// This is determined by the build system that produced
/// the `rust-project.json` in question. For instance, if buck were used,
/// the label might be something like `//ide/rust/rust-analyzer:rust-analyzer`.
///
/// Do not attempt to parse the contents of this string; it is a build system-specific
/// identifier similar to [`Crate::display_name`].
pub label: String,
/// Path corresponding to the build system-specific file defining the crate.
///
/// It is roughly analogous to [`ManifestPath`], but it should *not* be used with
/// [`crate::ProjectManifest::from_manifest_file`], as the build file may not be
/// be in the `rust-project.json`.
pub build_file: Utf8PathBuf,
/// The kind of target.
///
/// Examples (non-exhaustively) include [`TargetKind::Bin`], [`TargetKind::Lib`],
/// and [`TargetKind::Test`]. This information is used to determine what sort
/// of runnable codelens to provide, if any.
pub target_kind: TargetKind,
}
/// A template-like structure for describing runnables.
///
/// These are used for running and debugging binaries and tests without encoding
/// build system-specific knowledge into rust-analyzer.
///
/// # Example
///
/// Below is an example of a test runnable. `{label}` and `{test_id}`
/// are explained in [`Runnable::args`]'s documentation.
///
/// ```json
/// {
/// "program": "buck",
/// "args": [
/// "test",
/// "{label}",
/// "--",
/// "{test_id}",
/// "--print-passing-details"
/// ],
/// "cwd": "/home/user/repo-root/",
/// "kind": "testOne"
/// }
/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Runnable {
/// The program invoked by the runnable.
///
/// For example, this might be `cargo`, `buck`, or `bazel`.
pub program: String,
/// The arguments passed to [`Runnable::program`].
///
/// The args can contain two template strings: `{label}` and `{test_id}`.
/// rust-analyzer will find and replace `{label}` with [`Build::label`] and
/// `{test_id}` with the test name.
pub args: Vec<String>,
/// The current working directory of the runnable.
pub cwd: Utf8PathBuf,
pub kind: RunnableKind,
}
/// The kind of runnable.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RunnableKind {
Check,
/// Can run a binary.
Run,
/// Run a single test.
TestOne,
}
impl ProjectJson { impl ProjectJson {
/// Create a new ProjectJson instance. /// Create a new ProjectJson instance.
/// ///
@ -302,6 +202,106 @@ impl ProjectJson {
} }
} }
/// A crate points to the root module of a crate and lists the dependencies of the crate. This is
/// useful in creating the crate graph.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Crate {
pub(crate) display_name: Option<CrateDisplayName>,
pub root_module: AbsPathBuf,
pub(crate) edition: Edition,
pub(crate) version: Option<String>,
pub(crate) deps: Vec<Dep>,
pub(crate) cfg: Vec<CfgAtom>,
pub(crate) target: Option<String>,
pub(crate) env: FxHashMap<String, String>,
pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
pub(crate) is_workspace_member: bool,
pub(crate) include: Vec<AbsPathBuf>,
pub(crate) exclude: Vec<AbsPathBuf>,
pub(crate) is_proc_macro: bool,
pub(crate) repository: Option<String>,
pub build: Option<Build>,
}
/// Additional, build-specific data about a crate.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Build {
/// The name associated with this crate.
///
/// This is determined by the build system that produced
/// the `rust-project.json` in question. For instance, if buck were used,
/// the label might be something like `//ide/rust/rust-analyzer:rust-analyzer`.
///
/// Do not attempt to parse the contents of this string; it is a build system-specific
/// identifier similar to [`Crate::display_name`].
pub label: String,
/// Path corresponding to the build system-specific file defining the crate.
///
/// It is roughly analogous to [`ManifestPath`], but it should *not* be used with
/// [`crate::ProjectManifest::from_manifest_file`], as the build file may not be
/// be in the `rust-project.json`.
pub build_file: Utf8PathBuf,
/// The kind of target.
///
/// Examples (non-exhaustively) include [`TargetKind::Bin`], [`TargetKind::Lib`],
/// and [`TargetKind::Test`]. This information is used to determine what sort
/// of runnable codelens to provide, if any.
pub target_kind: TargetKind,
}
/// A template-like structure for describing runnables.
///
/// These are used for running and debugging binaries and tests without encoding
/// build system-specific knowledge into rust-analyzer.
///
/// # Example
///
/// Below is an example of a test runnable. `{label}` and `{test_id}`
/// are explained in [`Runnable::args`]'s documentation.
///
/// ```json
/// {
/// "program": "buck",
/// "args": [
/// "test",
/// "{label}",
/// "--",
/// "{test_id}",
/// "--print-passing-details"
/// ],
/// "cwd": "/home/user/repo-root/",
/// "kind": "testOne"
/// }
/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Runnable {
/// The program invoked by the runnable.
///
/// For example, this might be `cargo`, `buck`, or `bazel`.
pub program: String,
/// The arguments passed to [`Runnable::program`].
///
/// The args can contain two template strings: `{label}` and `{test_id}`.
/// rust-analyzer will find and replace `{label}` with [`Build::label`] and
/// `{test_id}` with the test name.
pub args: Vec<String>,
/// The current working directory of the runnable.
pub cwd: Utf8PathBuf,
pub kind: RunnableKind,
}
/// The kind of runnable.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RunnableKind {
Check,
/// Can run a binary.
Run,
/// Run a single test.
TestOne,
}
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct ProjectJsonData { pub struct ProjectJsonData {
sysroot: Option<Utf8PathBuf>, sysroot: Option<Utf8PathBuf>,
@ -407,6 +407,29 @@ pub enum TargetKindData {
Lib, Lib,
Test, Test,
} }
/// Identifies a crate by position in the crates array.
///
/// This will differ from `CrateId` when multiple `ProjectJson`
/// workspaces are loaded.
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[serde(transparent)]
pub struct CrateArrayIdx(pub usize);
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub(crate) struct Dep {
/// Identifies a crate by position in the crates array.
#[serde(rename = "crate")]
pub(crate) krate: CrateArrayIdx,
#[serde(serialize_with = "serialize_crate_name")]
#[serde(deserialize_with = "deserialize_crate_name")]
pub(crate) name: CrateName,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
struct CrateSource {
include_dirs: Vec<Utf8PathBuf>,
exclude_dirs: Vec<Utf8PathBuf>,
}
impl From<TargetKindData> for TargetKind { impl From<TargetKindData> for TargetKind {
fn from(data: TargetKindData) -> Self { fn from(data: TargetKindData) -> Self {
@ -445,30 +468,6 @@ impl From<RunnableKindData> for RunnableKind {
} }
} }
/// Identifies a crate by position in the crates array.
///
/// This will differ from `CrateId` when multiple `ProjectJson`
/// workspaces are loaded.
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[serde(transparent)]
pub struct CrateArrayIdx(pub usize);
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub(crate) struct Dep {
/// Identifies a crate by position in the crates array.
#[serde(rename = "crate")]
pub(crate) krate: CrateArrayIdx,
#[serde(serialize_with = "serialize_crate_name")]
#[serde(deserialize_with = "deserialize_crate_name")]
pub(crate) name: CrateName,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
struct CrateSource {
include_dirs: Vec<Utf8PathBuf>,
exclude_dirs: Vec<Utf8PathBuf>,
}
fn deserialize_crate_name<'de, D>(de: D) -> std::result::Result<CrateName, D::Error> fn deserialize_crate_name<'de, D>(de: D) -> std::result::Result<CrateName, D::Error>
where where
D: de::Deserializer<'de>, D: de::Deserializer<'de>,

View file

@ -47,9 +47,10 @@ always-assert = "0.2.0"
walkdir = "2.3.2" walkdir = "2.3.2"
semver.workspace = true semver.workspace = true
memchr = "2.7.1" memchr = "2.7.1"
cargo_metadata.workspace = true
process-wrap.workspace = true
cfg.workspace = true cfg.workspace = true
flycheck.workspace = true
hir-def.workspace = true hir-def.workspace = true
hir-ty.workspace = true hir-ty.workspace = true
hir.workspace = true hir.workspace = true

View file

@ -1,5 +1,5 @@
//! Utilities for running a cargo command like `cargo check` or `cargo test` in a separate thread and //! Utilities for running a cargo command like `cargo check` or `cargo test` in a separate thread
//! parse its stdout/stderr. //! and parse its stdout/stderr.
use std::{ use std::{
ffi::OsString, ffi::OsString,

View file

@ -7,7 +7,6 @@ use std::{fmt, iter, ops::Not, sync::OnceLock};
use cfg::{CfgAtom, CfgDiff}; use cfg::{CfgAtom, CfgDiff};
use dirs::config_dir; use dirs::config_dir;
use flycheck::{CargoOptions, FlycheckConfig};
use hir::Symbol; use hir::Symbol;
use ide::{ use ide::{
AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode, AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
@ -37,6 +36,7 @@ use vfs::{AbsPath, AbsPathBuf, VfsPath};
use crate::{ use crate::{
capabilities::ClientCapabilities, capabilities::ClientCapabilities,
diagnostics::DiagnosticsMapConfig, diagnostics::DiagnosticsMapConfig,
flycheck::{CargoOptions, FlycheckConfig},
lsp_ext::{WorkspaceSymbolSearchKind, WorkspaceSymbolSearchScope}, lsp_ext::{WorkspaceSymbolSearchKind, WorkspaceSymbolSearchScope},
}; };
@ -1899,7 +1899,7 @@ impl Config {
*self.check_workspace(None) *self.check_workspace(None)
} }
pub fn cargo_test_options(&self) -> CargoOptions { pub(crate) fn cargo_test_options(&self) -> CargoOptions {
CargoOptions { CargoOptions {
target_triples: self.cargo_target(None).clone().into_iter().collect(), target_triples: self.cargo_target(None).clone().into_iter().collect(),
all_targets: false, all_targets: false,
@ -1915,7 +1915,7 @@ impl Config {
} }
} }
pub fn flycheck(&self) -> FlycheckConfig { pub(crate) fn flycheck(&self) -> FlycheckConfig {
match &self.check_overrideCommand(None) { match &self.check_overrideCommand(None) {
Some(args) if !args.is_empty() => { Some(args) if !args.is_empty() => {
let mut args = args.clone(); let mut args = args.clone();
@ -1925,16 +1925,18 @@ impl Config {
args, args,
extra_env: self.check_extra_env(), extra_env: self.check_extra_env(),
invocation_strategy: match self.check_invocationStrategy(None) { invocation_strategy: match self.check_invocationStrategy(None) {
InvocationStrategy::Once => flycheck::InvocationStrategy::Once, InvocationStrategy::Once => crate::flycheck::InvocationStrategy::Once,
InvocationStrategy::PerWorkspace => { InvocationStrategy::PerWorkspace => {
flycheck::InvocationStrategy::PerWorkspace crate::flycheck::InvocationStrategy::PerWorkspace
} }
}, },
invocation_location: match self.check_invocationLocation(None) { invocation_location: match self.check_invocationLocation(None) {
InvocationLocation::Root => { InvocationLocation::Root => {
flycheck::InvocationLocation::Root(self.root_path.clone()) crate::flycheck::InvocationLocation::Root(self.root_path.clone())
}
InvocationLocation::Workspace => {
crate::flycheck::InvocationLocation::Workspace
} }
InvocationLocation::Workspace => flycheck::InvocationLocation::Workspace,
}, },
} }
} }

View file

@ -1,7 +1,7 @@
//! This module provides the functionality needed to convert diagnostics from //! This module provides the functionality needed to convert diagnostics from
//! `cargo check` json format to the LSP diagnostic format. //! `cargo check` json format to the LSP diagnostic format.
use flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan}; use crate::flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan};
use itertools::Itertools; use itertools::Itertools;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use stdx::format_to; use stdx::format_to;
@ -17,8 +17,8 @@ use super::{DiagnosticsMapConfig, Fix};
/// Determines the LSP severity from a diagnostic /// Determines the LSP severity from a diagnostic
fn diagnostic_severity( fn diagnostic_severity(
config: &DiagnosticsMapConfig, config: &DiagnosticsMapConfig,
level: flycheck::DiagnosticLevel, level: crate::flycheck::DiagnosticLevel,
code: Option<flycheck::DiagnosticCode>, code: Option<crate::flycheck::DiagnosticCode>,
) -> Option<lsp_types::DiagnosticSeverity> { ) -> Option<lsp_types::DiagnosticSeverity> {
let res = match level { let res = match level {
DiagnosticLevel::Ice => lsp_types::DiagnosticSeverity::ERROR, DiagnosticLevel::Ice => lsp_types::DiagnosticSeverity::ERROR,
@ -181,7 +181,7 @@ enum MappedRustChildDiagnostic {
fn map_rust_child_diagnostic( fn map_rust_child_diagnostic(
config: &DiagnosticsMapConfig, config: &DiagnosticsMapConfig,
workspace_root: &AbsPath, workspace_root: &AbsPath,
rd: &flycheck::Diagnostic, rd: &crate::flycheck::Diagnostic,
snap: &GlobalStateSnapshot, snap: &GlobalStateSnapshot,
) -> MappedRustChildDiagnostic { ) -> MappedRustChildDiagnostic {
let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect(); let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
@ -284,7 +284,7 @@ pub(crate) struct MappedRustDiagnostic {
/// If the diagnostic has no primary span this will return `None` /// If the diagnostic has no primary span this will return `None`
pub(crate) fn map_rust_diagnostic_to_lsp( pub(crate) fn map_rust_diagnostic_to_lsp(
config: &DiagnosticsMapConfig, config: &DiagnosticsMapConfig,
rd: &flycheck::Diagnostic, rd: &crate::flycheck::Diagnostic,
workspace_root: &AbsPath, workspace_root: &AbsPath,
snap: &GlobalStateSnapshot, snap: &GlobalStateSnapshot,
) -> Vec<MappedRustDiagnostic> { ) -> Vec<MappedRustDiagnostic> {
@ -537,7 +537,8 @@ mod tests {
} }
fn check_with_config(config: DiagnosticsMapConfig, diagnostics_json: &str, expect: ExpectFile) { fn check_with_config(config: DiagnosticsMapConfig, diagnostics_json: &str, expect: ExpectFile) {
let diagnostic: flycheck::Diagnostic = serde_json::from_str(diagnostics_json).unwrap(); let diagnostic: crate::flycheck::Diagnostic =
serde_json::from_str(diagnostics_json).unwrap();
let workspace_root: &AbsPath = Utf8Path::new("/test/").try_into().unwrap(); let workspace_root: &AbsPath = Utf8Path::new("/test/").try_into().unwrap();
let (sender, _) = crossbeam_channel::unbounded(); let (sender, _) = crossbeam_channel::unbounded();
let state = GlobalState::new( let state = GlobalState::new(

View file

@ -1,4 +1,5 @@
//! A `cargo-metadata`-equivalent for non-Cargo build systems. //! Infrastructure for lazy project discovery. Currently only support rust-project.json discovery
//! via a custom discover command.
use std::{io, process::Command}; use std::{io, process::Command};
use crossbeam_channel::Sender; use crossbeam_channel::Sender;
@ -9,19 +10,19 @@ use serde_json::Value;
use crate::command::{CommandHandle, ParseFromLine}; use crate::command::{CommandHandle, ParseFromLine};
pub const ARG_PLACEHOLDER: &str = "{arg}"; pub(crate) const ARG_PLACEHOLDER: &str = "{arg}";
/// A command wrapper for getting a `rust-project.json`. /// A command wrapper for getting a `rust-project.json`.
/// ///
/// This is analogous to `cargo-metadata`, but for non-Cargo build systems. /// This is analogous to discovering a cargo project + running `cargo-metadata` on it, but for non-Cargo build systems.
pub struct Discover { pub(crate) struct DiscoverCommand {
command: Vec<String>, command: Vec<String>,
sender: Sender<DiscoverProjectMessage>, sender: Sender<DiscoverProjectMessage>,
} }
#[derive(PartialEq, Clone, Debug, Serialize)] #[derive(PartialEq, Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub enum DiscoverArgument { pub(crate) enum DiscoverArgument {
Path(#[serde(serialize_with = "serialize_abs_pathbuf")] AbsPathBuf), Path(#[serde(serialize_with = "serialize_abs_pathbuf")] AbsPathBuf),
Buildfile(#[serde(serialize_with = "serialize_abs_pathbuf")] AbsPathBuf), Buildfile(#[serde(serialize_with = "serialize_abs_pathbuf")] AbsPathBuf),
} }
@ -34,14 +35,14 @@ where
se.serialize_str(path.as_str()) se.serialize_str(path.as_str())
} }
impl Discover { impl DiscoverCommand {
/// Create a new [Discover]. /// Create a new [DiscoverCommand].
pub fn new(sender: Sender<DiscoverProjectMessage>, command: Vec<String>) -> Self { pub(crate) fn new(sender: Sender<DiscoverProjectMessage>, command: Vec<String>) -> Self {
Self { sender, command } Self { sender, command }
} }
/// Spawn the command inside [Discover] and report progress, if any. /// Spawn the command inside [Discover] and report progress, if any.
pub fn spawn(&self, discover_arg: DiscoverArgument) -> io::Result<DiscoverHandle> { pub(crate) fn spawn(&self, discover_arg: DiscoverArgument) -> io::Result<DiscoverHandle> {
let command = &self.command[0]; let command = &self.command[0];
let args = &self.command[1..]; let args = &self.command[1..];
@ -65,7 +66,7 @@ impl Discover {
/// A handle to a spawned [Discover]. /// A handle to a spawned [Discover].
#[derive(Debug)] #[derive(Debug)]
pub struct DiscoverHandle { pub(crate) struct DiscoverHandle {
_handle: CommandHandle<DiscoverProjectMessage>, _handle: CommandHandle<DiscoverProjectMessage>,
} }
@ -81,7 +82,7 @@ enum DiscoverProjectData {
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum DiscoverProjectMessage { pub(crate) enum DiscoverProjectMessage {
Finished { project: ProjectJsonData, buildfile: AbsPathBuf }, Finished { project: ProjectJsonData, buildfile: AbsPathBuf },
Error { error: String, source: Option<String> }, Error { error: String, source: Option<String> },
Progress { message: String }, Progress { message: String },

View file

@ -1,11 +1,6 @@
//! Flycheck provides the functionality needed to run `cargo check` or //! Flycheck provides the functionality needed to run `cargo check` to provide
//! another compatible command (f.x. clippy) in a background thread and provide
//! LSP diagnostics based on the output of the command. //! LSP diagnostics based on the output of the command.
// FIXME: This crate now handles running `cargo test` needed in the test explorer in
// addition to `cargo check`. Either split it into 3 crates (one for test, one for check
// and one common utilities) or change its name and docs to reflect the current state.
use std::{fmt, io, process::Command, time::Duration}; use std::{fmt, io, process::Command, time::Duration};
use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
@ -13,47 +8,41 @@ use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use serde::Deserialize; use serde::Deserialize;
pub use cargo_metadata::diagnostic::{ pub(crate) use cargo_metadata::diagnostic::{
Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan, Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan,
DiagnosticSpanMacroExpansion,
}; };
use toolchain::Tool; use toolchain::Tool;
mod command; use crate::command::{CommandHandle, ParseFromLine};
pub mod project_json;
mod test_runner;
use command::{CommandHandle, ParseFromLine};
pub use test_runner::{CargoTestHandle, CargoTestMessage, TestState, TestTarget};
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum InvocationStrategy { pub(crate) enum InvocationStrategy {
Once, Once,
#[default] #[default]
PerWorkspace, PerWorkspace,
} }
#[derive(Clone, Debug, Default, PartialEq, Eq)] #[derive(Clone, Debug, Default, PartialEq, Eq)]
pub enum InvocationLocation { pub(crate) enum InvocationLocation {
Root(AbsPathBuf), Root(AbsPathBuf),
#[default] #[default]
Workspace, Workspace,
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct CargoOptions { pub(crate) struct CargoOptions {
pub target_triples: Vec<String>, pub(crate) target_triples: Vec<String>,
pub all_targets: bool, pub(crate) all_targets: bool,
pub no_default_features: bool, pub(crate) no_default_features: bool,
pub all_features: bool, pub(crate) all_features: bool,
pub features: Vec<String>, pub(crate) features: Vec<String>,
pub extra_args: Vec<String>, pub(crate) extra_args: Vec<String>,
pub extra_env: FxHashMap<String, String>, pub(crate) extra_env: FxHashMap<String, String>,
pub target_dir: Option<Utf8PathBuf>, pub(crate) target_dir: Option<Utf8PathBuf>,
} }
impl CargoOptions { impl CargoOptions {
fn apply_on_command(&self, cmd: &mut Command) { pub(crate) fn apply_on_command(&self, cmd: &mut Command) {
for target in &self.target_triples { for target in &self.target_triples {
cmd.args(["--target", target.as_str()]); cmd.args(["--target", target.as_str()]);
} }
@ -79,7 +68,7 @@ impl CargoOptions {
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum FlycheckConfig { pub(crate) enum FlycheckConfig {
CargoCommand { CargoCommand {
command: String, command: String,
options: CargoOptions, options: CargoOptions,
@ -110,7 +99,7 @@ impl fmt::Display for FlycheckConfig {
/// diagnostics based on the output. /// diagnostics based on the output.
/// The spawned thread is shut down when this struct is dropped. /// The spawned thread is shut down when this struct is dropped.
#[derive(Debug)] #[derive(Debug)]
pub struct FlycheckHandle { pub(crate) struct FlycheckHandle {
// XXX: drop order is significant // XXX: drop order is significant
sender: Sender<StateChange>, sender: Sender<StateChange>,
_thread: stdx::thread::JoinHandle, _thread: stdx::thread::JoinHandle,
@ -118,9 +107,9 @@ pub struct FlycheckHandle {
} }
impl FlycheckHandle { impl FlycheckHandle {
pub fn spawn( pub(crate) fn spawn(
id: usize, id: usize,
sender: Box<dyn Fn(Message) + Send>, sender: Box<dyn Fn(FlycheckMessage) + Send>,
config: FlycheckConfig, config: FlycheckConfig,
sysroot_root: Option<AbsPathBuf>, sysroot_root: Option<AbsPathBuf>,
workspace_root: AbsPathBuf, workspace_root: AbsPathBuf,
@ -137,28 +126,28 @@ impl FlycheckHandle {
} }
/// Schedule a re-start of the cargo check worker to do a workspace wide check. /// Schedule a re-start of the cargo check worker to do a workspace wide check.
pub fn restart_workspace(&self, saved_file: Option<AbsPathBuf>) { pub(crate) fn restart_workspace(&self, saved_file: Option<AbsPathBuf>) {
self.sender.send(StateChange::Restart { package: None, saved_file }).unwrap(); self.sender.send(StateChange::Restart { package: None, saved_file }).unwrap();
} }
/// Schedule a re-start of the cargo check worker to do a package wide check. /// Schedule a re-start of the cargo check worker to do a package wide check.
pub fn restart_for_package(&self, package: String) { pub(crate) fn restart_for_package(&self, package: String) {
self.sender self.sender
.send(StateChange::Restart { package: Some(package), saved_file: None }) .send(StateChange::Restart { package: Some(package), saved_file: None })
.unwrap(); .unwrap();
} }
/// Stop this cargo check worker. /// Stop this cargo check worker.
pub fn cancel(&self) { pub(crate) fn cancel(&self) {
self.sender.send(StateChange::Cancel).unwrap(); self.sender.send(StateChange::Cancel).unwrap();
} }
pub fn id(&self) -> usize { pub(crate) fn id(&self) -> usize {
self.id self.id
} }
} }
pub enum Message { pub(crate) enum FlycheckMessage {
/// Request adding a diagnostic with fixes included to a file /// Request adding a diagnostic with fixes included to a file
AddDiagnostic { id: usize, workspace_root: AbsPathBuf, diagnostic: Diagnostic }, AddDiagnostic { id: usize, workspace_root: AbsPathBuf, diagnostic: Diagnostic },
@ -173,19 +162,19 @@ pub enum Message {
}, },
} }
impl fmt::Debug for Message { impl fmt::Debug for FlycheckMessage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Message::AddDiagnostic { id, workspace_root, diagnostic } => f FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic } => f
.debug_struct("AddDiagnostic") .debug_struct("AddDiagnostic")
.field("id", id) .field("id", id)
.field("workspace_root", workspace_root) .field("workspace_root", workspace_root)
.field("diagnostic_code", &diagnostic.code.as_ref().map(|it| &it.code)) .field("diagnostic_code", &diagnostic.code.as_ref().map(|it| &it.code))
.finish(), .finish(),
Message::ClearDiagnostics { id } => { FlycheckMessage::ClearDiagnostics { id } => {
f.debug_struct("ClearDiagnostics").field("id", id).finish() f.debug_struct("ClearDiagnostics").field("id", id).finish()
} }
Message::Progress { id, progress } => { FlycheckMessage::Progress { id, progress } => {
f.debug_struct("Progress").field("id", id).field("progress", progress).finish() f.debug_struct("Progress").field("id", id).field("progress", progress).finish()
} }
} }
@ -193,7 +182,7 @@ impl fmt::Debug for Message {
} }
#[derive(Debug)] #[derive(Debug)]
pub enum Progress { pub(crate) enum Progress {
DidStart, DidStart,
DidCheckCrate(String), DidCheckCrate(String),
DidFinish(io::Result<()>), DidFinish(io::Result<()>),
@ -210,7 +199,7 @@ enum StateChange {
struct FlycheckActor { struct FlycheckActor {
/// The workspace id of this flycheck instance. /// The workspace id of this flycheck instance.
id: usize, id: usize,
sender: Box<dyn Fn(Message) + Send>, sender: Box<dyn Fn(FlycheckMessage) + Send>,
config: FlycheckConfig, config: FlycheckConfig,
manifest_path: Option<AbsPathBuf>, manifest_path: Option<AbsPathBuf>,
/// Either the workspace root of the workspace we are flychecking, /// Either the workspace root of the workspace we are flychecking,
@ -241,12 +230,12 @@ enum FlycheckStatus {
Finished, Finished,
} }
pub const SAVED_FILE_PLACEHOLDER: &str = "$saved_file"; pub(crate) const SAVED_FILE_PLACEHOLDER: &str = "$saved_file";
impl FlycheckActor { impl FlycheckActor {
fn new( fn new(
id: usize, id: usize,
sender: Box<dyn Fn(Message) + Send>, sender: Box<dyn Fn(FlycheckMessage) + Send>,
config: FlycheckConfig, config: FlycheckConfig,
sysroot_root: Option<AbsPathBuf>, sysroot_root: Option<AbsPathBuf>,
workspace_root: AbsPathBuf, workspace_root: AbsPathBuf,
@ -267,7 +256,7 @@ impl FlycheckActor {
} }
fn report_progress(&self, progress: Progress) { fn report_progress(&self, progress: Progress) {
self.send(Message::Progress { id: self.id, progress }); self.send(FlycheckMessage::Progress { id: self.id, progress });
} }
fn next_event(&self, inbox: &Receiver<StateChange>) -> Option<Event> { fn next_event(&self, inbox: &Receiver<StateChange>) -> Option<Event> {
@ -340,7 +329,7 @@ impl FlycheckActor {
); );
} }
if self.status == FlycheckStatus::Started { if self.status == FlycheckStatus::Started {
self.send(Message::ClearDiagnostics { id: self.id }); self.send(FlycheckMessage::ClearDiagnostics { id: self.id });
} }
self.report_progress(Progress::DidFinish(res)); self.report_progress(Progress::DidFinish(res));
self.status = FlycheckStatus::Finished; self.status = FlycheckStatus::Finished;
@ -362,9 +351,9 @@ impl FlycheckActor {
"diagnostic received" "diagnostic received"
); );
if self.status == FlycheckStatus::Started { if self.status == FlycheckStatus::Started {
self.send(Message::ClearDiagnostics { id: self.id }); self.send(FlycheckMessage::ClearDiagnostics { id: self.id });
} }
self.send(Message::AddDiagnostic { self.send(FlycheckMessage::AddDiagnostic {
id: self.id, id: self.id,
workspace_root: self.root.clone(), workspace_root: self.root.clone(),
diagnostic: msg, diagnostic: msg,
@ -489,7 +478,7 @@ impl FlycheckActor {
Some(cmd) Some(cmd)
} }
fn send(&self, check_task: Message) { fn send(&self, check_task: FlycheckMessage) {
(self.sender)(check_task); (self.sender)(check_task);
} }
} }

View file

@ -6,7 +6,6 @@
use std::{ops::Not as _, time::Instant}; use std::{ops::Not as _, time::Instant};
use crossbeam_channel::{unbounded, Receiver, Sender}; use crossbeam_channel::{unbounded, Receiver, Sender};
use flycheck::{project_json, FlycheckHandle};
use hir::ChangeWithProcMacros; use hir::ChangeWithProcMacros;
use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId}; use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId};
use ide_db::base_db::{CrateId, ProcMacroPaths, SourceDatabase, SourceRootDatabase}; use ide_db::base_db::{CrateId, ProcMacroPaths, SourceDatabase, SourceRootDatabase};
@ -28,6 +27,8 @@ use vfs::{AbsPathBuf, AnchoredPathBuf, ChangeKind, Vfs, VfsPath};
use crate::{ use crate::{
config::{Config, ConfigChange, ConfigErrors, RatomlFileKind}, config::{Config, ConfigChange, ConfigErrors, RatomlFileKind},
diagnostics::{CheckFixes, DiagnosticCollection}, diagnostics::{CheckFixes, DiagnosticCollection},
discover,
flycheck::{FlycheckHandle, FlycheckMessage},
line_index::{LineEndings, LineIndex}, line_index::{LineEndings, LineIndex},
lsp::{from_proto, to_proto::url_from_abs_path}, lsp::{from_proto, to_proto::url_from_abs_path},
lsp_ext, lsp_ext,
@ -37,6 +38,7 @@ use crate::{
reload, reload,
target_spec::{CargoTargetSpec, ProjectJsonTargetSpec, TargetSpec}, target_spec::{CargoTargetSpec, ProjectJsonTargetSpec, TargetSpec},
task_pool::{TaskPool, TaskQueue}, task_pool::{TaskPool, TaskQueue},
test_runner::{CargoTestHandle, CargoTestMessage},
}; };
pub(crate) struct FetchWorkspaceRequest { pub(crate) struct FetchWorkspaceRequest {
@ -88,20 +90,20 @@ pub(crate) struct GlobalState {
// Flycheck // Flycheck
pub(crate) flycheck: Arc<[FlycheckHandle]>, pub(crate) flycheck: Arc<[FlycheckHandle]>,
pub(crate) flycheck_sender: Sender<flycheck::Message>, pub(crate) flycheck_sender: Sender<FlycheckMessage>,
pub(crate) flycheck_receiver: Receiver<flycheck::Message>, pub(crate) flycheck_receiver: Receiver<FlycheckMessage>,
pub(crate) last_flycheck_error: Option<String>, pub(crate) last_flycheck_error: Option<String>,
// Test explorer // Test explorer
pub(crate) test_run_session: Option<Vec<flycheck::CargoTestHandle>>, pub(crate) test_run_session: Option<Vec<CargoTestHandle>>,
pub(crate) test_run_sender: Sender<flycheck::CargoTestMessage>, pub(crate) test_run_sender: Sender<CargoTestMessage>,
pub(crate) test_run_receiver: Receiver<flycheck::CargoTestMessage>, pub(crate) test_run_receiver: Receiver<CargoTestMessage>,
pub(crate) test_run_remaining_jobs: usize, pub(crate) test_run_remaining_jobs: usize,
// Project loading // Project loading
pub(crate) discover_handle: Option<project_json::DiscoverHandle>, pub(crate) discover_handle: Option<discover::DiscoverHandle>,
pub(crate) discover_sender: Sender<project_json::DiscoverProjectMessage>, pub(crate) discover_sender: Sender<discover::DiscoverProjectMessage>,
pub(crate) discover_receiver: Receiver<project_json::DiscoverProjectMessage>, pub(crate) discover_receiver: Receiver<discover::DiscoverProjectMessage>,
// VFS // VFS
pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>, pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,

View file

@ -51,6 +51,7 @@ use crate::{
FetchDependencyListResult, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams, FetchDependencyListResult, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams,
}, },
target_spec::{CargoTargetSpec, TargetSpec}, target_spec::{CargoTargetSpec, TargetSpec},
test_runner::{CargoTestHandle, TestTarget},
}; };
pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> anyhow::Result<()> { pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> anyhow::Result<()> {
@ -246,15 +247,15 @@ pub(crate) fn handle_run_test(
if let ProjectWorkspaceKind::Cargo { cargo, .. } = &ws.kind { if let ProjectWorkspaceKind::Cargo { cargo, .. } = &ws.kind {
let test_target = if let Some(namespace_root) = namespace_root { let test_target = if let Some(namespace_root) = namespace_root {
if let Some(package_name) = find_package_name(namespace_root, cargo) { if let Some(package_name) = find_package_name(namespace_root, cargo) {
flycheck::TestTarget::Package(package_name) TestTarget::Package(package_name)
} else { } else {
flycheck::TestTarget::Workspace TestTarget::Workspace
} }
} else { } else {
flycheck::TestTarget::Workspace TestTarget::Workspace
}; };
let handle = flycheck::CargoTestHandle::new( let handle = CargoTestHandle::new(
test_path, test_path,
state.config.cargo_test_options(), state.config.cargo_test_options(),
cargo.workspace_root(), cargo.workspace_root(),

View file

@ -12,9 +12,12 @@
pub mod cli; pub mod cli;
mod capabilities; mod capabilities;
mod command;
mod diagnostics; mod diagnostics;
mod diff; mod diff;
mod discover;
mod dispatch; mod dispatch;
mod flycheck;
mod hack_recover_crate_name; mod hack_recover_crate_name;
mod line_index; mod line_index;
mod main_loop; mod main_loop;
@ -23,6 +26,7 @@ mod op_queue;
mod reload; mod reload;
mod target_spec; mod target_spec;
mod task_pool; mod task_pool;
mod test_runner;
mod version; mod version;
mod handlers { mod handlers {

View file

@ -9,7 +9,6 @@ use std::{
use always_assert::always; use always_assert::always;
use crossbeam_channel::{select, Receiver}; use crossbeam_channel::{select, Receiver};
use flycheck::project_json;
use ide_db::base_db::{SourceDatabase, SourceRootDatabase, VfsPath}; use ide_db::base_db::{SourceDatabase, SourceRootDatabase, VfsPath};
use lsp_server::{Connection, Notification, Request}; use lsp_server::{Connection, Notification, Request};
use lsp_types::{notification::Notification as _, TextDocumentIdentifier}; use lsp_types::{notification::Notification as _, TextDocumentIdentifier};
@ -20,7 +19,9 @@ use vfs::{loader::LoadingProgress, AbsPathBuf, FileId};
use crate::{ use crate::{
config::Config, config::Config,
diagnostics::{fetch_native_diagnostics, DiagnosticsGeneration, NativeDiagnosticsFetchKind}, diagnostics::{fetch_native_diagnostics, DiagnosticsGeneration, NativeDiagnosticsFetchKind},
discover::{DiscoverArgument, DiscoverCommand, DiscoverProjectMessage},
dispatch::{NotificationDispatcher, RequestDispatcher}, dispatch::{NotificationDispatcher, RequestDispatcher},
flycheck::{self, FlycheckMessage},
global_state::{file_id_to_url, url_to_file_id, FetchWorkspaceRequest, GlobalState}, global_state::{file_id_to_url, url_to_file_id, FetchWorkspaceRequest, GlobalState},
hack_recover_crate_name, hack_recover_crate_name,
lsp::{ lsp::{
@ -29,6 +30,7 @@ use crate::{
}, },
lsp_ext, lsp_ext,
reload::{BuildDataProgress, ProcMacroProgress, ProjectWorkspaceProgress}, reload::{BuildDataProgress, ProcMacroProgress, ProjectWorkspaceProgress},
test_runner::{CargoTestMessage, TestState},
}; };
pub fn main_loop(config: Config, connection: Connection) -> anyhow::Result<()> { pub fn main_loop(config: Config, connection: Connection) -> anyhow::Result<()> {
@ -61,9 +63,9 @@ enum Event {
Task(Task), Task(Task),
QueuedTask(QueuedTask), QueuedTask(QueuedTask),
Vfs(vfs::loader::Message), Vfs(vfs::loader::Message),
Flycheck(flycheck::Message), Flycheck(FlycheckMessage),
TestResult(flycheck::CargoTestMessage), TestResult(CargoTestMessage),
DiscoverProject(project_json::DiscoverProjectMessage), DiscoverProject(DiscoverProjectMessage),
} }
impl fmt::Display for Event { impl fmt::Display for Event {
@ -689,8 +691,7 @@ impl GlobalState {
// `self.report_progress` is called later // `self.report_progress` is called later
let title = &cfg.progress_label.clone(); let title = &cfg.progress_label.clone();
let command = cfg.command.clone(); let command = cfg.command.clone();
let discover = let discover = DiscoverCommand::new(self.discover_sender.clone(), command);
project_json::Discover::new(self.discover_sender.clone(), command);
self.report_progress(title, Progress::Begin, None, None, None); self.report_progress(title, Progress::Begin, None, None, None);
self.discover_workspace_queue self.discover_workspace_queue
@ -698,12 +699,8 @@ impl GlobalState {
let _ = self.discover_workspace_queue.should_start_op(); let _ = self.discover_workspace_queue.should_start_op();
let arg = match arg { let arg = match arg {
DiscoverProjectParam::Buildfile(it) => { DiscoverProjectParam::Buildfile(it) => DiscoverArgument::Buildfile(it),
project_json::DiscoverArgument::Buildfile(it) DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it),
}
DiscoverProjectParam::Path(it) => {
project_json::DiscoverArgument::Path(it)
}
}; };
let handle = discover.spawn(arg).unwrap(); let handle = discover.spawn(arg).unwrap();
@ -852,14 +849,14 @@ impl GlobalState {
} }
} }
fn handle_discover_msg(&mut self, message: project_json::DiscoverProjectMessage) { fn handle_discover_msg(&mut self, message: DiscoverProjectMessage) {
let title = self let title = self
.config .config
.discover_workspace_config() .discover_workspace_config()
.map(|cfg| cfg.progress_label.clone()) .map(|cfg| cfg.progress_label.clone())
.expect("No title could be found; this is a bug"); .expect("No title could be found; this is a bug");
match message { match message {
project_json::DiscoverProjectMessage::Finished { project, buildfile } => { DiscoverProjectMessage::Finished { project, buildfile } => {
self.report_progress(&title, Progress::End, None, None, None); self.report_progress(&title, Progress::End, None, None, None);
self.discover_workspace_queue.op_completed(()); self.discover_workspace_queue.op_completed(());
@ -867,10 +864,10 @@ impl GlobalState {
config.add_linked_projects(project, buildfile); config.add_linked_projects(project, buildfile);
self.update_configuration(config); self.update_configuration(config);
} }
project_json::DiscoverProjectMessage::Progress { message } => { DiscoverProjectMessage::Progress { message } => {
self.report_progress(&title, Progress::Report, Some(message), None, None) self.report_progress(&title, Progress::Report, Some(message), None, None)
} }
project_json::DiscoverProjectMessage::Error { error, source } => { DiscoverProjectMessage::Error { error, source } => {
let message = format!("Project discovery failed: {error}"); let message = format!("Project discovery failed: {error}");
self.discover_workspace_queue.op_completed(()); self.discover_workspace_queue.op_completed(());
self.show_and_log_error(message.clone(), source); self.show_and_log_error(message.clone(), source);
@ -879,16 +876,14 @@ impl GlobalState {
} }
} }
fn handle_cargo_test_msg(&mut self, message: flycheck::CargoTestMessage) { fn handle_cargo_test_msg(&mut self, message: CargoTestMessage) {
match message { match message {
flycheck::CargoTestMessage::Test { name, state } => { CargoTestMessage::Test { name, state } => {
let state = match state { let state = match state {
flycheck::TestState::Started => lsp_ext::TestState::Started, TestState::Started => lsp_ext::TestState::Started,
flycheck::TestState::Ignored => lsp_ext::TestState::Skipped, TestState::Ignored => lsp_ext::TestState::Skipped,
flycheck::TestState::Ok => lsp_ext::TestState::Passed, TestState::Ok => lsp_ext::TestState::Passed,
flycheck::TestState::Failed { stdout } => { TestState::Failed { stdout } => lsp_ext::TestState::Failed { message: stdout },
lsp_ext::TestState::Failed { message: stdout }
}
}; };
let Some(test_id) = hack_recover_crate_name::lookup_name(name) else { let Some(test_id) = hack_recover_crate_name::lookup_name(name) else {
return; return;
@ -897,23 +892,23 @@ impl GlobalState {
lsp_ext::ChangeTestStateParams { test_id, state }, lsp_ext::ChangeTestStateParams { test_id, state },
); );
} }
flycheck::CargoTestMessage::Suite => (), CargoTestMessage::Suite => (),
flycheck::CargoTestMessage::Finished => { CargoTestMessage::Finished => {
self.test_run_remaining_jobs = self.test_run_remaining_jobs.saturating_sub(1); self.test_run_remaining_jobs = self.test_run_remaining_jobs.saturating_sub(1);
if self.test_run_remaining_jobs == 0 { if self.test_run_remaining_jobs == 0 {
self.send_notification::<lsp_ext::EndRunTest>(()); self.send_notification::<lsp_ext::EndRunTest>(());
self.test_run_session = None; self.test_run_session = None;
} }
} }
flycheck::CargoTestMessage::Custom { text } => { CargoTestMessage::Custom { text } => {
self.send_notification::<lsp_ext::AppendOutputToRunTest>(text); self.send_notification::<lsp_ext::AppendOutputToRunTest>(text);
} }
} }
} }
fn handle_flycheck_msg(&mut self, message: flycheck::Message) { fn handle_flycheck_msg(&mut self, message: FlycheckMessage) {
match message { match message {
flycheck::Message::AddDiagnostic { id, workspace_root, diagnostic } => { FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic } => {
let snap = self.snapshot(); let snap = self.snapshot();
let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
&self.config.diagnostics_map(), &self.config.diagnostics_map(),
@ -939,9 +934,9 @@ impl GlobalState {
} }
} }
flycheck::Message::ClearDiagnostics { id } => self.diagnostics.clear_check(id), FlycheckMessage::ClearDiagnostics { id } => self.diagnostics.clear_check(id),
flycheck::Message::Progress { id, progress } => { FlycheckMessage::Progress { id, progress } => {
let (state, message) = match progress { let (state, message) = match progress {
flycheck::Progress::DidStart => (Progress::Begin, None), flycheck::Progress::DidStart => (Progress::Begin, None),
flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)), flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)),

View file

@ -15,7 +15,6 @@
// FIXME: This is a mess that needs some untangling work // FIXME: This is a mess that needs some untangling work
use std::{iter, mem}; use std::{iter, mem};
use flycheck::{FlycheckConfig, FlycheckHandle};
use hir::{db::DefDatabase, ChangeWithProcMacros, ProcMacros, ProcMacrosBuilder}; use hir::{db::DefDatabase, ChangeWithProcMacros, ProcMacros, ProcMacrosBuilder};
use ide_db::{ use ide_db::{
base_db::{salsa::Durability, CrateGraph, ProcMacroPaths, Version}, base_db::{salsa::Durability, CrateGraph, ProcMacroPaths, Version},
@ -32,6 +31,7 @@ use vfs::{AbsPath, AbsPathBuf, ChangeKind};
use crate::{ use crate::{
config::{Config, FilesWatcher, LinkedProject}, config::{Config, FilesWatcher, LinkedProject},
flycheck::{FlycheckConfig, FlycheckHandle},
global_state::{FetchWorkspaceRequest, GlobalState}, global_state::{FetchWorkspaceRequest, GlobalState},
lsp_ext, lsp_ext,
main_loop::{DiscoverProjectParam, Task}, main_loop::{DiscoverProjectParam, Task},
@ -749,12 +749,14 @@ impl GlobalState {
let config = self.config.flycheck(); let config = self.config.flycheck();
let sender = self.flycheck_sender.clone(); let sender = self.flycheck_sender.clone();
let invocation_strategy = match config { let invocation_strategy = match config {
FlycheckConfig::CargoCommand { .. } => flycheck::InvocationStrategy::PerWorkspace, FlycheckConfig::CargoCommand { .. } => {
crate::flycheck::InvocationStrategy::PerWorkspace
}
FlycheckConfig::CustomCommand { invocation_strategy, .. } => invocation_strategy, FlycheckConfig::CustomCommand { invocation_strategy, .. } => invocation_strategy,
}; };
self.flycheck = match invocation_strategy { self.flycheck = match invocation_strategy {
flycheck::InvocationStrategy::Once => vec![FlycheckHandle::spawn( crate::flycheck::InvocationStrategy::Once => vec![FlycheckHandle::spawn(
0, 0,
Box::new(move |msg| sender.send(msg).unwrap()), Box::new(move |msg| sender.send(msg).unwrap()),
config, config,
@ -762,7 +764,7 @@ impl GlobalState {
self.config.root_path().clone(), self.config.root_path().clone(),
None, None,
)], )],
flycheck::InvocationStrategy::PerWorkspace => { crate::flycheck::InvocationStrategy::PerWorkspace => {
self.workspaces self.workspaces
.iter() .iter()
.enumerate() .enumerate()

View file

@ -10,12 +10,12 @@ use toolchain::Tool;
use crate::{ use crate::{
command::{CommandHandle, ParseFromLine}, command::{CommandHandle, ParseFromLine},
CargoOptions, flycheck::CargoOptions,
}; };
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(tag = "event", rename_all = "camelCase")] #[serde(tag = "event", rename_all = "camelCase")]
pub enum TestState { pub(crate) enum TestState {
Started, Started,
Ok, Ok,
Ignored, Ignored,
@ -24,7 +24,7 @@ pub enum TestState {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(tag = "type", rename_all = "camelCase")] #[serde(tag = "type", rename_all = "camelCase")]
pub enum CargoTestMessage { pub(crate) enum CargoTestMessage {
Test { Test {
name: String, name: String,
#[serde(flatten)] #[serde(flatten)]
@ -54,7 +54,7 @@ impl ParseFromLine for CargoTestMessage {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct CargoTestHandle { pub(crate) struct CargoTestHandle {
_handle: CommandHandle<CargoTestMessage>, _handle: CommandHandle<CargoTestMessage>,
} }
@ -64,13 +64,13 @@ pub struct CargoTestHandle {
// cargo test --package my-package --no-fail-fast -- module::func -Z unstable-options --format=json // cargo test --package my-package --no-fail-fast -- module::func -Z unstable-options --format=json
#[derive(Debug)] #[derive(Debug)]
pub enum TestTarget { pub(crate) enum TestTarget {
Workspace, Workspace,
Package(String), Package(String),
} }
impl CargoTestHandle { impl CargoTestHandle {
pub fn new( pub(crate) fn new(
path: Option<&str>, path: Option<&str>,
options: CargoOptions, options: CargoOptions,
root: &AbsPath, root: &AbsPath,