Merge remote-tracking branch 'upstream/master' into compute-lazy-assits

# Conflicts:
#	crates/rust-analyzer/src/to_proto.rs
This commit is contained in:
Mikhail Rakhmanov
2020-06-02 23:22:45 +02:00
15 changed files with 546 additions and 343 deletions

View File

@@ -87,6 +87,9 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
"ssr": true,
"onEnter": true,
"parentModule": true,
"runnables": {
"kinds": [ "cargo" ],
},
})),
}
}

View File

@@ -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<CargoTargetSpec>,
kind: &RunnableKind,
features_needed: &Vec<SmolStr>,
cfgs: &[CfgExpr],
) -> Result<(Vec<String>, Vec<String>)> {
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<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());
}
}

View File

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

View File

@@ -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 {}
@@ -128,7 +127,7 @@ pub enum Runnables {}
impl Request for Runnables {
type Params = RunnablesParams;
type Result = Vec<Runnable>;
const METHOD: &'static str = "rust-analyzer/runnables";
const METHOD: &'static str = "experimental/runnables";
}
#[derive(Serialize, Deserialize, Debug)]
@@ -138,25 +137,31 @@ pub struct RunnablesParams {
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)]
#[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<String>,
pub extra_args: Vec<String>,
pub env: FxHashMap<String, String>,
pub cwd: Option<PathBuf>,
pub struct CargoRunnable {
#[serde(skip_serializing_if = "Option::is_none")]
pub workspace_root: Option<PathBuf>,
// command, --package and --lib stuff
pub cargo_args: Vec<String>,
// stuff after --
pub executable_args: Vec<String>,
}
pub enum InlayHints {}

View File

@@ -17,15 +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 rustc_hash::FxHashMap;
use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize};
use serde::{Deserialize, Serialize};
use serde_json::to_value;
use stdx::format_to;
@@ -403,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;
}
}
@@ -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
@@ -424,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(),
},
});
}
}
@@ -755,11 +758,11 @@ pub fn handle_code_action(
if world.config.client_caps.resolve_code_action {
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 {
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();
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
.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 {
let lens = CodeLens {
range: r.range,
range,
command: Some(Command {
title: run_title.to_string(),
command: "rust-analyzer.runSingle".into(),
@@ -848,13 +852,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(),
@@ -1008,65 +1007,6 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<Dia
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(
world: WorldSnapshot,
params: InlayHintsParams,
@@ -1203,54 +1143,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());
}
}

View File

@@ -4,12 +4,14 @@ use ra_ide::{
Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind,
FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel,
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_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 {
let line_col = line_index.line_col(offset);
@@ -658,3 +660,35 @@ pub(crate) fn resolved_code_action(
};
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,
},
})
}