4710: New runnables r=matklad a=matklad

bors d=@vsrs

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2020-06-02 20:44:56 +00:00 committed by GitHub
commit 2f6ab77708
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 427 additions and 225 deletions

View File

@ -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,
)
} }
} }

View File

@ -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",

View File

@ -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" ],
},
})), })),
} }
} }

View File

@ -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 {}
@ -111,7 +110,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)]
@ -121,25 +120,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 {}

View File

@ -23,7 +23,6 @@ use ra_ide::{
use ra_prof::profile; use ra_prof::profile;
use ra_project_model::TargetKind; use ra_project_model::TargetKind;
use ra_syntax::{AstNode, 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;
@ -401,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;
} }
} }
@ -422,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(),
},
}); });
} }
} }
@ -782,10 +787,11 @@ pub fn handle_code_lens(
} }
}; };
let mut r = to_proto::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(),
@ -797,13 +803,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(),

View File

@ -8,7 +8,6 @@ use ra_ide::{
}; };
use ra_syntax::{SyntaxKind, TextRange, TextSize}; use ra_syntax::{SyntaxKind, TextRange, TextSize};
use ra_vfs::LineEndings; use ra_vfs::LineEndings;
use rustc_hash::FxHashMap;
use crate::{ use crate::{
cargo_target_spec::CargoTargetSpec, lsp_ext, semantic_tokens, world::WorldSnapshot, Result, cargo_target_spec::CargoTargetSpec, lsp_ext, semantic_tokens, world::WorldSnapshot, Result,
@ -638,9 +637,8 @@ pub(crate) fn runnable(
) -> Result<lsp_ext::Runnable> { ) -> Result<lsp_ext::Runnable> {
let spec = CargoTargetSpec::for_file(world, file_id)?; let spec = CargoTargetSpec::for_file(world, file_id)?;
let target = spec.as_ref().map(|s| s.target.clone()); let target = spec.as_ref().map(|s| s.target.clone());
let (args, extra_args) = let (cargo_args, executable_args) =
CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?; CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?;
let line_index = world.analysis().file_line_index(file_id)?;
let label = match &runnable.kind { let label = match &runnable.kind {
RunnableKind::Test { test_id, .. } => format!("test {}", test_id), RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
RunnableKind::TestMod { path } => format!("test-mod {}", path), RunnableKind::TestMod { path } => format!("test-mod {}", path),
@ -650,18 +648,16 @@ pub(crate) fn runnable(
target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t)) target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
} }
}; };
let location = location_link(world, None, runnable.nav)?;
Ok(lsp_ext::Runnable { Ok(lsp_ext::Runnable {
range: range(&line_index, runnable.range),
label, label,
location: Some(location),
kind: lsp_ext::RunnableKind::Cargo, kind: lsp_ext::RunnableKind::Cargo,
args, args: lsp_ext::CargoRunnable {
extra_args, workspace_root: world.workspace_root_for(file_id).map(|root| root.to_owned()),
env: { cargo_args,
let mut m = FxHashMap::default(); executable_args,
m.insert("RUST_BACKTRACE".to_string(), "short".to_string());
m
}, },
cwd: world.workspace_root_for(file_id).map(|root| root.to_owned()),
}) })
} }

View File

@ -76,30 +76,33 @@ fn foo() {
server.request::<Runnables>( server.request::<Runnables>(
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"],
"kind": "cargo", "executableArgs": ["foo", "--nocapture"],
"env": { "RUST_BACKTRACE": "short" }, },
"cwd": null, "kind": "cargo",
"label": "test foo", "label": "test foo",
"range": { "location": {
"end": { "character": 1, "line": 2 }, "targetRange": {
"start": { "character": 0, "line": 0 } "end": { "character": 1, "line": 2 },
"start": { "character": 0, "line": 0 }
},
"targetSelectionRange": {
"end": { "character": 6, "line": 1 },
"start": { "character": 3, "line": 1 }
},
"targetUri": "file:///[..]/lib.rs"
}
},
{
"args": {
"cargoArgs": ["check", "--workspace"],
"executableArgs": [],
},
"kind": "cargo",
"label": "cargo check --workspace"
} }
},
{
"args": ["check", "--workspace"],
"extraArgs": [],
"kind": "cargo",
"env": {},
"cwd": null,
"label": "cargo check --workspace",
"range": {
"end": { "character": 0, "line": 0 },
"start": { "character": 0, "line": 0 }
}
}
]), ]),
); );
} }
@ -138,42 +141,44 @@ fn main() {}
server.request::<Runnables>( server.request::<Runnables>(
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"],
"kind": "cargo", "executableArgs": ["test_eggs", "--exact", "--nocapture"],
"env": { "RUST_BACKTRACE": "short" }, "workspaceRoot": server.path().join("foo")
"label": "test test_eggs", },
"range": { "kind": "cargo",
"label": "test test_eggs",
"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 }
"args": [ "check", "--package", "foo" ],
"extraArgs": [],
"kind": "cargo",
"env": {},
"label": "cargo check -p foo",
"range": {
"end": { "character": 0, "line": 0 },
"start": { "character": 0, "line": 0 }
}, },
"cwd": server.path().join("foo") "targetUri": "file:///[..]/tests/spam.rs"
},
{
"args": [ "test", "--package", "foo" ],
"extraArgs": [],
"kind": "cargo",
"env": {},
"label": "cargo test -p foo",
"range": {
"end": { "character": 0, "line": 0 },
"start": { "character": 0, "line": 0 }
},
"cwd": server.path().join("foo")
} }
},
{
"args": {
"cargoArgs": ["check", "--package", "foo"],
"executableArgs": [],
"workspaceRoot": server.path().join("foo")
},
"kind": "cargo",
"label": "cargo check -p foo"
},
{
"args": {
"cargoArgs": ["test", "--package", "foo"],
"executableArgs": [],
"workspaceRoot": server.path().join("foo")
},
"kind": "cargo",
"label": "cargo test -p foo"
}
]), ]),
); );
} }

View File

@ -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;
}
```

View File

@ -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,
}; };
} }

View File

@ -46,18 +46,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;

View File

@ -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(