Merge remote-tracking branch 'upstream/master' into compute-lazy-assits
# Conflicts: # crates/rust-analyzer/src/to_proto.rs
This commit is contained in:
@@ -92,15 +92,16 @@ impl NavigationTarget {
|
|||||||
let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
|
let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
|
||||||
if let Some(src) = module.declaration_source(db) {
|
if let Some(src) = module.declaration_source(db) {
|
||||||
let frange = original_range(db, src.as_ref().map(|it| it.syntax()));
|
let frange = original_range(db, src.as_ref().map(|it| it.syntax()));
|
||||||
return NavigationTarget::from_syntax(
|
let mut res = NavigationTarget::from_syntax(
|
||||||
frange.file_id,
|
frange.file_id,
|
||||||
name,
|
name,
|
||||||
None,
|
None,
|
||||||
frange.range,
|
frange.range,
|
||||||
src.value.syntax().kind(),
|
src.value.syntax().kind(),
|
||||||
src.value.doc_comment_text(),
|
|
||||||
src.value.short_label(),
|
|
||||||
);
|
);
|
||||||
|
res.docs = src.value.doc_comment_text();
|
||||||
|
res.description = src.value.short_label();
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
module.to_nav(db)
|
module.to_nav(db)
|
||||||
}
|
}
|
||||||
@@ -130,11 +131,9 @@ impl NavigationTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Allows `NavigationTarget` to be created from a `NameOwner`
|
/// Allows `NavigationTarget` to be created from a `NameOwner`
|
||||||
fn from_named(
|
pub(crate) fn from_named(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
node: InFile<&dyn ast::NameOwner>,
|
node: InFile<&dyn ast::NameOwner>,
|
||||||
docs: Option<String>,
|
|
||||||
description: Option<String>,
|
|
||||||
) -> NavigationTarget {
|
) -> NavigationTarget {
|
||||||
//FIXME: use `_` instead of empty string
|
//FIXME: use `_` instead of empty string
|
||||||
let name = node.value.name().map(|it| it.text().clone()).unwrap_or_default();
|
let name = node.value.name().map(|it| it.text().clone()).unwrap_or_default();
|
||||||
@@ -148,8 +147,6 @@ impl NavigationTarget {
|
|||||||
focus_range,
|
focus_range,
|
||||||
frange.range,
|
frange.range,
|
||||||
node.value.syntax().kind(),
|
node.value.syntax().kind(),
|
||||||
docs,
|
|
||||||
description,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,8 +156,6 @@ impl NavigationTarget {
|
|||||||
focus_range: Option<TextRange>,
|
focus_range: Option<TextRange>,
|
||||||
full_range: TextRange,
|
full_range: TextRange,
|
||||||
kind: SyntaxKind,
|
kind: SyntaxKind,
|
||||||
docs: Option<String>,
|
|
||||||
description: Option<String>,
|
|
||||||
) -> NavigationTarget {
|
) -> NavigationTarget {
|
||||||
NavigationTarget {
|
NavigationTarget {
|
||||||
file_id,
|
file_id,
|
||||||
@@ -169,8 +164,8 @@ impl NavigationTarget {
|
|||||||
full_range,
|
full_range,
|
||||||
focus_range,
|
focus_range,
|
||||||
container_name: None,
|
container_name: None,
|
||||||
description,
|
description: None,
|
||||||
docs,
|
docs: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,12 +233,11 @@ where
|
|||||||
{
|
{
|
||||||
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
|
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
|
||||||
let src = self.source(db);
|
let src = self.source(db);
|
||||||
NavigationTarget::from_named(
|
let mut res =
|
||||||
db,
|
NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
|
||||||
src.as_ref().map(|it| it as &dyn ast::NameOwner),
|
res.docs = src.value.doc_comment_text();
|
||||||
src.value.doc_comment_text(),
|
res.description = src.value.short_label();
|
||||||
src.value.short_label(),
|
res
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,15 +252,7 @@ impl ToNav for hir::Module {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let frange = original_range(db, src.with_value(syntax));
|
let frange = original_range(db, src.with_value(syntax));
|
||||||
NavigationTarget::from_syntax(
|
NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, syntax.kind())
|
||||||
frange.file_id,
|
|
||||||
name,
|
|
||||||
focus,
|
|
||||||
frange.range,
|
|
||||||
syntax.kind(),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,8 +271,6 @@ impl ToNav for hir::ImplDef {
|
|||||||
None,
|
None,
|
||||||
frange.range,
|
frange.range,
|
||||||
src.value.syntax().kind(),
|
src.value.syntax().kind(),
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -296,12 +280,12 @@ impl ToNav for hir::Field {
|
|||||||
let src = self.source(db);
|
let src = self.source(db);
|
||||||
|
|
||||||
match &src.value {
|
match &src.value {
|
||||||
FieldSource::Named(it) => NavigationTarget::from_named(
|
FieldSource::Named(it) => {
|
||||||
db,
|
let mut res = NavigationTarget::from_named(db, src.with_value(it));
|
||||||
src.with_value(it),
|
res.docs = it.doc_comment_text();
|
||||||
it.doc_comment_text(),
|
res.description = it.short_label();
|
||||||
it.short_label(),
|
res
|
||||||
),
|
}
|
||||||
FieldSource::Pos(it) => {
|
FieldSource::Pos(it) => {
|
||||||
let frange = original_range(db, src.with_value(it.syntax()));
|
let frange = original_range(db, src.with_value(it.syntax()));
|
||||||
NavigationTarget::from_syntax(
|
NavigationTarget::from_syntax(
|
||||||
@@ -310,8 +294,6 @@ impl ToNav for hir::Field {
|
|||||||
None,
|
None,
|
||||||
frange.range,
|
frange.range,
|
||||||
it.syntax().kind(),
|
it.syntax().kind(),
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -322,12 +304,10 @@ impl ToNav for hir::MacroDef {
|
|||||||
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
|
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
|
||||||
let src = self.source(db);
|
let src = self.source(db);
|
||||||
log::debug!("nav target {:#?}", src.value.syntax());
|
log::debug!("nav target {:#?}", src.value.syntax());
|
||||||
NavigationTarget::from_named(
|
let mut res =
|
||||||
db,
|
NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
|
||||||
src.as_ref().map(|it| it as &dyn ast::NameOwner),
|
res.docs = src.value.doc_comment_text();
|
||||||
src.value.doc_comment_text(),
|
res
|
||||||
None,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
|
use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use ra_cfg::CfgExpr;
|
||||||
use ra_ide_db::RootDatabase;
|
use ra_ide_db::RootDatabase;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner},
|
ast::{self, AstNode, AttrsOwner, DocCommentsOwner, ModuleItemOwner, NameOwner},
|
||||||
match_ast, SyntaxNode, TextRange,
|
match_ast, SyntaxNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::FileId;
|
use crate::{display::ToNav, FileId, NavigationTarget};
|
||||||
use ast::DocCommentsOwner;
|
|
||||||
use ra_cfg::CfgExpr;
|
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Runnable {
|
pub struct Runnable {
|
||||||
pub range: TextRange,
|
pub nav: NavigationTarget,
|
||||||
pub kind: RunnableKind,
|
pub kind: RunnableKind,
|
||||||
pub cfg_exprs: Vec<CfgExpr>,
|
pub cfg_exprs: Vec<CfgExpr>,
|
||||||
}
|
}
|
||||||
@@ -24,8 +24,8 @@ pub enum TestId {
|
|||||||
Path(String),
|
Path(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for TestId {
|
impl fmt::Display for TestId {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
TestId::Name(name) => write!(f, "{}", name),
|
TestId::Name(name) => write!(f, "{}", name),
|
||||||
TestId::Path(path) => write!(f, "{}", path),
|
TestId::Path(path) => write!(f, "{}", path),
|
||||||
@@ -131,7 +131,8 @@ fn runnable_fn(
|
|||||||
let cfg_exprs =
|
let cfg_exprs =
|
||||||
attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
|
attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
|
||||||
|
|
||||||
Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs })
|
let nav = NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &fn_def));
|
||||||
|
Some(Runnable { nav, kind, cfg_exprs })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -183,7 +184,6 @@ fn runnable_mod(
|
|||||||
if !has_test_function {
|
if !has_test_function {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let range = module.syntax().text_range();
|
|
||||||
let module_def = sema.to_def(&module)?;
|
let module_def = sema.to_def(&module)?;
|
||||||
|
|
||||||
let path = module_def
|
let path = module_def
|
||||||
@@ -197,7 +197,8 @@ fn runnable_mod(
|
|||||||
let cfg_exprs =
|
let cfg_exprs =
|
||||||
attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
|
attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
|
||||||
|
|
||||||
Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs })
|
let nav = module_def.to_nav(sema.db);
|
||||||
|
Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -227,12 +228,38 @@ mod tests {
|
|||||||
@r###"
|
@r###"
|
||||||
[
|
[
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 1..21,
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
full_range: 1..21,
|
||||||
|
name: "main",
|
||||||
|
kind: FN_DEF,
|
||||||
|
focus_range: Some(
|
||||||
|
12..16,
|
||||||
|
),
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
kind: Bin,
|
kind: Bin,
|
||||||
cfg_exprs: [],
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 22..46,
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
full_range: 22..46,
|
||||||
|
name: "test_foo",
|
||||||
|
kind: FN_DEF,
|
||||||
|
focus_range: Some(
|
||||||
|
33..41,
|
||||||
|
),
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
kind: Test {
|
kind: Test {
|
||||||
test_id: Path(
|
test_id: Path(
|
||||||
"test_foo",
|
"test_foo",
|
||||||
@@ -244,7 +271,20 @@ mod tests {
|
|||||||
cfg_exprs: [],
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 47..81,
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
full_range: 47..81,
|
||||||
|
name: "test_foo",
|
||||||
|
kind: FN_DEF,
|
||||||
|
focus_range: Some(
|
||||||
|
68..76,
|
||||||
|
),
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
kind: Test {
|
kind: Test {
|
||||||
test_id: Path(
|
test_id: Path(
|
||||||
"test_foo",
|
"test_foo",
|
||||||
@@ -279,12 +319,38 @@ mod tests {
|
|||||||
@r###"
|
@r###"
|
||||||
[
|
[
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 1..21,
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
full_range: 1..21,
|
||||||
|
name: "main",
|
||||||
|
kind: FN_DEF,
|
||||||
|
focus_range: Some(
|
||||||
|
12..16,
|
||||||
|
),
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
kind: Bin,
|
kind: Bin,
|
||||||
cfg_exprs: [],
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 22..64,
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
full_range: 22..64,
|
||||||
|
name: "foo",
|
||||||
|
kind: FN_DEF,
|
||||||
|
focus_range: Some(
|
||||||
|
56..59,
|
||||||
|
),
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
kind: DocTest {
|
kind: DocTest {
|
||||||
test_id: Path(
|
test_id: Path(
|
||||||
"foo",
|
"foo",
|
||||||
@@ -319,12 +385,38 @@ mod tests {
|
|||||||
@r###"
|
@r###"
|
||||||
[
|
[
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 1..21,
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
full_range: 1..21,
|
||||||
|
name: "main",
|
||||||
|
kind: FN_DEF,
|
||||||
|
focus_range: Some(
|
||||||
|
12..16,
|
||||||
|
),
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
kind: Bin,
|
kind: Bin,
|
||||||
cfg_exprs: [],
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 51..105,
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
full_range: 51..105,
|
||||||
|
name: "foo",
|
||||||
|
kind: FN_DEF,
|
||||||
|
focus_range: Some(
|
||||||
|
97..100,
|
||||||
|
),
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
kind: DocTest {
|
kind: DocTest {
|
||||||
test_id: Path(
|
test_id: Path(
|
||||||
"Data::foo",
|
"Data::foo",
|
||||||
@@ -354,14 +446,40 @@ mod tests {
|
|||||||
@r###"
|
@r###"
|
||||||
[
|
[
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 1..59,
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
full_range: 1..59,
|
||||||
|
name: "test_mod",
|
||||||
|
kind: MODULE,
|
||||||
|
focus_range: Some(
|
||||||
|
13..21,
|
||||||
|
),
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
kind: TestMod {
|
kind: TestMod {
|
||||||
path: "test_mod",
|
path: "test_mod",
|
||||||
},
|
},
|
||||||
cfg_exprs: [],
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 28..57,
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
full_range: 28..57,
|
||||||
|
name: "test_foo1",
|
||||||
|
kind: FN_DEF,
|
||||||
|
focus_range: Some(
|
||||||
|
43..52,
|
||||||
|
),
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
kind: Test {
|
kind: Test {
|
||||||
test_id: Path(
|
test_id: Path(
|
||||||
"test_mod::test_foo1",
|
"test_mod::test_foo1",
|
||||||
@@ -396,14 +514,40 @@ mod tests {
|
|||||||
@r###"
|
@r###"
|
||||||
[
|
[
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 23..85,
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
full_range: 23..85,
|
||||||
|
name: "test_mod",
|
||||||
|
kind: MODULE,
|
||||||
|
focus_range: Some(
|
||||||
|
27..35,
|
||||||
|
),
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
kind: TestMod {
|
kind: TestMod {
|
||||||
path: "foo::test_mod",
|
path: "foo::test_mod",
|
||||||
},
|
},
|
||||||
cfg_exprs: [],
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 46..79,
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
full_range: 46..79,
|
||||||
|
name: "test_foo1",
|
||||||
|
kind: FN_DEF,
|
||||||
|
focus_range: Some(
|
||||||
|
65..74,
|
||||||
|
),
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
kind: Test {
|
kind: Test {
|
||||||
test_id: Path(
|
test_id: Path(
|
||||||
"foo::test_mod::test_foo1",
|
"foo::test_mod::test_foo1",
|
||||||
@@ -440,14 +584,40 @@ mod tests {
|
|||||||
@r###"
|
@r###"
|
||||||
[
|
[
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 41..115,
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
full_range: 41..115,
|
||||||
|
name: "test_mod",
|
||||||
|
kind: MODULE,
|
||||||
|
focus_range: Some(
|
||||||
|
45..53,
|
||||||
|
),
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
kind: TestMod {
|
kind: TestMod {
|
||||||
path: "foo::bar::test_mod",
|
path: "foo::bar::test_mod",
|
||||||
},
|
},
|
||||||
cfg_exprs: [],
|
cfg_exprs: [],
|
||||||
},
|
},
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 68..105,
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
full_range: 68..105,
|
||||||
|
name: "test_foo1",
|
||||||
|
kind: FN_DEF,
|
||||||
|
focus_range: Some(
|
||||||
|
91..100,
|
||||||
|
),
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
kind: Test {
|
kind: Test {
|
||||||
test_id: Path(
|
test_id: Path(
|
||||||
"foo::bar::test_mod::test_foo1",
|
"foo::bar::test_mod::test_foo1",
|
||||||
@@ -479,7 +649,20 @@ mod tests {
|
|||||||
@r###"
|
@r###"
|
||||||
[
|
[
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 1..58,
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
full_range: 1..58,
|
||||||
|
name: "test_foo1",
|
||||||
|
kind: FN_DEF,
|
||||||
|
focus_range: Some(
|
||||||
|
44..53,
|
||||||
|
),
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
kind: Test {
|
kind: Test {
|
||||||
test_id: Path(
|
test_id: Path(
|
||||||
"test_foo1",
|
"test_foo1",
|
||||||
@@ -516,7 +699,20 @@ mod tests {
|
|||||||
@r###"
|
@r###"
|
||||||
[
|
[
|
||||||
Runnable {
|
Runnable {
|
||||||
range: 1..80,
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
full_range: 1..80,
|
||||||
|
name: "test_foo1",
|
||||||
|
kind: FN_DEF,
|
||||||
|
focus_range: Some(
|
||||||
|
66..75,
|
||||||
|
),
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
kind: Test {
|
kind: Test {
|
||||||
test_id: Path(
|
test_id: Path(
|
||||||
"test_foo1",
|
"test_foo1",
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ impl Default for CargoConfig {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
CargoConfig {
|
CargoConfig {
|
||||||
no_default_features: false,
|
no_default_features: false,
|
||||||
all_features: true,
|
all_features: false,
|
||||||
features: Vec::new(),
|
features: Vec::new(),
|
||||||
load_out_dirs_from_check: false,
|
load_out_dirs_from_check: false,
|
||||||
target: None,
|
target: None,
|
||||||
|
|||||||
@@ -87,6 +87,9 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
|
|||||||
"ssr": true,
|
"ssr": true,
|
||||||
"onEnter": true,
|
"onEnter": true,
|
||||||
"parentModule": true,
|
"parentModule": true,
|
||||||
|
"runnables": {
|
||||||
|
"kinds": [ "cargo" ],
|
||||||
|
},
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
//! See `CargoTargetSpec`
|
//! See `CargoTargetSpec`
|
||||||
|
|
||||||
|
use ra_cfg::CfgExpr;
|
||||||
use ra_ide::{FileId, RunnableKind, TestId};
|
use ra_ide::{FileId, RunnableKind, TestId};
|
||||||
use ra_project_model::{self, ProjectWorkspace, TargetKind};
|
use ra_project_model::{self, ProjectWorkspace, TargetKind};
|
||||||
|
|
||||||
use crate::{world::WorldSnapshot, Result};
|
use crate::{world::WorldSnapshot, Result};
|
||||||
use ra_syntax::SmolStr;
|
|
||||||
|
|
||||||
/// Abstract representation of Cargo target.
|
/// Abstract representation of Cargo target.
|
||||||
///
|
///
|
||||||
@@ -21,7 +21,7 @@ impl CargoTargetSpec {
|
|||||||
pub(crate) fn runnable_args(
|
pub(crate) fn runnable_args(
|
||||||
spec: Option<CargoTargetSpec>,
|
spec: Option<CargoTargetSpec>,
|
||||||
kind: &RunnableKind,
|
kind: &RunnableKind,
|
||||||
features_needed: &Vec<SmolStr>,
|
cfgs: &[CfgExpr],
|
||||||
) -> Result<(Vec<String>, Vec<String>)> {
|
) -> Result<(Vec<String>, Vec<String>)> {
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
let mut extra_args = Vec::new();
|
let mut extra_args = Vec::new();
|
||||||
@@ -76,10 +76,14 @@ impl CargoTargetSpec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
features_needed.iter().for_each(|feature| {
|
let mut features = Vec::new();
|
||||||
|
for cfg in cfgs {
|
||||||
|
required_features(cfg, &mut features);
|
||||||
|
}
|
||||||
|
for feature in features {
|
||||||
args.push("--features".to_string());
|
args.push("--features".to_string());
|
||||||
args.push(feature.to_string());
|
args.push(feature);
|
||||||
});
|
}
|
||||||
|
|
||||||
Ok((args, extra_args))
|
Ok((args, extra_args))
|
||||||
}
|
}
|
||||||
@@ -140,3 +144,74 @@ impl CargoTargetSpec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fill minimal features needed
|
||||||
|
fn required_features(cfg_expr: &CfgExpr, features: &mut Vec<String>) {
|
||||||
|
match cfg_expr {
|
||||||
|
CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.to_string()),
|
||||||
|
CfgExpr::All(preds) => {
|
||||||
|
preds.iter().for_each(|cfg| required_features(cfg, features));
|
||||||
|
}
|
||||||
|
CfgExpr::Any(preds) => {
|
||||||
|
for cfg in preds {
|
||||||
|
let len_features = features.len();
|
||||||
|
required_features(cfg, features);
|
||||||
|
if len_features != features.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use mbe::{ast_to_token_tree, TokenMap};
|
||||||
|
use ra_cfg::parse_cfg;
|
||||||
|
use ra_syntax::{
|
||||||
|
ast::{self, AstNode},
|
||||||
|
SmolStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
|
||||||
|
let source_file = ast::SourceFile::parse(input).ok().unwrap();
|
||||||
|
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
||||||
|
ast_to_token_tree(&tt).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cfg_expr_minimal_features_needed() {
|
||||||
|
let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
|
||||||
|
let cfg_expr = parse_cfg(&subtree);
|
||||||
|
let mut min_features = vec![];
|
||||||
|
required_features(&cfg_expr, &mut min_features);
|
||||||
|
|
||||||
|
assert_eq!(min_features, vec![SmolStr::new("baz")]);
|
||||||
|
|
||||||
|
let (subtree, _) =
|
||||||
|
get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
|
||||||
|
let cfg_expr = parse_cfg(&subtree);
|
||||||
|
|
||||||
|
let mut min_features = vec![];
|
||||||
|
required_features(&cfg_expr, &mut min_features);
|
||||||
|
assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]);
|
||||||
|
|
||||||
|
let (subtree, _) =
|
||||||
|
get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
|
||||||
|
let cfg_expr = parse_cfg(&subtree);
|
||||||
|
|
||||||
|
let mut min_features = vec![];
|
||||||
|
required_features(&cfg_expr, &mut min_features);
|
||||||
|
assert_eq!(min_features, vec![SmolStr::new("baz")]);
|
||||||
|
|
||||||
|
let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
|
||||||
|
let cfg_expr = parse_cfg(&subtree);
|
||||||
|
|
||||||
|
let mut min_features = vec![];
|
||||||
|
required_features(&cfg_expr, &mut min_features);
|
||||||
|
assert!(min_features.is_empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ impl Default for Config {
|
|||||||
check: Some(FlycheckConfig::CargoCommand {
|
check: Some(FlycheckConfig::CargoCommand {
|
||||||
command: "check".to_string(),
|
command: "check".to_string(),
|
||||||
all_targets: true,
|
all_targets: true,
|
||||||
all_features: true,
|
all_features: false,
|
||||||
extra_args: Vec::new(),
|
extra_args: Vec::new(),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ use std::{collections::HashMap, path::PathBuf};
|
|||||||
|
|
||||||
use lsp_types::request::Request;
|
use lsp_types::request::Request;
|
||||||
use lsp_types::{Position, Range, TextDocumentIdentifier};
|
use lsp_types::{Position, Range, TextDocumentIdentifier};
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub enum AnalyzerStatus {}
|
pub enum AnalyzerStatus {}
|
||||||
@@ -128,7 +127,7 @@ pub enum Runnables {}
|
|||||||
impl Request for Runnables {
|
impl Request for Runnables {
|
||||||
type Params = RunnablesParams;
|
type Params = RunnablesParams;
|
||||||
type Result = Vec<Runnable>;
|
type Result = Vec<Runnable>;
|
||||||
const METHOD: &'static str = "rust-analyzer/runnables";
|
const METHOD: &'static str = "experimental/runnables";
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@@ -138,25 +137,31 @@ pub struct RunnablesParams {
|
|||||||
pub position: Option<Position>,
|
pub position: Option<Position>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must strictly correspond to the executable name
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Runnable {
|
||||||
|
pub label: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub location: Option<lsp_types::LocationLink>,
|
||||||
|
pub kind: RunnableKind,
|
||||||
|
pub args: CargoRunnable,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum RunnableKind {
|
pub enum RunnableKind {
|
||||||
Cargo,
|
Cargo,
|
||||||
Rustc,
|
|
||||||
Rustup,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Runnable {
|
pub struct CargoRunnable {
|
||||||
pub range: Range,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub label: String,
|
pub workspace_root: Option<PathBuf>,
|
||||||
pub kind: RunnableKind,
|
// command, --package and --lib stuff
|
||||||
pub args: Vec<String>,
|
pub cargo_args: Vec<String>,
|
||||||
pub extra_args: Vec<String>,
|
// stuff after --
|
||||||
pub env: FxHashMap<String, String>,
|
pub executable_args: Vec<String>,
|
||||||
pub cwd: Option<PathBuf>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum InlayHints {}
|
pub enum InlayHints {}
|
||||||
|
|||||||
@@ -17,15 +17,12 @@ use lsp_types::{
|
|||||||
SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
|
SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
|
||||||
SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit,
|
SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit,
|
||||||
};
|
};
|
||||||
use ra_cfg::CfgExpr;
|
|
||||||
use ra_ide::{
|
use ra_ide::{
|
||||||
FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
|
FileId, FilePosition, FileRange, Query, RangeInfo, RunnableKind, SearchScope, TextEdit,
|
||||||
TextEdit,
|
|
||||||
};
|
};
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
use ra_project_model::TargetKind;
|
use ra_project_model::TargetKind;
|
||||||
use ra_syntax::{AstNode, SmolStr, SyntaxKind, TextRange, TextSize};
|
use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize};
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::to_value;
|
use serde_json::to_value;
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
@@ -403,7 +400,7 @@ pub fn handle_runnables(
|
|||||||
let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?;
|
let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?;
|
||||||
for runnable in world.analysis().runnables(file_id)? {
|
for runnable in world.analysis().runnables(file_id)? {
|
||||||
if let Some(offset) = offset {
|
if let Some(offset) = offset {
|
||||||
if !runnable.range.contains_inclusive(offset) {
|
if !runnable.nav.full_range().contains_inclusive(offset) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -416,7 +413,7 @@ pub fn handle_runnables(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.push(to_lsp_runnable(&world, file_id, runnable)?);
|
res.push(to_proto::runnable(&world, file_id, runnable)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add `cargo check` and `cargo test` for the whole package
|
// Add `cargo check` and `cargo test` for the whole package
|
||||||
@@ -424,25 +421,31 @@ pub fn handle_runnables(
|
|||||||
Some(spec) => {
|
Some(spec) => {
|
||||||
for &cmd in ["check", "test"].iter() {
|
for &cmd in ["check", "test"].iter() {
|
||||||
res.push(lsp_ext::Runnable {
|
res.push(lsp_ext::Runnable {
|
||||||
range: Default::default(),
|
|
||||||
label: format!("cargo {} -p {}", cmd, spec.package),
|
label: format!("cargo {} -p {}", cmd, spec.package),
|
||||||
|
location: None,
|
||||||
kind: lsp_ext::RunnableKind::Cargo,
|
kind: lsp_ext::RunnableKind::Cargo,
|
||||||
args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()],
|
args: lsp_ext::CargoRunnable {
|
||||||
extra_args: Vec::new(),
|
workspace_root: workspace_root.map(|root| root.to_owned()),
|
||||||
env: FxHashMap::default(),
|
cargo_args: vec![
|
||||||
cwd: workspace_root.map(|root| root.to_owned()),
|
cmd.to_string(),
|
||||||
|
"--package".to_string(),
|
||||||
|
spec.package.clone(),
|
||||||
|
],
|
||||||
|
executable_args: Vec::new(),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
res.push(lsp_ext::Runnable {
|
res.push(lsp_ext::Runnable {
|
||||||
range: Default::default(),
|
|
||||||
label: "cargo check --workspace".to_string(),
|
label: "cargo check --workspace".to_string(),
|
||||||
|
location: None,
|
||||||
kind: lsp_ext::RunnableKind::Cargo,
|
kind: lsp_ext::RunnableKind::Cargo,
|
||||||
args: vec!["check".to_string(), "--workspace".to_string()],
|
args: lsp_ext::CargoRunnable {
|
||||||
extra_args: Vec::new(),
|
workspace_root: workspace_root.map(|root| root.to_owned()),
|
||||||
env: FxHashMap::default(),
|
cargo_args: vec!["check".to_string(), "--workspace".to_string()],
|
||||||
cwd: workspace_root.map(|root| root.to_owned()),
|
executable_args: Vec::new(),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -755,11 +758,11 @@ pub fn handle_code_action(
|
|||||||
if world.config.client_caps.resolve_code_action {
|
if world.config.client_caps.resolve_code_action {
|
||||||
for assist in world.analysis().unresolved_assists(&world.config.assist, frange)?.into_iter()
|
for assist in world.analysis().unresolved_assists(&world.config.assist, frange)?.into_iter()
|
||||||
{
|
{
|
||||||
res.push(to_proto::unresolved_code_action(&world, assist)?.into());
|
res.push(to_proto::unresolved_code_action(&world, assist)?);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for assist in world.analysis().resolved_assists(&world.config.assist, frange)?.into_iter() {
|
for assist in world.analysis().resolved_assists(&world.config.assist, frange)?.into_iter() {
|
||||||
res.push(to_proto::resolved_code_action(&world, assist)?.into());
|
res.push(to_proto::resolved_code_action(&world, assist)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -782,7 +785,7 @@ pub fn handle_resolve_code_action(
|
|||||||
let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
|
let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
|
||||||
|
|
||||||
for assist in world.analysis().resolved_assists(&world.config.assist, frange)?.into_iter() {
|
for assist in world.analysis().resolved_assists(&world.config.assist, frange)?.into_iter() {
|
||||||
res.push(to_proto::resolved_code_action(&world, assist)?.into());
|
res.push(to_proto::resolved_code_action(&world, assist)?);
|
||||||
}
|
}
|
||||||
Ok(res
|
Ok(res
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -833,10 +836,11 @@ pub fn handle_code_lens(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut r = to_lsp_runnable(&world, file_id, runnable)?;
|
let range = to_proto::range(&line_index, runnable.nav.range());
|
||||||
|
let r = to_proto::runnable(&world, file_id, runnable)?;
|
||||||
if world.config.lens.run {
|
if world.config.lens.run {
|
||||||
let lens = CodeLens {
|
let lens = CodeLens {
|
||||||
range: r.range,
|
range,
|
||||||
command: Some(Command {
|
command: Some(Command {
|
||||||
title: run_title.to_string(),
|
title: run_title.to_string(),
|
||||||
command: "rust-analyzer.runSingle".into(),
|
command: "rust-analyzer.runSingle".into(),
|
||||||
@@ -848,13 +852,8 @@ pub fn handle_code_lens(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if debugee && world.config.lens.debug {
|
if debugee && world.config.lens.debug {
|
||||||
if r.args[0] == "run" {
|
|
||||||
r.args[0] = "build".into();
|
|
||||||
} else {
|
|
||||||
r.args.push("--no-run".into());
|
|
||||||
}
|
|
||||||
let debug_lens = CodeLens {
|
let debug_lens = CodeLens {
|
||||||
range: r.range,
|
range,
|
||||||
command: Some(Command {
|
command: Some(Command {
|
||||||
title: "Debug".into(),
|
title: "Debug".into(),
|
||||||
command: "rust-analyzer.debugSingle".into(),
|
command: "rust-analyzer.debugSingle".into(),
|
||||||
@@ -1008,65 +1007,6 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<Dia
|
|||||||
Ok(DiagnosticTask::SetNative(file_id, diagnostics))
|
Ok(DiagnosticTask::SetNative(file_id, diagnostics))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_lsp_runnable(
|
|
||||||
world: &WorldSnapshot,
|
|
||||||
file_id: FileId,
|
|
||||||
runnable: Runnable,
|
|
||||||
) -> Result<lsp_ext::Runnable> {
|
|
||||||
let spec = CargoTargetSpec::for_file(world, file_id)?;
|
|
||||||
let target = spec.as_ref().map(|s| s.target.clone());
|
|
||||||
let mut features_needed = vec![];
|
|
||||||
for cfg_expr in &runnable.cfg_exprs {
|
|
||||||
collect_minimal_features_needed(cfg_expr, &mut features_needed);
|
|
||||||
}
|
|
||||||
let (args, extra_args) =
|
|
||||||
CargoTargetSpec::runnable_args(spec, &runnable.kind, &features_needed)?;
|
|
||||||
let line_index = world.analysis().file_line_index(file_id)?;
|
|
||||||
let label = match &runnable.kind {
|
|
||||||
RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
|
|
||||||
RunnableKind::TestMod { path } => format!("test-mod {}", path),
|
|
||||||
RunnableKind::Bench { test_id } => format!("bench {}", test_id),
|
|
||||||
RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id),
|
|
||||||
RunnableKind::Bin => {
|
|
||||||
target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(lsp_ext::Runnable {
|
|
||||||
range: to_proto::range(&line_index, runnable.range),
|
|
||||||
label,
|
|
||||||
kind: lsp_ext::RunnableKind::Cargo,
|
|
||||||
args,
|
|
||||||
extra_args,
|
|
||||||
env: {
|
|
||||||
let mut m = FxHashMap::default();
|
|
||||||
m.insert("RUST_BACKTRACE".to_string(), "short".to_string());
|
|
||||||
m
|
|
||||||
},
|
|
||||||
cwd: world.workspace_root_for(file_id).map(|root| root.to_owned()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fill minimal features needed
|
|
||||||
fn collect_minimal_features_needed(cfg_expr: &CfgExpr, features: &mut Vec<SmolStr>) {
|
|
||||||
match cfg_expr {
|
|
||||||
CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()),
|
|
||||||
CfgExpr::All(preds) => {
|
|
||||||
preds.iter().for_each(|cfg| collect_minimal_features_needed(cfg, features));
|
|
||||||
}
|
|
||||||
CfgExpr::Any(preds) => {
|
|
||||||
for cfg in preds {
|
|
||||||
let len_features = features.len();
|
|
||||||
collect_minimal_features_needed(cfg, features);
|
|
||||||
if len_features != features.len() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_inlay_hints(
|
pub fn handle_inlay_hints(
|
||||||
world: WorldSnapshot,
|
world: WorldSnapshot,
|
||||||
params: InlayHintsParams,
|
params: InlayHintsParams,
|
||||||
@@ -1203,54 +1143,3 @@ pub fn handle_semantic_tokens_range(
|
|||||||
let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
|
let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
|
||||||
Ok(Some(semantic_tokens.into()))
|
Ok(Some(semantic_tokens.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
use mbe::{ast_to_token_tree, TokenMap};
|
|
||||||
use ra_cfg::parse_cfg;
|
|
||||||
use ra_syntax::{
|
|
||||||
ast::{self, AstNode},
|
|
||||||
SmolStr,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
|
|
||||||
let source_file = ast::SourceFile::parse(input).ok().unwrap();
|
|
||||||
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
|
||||||
ast_to_token_tree(&tt).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_cfg_expr_minimal_features_needed() {
|
|
||||||
let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
|
|
||||||
let cfg_expr = parse_cfg(&subtree);
|
|
||||||
let mut min_features = vec![];
|
|
||||||
collect_minimal_features_needed(&cfg_expr, &mut min_features);
|
|
||||||
|
|
||||||
assert_eq!(min_features, vec![SmolStr::new("baz")]);
|
|
||||||
|
|
||||||
let (subtree, _) =
|
|
||||||
get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
|
|
||||||
let cfg_expr = parse_cfg(&subtree);
|
|
||||||
|
|
||||||
let mut min_features = vec![];
|
|
||||||
collect_minimal_features_needed(&cfg_expr, &mut min_features);
|
|
||||||
assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]);
|
|
||||||
|
|
||||||
let (subtree, _) =
|
|
||||||
get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
|
|
||||||
let cfg_expr = parse_cfg(&subtree);
|
|
||||||
|
|
||||||
let mut min_features = vec![];
|
|
||||||
collect_minimal_features_needed(&cfg_expr, &mut min_features);
|
|
||||||
assert_eq!(min_features, vec![SmolStr::new("baz")]);
|
|
||||||
|
|
||||||
let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
|
|
||||||
let cfg_expr = parse_cfg(&subtree);
|
|
||||||
|
|
||||||
let mut min_features = vec![];
|
|
||||||
collect_minimal_features_needed(&cfg_expr, &mut min_features);
|
|
||||||
assert!(min_features.is_empty());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ use ra_ide::{
|
|||||||
Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind,
|
Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind,
|
||||||
FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel,
|
FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel,
|
||||||
InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess,
|
InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess,
|
||||||
ResolvedAssist, Severity, SourceChange, SourceFileEdit, TextEdit,
|
ResolvedAssist, Runnable, RunnableKind, Severity, SourceChange, SourceFileEdit, TextEdit,
|
||||||
};
|
};
|
||||||
use ra_syntax::{SyntaxKind, TextRange, TextSize};
|
use ra_syntax::{SyntaxKind, TextRange, TextSize};
|
||||||
use ra_vfs::LineEndings;
|
use ra_vfs::LineEndings;
|
||||||
|
|
||||||
use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result};
|
use crate::{
|
||||||
|
cargo_target_spec::CargoTargetSpec, lsp_ext, semantic_tokens, world::WorldSnapshot, Result,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
|
pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
|
||||||
let line_col = line_index.line_col(offset);
|
let line_col = line_index.line_col(offset);
|
||||||
@@ -658,3 +660,35 @@ pub(crate) fn resolved_code_action(
|
|||||||
};
|
};
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn runnable(
|
||||||
|
world: &WorldSnapshot,
|
||||||
|
file_id: FileId,
|
||||||
|
runnable: Runnable,
|
||||||
|
) -> Result<lsp_ext::Runnable> {
|
||||||
|
let spec = CargoTargetSpec::for_file(world, file_id)?;
|
||||||
|
let target = spec.as_ref().map(|s| s.target.clone());
|
||||||
|
let (cargo_args, executable_args) =
|
||||||
|
CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?;
|
||||||
|
let label = match &runnable.kind {
|
||||||
|
RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
|
||||||
|
RunnableKind::TestMod { path } => format!("test-mod {}", path),
|
||||||
|
RunnableKind::Bench { test_id } => format!("bench {}", test_id),
|
||||||
|
RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id),
|
||||||
|
RunnableKind::Bin => {
|
||||||
|
target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let location = location_link(world, None, runnable.nav)?;
|
||||||
|
|
||||||
|
Ok(lsp_ext::Runnable {
|
||||||
|
label,
|
||||||
|
location: Some(location),
|
||||||
|
kind: lsp_ext::RunnableKind::Cargo,
|
||||||
|
args: lsp_ext::CargoRunnable {
|
||||||
|
workspace_root: world.workspace_root_for(file_id).map(|root| root.to_owned()),
|
||||||
|
cargo_args,
|
||||||
|
executable_args,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -77,28 +77,31 @@ fn foo() {
|
|||||||
RunnablesParams { text_document: server.doc_id("lib.rs"), position: None },
|
RunnablesParams { text_document: server.doc_id("lib.rs"), position: None },
|
||||||
json!([
|
json!([
|
||||||
{
|
{
|
||||||
"args": [ "test" ],
|
"args": {
|
||||||
"extraArgs": [ "foo", "--nocapture" ],
|
"cargoArgs": ["test"],
|
||||||
|
"executableArgs": ["foo", "--nocapture"],
|
||||||
|
},
|
||||||
"kind": "cargo",
|
"kind": "cargo",
|
||||||
"env": { "RUST_BACKTRACE": "short" },
|
|
||||||
"cwd": null,
|
|
||||||
"label": "test foo",
|
"label": "test foo",
|
||||||
"range": {
|
"location": {
|
||||||
|
"targetRange": {
|
||||||
"end": { "character": 1, "line": 2 },
|
"end": { "character": 1, "line": 2 },
|
||||||
"start": { "character": 0, "line": 0 }
|
"start": { "character": 0, "line": 0 }
|
||||||
|
},
|
||||||
|
"targetSelectionRange": {
|
||||||
|
"end": { "character": 6, "line": 1 },
|
||||||
|
"start": { "character": 3, "line": 1 }
|
||||||
|
},
|
||||||
|
"targetUri": "file:///[..]/lib.rs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"args": ["check", "--workspace"],
|
"args": {
|
||||||
"extraArgs": [],
|
"cargoArgs": ["check", "--workspace"],
|
||||||
|
"executableArgs": [],
|
||||||
|
},
|
||||||
"kind": "cargo",
|
"kind": "cargo",
|
||||||
"env": {},
|
"label": "cargo check --workspace"
|
||||||
"cwd": null,
|
|
||||||
"label": "cargo check --workspace",
|
|
||||||
"range": {
|
|
||||||
"end": { "character": 0, "line": 0 },
|
|
||||||
"start": { "character": 0, "line": 0 }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
@@ -139,40 +142,42 @@ fn main() {}
|
|||||||
RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
|
RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
|
||||||
json!([
|
json!([
|
||||||
{
|
{
|
||||||
"args": [ "test", "--package", "foo", "--test", "spam" ],
|
"args": {
|
||||||
"extraArgs": [ "test_eggs", "--exact", "--nocapture" ],
|
"cargoArgs": ["test", "--package", "foo", "--test", "spam"],
|
||||||
|
"executableArgs": ["test_eggs", "--exact", "--nocapture"],
|
||||||
|
"workspaceRoot": server.path().join("foo")
|
||||||
|
},
|
||||||
"kind": "cargo",
|
"kind": "cargo",
|
||||||
"env": { "RUST_BACKTRACE": "short" },
|
|
||||||
"label": "test test_eggs",
|
"label": "test test_eggs",
|
||||||
"range": {
|
"location": {
|
||||||
|
"targetRange": {
|
||||||
"end": { "character": 17, "line": 1 },
|
"end": { "character": 17, "line": 1 },
|
||||||
"start": { "character": 0, "line": 0 }
|
"start": { "character": 0, "line": 0 }
|
||||||
},
|
},
|
||||||
"cwd": server.path().join("foo")
|
"targetSelectionRange": {
|
||||||
|
"end": { "character": 12, "line": 1 },
|
||||||
|
"start": { "character": 3, "line": 1 }
|
||||||
|
},
|
||||||
|
"targetUri": "file:///[..]/tests/spam.rs"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"args": [ "check", "--package", "foo" ],
|
"args": {
|
||||||
"extraArgs": [],
|
"cargoArgs": ["check", "--package", "foo"],
|
||||||
"kind": "cargo",
|
"executableArgs": [],
|
||||||
"env": {},
|
"workspaceRoot": server.path().join("foo")
|
||||||
"label": "cargo check -p foo",
|
|
||||||
"range": {
|
|
||||||
"end": { "character": 0, "line": 0 },
|
|
||||||
"start": { "character": 0, "line": 0 }
|
|
||||||
},
|
},
|
||||||
"cwd": server.path().join("foo")
|
"kind": "cargo",
|
||||||
|
"label": "cargo check -p foo"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"args": [ "test", "--package", "foo" ],
|
"args": {
|
||||||
"extraArgs": [],
|
"cargoArgs": ["test", "--package", "foo"],
|
||||||
"kind": "cargo",
|
"executableArgs": [],
|
||||||
"env": {},
|
"workspaceRoot": server.path().join("foo")
|
||||||
"label": "cargo test -p foo",
|
|
||||||
"range": {
|
|
||||||
"end": { "character": 0, "line": 0 },
|
|
||||||
"start": { "character": 0, "line": 0 }
|
|
||||||
},
|
},
|
||||||
"cwd": server.path().join("foo")
|
"kind": "cargo",
|
||||||
|
"label": "cargo test -p foo"
|
||||||
}
|
}
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -311,6 +311,50 @@ Moreover, it would be cool if editors didn't need to implement even basic langua
|
|||||||
This is how `SelectionRange` request works.
|
This is how `SelectionRange` request works.
|
||||||
* Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs?
|
* Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs?
|
||||||
|
|
||||||
|
## Runnables
|
||||||
|
|
||||||
|
**Issue:** https://github.com/microsoft/language-server-protocol/issues/944
|
||||||
|
|
||||||
|
**Server Capability:** `{ "runnables": { "kinds": string[] } }`
|
||||||
|
|
||||||
|
This request is send from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`).
|
||||||
|
|
||||||
|
**Method:** `experimental/runnables`
|
||||||
|
|
||||||
|
**Request:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface RunnablesParams {
|
||||||
|
textDocument: TextDocumentIdentifier;
|
||||||
|
/// If null, compute runnables for the whole file.
|
||||||
|
position?: Position;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:** `Runnable[]`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface Runnable {
|
||||||
|
label: string;
|
||||||
|
/// If this Runnable is associated with a specific function/module, etc, the location of this item
|
||||||
|
location?: LocationLink;
|
||||||
|
/// Running things is necessary technology specific, `kind` needs to be advertised via server capabilities,
|
||||||
|
// the type of `args` is specific to `kind`. The actual running is handled by the client.
|
||||||
|
kind: string;
|
||||||
|
args: any;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
rust-analyzer supports only one `kind`, `"cargo"`. The `args` for `"cargo"` look like this:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
workspaceRoot?: string;
|
||||||
|
cargoArgs: string[];
|
||||||
|
executableArgs: string[];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Analyzer Status
|
## Analyzer Status
|
||||||
|
|
||||||
**Method:** `rust-analyzer/analyzerStatus`
|
**Method:** `rust-analyzer/analyzerStatus`
|
||||||
@@ -399,39 +443,3 @@ interface InlayHint {
|
|||||||
label: string,
|
label: string,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Runnables
|
|
||||||
|
|
||||||
**Method:** `rust-analyzer/runnables`
|
|
||||||
|
|
||||||
This request is send from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`).
|
|
||||||
Note that we plan to move this request to `experimental/runnables`, as it is not really Rust-specific, but the current API is not necessary the right one.
|
|
||||||
Upstream issue: https://github.com/microsoft/language-server-protocol/issues/944
|
|
||||||
|
|
||||||
**Request:**
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface RunnablesParams {
|
|
||||||
textDocument: TextDocumentIdentifier;
|
|
||||||
/// If null, compute runnables for the whole file.
|
|
||||||
position?: Position;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response:** `Runnable[]`
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface Runnable {
|
|
||||||
/// The range this runnable is applicable for.
|
|
||||||
range: lc.Range;
|
|
||||||
/// The label to show in the UI.
|
|
||||||
label: string;
|
|
||||||
/// The following fields describe a process to spawn.
|
|
||||||
kind: "cargo" | "rustc" | "rustup";
|
|
||||||
args: string[];
|
|
||||||
/// Args for cargo after `--`.
|
|
||||||
extraArgs: string[];
|
|
||||||
env: { [key: string]: string };
|
|
||||||
cwd: string | null;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -238,7 +238,7 @@
|
|||||||
},
|
},
|
||||||
"rust-analyzer.cargo.allFeatures": {
|
"rust-analyzer.cargo.allFeatures": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true,
|
"default": false,
|
||||||
"description": "Activate all available features"
|
"description": "Activate all available features"
|
||||||
},
|
},
|
||||||
"rust-analyzer.cargo.features": {
|
"rust-analyzer.cargo.features": {
|
||||||
@@ -319,7 +319,7 @@
|
|||||||
},
|
},
|
||||||
"rust-analyzer.checkOnSave.allFeatures": {
|
"rust-analyzer.checkOnSave.allFeatures": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true,
|
"default": false,
|
||||||
"markdownDescription": "Check with all features (will be passed as `--all-features`)"
|
"markdownDescription": "Check with all features (will be passed as `--all-features`)"
|
||||||
},
|
},
|
||||||
"rust-analyzer.inlayHints.enable": {
|
"rust-analyzer.inlayHints.enable": {
|
||||||
|
|||||||
@@ -114,8 +114,8 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<v
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getDebugExecutable(runnable: ra.Runnable): Promise<string> {
|
async function getDebugExecutable(runnable: ra.Runnable): Promise<string> {
|
||||||
const cargo = new Cargo(runnable.cwd || '.', debugOutput);
|
const cargo = new Cargo(runnable.args.workspaceRoot || '.', debugOutput);
|
||||||
const executable = await cargo.executableFromArgs(runnable.args);
|
const executable = await cargo.executableFromArgs(runnable.args.cargoArgs);
|
||||||
|
|
||||||
// if we are here, there were no compilation errors.
|
// if we are here, there were no compilation errors.
|
||||||
return executable;
|
return executable;
|
||||||
@@ -127,8 +127,8 @@ function getLldbDebugConfig(runnable: ra.Runnable, executable: string, sourceFil
|
|||||||
request: "launch",
|
request: "launch",
|
||||||
name: runnable.label,
|
name: runnable.label,
|
||||||
program: executable,
|
program: executable,
|
||||||
args: runnable.extraArgs,
|
args: runnable.args.executableArgs,
|
||||||
cwd: runnable.cwd,
|
cwd: runnable.args.workspaceRoot,
|
||||||
sourceMap: sourceFileMap,
|
sourceMap: sourceFileMap,
|
||||||
sourceLanguages: ["rust"]
|
sourceLanguages: ["rust"]
|
||||||
};
|
};
|
||||||
@@ -140,8 +140,8 @@ function getCppvsDebugConfig(runnable: ra.Runnable, executable: string, sourceFi
|
|||||||
request: "launch",
|
request: "launch",
|
||||||
name: runnable.label,
|
name: runnable.label,
|
||||||
program: executable,
|
program: executable,
|
||||||
args: runnable.extraArgs,
|
args: runnable.args.executableArgs,
|
||||||
cwd: runnable.cwd,
|
cwd: runnable.args.workspaceRoot,
|
||||||
sourceFileMap: sourceFileMap,
|
sourceFileMap: sourceFileMap,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,18 +53,17 @@ export interface RunnablesParams {
|
|||||||
position: lc.Position | null;
|
position: lc.Position | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RunnableKind = "cargo" | "rustc" | "rustup";
|
|
||||||
|
|
||||||
export interface Runnable {
|
export interface Runnable {
|
||||||
range: lc.Range;
|
|
||||||
label: string;
|
label: string;
|
||||||
kind: RunnableKind;
|
location?: lc.LocationLink;
|
||||||
args: string[];
|
kind: "cargo";
|
||||||
extraArgs: string[];
|
args: {
|
||||||
env: { [key: string]: string };
|
workspaceRoot?: string;
|
||||||
cwd: string | null;
|
cargoArgs: string[];
|
||||||
|
executableArgs: string[];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>("rust-analyzer/runnables");
|
export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>("experimental/runnables");
|
||||||
|
|
||||||
export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint | InlayHint.ChainingHint;
|
export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint | InlayHint.ChainingHint;
|
||||||
|
|
||||||
|
|||||||
@@ -103,18 +103,27 @@ interface CargoTaskDefinition extends vscode.TaskDefinition {
|
|||||||
env?: { [key: string]: string };
|
env?: { [key: string]: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createTask(spec: ra.Runnable): vscode.Task {
|
export function createTask(runnable: ra.Runnable): vscode.Task {
|
||||||
const TASK_SOURCE = 'Rust';
|
const TASK_SOURCE = 'Rust';
|
||||||
|
|
||||||
|
let command;
|
||||||
|
switch (runnable.kind) {
|
||||||
|
case "cargo": command = toolchain.getPathForExecutable("cargo");
|
||||||
|
}
|
||||||
|
const args = runnable.args.cargoArgs;
|
||||||
|
if (runnable.args.executableArgs.length > 0) {
|
||||||
|
args.push('--', ...runnable.args.executableArgs);
|
||||||
|
}
|
||||||
const definition: CargoTaskDefinition = {
|
const definition: CargoTaskDefinition = {
|
||||||
type: 'cargo',
|
type: 'cargo',
|
||||||
label: spec.label,
|
label: runnable.label,
|
||||||
command: toolchain.getPathForExecutable(spec.kind),
|
command,
|
||||||
args: spec.extraArgs ? [...spec.args, '--', ...spec.extraArgs] : spec.args,
|
args,
|
||||||
env: Object.assign({}, process.env, spec.env),
|
env: Object.assign({}, process.env as { [key: string]: string }, { "RUST_BACKTRACE": "short" }),
|
||||||
};
|
};
|
||||||
|
|
||||||
const execOption: vscode.ShellExecutionOptions = {
|
const execOption: vscode.ShellExecutionOptions = {
|
||||||
cwd: spec.cwd || '.',
|
cwd: runnable.args.workspaceRoot || '.',
|
||||||
env: definition.env,
|
env: definition.env,
|
||||||
};
|
};
|
||||||
const exec = new vscode.ShellExecution(
|
const exec = new vscode.ShellExecution(
|
||||||
|
|||||||
Reference in New Issue
Block a user