mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-30 19:49:36 +00:00
Merge pull request #18757 from roife/fix-17812
feat: support updating snapshot tests with codelens/hovering/runnables
This commit is contained in:
commit
a612fc9a16
13 changed files with 540 additions and 97 deletions
|
|
@ -5933,6 +5933,12 @@ impl HasCrate for Adt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasCrate for Impl {
|
||||||
|
fn krate(&self, db: &dyn HirDatabase) -> Crate {
|
||||||
|
self.module(db).krate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl HasCrate for Module {
|
impl HasCrate for Module {
|
||||||
fn krate(&self, _: &dyn HirDatabase) -> Crate {
|
fn krate(&self, _: &dyn HirDatabase) -> Crate {
|
||||||
Module::krate(*self)
|
Module::krate(*self)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -3260,6 +3260,11 @@ fn foo_$0test() {}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cfg: None,
|
cfg: None,
|
||||||
|
update_test: UpdateTest {
|
||||||
|
expect_test: false,
|
||||||
|
insta: false,
|
||||||
|
snapbox: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
@ -3295,6 +3300,11 @@ mod tests$0 {
|
||||||
path: "tests",
|
path: "tests",
|
||||||
},
|
},
|
||||||
cfg: None,
|
cfg: None,
|
||||||
|
update_test: UpdateTest {
|
||||||
|
expect_test: false,
|
||||||
|
insta: false,
|
||||||
|
snapbox: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
@ -10029,3 +10039,99 @@ fn bar() {
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_runnables_with_snapshot_tests() {
|
||||||
|
check_actions(
|
||||||
|
r#"
|
||||||
|
//- /lib.rs crate:foo deps:expect_test,insta,snapbox
|
||||||
|
use expect_test::expect;
|
||||||
|
use insta::assert_debug_snapshot;
|
||||||
|
use snapbox::Assert;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test$0() {
|
||||||
|
let actual = "new25";
|
||||||
|
expect!["new25"].assert_eq(&actual);
|
||||||
|
Assert::new()
|
||||||
|
.action_env("SNAPSHOTS")
|
||||||
|
.eq(actual, snapbox::str!["new25"]);
|
||||||
|
assert_debug_snapshot!(actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
//- /lib.rs crate:expect_test
|
||||||
|
struct Expect;
|
||||||
|
|
||||||
|
impl Expect {
|
||||||
|
fn assert_eq(&self, actual: &str) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! expect {
|
||||||
|
($e:expr) => Expect; // dummy
|
||||||
|
}
|
||||||
|
|
||||||
|
//- /lib.rs crate:insta
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! assert_debug_snapshot {
|
||||||
|
($e:expr) => {}; // dummy
|
||||||
|
}
|
||||||
|
|
||||||
|
//- /lib.rs crate:snapbox
|
||||||
|
pub struct Assert;
|
||||||
|
|
||||||
|
impl Assert {
|
||||||
|
pub fn new() -> Self { Assert }
|
||||||
|
|
||||||
|
pub fn action_env(&self, env: &str) -> &Self { self }
|
||||||
|
|
||||||
|
pub fn eq(&self, actual: &str, expected: &str) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! str {
|
||||||
|
($e:expr) => ""; // dummy
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
[
|
||||||
|
Reference(
|
||||||
|
FilePositionWrapper {
|
||||||
|
file_id: FileId(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
offset: 92,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Runnable(
|
||||||
|
Runnable {
|
||||||
|
use_name_in_title: false,
|
||||||
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
full_range: 81..301,
|
||||||
|
focus_range: 92..96,
|
||||||
|
name: "test",
|
||||||
|
kind: Function,
|
||||||
|
},
|
||||||
|
kind: Test {
|
||||||
|
test_id: Path(
|
||||||
|
"test",
|
||||||
|
),
|
||||||
|
attr: TestAttr {
|
||||||
|
ignore: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cfg: None,
|
||||||
|
update_test: UpdateTest {
|
||||||
|
expect_test: true,
|
||||||
|
insta: true,
|
||||||
|
snapbox: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
use std::fmt;
|
use std::{fmt, sync::OnceLock};
|
||||||
|
|
||||||
|
use arrayvec::ArrayVec;
|
||||||
use ast::HasName;
|
use ast::HasName;
|
||||||
use cfg::{CfgAtom, CfgExpr};
|
use cfg::{CfgAtom, CfgExpr};
|
||||||
use hir::{
|
use hir::{
|
||||||
db::HirDatabase, sym, AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, HirFileIdExt,
|
db::HirDatabase, sym, AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, HirFileIdExt,
|
||||||
Semantics,
|
ModPath, Name, PathKind, Semantics, Symbol,
|
||||||
};
|
};
|
||||||
use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn};
|
use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
|
|
@ -15,11 +16,12 @@ use ide_db::{
|
||||||
FilePosition, FxHashMap, FxHashSet, RootDatabase, SymbolKind,
|
FilePosition, FxHashMap, FxHashSet, RootDatabase, SymbolKind,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use smallvec::SmallVec;
|
||||||
use span::{Edition, TextSize};
|
use span::{Edition, TextSize};
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode},
|
ast::{self, AstNode},
|
||||||
SmolStr, SyntaxNode, ToSmolStr,
|
format_smolstr, SmolStr, SyntaxNode, ToSmolStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{references, FileId, NavigationTarget, ToNav, TryToNav};
|
use crate::{references, FileId, NavigationTarget, ToNav, TryToNav};
|
||||||
|
|
@ -30,6 +32,7 @@ pub struct Runnable {
|
||||||
pub nav: NavigationTarget,
|
pub nav: NavigationTarget,
|
||||||
pub kind: RunnableKind,
|
pub kind: RunnableKind,
|
||||||
pub cfg: Option<CfgExpr>,
|
pub cfg: Option<CfgExpr>,
|
||||||
|
pub update_test: UpdateTest,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
|
|
@ -334,14 +337,20 @@ pub(crate) fn runnable_fn(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let fn_source = sema.source(def)?;
|
||||||
let nav = NavigationTarget::from_named(
|
let nav = NavigationTarget::from_named(
|
||||||
sema.db,
|
sema.db,
|
||||||
def.source(sema.db)?.as_ref().map(|it| it as &dyn ast::HasName),
|
fn_source.as_ref().map(|it| it as &dyn ast::HasName),
|
||||||
SymbolKind::Function,
|
SymbolKind::Function,
|
||||||
)
|
)
|
||||||
.call_site();
|
.call_site();
|
||||||
|
|
||||||
|
let file_range = fn_source.syntax().original_file_range_with_macro_call_body(sema.db);
|
||||||
|
let update_test =
|
||||||
|
UpdateTest::find_snapshot_macro(sema, &fn_source.file_syntax(sema.db), file_range);
|
||||||
|
|
||||||
let cfg = def.attrs(sema.db).cfg();
|
let cfg = def.attrs(sema.db).cfg();
|
||||||
Some(Runnable { use_name_in_title: false, nav, kind, cfg })
|
Some(Runnable { use_name_in_title: false, nav, kind, cfg, update_test })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn runnable_mod(
|
pub(crate) fn runnable_mod(
|
||||||
|
|
@ -366,7 +375,22 @@ pub(crate) fn runnable_mod(
|
||||||
let attrs = def.attrs(sema.db);
|
let attrs = def.attrs(sema.db);
|
||||||
let cfg = attrs.cfg();
|
let cfg = attrs.cfg();
|
||||||
let nav = NavigationTarget::from_module_to_decl(sema.db, def).call_site();
|
let nav = NavigationTarget::from_module_to_decl(sema.db, def).call_site();
|
||||||
Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::TestMod { path }, cfg })
|
|
||||||
|
let module_source = sema.module_definition_node(def);
|
||||||
|
let module_syntax = module_source.file_syntax(sema.db);
|
||||||
|
let file_range = hir::FileRange {
|
||||||
|
file_id: module_source.file_id.original_file(sema.db),
|
||||||
|
range: module_syntax.text_range(),
|
||||||
|
};
|
||||||
|
let update_test = UpdateTest::find_snapshot_macro(sema, &module_syntax, file_range);
|
||||||
|
|
||||||
|
Some(Runnable {
|
||||||
|
use_name_in_title: false,
|
||||||
|
nav,
|
||||||
|
kind: RunnableKind::TestMod { path },
|
||||||
|
cfg,
|
||||||
|
update_test,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn runnable_impl(
|
pub(crate) fn runnable_impl(
|
||||||
|
|
@ -392,7 +416,19 @@ pub(crate) fn runnable_impl(
|
||||||
test_id.retain(|c| c != ' ');
|
test_id.retain(|c| c != ' ');
|
||||||
let test_id = TestId::Path(test_id);
|
let test_id = TestId::Path(test_id);
|
||||||
|
|
||||||
Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::DocTest { test_id }, cfg })
|
let impl_source = sema.source(*def)?;
|
||||||
|
let impl_syntax = impl_source.syntax();
|
||||||
|
let file_range = impl_syntax.original_file_range_with_macro_call_body(sema.db);
|
||||||
|
let update_test =
|
||||||
|
UpdateTest::find_snapshot_macro(sema, &impl_syntax.file_syntax(sema.db), file_range);
|
||||||
|
|
||||||
|
Some(Runnable {
|
||||||
|
use_name_in_title: false,
|
||||||
|
nav,
|
||||||
|
kind: RunnableKind::DocTest { test_id },
|
||||||
|
cfg,
|
||||||
|
update_test,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_cfg_test(attrs: AttrsWithOwner) -> bool {
|
fn has_cfg_test(attrs: AttrsWithOwner) -> bool {
|
||||||
|
|
@ -404,6 +440,8 @@ fn runnable_mod_outline_definition(
|
||||||
sema: &Semantics<'_, RootDatabase>,
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
def: hir::Module,
|
def: hir::Module,
|
||||||
) -> Option<Runnable> {
|
) -> Option<Runnable> {
|
||||||
|
def.as_source_file_id(sema.db)?;
|
||||||
|
|
||||||
if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(def.attrs(sema.db)))
|
if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(def.attrs(sema.db)))
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -421,16 +459,22 @@ fn runnable_mod_outline_definition(
|
||||||
|
|
||||||
let attrs = def.attrs(sema.db);
|
let attrs = def.attrs(sema.db);
|
||||||
let cfg = attrs.cfg();
|
let cfg = attrs.cfg();
|
||||||
if def.as_source_file_id(sema.db).is_some() {
|
|
||||||
|
let mod_source = sema.module_definition_node(def);
|
||||||
|
let mod_syntax = mod_source.file_syntax(sema.db);
|
||||||
|
let file_range = hir::FileRange {
|
||||||
|
file_id: mod_source.file_id.original_file(sema.db),
|
||||||
|
range: mod_syntax.text_range(),
|
||||||
|
};
|
||||||
|
let update_test = UpdateTest::find_snapshot_macro(sema, &mod_syntax, file_range);
|
||||||
|
|
||||||
Some(Runnable {
|
Some(Runnable {
|
||||||
use_name_in_title: false,
|
use_name_in_title: false,
|
||||||
nav: def.to_nav(sema.db).call_site(),
|
nav: def.to_nav(sema.db).call_site(),
|
||||||
kind: RunnableKind::TestMod { path },
|
kind: RunnableKind::TestMod { path },
|
||||||
cfg,
|
cfg,
|
||||||
|
update_test,
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
|
fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
|
||||||
|
|
@ -495,6 +539,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
|
||||||
nav,
|
nav,
|
||||||
kind: RunnableKind::DocTest { test_id },
|
kind: RunnableKind::DocTest { test_id },
|
||||||
cfg: attrs.cfg(),
|
cfg: attrs.cfg(),
|
||||||
|
update_test: UpdateTest::default(),
|
||||||
};
|
};
|
||||||
Some(res)
|
Some(res)
|
||||||
}
|
}
|
||||||
|
|
@ -575,6 +620,128 @@ fn has_test_function_or_multiple_test_submodules(
|
||||||
number_of_test_submodules > 1
|
number_of_test_submodules > 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct UpdateTest {
|
||||||
|
pub expect_test: bool,
|
||||||
|
pub insta: bool,
|
||||||
|
pub snapbox: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
static SNAPSHOT_TEST_MACROS: OnceLock<FxHashMap<&str, Vec<ModPath>>> = OnceLock::new();
|
||||||
|
|
||||||
|
impl UpdateTest {
|
||||||
|
const EXPECT_CRATE: &str = "expect_test";
|
||||||
|
const EXPECT_MACROS: &[&str] = &["expect", "expect_file"];
|
||||||
|
|
||||||
|
const INSTA_CRATE: &str = "insta";
|
||||||
|
const INSTA_MACROS: &[&str] = &[
|
||||||
|
"assert_snapshot",
|
||||||
|
"assert_debug_snapshot",
|
||||||
|
"assert_display_snapshot",
|
||||||
|
"assert_json_snapshot",
|
||||||
|
"assert_yaml_snapshot",
|
||||||
|
"assert_ron_snapshot",
|
||||||
|
"assert_toml_snapshot",
|
||||||
|
"assert_csv_snapshot",
|
||||||
|
"assert_compact_json_snapshot",
|
||||||
|
"assert_compact_debug_snapshot",
|
||||||
|
"assert_binary_snapshot",
|
||||||
|
];
|
||||||
|
|
||||||
|
const SNAPBOX_CRATE: &str = "snapbox";
|
||||||
|
const SNAPBOX_MACROS: &[&str] = &["assert_data_eq", "file", "str"];
|
||||||
|
|
||||||
|
fn find_snapshot_macro(
|
||||||
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
scope: &SyntaxNode,
|
||||||
|
file_range: hir::FileRange,
|
||||||
|
) -> Self {
|
||||||
|
fn init<'a>(
|
||||||
|
krate_name: &'a str,
|
||||||
|
paths: &[&str],
|
||||||
|
map: &mut FxHashMap<&'a str, Vec<ModPath>>,
|
||||||
|
) {
|
||||||
|
let mut res = Vec::with_capacity(paths.len());
|
||||||
|
let krate = Name::new_symbol_root(Symbol::intern(krate_name));
|
||||||
|
for path in paths {
|
||||||
|
let segments = [krate.clone(), Name::new_symbol_root(Symbol::intern(path))];
|
||||||
|
let mod_path = ModPath::from_segments(PathKind::Abs, segments);
|
||||||
|
res.push(mod_path);
|
||||||
|
}
|
||||||
|
map.insert(krate_name, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mod_paths = SNAPSHOT_TEST_MACROS.get_or_init(|| {
|
||||||
|
let mut map = FxHashMap::default();
|
||||||
|
init(Self::EXPECT_CRATE, Self::EXPECT_MACROS, &mut map);
|
||||||
|
init(Self::INSTA_CRATE, Self::INSTA_MACROS, &mut map);
|
||||||
|
init(Self::SNAPBOX_CRATE, Self::SNAPBOX_MACROS, &mut map);
|
||||||
|
map
|
||||||
|
});
|
||||||
|
|
||||||
|
let search_scope = SearchScope::file_range(file_range);
|
||||||
|
let find_macro = |paths: &[ModPath]| {
|
||||||
|
for path in paths {
|
||||||
|
let Some(items) = sema.resolve_mod_path(scope, path) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for item in items {
|
||||||
|
if let hir::ItemInNs::Macros(makro) = item {
|
||||||
|
if Definition::Macro(makro)
|
||||||
|
.usages(sema)
|
||||||
|
.in_scope(&search_scope)
|
||||||
|
.at_least_one()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
UpdateTest {
|
||||||
|
expect_test: find_macro(mod_paths.get(Self::EXPECT_CRATE).unwrap()),
|
||||||
|
insta: find_macro(mod_paths.get(Self::INSTA_CRATE).unwrap()),
|
||||||
|
snapbox: find_macro(mod_paths.get(Self::SNAPBOX_CRATE).unwrap()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn label(&self) -> Option<SmolStr> {
|
||||||
|
let mut builder: SmallVec<[_; 3]> = SmallVec::new();
|
||||||
|
if self.expect_test {
|
||||||
|
builder.push("Expect");
|
||||||
|
}
|
||||||
|
if self.insta {
|
||||||
|
builder.push("Insta");
|
||||||
|
}
|
||||||
|
if self.snapbox {
|
||||||
|
builder.push("Snapbox");
|
||||||
|
}
|
||||||
|
|
||||||
|
let res: SmolStr = builder.join(" + ").into();
|
||||||
|
if res.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(format_smolstr!("↺\u{fe0e} Update Tests ({res})"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn env(&self) -> ArrayVec<(&str, &str), 3> {
|
||||||
|
let mut env = ArrayVec::new();
|
||||||
|
if self.expect_test {
|
||||||
|
env.push(("UPDATE_EXPECT", "1"));
|
||||||
|
}
|
||||||
|
if self.insta {
|
||||||
|
env.push(("INSTA_UPDATE", "always"));
|
||||||
|
}
|
||||||
|
if self.snapbox {
|
||||||
|
env.push(("SNAPSHOTS", "overwrite"));
|
||||||
|
}
|
||||||
|
env
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use expect_test::{expect, Expect};
|
use expect_test::{expect, Expect};
|
||||||
|
|
@ -1337,18 +1504,18 @@ mod tests {
|
||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
full_range: 52..115,
|
full_range: 121..185,
|
||||||
focus_range: 67..75,
|
focus_range: 136..145,
|
||||||
name: "foo_test",
|
name: "foo2_test",
|
||||||
kind: Function,
|
kind: Function,
|
||||||
},
|
},
|
||||||
NavigationTarget {
|
NavigationTarget {
|
||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
full_range: 121..185,
|
full_range: 52..115,
|
||||||
focus_range: 136..145,
|
focus_range: 67..75,
|
||||||
name: "foo2_test",
|
name: "foo_test",
|
||||||
kind: Function,
|
kind: Function,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,9 @@ config_data! {
|
||||||
/// Whether to show `Run` action. Only applies when
|
/// Whether to show `Run` action. Only applies when
|
||||||
/// `#rust-analyzer.hover.actions.enable#` is set.
|
/// `#rust-analyzer.hover.actions.enable#` is set.
|
||||||
hover_actions_run_enable: bool = true,
|
hover_actions_run_enable: bool = true,
|
||||||
|
/// Whether to show `Update Test` action. Only applies when
|
||||||
|
/// `#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set.
|
||||||
|
hover_actions_updateTest_enable: bool = true,
|
||||||
|
|
||||||
/// Whether to show documentation on hover.
|
/// Whether to show documentation on hover.
|
||||||
hover_documentation_enable: bool = true,
|
hover_documentation_enable: bool = true,
|
||||||
|
|
@ -243,6 +246,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_updateTest_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 +1167,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 +1203,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 +1216,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 {
|
||||||
|
|
@ -1222,6 +1230,7 @@ pub struct HoverActionsConfig {
|
||||||
pub references: bool,
|
pub references: bool,
|
||||||
pub run: bool,
|
pub run: bool,
|
||||||
pub debug: bool,
|
pub debug: bool,
|
||||||
|
pub update_test: bool,
|
||||||
pub goto_type_def: bool,
|
pub goto_type_def: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1231,6 +1240,7 @@ impl HoverActionsConfig {
|
||||||
references: false,
|
references: false,
|
||||||
run: false,
|
run: false,
|
||||||
debug: false,
|
debug: false,
|
||||||
|
update_test: false,
|
||||||
goto_type_def: false,
|
goto_type_def: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1243,7 +1253,7 @@ impl HoverActionsConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runnable(&self) -> bool {
|
pub fn runnable(&self) -> bool {
|
||||||
self.run || self.debug
|
self.run || self.debug || self.update_test
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1517,6 +1527,9 @@ impl Config {
|
||||||
references: enable && self.hover_actions_references_enable().to_owned(),
|
references: enable && self.hover_actions_references_enable().to_owned(),
|
||||||
run: enable && self.hover_actions_run_enable().to_owned(),
|
run: enable && self.hover_actions_run_enable().to_owned(),
|
||||||
debug: enable && self.hover_actions_debug_enable().to_owned(),
|
debug: enable && self.hover_actions_debug_enable().to_owned(),
|
||||||
|
update_test: enable
|
||||||
|
&& self.hover_actions_run_enable().to_owned()
|
||||||
|
&& self.hover_actions_updateTest_enable().to_owned(),
|
||||||
goto_type_def: enable && self.hover_actions_gotoTypeDef_enable().to_owned(),
|
goto_type_def: enable && self.hover_actions_gotoTypeDef_enable().to_owned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2120,6 +2133,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_updateTest_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(),
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use paths::Utf8PathBuf;
|
||||||
use project_model::{CargoWorkspace, ManifestPath, ProjectWorkspaceKind, TargetKind};
|
use project_model::{CargoWorkspace, ManifestPath, ProjectWorkspaceKind, TargetKind};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use stdx::{format_to, never};
|
use stdx::{format_to, never};
|
||||||
use syntax::{algo, ast, AstNode, TextRange, TextSize};
|
use syntax::{TextRange, TextSize};
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
|
use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
|
||||||
|
|
||||||
|
|
@ -928,39 +928,32 @@ pub(crate) fn handle_runnables(
|
||||||
let offset = params.position.and_then(|it| from_proto::offset(&line_index, it).ok());
|
let offset = params.position.and_then(|it| from_proto::offset(&line_index, it).ok());
|
||||||
let target_spec = TargetSpec::for_file(&snap, file_id)?;
|
let target_spec = TargetSpec::for_file(&snap, file_id)?;
|
||||||
|
|
||||||
let expect_test = match offset {
|
|
||||||
Some(offset) => {
|
|
||||||
let source_file = snap.analysis.parse(file_id)?;
|
|
||||||
algo::find_node_at_offset::<ast::MacroCall>(source_file.syntax(), offset)
|
|
||||||
.and_then(|it| it.path()?.segment()?.name_ref())
|
|
||||||
.map_or(false, |it| it.text() == "expect" || it.text() == "expect_file")
|
|
||||||
}
|
|
||||||
None => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
for runnable in snap.analysis.runnables(file_id)? {
|
for runnable in snap.analysis.runnables(file_id)? {
|
||||||
if should_skip_for_offset(&runnable, offset) {
|
if should_skip_for_offset(&runnable, offset)
|
||||||
continue;
|
|| should_skip_target(&runnable, target_spec.as_ref())
|
||||||
}
|
{
|
||||||
if should_skip_target(&runnable, target_spec.as_ref()) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let update_test = runnable.update_test;
|
||||||
if let Some(mut runnable) = to_proto::runnable(&snap, runnable)? {
|
if let Some(mut runnable) = to_proto::runnable(&snap, runnable)? {
|
||||||
if expect_test {
|
if let Some(runnable) =
|
||||||
|
to_proto::make_update_runnable(&runnable, &update_test.label(), &update_test.env())
|
||||||
|
{
|
||||||
|
res.push(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
if let lsp_ext::RunnableArgs::Cargo(r) = &mut runnable.args {
|
if let lsp_ext::RunnableArgs::Cargo(r) = &mut runnable.args {
|
||||||
runnable.label = format!("{} + expect", runnable.label);
|
|
||||||
r.environment.insert("UPDATE_EXPECT".to_owned(), "1".to_owned());
|
|
||||||
if let Some(TargetSpec::Cargo(CargoTargetSpec {
|
if let Some(TargetSpec::Cargo(CargoTargetSpec {
|
||||||
sysroot_root: Some(sysroot_root),
|
sysroot_root: Some(sysroot_root),
|
||||||
..
|
..
|
||||||
})) = &target_spec
|
})) = &target_spec
|
||||||
{
|
{
|
||||||
r.environment
|
r.environment.insert("RUSTC_TOOLCHAIN".to_owned(), sysroot_root.to_string());
|
||||||
.insert("RUSTC_TOOLCHAIN".to_owned(), sysroot_root.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
res.push(runnable);
|
res.push(runnable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2142,6 +2135,7 @@ fn runnable_action_links(
|
||||||
}
|
}
|
||||||
|
|
||||||
let title = runnable.title();
|
let title = runnable.title();
|
||||||
|
let update_test = runnable.update_test;
|
||||||
let r = to_proto::runnable(snap, runnable).ok()??;
|
let r = to_proto::runnable(snap, runnable).ok()??;
|
||||||
|
|
||||||
let mut group = lsp_ext::CommandLinkGroup::default();
|
let mut group = lsp_ext::CommandLinkGroup::default();
|
||||||
|
|
@ -2153,7 +2147,15 @@ fn runnable_action_links(
|
||||||
|
|
||||||
if hover_actions_config.debug && client_commands_config.debug_single {
|
if hover_actions_config.debug && client_commands_config.debug_single {
|
||||||
let dbg_command = to_proto::command::debug_single(&r);
|
let dbg_command = to_proto::command::debug_single(&r);
|
||||||
group.commands.push(to_command_link(dbg_command, r.label));
|
group.commands.push(to_command_link(dbg_command, r.label.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if hover_actions_config.update_test && client_commands_config.run_single {
|
||||||
|
let label = update_test.label();
|
||||||
|
if let Some(r) = to_proto::make_update_runnable(&r, &label, &update_test.env()) {
|
||||||
|
let update_command = to_proto::command::run_single(&r, label.unwrap().as_str());
|
||||||
|
group.commands.push(to_command_link(update_command, r.label.clone()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(group)
|
Some(group)
|
||||||
|
|
|
||||||
|
|
@ -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")]
|
||||||
|
|
|
||||||
|
|
@ -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,18 @@ pub(crate) fn code_lens(
|
||||||
data: None,
|
data: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if lens_config.update_test && client_commands_config.run_single {
|
||||||
|
let label = update_test.label();
|
||||||
|
let env = update_test.env();
|
||||||
|
if let Some(r) = make_update_runnable(&r, &label, &env) {
|
||||||
|
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 +1800,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 +1852,28 @@ pub(crate) mod command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn make_update_runnable(
|
||||||
|
runnable: &lsp_ext::Runnable,
|
||||||
|
label: &Option<SmolStr>,
|
||||||
|
env: &[(&str, &str)],
|
||||||
|
) -> 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!();
|
||||||
|
};
|
||||||
|
|
||||||
|
r.environment.extend(env.iter().map(|(k, v)| (k.to_string(), v.to_string())));
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -497,6 +497,12 @@ Whether to show `References` action. Only applies when
|
||||||
Whether to show `Run` action. Only applies when
|
Whether to show `Run` action. Only applies when
|
||||||
`#rust-analyzer.hover.actions.enable#` is set.
|
`#rust-analyzer.hover.actions.enable#` is set.
|
||||||
--
|
--
|
||||||
|
[[rust-analyzer.hover.actions.updateTest.enable]]rust-analyzer.hover.actions.updateTest.enable (default: `true`)::
|
||||||
|
+
|
||||||
|
--
|
||||||
|
Whether to show `Update Test` action. Only applies when
|
||||||
|
`#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set.
|
||||||
|
--
|
||||||
[[rust-analyzer.hover.documentation.enable]]rust-analyzer.hover.documentation.enable (default: `true`)::
|
[[rust-analyzer.hover.documentation.enable]]rust-analyzer.hover.documentation.enable (default: `true`)::
|
||||||
+
|
+
|
||||||
--
|
--
|
||||||
|
|
@ -808,6 +814,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.updateTest.enable]]rust-analyzer.lens.updateTest.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: `[]`)::
|
||||||
+
|
+
|
||||||
--
|
--
|
||||||
|
|
|
||||||
|
|
@ -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."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -1515,6 +1520,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "hover",
|
||||||
|
"properties": {
|
||||||
|
"rust-analyzer.hover.actions.updateTest.enable": {
|
||||||
|
"markdownDescription": "Whether to show `Update Test` action. Only applies when\n`#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set.",
|
||||||
|
"default": true,
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "hover",
|
"title": "hover",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -2295,6 +2310,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "lens",
|
||||||
|
"properties": {
|
||||||
|
"rust-analyzer.lens.updateTest.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": {
|
||||||
|
|
|
||||||
|
|
@ -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)") {
|
||||||
|
await 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 = {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue