feat: support UpdateTest in codelens

This commit is contained in:
roife 2024-12-25 15:56:06 +08:00
parent dd788255b4
commit edb61b10ab
12 changed files with 204 additions and 57 deletions

View file

@ -316,6 +316,11 @@ fn main() {
}, },
kind: Bin, kind: Bin,
cfg: None, cfg: None,
update_test: UpdateTest {
expect_test: false,
insta: false,
snapbox: false,
},
}, },
), ),
}, },
@ -401,6 +406,11 @@ fn main() {
}, },
kind: Bin, kind: Bin,
cfg: None, cfg: None,
update_test: UpdateTest {
expect_test: false,
insta: false,
snapbox: false,
},
}, },
), ),
}, },
@ -537,6 +547,11 @@ fn main() {
}, },
kind: Bin, kind: Bin,
cfg: None, cfg: None,
update_test: UpdateTest {
expect_test: false,
insta: false,
snapbox: false,
},
}, },
), ),
}, },
@ -597,6 +612,11 @@ fn main() {}
}, },
kind: Bin, kind: Bin,
cfg: None, cfg: None,
update_test: UpdateTest {
expect_test: false,
insta: false,
snapbox: false,
},
}, },
), ),
}, },
@ -709,6 +729,11 @@ fn main() {
}, },
kind: Bin, kind: Bin,
cfg: None, cfg: None,
update_test: UpdateTest {
expect_test: false,
insta: false,
snapbox: false,
},
}, },
), ),
}, },
@ -744,6 +769,20 @@ mod tests {
"#, "#,
expect![[r#" expect![[r#"
[ [
Annotation {
range: 3..7,
kind: HasReferences {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 3,
},
data: Some(
[],
),
},
},
Annotation { Annotation {
range: 3..7, range: 3..7,
kind: Runnable( kind: Runnable(
@ -760,23 +799,14 @@ mod tests {
}, },
kind: Bin, kind: Bin,
cfg: None, cfg: None,
update_test: UpdateTest {
expect_test: false,
insta: false,
snapbox: false,
},
}, },
), ),
}, },
Annotation {
range: 3..7,
kind: HasReferences {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 3,
},
data: Some(
[],
),
},
},
Annotation { Annotation {
range: 18..23, range: 18..23,
kind: Runnable( kind: Runnable(
@ -796,6 +826,11 @@ mod tests {
path: "tests", path: "tests",
}, },
cfg: None, cfg: None,
update_test: UpdateTest {
expect_test: false,
insta: false,
snapbox: false,
},
}, },
), ),
}, },
@ -822,6 +857,11 @@ mod tests {
}, },
}, },
cfg: None, cfg: None,
update_test: UpdateTest {
expect_test: false,
insta: false,
snapbox: false,
},
}, },
), ),
}, },

View file

@ -3213,6 +3213,11 @@ fn foo_$0test() {}
}, },
}, },
cfg: None, cfg: None,
update_test: UpdateTest {
expect_test: false,
insta: false,
snapbox: false,
},
}, },
), ),
] ]
@ -3230,28 +3235,33 @@ mod tests$0 {
} }
"#, "#,
expect![[r#" expect![[r#"
[ [
Runnable( Runnable(
Runnable { Runnable {
use_name_in_title: false, use_name_in_title: false,
nav: NavigationTarget { nav: NavigationTarget {
file_id: FileId( file_id: FileId(
0, 0,
), ),
full_range: 0..46, full_range: 0..46,
focus_range: 4..9, focus_range: 4..9,
name: "tests", name: "tests",
kind: Module, kind: Module,
description: "mod tests", description: "mod tests",
},
kind: TestMod {
path: "tests",
},
cfg: None,
}, },
), kind: TestMod {
] path: "tests",
"#]], },
cfg: None,
update_test: UpdateTest {
expect_test: false,
insta: false,
snapbox: false,
},
},
),
]
"#]],
); );
} }

View file

@ -243,6 +243,9 @@ config_data! {
/// Whether to show `Run` lens. Only applies when /// Whether to show `Run` lens. Only applies when
/// `#rust-analyzer.lens.enable#` is set. /// `#rust-analyzer.lens.enable#` is set.
lens_run_enable: bool = true, lens_run_enable: bool = true,
/// Whether to show `Update Test` lens. Only applies when
/// `#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set.
lens_update_test_enable: bool = true,
/// Disable project auto-discovery in favor of explicitly specified set /// Disable project auto-discovery in favor of explicitly specified set
/// of projects. /// of projects.
@ -1161,6 +1164,7 @@ pub struct LensConfig {
// runnables // runnables
pub run: bool, pub run: bool,
pub debug: bool, pub debug: bool,
pub update_test: bool,
pub interpret: bool, pub interpret: bool,
// implementations // implementations
@ -1196,6 +1200,7 @@ impl LensConfig {
pub fn any(&self) -> bool { pub fn any(&self) -> bool {
self.run self.run
|| self.debug || self.debug
|| self.update_test
|| self.implementations || self.implementations
|| self.method_refs || self.method_refs
|| self.refs_adt || self.refs_adt
@ -1208,7 +1213,7 @@ impl LensConfig {
} }
pub fn runnable(&self) -> bool { pub fn runnable(&self) -> bool {
self.run || self.debug self.run || self.debug || self.update_test
} }
pub fn references(&self) -> bool { pub fn references(&self) -> bool {
@ -2120,6 +2125,9 @@ impl Config {
LensConfig { LensConfig {
run: *self.lens_enable() && *self.lens_run_enable(), run: *self.lens_enable() && *self.lens_run_enable(),
debug: *self.lens_enable() && *self.lens_debug_enable(), debug: *self.lens_enable() && *self.lens_debug_enable(),
update_test: *self.lens_enable()
&& *self.lens_update_test_enable()
&& *self.lens_run_enable(),
interpret: *self.lens_enable() && *self.lens_run_enable() && *self.interpret_tests(), interpret: *self.lens_enable() && *self.lens_run_enable() && *self.interpret_tests(),
implementations: *self.lens_enable() && *self.lens_implementations_enable(), implementations: *self.lens_enable() && *self.lens_implementations_enable(),
method_refs: *self.lens_enable() && *self.lens_references_method_enable(), method_refs: *self.lens_enable() && *self.lens_references_method_enable(),

View file

@ -427,14 +427,14 @@ impl Request for Runnables {
const METHOD: &'static str = "experimental/runnables"; const METHOD: &'static str = "experimental/runnables";
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct RunnablesParams { pub struct RunnablesParams {
pub text_document: TextDocumentIdentifier, pub text_document: TextDocumentIdentifier,
pub position: Option<Position>, pub position: Option<Position>,
} }
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Runnable { pub struct Runnable {
pub label: String, pub label: String,
@ -444,7 +444,7 @@ pub struct Runnable {
pub args: RunnableArgs, pub args: RunnableArgs,
} }
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
#[serde(untagged)] #[serde(untagged)]
pub enum RunnableArgs { pub enum RunnableArgs {
@ -452,14 +452,14 @@ pub enum RunnableArgs {
Shell(ShellRunnableArgs), Shell(ShellRunnableArgs),
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
pub enum RunnableKind { pub enum RunnableKind {
Cargo, Cargo,
Shell, Shell,
} }
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CargoRunnableArgs { pub struct CargoRunnableArgs {
#[serde(skip_serializing_if = "FxHashMap::is_empty")] #[serde(skip_serializing_if = "FxHashMap::is_empty")]
@ -475,7 +475,7 @@ pub struct CargoRunnableArgs {
pub executable_args: Vec<String>, pub executable_args: Vec<String>,
} }
#[derive(Deserialize, Serialize, Debug)] #[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct ShellRunnableArgs { pub struct ShellRunnableArgs {
#[serde(skip_serializing_if = "FxHashMap::is_empty")] #[serde(skip_serializing_if = "FxHashMap::is_empty")]

View file

@ -20,6 +20,7 @@ use itertools::Itertools;
use paths::{Utf8Component, Utf8Prefix}; use paths::{Utf8Component, Utf8Prefix};
use semver::VersionReq; use semver::VersionReq;
use serde_json::to_value; use serde_json::to_value;
use syntax::SmolStr;
use vfs::AbsPath; use vfs::AbsPath;
use crate::{ use crate::{
@ -1567,6 +1568,7 @@ pub(crate) fn code_lens(
let line_index = snap.file_line_index(run.nav.file_id)?; let line_index = snap.file_line_index(run.nav.file_id)?;
let annotation_range = range(&line_index, annotation.range); let annotation_range = range(&line_index, annotation.range);
let update_test = run.update_test;
let title = run.title(); let title = run.title();
let can_debug = match run.kind { let can_debug = match run.kind {
ide::RunnableKind::DocTest { .. } => false, ide::RunnableKind::DocTest { .. } => false,
@ -1602,6 +1604,17 @@ pub(crate) fn code_lens(
data: None, data: None,
}) })
} }
if lens_config.update_test && client_commands_config.run_single {
let label = update_test.label();
if let Some(r) = make_update_runnable(&r, &label) {
let command = command::run_single(&r, label.unwrap().as_str());
acc.push(lsp_types::CodeLens {
range: annotation_range,
command: Some(command),
data: None,
})
}
}
} }
if lens_config.interpret { if lens_config.interpret {
@ -1786,7 +1799,7 @@ pub(crate) mod command {
pub(crate) fn debug_single(runnable: &lsp_ext::Runnable) -> lsp_types::Command { pub(crate) fn debug_single(runnable: &lsp_ext::Runnable) -> lsp_types::Command {
lsp_types::Command { lsp_types::Command {
title: "Debug".into(), title: "\u{fe0e} Debug".into(),
command: "rust-analyzer.debugSingle".into(), command: "rust-analyzer.debugSingle".into(),
arguments: Some(vec![to_value(runnable).unwrap()]), arguments: Some(vec![to_value(runnable).unwrap()]),
} }
@ -1838,6 +1851,29 @@ pub(crate) mod command {
} }
} }
fn make_update_runnable(
runnable: &lsp_ext::Runnable,
label: &Option<SmolStr>,
) -> Option<lsp_ext::Runnable> {
if !matches!(runnable.args, lsp_ext::RunnableArgs::Cargo(_)) {
return None;
}
let label = label.as_ref()?;
let mut runnable = runnable.clone();
runnable.label = format!("{} + {}", runnable.label, label);
let lsp_ext::RunnableArgs::Cargo(r) = &mut runnable.args else {
unreachable!();
};
let environment_vars =
[("UPDATE_EXPECT", "1"), ("INSTA_UPDATE", "always"), ("SNAPSHOTS", "overwrite")];
r.environment.extend(environment_vars.into_iter().map(|(k, v)| (k.to_owned(), v.to_owned())));
Some(runnable)
}
pub(crate) fn implementation_title(count: usize) -> String { pub(crate) fn implementation_title(count: usize) -> String {
if count == 1 { if count == 1 {
"1 implementation".into() "1 implementation".into()

View file

@ -1,5 +1,5 @@
<!--- <!---
lsp/ext.rs hash: 9790509d87670c22 lsp/ext.rs hash: 512c06cd8b46a21d
If you need to change the above hash to make the test pass, please check if you If you need to change the above hash to make the test pass, please check if you
need to adjust this doc as well and ping this issue: need to adjust this doc as well and ping this issue:

View file

@ -808,6 +808,12 @@ Only applies when `#rust-analyzer.lens.enable#` is set.
Whether to show `Run` lens. Only applies when Whether to show `Run` lens. Only applies when
`#rust-analyzer.lens.enable#` is set. `#rust-analyzer.lens.enable#` is set.
-- --
[[rust-analyzer.lens.update.test.enable]]rust-analyzer.lens.update.test.enable (default: `true`)::
+
--
Whether to show `Update Test` lens. Only applies when
`#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set.
--
[[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`):: [[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`)::
+ +
-- --

View file

@ -407,6 +407,11 @@
"$rustc" "$rustc"
], ],
"markdownDescription": "Problem matchers to use for `rust-analyzer.run` command, eg `[\"$rustc\", \"$rust-panic\"]`." "markdownDescription": "Problem matchers to use for `rust-analyzer.run` command, eg `[\"$rustc\", \"$rust-panic\"]`."
},
"rust-analyzer.runnables.askBeforeUpdateTest": {
"type": "boolean",
"default": true,
"markdownDescription": "Ask before updating the test when running it."
} }
} }
}, },
@ -2295,6 +2300,16 @@
} }
} }
}, },
{
"title": "lens",
"properties": {
"rust-analyzer.lens.update.test.enable": {
"markdownDescription": "Whether to show `Update Test` lens. Only applies when\n`#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set.",
"default": true,
"type": "boolean"
}
}
},
{ {
"title": "general", "title": "general",
"properties": { "properties": {

View file

@ -348,9 +348,9 @@ class ExperimentalFeatures implements lc.StaticFeature {
initialize( initialize(
_capabilities: lc.ServerCapabilities, _capabilities: lc.ServerCapabilities,
_documentSelector: lc.DocumentSelector | undefined, _documentSelector: lc.DocumentSelector | undefined,
): void {} ): void { }
dispose(): void {} dispose(): void { }
clear(): void {} clear(): void { }
} }
class OverrideFeatures implements lc.StaticFeature { class OverrideFeatures implements lc.StaticFeature {
@ -368,9 +368,9 @@ class OverrideFeatures implements lc.StaticFeature {
initialize( initialize(
_capabilities: lc.ServerCapabilities, _capabilities: lc.ServerCapabilities,
_documentSelector: lc.DocumentSelector | undefined, _documentSelector: lc.DocumentSelector | undefined,
): void {} ): void { }
dispose(): void {} dispose(): void { }
clear(): void {} clear(): void { }
} }
function isCodeActionWithoutEditsAndCommands(value: any): boolean { function isCodeActionWithoutEditsAndCommands(value: any): boolean {
@ -398,9 +398,8 @@ export let HOVER_REFERENCE_COMMAND: ra.CommandLink[] = [];
function renderCommand(cmd: ra.CommandLink): string { function renderCommand(cmd: ra.CommandLink): string {
HOVER_REFERENCE_COMMAND.push(cmd); HOVER_REFERENCE_COMMAND.push(cmd);
return `[${cmd.title}](command:rust-analyzer.hoverRefCommandProxy?${ return `[${cmd.title}](command:rust-analyzer.hoverRefCommandProxy?${HOVER_REFERENCE_COMMAND.length - 1
HOVER_REFERENCE_COMMAND.length - 1 } '${cmd.tooltip}')`;
} '${cmd.tooltip}')`;
} }
function renderHoverActions(actions: ra.CommandLinkGroup[]): vscode.MarkdownString { function renderHoverActions(actions: ra.CommandLinkGroup[]): vscode.MarkdownString {

View file

@ -1139,11 +1139,37 @@ export function peekTests(ctx: CtxInit): Cmd {
}; };
} }
function isUpdatingTest(runnable: ra.Runnable): boolean {
if (!isCargoRunnableArgs(runnable.args)) {
return false;
}
const env = runnable.args.environment;
return env ? ['UPDATE_EXPECT', 'INSTA_UPDATE', 'SNAPSHOTS'].some(key => key in env) : false;
}
export function runSingle(ctx: CtxInit): Cmd { export function runSingle(ctx: CtxInit): Cmd {
return async (runnable: ra.Runnable) => { return async (runnable: ra.Runnable) => {
const editor = ctx.activeRustEditor; const editor = ctx.activeRustEditor;
if (!editor) return; if (!editor) return;
if (isUpdatingTest(runnable) && ctx.config.askBeforeUpdateTest) {
const selection = await vscode.window.showInformationMessage(
'rust-analyzer',
{ detail: 'Do you want to update tests?', modal: true },
'Update Now',
'Update (and Don\'t ask again)',
);
if (selection !== 'Update Now' && selection !== 'Update (and Don\'t ask again)') {
return;
}
if (selection === 'Update (and Don\'t ask again)') {
ctx.config.setAskBeforeUpdateTest(false);
}
}
const task = await createTaskFromRunnable(runnable, ctx.config); const task = await createTaskFromRunnable(runnable, ctx.config);
task.group = vscode.TaskGroup.Build; task.group = vscode.TaskGroup.Build;
task.presentationOptions = { task.presentationOptions = {

View file

@ -362,6 +362,13 @@ export class Config {
get initializeStopped() { get initializeStopped() {
return this.get<boolean>("initializeStopped"); return this.get<boolean>("initializeStopped");
} }
get askBeforeUpdateTest() {
return this.get<boolean>("runnables.askBeforeUpdateTest");
}
async setAskBeforeUpdateTest(value: boolean) {
await this.cfg.update("runnables.askBeforeUpdateTest", value, true);
}
} }
export function prepareVSCodeConfig<T>(resp: T): T { export function prepareVSCodeConfig<T>(resp: T): T {

View file

@ -148,7 +148,7 @@ function createCommands(): Record<string, CommandFactory> {
health: "stopped", health: "stopped",
}); });
}, },
disabled: (_) => async () => {}, disabled: (_) => async () => { },
}, },
analyzerStatus: { enabled: commands.analyzerStatus }, analyzerStatus: { enabled: commands.analyzerStatus },
@ -207,10 +207,10 @@ function checkConflictingExtensions() {
vscode.window vscode.window
.showWarningMessage( .showWarningMessage(
`You have both the rust-analyzer (rust-lang.rust-analyzer) and Rust (rust-lang.rust) ` + `You have both the rust-analyzer (rust-lang.rust-analyzer) and Rust (rust-lang.rust) ` +
"plugins enabled. These are known to conflict and cause various functions of " + "plugins enabled. These are known to conflict and cause various functions of " +
"both plugins to not work correctly. You should disable one of them.", "both plugins to not work correctly. You should disable one of them.",
"Got it", "Got it",
) )
.then(() => {}, console.error); .then(() => { }, console.error);
} }
} }