From 31f282636bb1b1d701d41f7c7fedb11a5511cabd Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 2 Jun 2020 16:30:26 +0200 Subject: Minor --- crates/rust-analyzer/src/cargo_target_spec.rs | 85 ++++++++++++++++-- crates/rust-analyzer/src/main_loop/handlers.rs | 120 +------------------------ crates/rust-analyzer/src/to_proto.rs | 44 ++++++++- 3 files changed, 125 insertions(+), 124 deletions(-) diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs index 441fb61df..008518a08 100644 --- a/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/crates/rust-analyzer/src/cargo_target_spec.rs @@ -1,10 +1,10 @@ //! See `CargoTargetSpec` +use ra_cfg::CfgExpr; use ra_ide::{FileId, RunnableKind, TestId}; use ra_project_model::{self, ProjectWorkspace, TargetKind}; use crate::{world::WorldSnapshot, Result}; -use ra_syntax::SmolStr; /// Abstract representation of Cargo target. /// @@ -21,7 +21,7 @@ impl CargoTargetSpec { pub(crate) fn runnable_args( spec: Option, kind: &RunnableKind, - features_needed: &Vec, + cfgs: &[CfgExpr], ) -> Result<(Vec, Vec)> { let mut 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(feature.to_string()); - }); + args.push(feature); + } Ok((args, extra_args)) } @@ -140,3 +144,74 @@ impl CargoTargetSpec { } } } + +/// Fill minimal features needed +fn required_features(cfg_expr: &CfgExpr, features: &mut Vec) { + 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()); + } +} diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index bc7c7f1ef..410c654ab 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -17,14 +17,12 @@ use lsp_types::{ SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, }; -use ra_cfg::CfgExpr; use ra_ide::{ - FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, - TextEdit, + FileId, FilePosition, FileRange, Query, RangeInfo, RunnableKind, SearchScope, TextEdit, }; use ra_prof::profile; 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_json::to_value; @@ -416,7 +414,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 @@ -784,7 +782,7 @@ pub fn handle_code_lens( } }; - let mut r = to_lsp_runnable(&world, file_id, runnable)?; + let mut r = to_proto::runnable(&world, file_id, runnable)?; if world.config.lens.run { let lens = CodeLens { range: r.range, @@ -959,65 +957,6 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result Result { - 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) { - 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( world: WorldSnapshot, params: InlayHintsParams, @@ -1154,54 +1093,3 @@ pub fn handle_semantic_tokens_range( let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); 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()); - } -} diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 2fbbb4e63..66144fe24 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -3,13 +3,16 @@ use ra_db::{FileId, FileRange}; use ra_ide::{ Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel, - InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity, - SourceChange, SourceFileEdit, TextEdit, + InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Runnable, + RunnableKind, Severity, SourceChange, SourceFileEdit, TextEdit, }; use ra_syntax::{SyntaxKind, TextRange, TextSize}; use ra_vfs::LineEndings; +use rustc_hash::FxHashMap; -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 { let line_col = line_index.line_col(offset); @@ -627,3 +630,38 @@ pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result Result { + let spec = CargoTargetSpec::for_file(world, file_id)?; + let target = spec.as_ref().map(|s| s.target.clone()); + let (args, extra_args) = + CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?; + 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: 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()), + }) +} -- cgit v1.2.3 From 03039821195c9d9c4bbc1e4cbddb6378c43a6c52 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 2 Jun 2020 17:22:23 +0200 Subject: New runnables API --- crates/ra_ide/src/display/navigation_target.rs | 66 +++++++++----------------- crates/ra_ide/src/runnables.rs | 25 +++++----- crates/rust-analyzer/src/lsp_ext.rs | 28 ++++++----- crates/rust-analyzer/src/main_loop/handlers.rs | 41 ++++++++-------- crates/rust-analyzer/src/to_proto.rs | 18 +++---- editors/code/src/debug.ts | 12 ++--- editors/code/src/lsp_ext.ts | 15 +++--- editors/code/src/run.ts | 21 +++++--- 8 files changed, 108 insertions(+), 118 deletions(-) diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs index 5da28edd2..c7bb1e69f 100644 --- a/crates/ra_ide/src/display/navigation_target.rs +++ b/crates/ra_ide/src/display/navigation_target.rs @@ -92,15 +92,16 @@ impl NavigationTarget { let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); if let Some(src) = module.declaration_source(db) { 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, name, None, frange.range, 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) } @@ -130,11 +131,9 @@ impl NavigationTarget { } /// Allows `NavigationTarget` to be created from a `NameOwner` - fn from_named( + pub(crate) fn from_named( db: &RootDatabase, node: InFile<&dyn ast::NameOwner>, - docs: Option, - description: Option, ) -> NavigationTarget { //FIXME: use `_` instead of empty string let name = node.value.name().map(|it| it.text().clone()).unwrap_or_default(); @@ -148,8 +147,6 @@ impl NavigationTarget { focus_range, frange.range, node.value.syntax().kind(), - docs, - description, ) } @@ -159,8 +156,6 @@ impl NavigationTarget { focus_range: Option, full_range: TextRange, kind: SyntaxKind, - docs: Option, - description: Option, ) -> NavigationTarget { NavigationTarget { file_id, @@ -169,8 +164,8 @@ impl NavigationTarget { full_range, focus_range, container_name: None, - description, - docs, + description: None, + docs: None, } } } @@ -238,12 +233,11 @@ where { fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { let src = self.source(db); - NavigationTarget::from_named( - db, - src.as_ref().map(|it| it as &dyn ast::NameOwner), - src.value.doc_comment_text(), - src.value.short_label(), - ) + let mut res = + NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner)); + res.docs = src.value.doc_comment_text(); + res.description = src.value.short_label(); + res } } @@ -258,15 +252,7 @@ impl ToNav for hir::Module { } }; let frange = original_range(db, src.with_value(syntax)); - NavigationTarget::from_syntax( - frange.file_id, - name, - focus, - frange.range, - syntax.kind(), - None, - None, - ) + NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, syntax.kind()) } } @@ -285,8 +271,6 @@ impl ToNav for hir::ImplDef { None, frange.range, src.value.syntax().kind(), - None, - None, ) } } @@ -296,12 +280,12 @@ impl ToNav for hir::Field { let src = self.source(db); match &src.value { - FieldSource::Named(it) => NavigationTarget::from_named( - db, - src.with_value(it), - it.doc_comment_text(), - it.short_label(), - ), + FieldSource::Named(it) => { + let mut res = NavigationTarget::from_named(db, src.with_value(it)); + res.docs = it.doc_comment_text(); + res.description = it.short_label(); + res + } FieldSource::Pos(it) => { let frange = original_range(db, src.with_value(it.syntax())); NavigationTarget::from_syntax( @@ -310,8 +294,6 @@ impl ToNav for hir::Field { None, frange.range, it.syntax().kind(), - None, - None, ) } } @@ -322,12 +304,10 @@ impl ToNav for hir::MacroDef { fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { let src = self.source(db); log::debug!("nav target {:#?}", src.value.syntax()); - NavigationTarget::from_named( - db, - src.as_ref().map(|it| it as &dyn ast::NameOwner), - src.value.doc_comment_text(), - None, - ) + let mut res = + NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner)); + res.docs = src.value.doc_comment_text(); + res } } diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 286d45eee..9239ca61b 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -1,19 +1,19 @@ +use std::fmt; + use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; use itertools::Itertools; +use ra_cfg::CfgExpr; use ra_ide_db::RootDatabase; use ra_syntax::{ - ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, - match_ast, SyntaxNode, TextRange, + ast::{self, AstNode, AttrsOwner, DocCommentsOwner, ModuleItemOwner, NameOwner}, + match_ast, SyntaxNode, }; -use crate::FileId; -use ast::DocCommentsOwner; -use ra_cfg::CfgExpr; -use std::fmt::Display; +use crate::{display::ToNav, FileId, NavigationTarget}; #[derive(Debug)] pub struct Runnable { - pub range: TextRange, + pub nav: NavigationTarget, pub kind: RunnableKind, pub cfg_exprs: Vec, } @@ -24,8 +24,8 @@ pub enum TestId { Path(String), } -impl Display for TestId { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { +impl fmt::Display for TestId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { TestId::Name(name) => write!(f, "{}", name), TestId::Path(path) => write!(f, "{}", path), @@ -131,7 +131,8 @@ fn runnable_fn( let cfg_exprs = 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)] @@ -183,7 +184,6 @@ fn runnable_mod( if !has_test_function { return None; } - let range = module.syntax().text_range(); let module_def = sema.to_def(&module)?; let path = module_def @@ -197,7 +197,8 @@ fn runnable_mod( let cfg_exprs = 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)] diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 173c23b9e..9381f75d3 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -4,7 +4,6 @@ use std::{collections::HashMap, path::PathBuf}; use lsp_types::request::Request; use lsp_types::{Position, Range, TextDocumentIdentifier}; -use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; pub enum AnalyzerStatus {} @@ -121,25 +120,30 @@ pub struct RunnablesParams { pub position: Option, } -// 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, + pub kind: RunnableKind, + pub args: CargoRunnable, +} + #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "lowercase")] pub enum RunnableKind { Cargo, - Rustc, - Rustup, } #[derive(Deserialize, Serialize, Debug)] #[serde(rename_all = "camelCase")] -pub struct Runnable { - pub range: Range, - pub label: String, - pub kind: RunnableKind, - pub args: Vec, - pub extra_args: Vec, - pub env: FxHashMap, - pub cwd: Option, +pub struct CargoRunnable { + pub workspace_root: Option, + // command, --package and --lib stuff + pub cargo_args: Vec, + // stuff after -- + pub executable_args: Vec, } pub enum InlayHints {} diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 410c654ab..7fd691764 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -23,7 +23,6 @@ use ra_ide::{ use ra_prof::profile; use ra_project_model::TargetKind; use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; -use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; use serde_json::to_value; use stdx::format_to; @@ -401,7 +400,7 @@ pub fn handle_runnables( let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; for runnable in world.analysis().runnables(file_id)? { if let Some(offset) = offset { - if !runnable.range.contains_inclusive(offset) { + if !runnable.nav.full_range().contains_inclusive(offset) { continue; } } @@ -422,25 +421,31 @@ pub fn handle_runnables( Some(spec) => { for &cmd in ["check", "test"].iter() { res.push(lsp_ext::Runnable { - range: Default::default(), label: format!("cargo {} -p {}", cmd, spec.package), + location: None, kind: lsp_ext::RunnableKind::Cargo, - args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()], - extra_args: Vec::new(), - env: FxHashMap::default(), - cwd: workspace_root.map(|root| root.to_owned()), + args: lsp_ext::CargoRunnable { + workspace_root: workspace_root.map(|root| root.to_owned()), + cargo_args: vec![ + cmd.to_string(), + "--package".to_string(), + spec.package.clone(), + ], + executable_args: Vec::new(), + }, }) } } None => { res.push(lsp_ext::Runnable { - range: Default::default(), label: "cargo check --workspace".to_string(), + location: None, kind: lsp_ext::RunnableKind::Cargo, - args: vec!["check".to_string(), "--workspace".to_string()], - extra_args: Vec::new(), - env: FxHashMap::default(), - cwd: workspace_root.map(|root| root.to_owned()), + args: lsp_ext::CargoRunnable { + workspace_root: workspace_root.map(|root| root.to_owned()), + cargo_args: vec!["check".to_string(), "--workspace".to_string()], + 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 { let lens = CodeLens { - range: r.range, + range, command: Some(Command { title: run_title.to_string(), command: "rust-analyzer.runSingle".into(), @@ -797,13 +803,8 @@ pub fn handle_code_lens( } 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 { - range: r.range, + range, command: Some(Command { title: "Debug".into(), command: "rust-analyzer.debugSingle".into(), diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 66144fe24..85304aa87 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -8,7 +8,6 @@ use ra_ide::{ }; use ra_syntax::{SyntaxKind, TextRange, TextSize}; use ra_vfs::LineEndings; -use rustc_hash::FxHashMap; use crate::{ cargo_target_spec::CargoTargetSpec, lsp_ext, semantic_tokens, world::WorldSnapshot, Result, @@ -638,9 +637,8 @@ pub(crate) fn runnable( ) -> Result { let spec = CargoTargetSpec::for_file(world, file_id)?; 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)?; - 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), @@ -650,18 +648,16 @@ pub(crate) fn runnable( target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t)) } }; + let location = location_link(world, None, runnable.nav)?; Ok(lsp_ext::Runnable { - range: range(&line_index, runnable.range), label, + location: Some(location), kind: lsp_ext::RunnableKind::Cargo, - args, - extra_args, - env: { - let mut m = FxHashMap::default(); - m.insert("RUST_BACKTRACE".to_string(), "short".to_string()); - m + args: lsp_ext::CargoRunnable { + workspace_root: world.workspace_root_for(file_id).map(|root| root.to_owned()), + cargo_args, + executable_args, }, - cwd: world.workspace_root_for(file_id).map(|root| root.to_owned()), }) } diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index 1e421d407..a0c9b3ab2 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -114,8 +114,8 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise { - const cargo = new Cargo(runnable.cwd || '.', debugOutput); - const executable = await cargo.executableFromArgs(runnable.args); + const cargo = new Cargo(runnable.args.workspaceRoot || '.', debugOutput); + const executable = await cargo.executableFromArgs(runnable.args.cargoArgs); // if we are here, there were no compilation errors. return executable; @@ -127,8 +127,8 @@ function getLldbDebugConfig(runnable: ra.Runnable, executable: string, sourceFil request: "launch", name: runnable.label, program: executable, - args: runnable.extraArgs, - cwd: runnable.cwd, + args: runnable.args.executableArgs, + cwd: runnable.args.workspaceRoot, sourceMap: sourceFileMap, sourceLanguages: ["rust"] }; @@ -140,8 +140,8 @@ function getCppvsDebugConfig(runnable: ra.Runnable, executable: string, sourceFi request: "launch", name: runnable.label, program: executable, - args: runnable.extraArgs, - cwd: runnable.cwd, + args: runnable.args.executableArgs, + cwd: runnable.args.workspaceRoot, sourceFileMap: sourceFileMap, }; } diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 3e0b60699..73d573678 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -46,16 +46,15 @@ export interface RunnablesParams { position: lc.Position | null; } -export type RunnableKind = "cargo" | "rustc" | "rustup"; - export interface Runnable { - range: lc.Range; label: string; - kind: RunnableKind; - args: string[]; - extraArgs: string[]; - env: { [key: string]: string }; - cwd: string | null; + location?: lc.LocationLink; + kind: "cargo"; + args: { + workspaceRoot?: string; + cargoArgs: string[]; + executableArgs: string[]; + }; } export const runnables = new lc.RequestType("rust-analyzer/runnables"); diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index 5fc4f8e41..5c790741f 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts @@ -103,18 +103,27 @@ interface CargoTaskDefinition extends vscode.TaskDefinition { env?: { [key: string]: string }; } -export function createTask(spec: ra.Runnable): vscode.Task { +export function createTask(runnable: ra.Runnable): vscode.Task { 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 = { type: 'cargo', - label: spec.label, - command: toolchain.getPathForExecutable(spec.kind), - args: spec.extraArgs ? [...spec.args, '--', ...spec.extraArgs] : spec.args, - env: Object.assign({}, process.env, spec.env), + label: runnable.label, + command, + args, + env: Object.assign({}, process.env as { [key: string]: string }, { "RUST_BACKTRACE": "short" }), }; const execOption: vscode.ShellExecutionOptions = { - cwd: spec.cwd || '.', + cwd: runnable.args.workspaceRoot || '.', env: definition.env, }; const exec = new vscode.ShellExecution( -- cgit v1.2.3 From a83ab820a4633bac718ee0fd11f06d1b3142be6b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 2 Jun 2020 17:34:18 +0200 Subject: Spec better runnables --- crates/rust-analyzer/src/caps.rs | 3 ++ crates/rust-analyzer/src/lsp_ext.rs | 2 +- docs/dev/lsp-extensions.md | 80 ++++++++++++++++++++----------------- editors/code/src/lsp_ext.ts | 2 +- 4 files changed, 49 insertions(+), 38 deletions(-) diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index 345693524..673795e78 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs @@ -87,6 +87,9 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti "ssr": true, "onEnter": true, "parentModule": true, + "runnables": { + "kinds": [ "cargo" ], + }, })), } } diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 9381f75d3..5fa1eba1c 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -110,7 +110,7 @@ pub enum Runnables {} impl Request for Runnables { type Params = RunnablesParams; type Result = Vec; - const METHOD: &'static str = "rust-analyzer/runnables"; + const METHOD: &'static str = "experimental/runnables"; } #[derive(Serialize, Deserialize, Debug)] diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index b7237ee90..647cf6107 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -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. * 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 **Method:** `rust-analyzer/analyzerStatus` @@ -399,39 +443,3 @@ interface InlayHint { 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; -} -``` diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 73d573678..c51acfccb 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -56,7 +56,7 @@ export interface Runnable { executableArgs: string[]; }; } -export const runnables = new lc.RequestType("rust-analyzer/runnables"); +export const runnables = new lc.RequestType("experimental/runnables"); export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint | InlayHint.ChainingHint; -- cgit v1.2.3 From bc3db7c1ded0db2d3804b5ac3e5c35dd53350228 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 2 Jun 2020 18:02:58 +0200 Subject: Fix tests --- crates/ra_ide/src/runnables.rs | 225 +++++++++++++++++++++++-- crates/rust-analyzer/src/lsp_ext.rs | 1 + crates/rust-analyzer/tests/heavy_tests/main.rs | 113 +++++++------ 3 files changed, 270 insertions(+), 69 deletions(-) diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 9239ca61b..f32ce0d22 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs @@ -228,12 +228,38 @@ mod tests { @r###" [ 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, cfg_exprs: [], }, 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 { test_id: Path( "test_foo", @@ -245,7 +271,20 @@ mod tests { cfg_exprs: [], }, 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 { test_id: Path( "test_foo", @@ -280,12 +319,38 @@ mod tests { @r###" [ 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, cfg_exprs: [], }, 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 { test_id: Path( "foo", @@ -320,12 +385,38 @@ mod tests { @r###" [ 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, cfg_exprs: [], }, 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 { test_id: Path( "Data::foo", @@ -355,14 +446,40 @@ mod tests { @r###" [ 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 { path: "test_mod", }, cfg_exprs: [], }, 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 { test_id: Path( "test_mod::test_foo1", @@ -397,14 +514,40 @@ mod tests { @r###" [ 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 { path: "foo::test_mod", }, cfg_exprs: [], }, 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 { test_id: Path( "foo::test_mod::test_foo1", @@ -441,14 +584,40 @@ mod tests { @r###" [ 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 { path: "foo::bar::test_mod", }, cfg_exprs: [], }, 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 { test_id: Path( "foo::bar::test_mod::test_foo1", @@ -480,7 +649,20 @@ mod tests { @r###" [ 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 { test_id: Path( "test_foo1", @@ -517,7 +699,20 @@ mod tests { @r###" [ 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 { test_id: Path( "test_foo1", diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 5fa1eba1c..ec24ce5e0 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -139,6 +139,7 @@ pub enum RunnableKind { #[derive(Deserialize, Serialize, Debug)] #[serde(rename_all = "camelCase")] pub struct CargoRunnable { + #[serde(skip_serializing_if = "Option::is_none")] pub workspace_root: Option, // command, --package and --lib stuff pub cargo_args: Vec, diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index 8b473ff74..e18f973b8 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -76,30 +76,33 @@ fn foo() { server.request::( RunnablesParams { text_document: server.doc_id("lib.rs"), position: None }, json!([ - { - "args": [ "test" ], - "extraArgs": [ "foo", "--nocapture" ], - "kind": "cargo", - "env": { "RUST_BACKTRACE": "short" }, - "cwd": null, - "label": "test foo", - "range": { - "end": { "character": 1, "line": 2 }, - "start": { "character": 0, "line": 0 } - } - }, - { - "args": ["check", "--workspace"], - "extraArgs": [], - "kind": "cargo", - "env": {}, - "cwd": null, - "label": "cargo check --workspace", - "range": { - "end": { "character": 0, "line": 0 }, - "start": { "character": 0, "line": 0 } + { + "args": { + "cargoArgs": ["test"], + "executableArgs": ["foo", "--nocapture"], + }, + "kind": "cargo", + "label": "test foo", + "location": { + "targetRange": { + "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" } - } ]), ); } @@ -138,42 +141,44 @@ fn main() {} server.request::( RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None }, json!([ - { - "args": [ "test", "--package", "foo", "--test", "spam" ], - "extraArgs": [ "test_eggs", "--exact", "--nocapture" ], - "kind": "cargo", - "env": { "RUST_BACKTRACE": "short" }, - "label": "test test_eggs", - "range": { - "end": { "character": 17, "line": 1 }, - "start": { "character": 0, "line": 0 } - }, - "cwd": server.path().join("foo") + { + "args": { + "cargoArgs": ["test", "--package", "foo", "--test", "spam"], + "executableArgs": ["test_eggs", "--exact", "--nocapture"], + "workspaceRoot": server.path().join("foo") }, - { - "args": [ "check", "--package", "foo" ], - "extraArgs": [], - "kind": "cargo", - "env": {}, - "label": "cargo check -p foo", - "range": { - "end": { "character": 0, "line": 0 }, + "kind": "cargo", + "label": "test test_eggs", + "location": { + "targetRange": { + "end": { "character": 17, "line": 1 }, "start": { "character": 0, "line": 0 } }, - "cwd": server.path().join("foo") - }, - { - "args": [ "test", "--package", "foo" ], - "extraArgs": [], - "kind": "cargo", - "env": {}, - "label": "cargo test -p foo", - "range": { - "end": { "character": 0, "line": 0 }, - "start": { "character": 0, "line": 0 } + "targetSelectionRange": { + "end": { "character": 12, "line": 1 }, + "start": { "character": 3, "line": 1 } }, - "cwd": server.path().join("foo") + "targetUri": "file:///[..]/tests/spam.rs" } + }, + { + "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" + } ]), ); } -- cgit v1.2.3 From 7a66d9989713475a10eb20b8c772287b435fecd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 2 Jun 2020 12:24:33 +0300 Subject: Disable rust-analyzer.{cargo,checkOnSave}.allFeatures by default --- crates/ra_project_model/src/cargo_workspace.rs | 2 +- crates/rust-analyzer/src/config.rs | 2 +- editors/code/package.json | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index a306ce95f..4b7444039 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs @@ -64,7 +64,7 @@ impl Default for CargoConfig { fn default() -> Self { CargoConfig { no_default_features: false, - all_features: true, + all_features: false, features: Vec::new(), load_out_dirs_from_check: false, target: None, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index c0f7c2c0c..9c6e369d2 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -122,7 +122,7 @@ impl Default for Config { check: Some(FlycheckConfig::CargoCommand { command: "check".to_string(), all_targets: true, - all_features: true, + all_features: false, extra_args: Vec::new(), }), diff --git a/editors/code/package.json b/editors/code/package.json index 75dbafc05..d8f4287fd 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -238,7 +238,7 @@ }, "rust-analyzer.cargo.allFeatures": { "type": "boolean", - "default": true, + "default": false, "description": "Activate all available features" }, "rust-analyzer.cargo.features": { @@ -319,7 +319,7 @@ }, "rust-analyzer.checkOnSave.allFeatures": { "type": "boolean", - "default": true, + "default": false, "markdownDescription": "Check with all features (will be passed as `--all-features`)" }, "rust-analyzer.inlayHints.enable": { -- cgit v1.2.3