Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions crates/rust-analyzer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,19 @@ config_data! {

/// Subcommand used for bench runnables instead of `bench`.
runnables_bench_command: String = "bench".to_owned(),
/// Override the command used for debugging bench runnables.
/// The first element of the array should be the program to execute (for example, `cargo`).
///
/// Use the placeholders:
/// - `${package}`: package name.
/// - `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc.
/// - `${target}`: target name (empty for `--lib`).
/// - `${test_name}`: the test path filter, e.g. `module::bench_func`.
/// - `${exact}`: `--exact` for single benchmarks, empty for modules.
/// - `${include_ignored}`: always empty for benchmarks.
/// - `${executable_args}`: all of the above binary args bundled together
/// (includes `rust-analyzer.runnables.extraTestBinaryArgs`).
runnables_bench_debugOverrideCommand: Option<Vec<String>> = None,
/// Override the command used for bench runnables.
/// The first element of the array should be the program to execute (for example, `cargo`).
///
Expand Down Expand Up @@ -998,6 +1011,19 @@ config_data! {
runnables_extraTestBinaryArgs: Vec<String> = vec!["--nocapture".to_owned()],
/// Subcommand used for test runnables instead of `test`.
runnables_test_command: String = "test".to_owned(),
/// Override the command used for debugging test runnables.
/// The first element of the array should be the program to execute (for example, `cargo`).
///
/// Available placeholders:
/// - `${package}`: package name.
/// - `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc.
/// - `${target}`: target name (empty for `--lib`).
/// - `${test_name}`: the test path filter, e.g. `module::test_func`.
/// - `${exact}`: `--exact` for single tests, empty for modules.
/// - `${include_ignored}`: `--include-ignored` for single tests, empty otherwise.
/// - `${executable_args}`: all of the above binary args bundled together
/// (includes `rust-analyzer.runnables.extraTestBinaryArgs`).
runnables_test_debugOverrideCommand: Option<Vec<String>> = None,
/// Override the command used for test runnables.
/// The first element of the array should be the program to execute (for example, `cargo`).
///
Expand Down Expand Up @@ -1679,10 +1705,14 @@ pub struct RunnablesConfig {
pub test_command: String,
/// Override the command used for test runnables.
pub test_override_command: Option<Vec<String>>,
/// Override the command used for debugging test runnables.
pub test_debug_override_command: Option<Vec<String>>,
/// Subcommand used for doctest runnables instead of `bench`.
pub bench_command: String,
/// Override the command used for bench runnables.
pub bench_override_command: Option<Vec<String>>,
/// Override the command used for debugging bench runnables.
pub bench_debug_override_command: Option<Vec<String>>,
/// Override the command used for doctest runnables.
pub doc_test_override_command: Option<Vec<String>>,
}
Expand Down Expand Up @@ -2642,8 +2672,14 @@ impl Config {
extra_test_binary_args: self.runnables_extraTestBinaryArgs(source_root).clone(),
test_command: self.runnables_test_command(source_root).clone(),
test_override_command: self.runnables_test_overrideCommand(source_root).clone(),
test_debug_override_command: self
.runnables_test_debugOverrideCommand(source_root)
.clone(),
bench_command: self.runnables_bench_command(source_root).clone(),
bench_override_command: self.runnables_bench_overrideCommand(source_root).clone(),
bench_debug_override_command: self
.runnables_bench_debugOverrideCommand(source_root)
.clone(),
doc_test_override_command: self.runnables_doctest_overrideCommand(source_root).clone(),
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/rust-analyzer/src/handlers/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1063,6 +1063,7 @@ pub(crate) fn handle_runnables(
.into_iter()
.collect(),
}),
debug: None,
})
}
}
Expand All @@ -1085,6 +1086,7 @@ pub(crate) fn handle_runnables(
executable_args: Vec::new(),
environment: Default::default(),
}),
debug: None,
});
};
}
Expand Down
9 changes: 9 additions & 0 deletions crates/rust-analyzer/src/lsp/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,15 @@ pub struct Runnable {
pub location: Option<lsp_types::LocationLink>,
pub kind: RunnableKind,
pub args: RunnableArgs,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub debug: Option<RunnableCommand>,
}

#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct RunnableCommand {
pub kind: RunnableKind,
pub args: RunnableArgs,
}

#[derive(Deserialize, Serialize, Debug, Clone)]
Expand Down
34 changes: 33 additions & 1 deletion crates/rust-analyzer/src/lsp/to_proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1597,11 +1597,21 @@ pub(crate) fn runnable(
let label = runnable.label(Some(&target));
let location = location_link(snap, None, runnable.nav)?;

let environment = spec
let environment: rustc_hash::FxHashMap<_, _> = spec
.sysroot_root
.as_ref()
.map(|root| ("RUSTC_TOOLCHAIN".to_owned(), root.to_string()))
.into_iter()
.collect();
let debug =
CargoTargetSpec::debug_override_command(snap, Some(spec.clone()), &runnable.kind)
.and_then(|override_command| {
shell_runnable_command(
override_command,
environment.clone(),
cwd.clone().into(),
)
});

Ok(match override_command {
Some(override_command) => match override_command.split_first() {
Expand All @@ -1615,6 +1625,7 @@ pub(crate) fn runnable(
program: program.to_string(),
args: args.to_vec(),
}),
debug,
}),
_ => None,
},
Expand All @@ -1630,6 +1641,7 @@ pub(crate) fn runnable(
executable_args,
environment,
}),
debug,
}),
})
}
Expand All @@ -1650,6 +1662,7 @@ pub(crate) fn runnable(
location: Some(location),
kind: lsp_ext::RunnableKind::Shell,
args: lsp_ext::RunnableArgs::Shell(runnable_args),
debug: None,
}))
}
None => Ok(None),
Expand Down Expand Up @@ -1677,11 +1690,29 @@ pub(crate) fn runnable(
executable_args,
environment: Default::default(),
}),
debug: None,
}))
}
}
}

fn shell_runnable_command(
override_command: Vec<String>,
environment: rustc_hash::FxHashMap<String, String>,
cwd: paths::Utf8PathBuf,
) -> Option<lsp_ext::RunnableCommand> {
let (program, args) = override_command.split_first()?;
Some(lsp_ext::RunnableCommand {
kind: lsp_ext::RunnableKind::Shell,
args: lsp_ext::RunnableArgs::Shell(lsp_ext::ShellRunnableArgs {
environment,
cwd,
program: program.to_string(),
args: args.to_vec(),
}),
})
}

pub(crate) fn code_lens(
acc: &mut Vec<lsp_types::CodeLens>,
snap: &GlobalStateSnapshot,
Expand Down Expand Up @@ -1984,6 +2015,7 @@ pub(crate) fn make_update_runnable(

let mut runnable = runnable.clone();
runnable.label = format!("{} + {}", runnable.label, label);
runnable.debug = None;

let lsp_ext::RunnableArgs::Cargo(r) = &mut runnable.args else {
return None;
Expand Down
56 changes: 53 additions & 3 deletions crates/rust-analyzer/src/target_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,13 +221,64 @@ impl CargoTargetSpec {
RunnableKind::DocTest { test_id } => {
(config.doc_test_override_command, Some(test_id.to_string()))
}
RunnableKind::Bin => match spec {
RunnableKind::Bin => match &spec {
Some(CargoTargetSpec { target_kind: TargetKind::Test, .. }) => {
(config.test_override_command, None)
}
_ => (None, None),
},
};

Self::expand_override_command(
spec.as_ref(),
kind,
args,
test_name,
config.extra_test_binary_args,
)
}

pub(crate) fn debug_override_command(
snap: &GlobalStateSnapshot,
spec: Option<CargoTargetSpec>,
kind: &RunnableKind,
) -> Option<Vec<String>> {
let config = snap.config.runnables(None);
let (args, test_name) = match kind {
RunnableKind::Test { test_id, .. } => {
(config.test_debug_override_command, Some(test_id.to_string()))
}
RunnableKind::TestMod { path } => {
(config.test_debug_override_command, Some(path.clone()))
}
RunnableKind::Bench { test_id } => {
(config.bench_debug_override_command, Some(test_id.to_string()))
}
RunnableKind::Bin => match &spec {
Some(CargoTargetSpec { target_kind: TargetKind::Test, .. }) => {
(config.test_debug_override_command, None)
}
_ => (None, None),
},
_ => (None, None),
};

Self::expand_override_command(
spec.as_ref(),
kind,
args,
test_name,
config.extra_test_binary_args,
)
}

fn expand_override_command(
spec: Option<&CargoTargetSpec>,
kind: &RunnableKind,
args: Option<Vec<String>>,
test_name: Option<String>,
extra_test_binary_args: Vec<String>,
) -> Option<Vec<String>> {
let test_name = test_name.unwrap_or_default();

let exact = match kind {
Expand Down Expand Up @@ -256,7 +307,7 @@ impl CargoTargetSpec {
_ => "",
};

let replace_placeholders = |arg: String| match &spec {
let replace_placeholders = |arg: String| match spec {
Some(spec) => arg
.replace("${package}", &spec.package)
.replace("${target_arg}", target_arg(spec.target_kind))
Expand All @@ -267,7 +318,6 @@ impl CargoTargetSpec {
_ => arg,
};

let extra_test_binary_args = config.extra_test_binary_args;
let executable_args = Self::executable_args_for(kind, extra_test_binary_args);

args.map(|mut args| {
Expand Down
76 changes: 76 additions & 0 deletions crates/rust-analyzer/tests/slow-tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,82 @@ fn main() {}
);
}

#[test]
fn test_runnables_debug_override_command() {
if skip_slow_tests() {
return;
}

let server = Project::with_fixture(
r#"
//- /foo/Cargo.toml
[package]
name = "foo"
version = "0.0.0"

//- /foo/tests/spam.rs
#[test]
fn test_eggs() {}
"#,
)
.with_config(json!({
"runnables": {
"test": {
"overrideCommand": [
"cargo", "nextest", "run",
"--package", "${package}", "${target_arg}", "${target}",
"--", "${executable_args}",
],
"debugOverrideCommand": [
"cargo", "nextest", "run",
"--debugger", "codelldb-launch --",
"--package", "${package}", "${target_arg}", "${target}",
"--", "${executable_args}",
],
},
},
}))
.root("foo")
.server()
.wait_until_workspace_is_loaded();

server.request::<Runnables>(
RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
json!([
{
"args": {
"args": [
"nextest", "run",
"--package", "foo", "--test", "spam",
"--", "test_eggs", "--exact", "--nocapture", "--include-ignored",
],
"cwd": server.path().join("foo"),
"program": "cargo",
},
"debug": {
"args": {
"args": [
"nextest", "run",
"--debugger", "codelldb-launch --",
"--package", "foo", "--test", "spam",
"--", "test_eggs", "--exact", "--nocapture", "--include-ignored",
],
"cwd": server.path().join("foo"),
"program": "cargo",
},
"kind": "shell",
},
"kind": "shell",
"label": "test test_eggs",
"location": "{...}",
},
"{...}",
"{...}",
"{...}",
]),
);
}

// Each package in these workspaces should be run from its own root
#[test]
fn test_path_dependency_runnables() {
Expand Down
Loading