aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_cfg/src/cfg_expr.rs10
-rw-r--r--crates/ra_db/src/fixture.rs70
-rw-r--r--crates/ra_db/src/input.rs15
-rw-r--r--crates/ra_hir/src/code_model.rs18
-rw-r--r--crates/ra_hir/src/lib.rs1
-rw-r--r--crates/ra_hir_def/src/attr.rs2
-rw-r--r--crates/ra_hir_def/src/body.rs2
-rw-r--r--crates/ra_hir_def/src/lang_item.rs2
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs2
-rw-r--r--crates/ra_hir_expand/src/proc_macro.rs2
-rw-r--r--crates/ra_hir_ty/src/traits/builtin.rs3
-rw-r--r--crates/ra_ide/src/call_hierarchy.rs29
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs6
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs59
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs61
-rw-r--r--crates/ra_ide/src/completion/presentation.rs60
-rw-r--r--crates/ra_ide/src/display.rs3
-rw-r--r--crates/ra_ide/src/hover.rs16
-rw-r--r--crates/ra_ide/src/lib.rs3
-rw-r--r--crates/ra_ide/src/mock_analysis.rs123
-rw-r--r--crates/ra_ide/src/runnables.rs189
-rw-r--r--crates/ra_ide/src/snapshots/highlight_injection.html1
-rw-r--r--crates/ra_ide/src/snapshots/highlight_strings.html1
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html13
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html1
-rw-r--r--crates/ra_ide/src/ssr.rs8
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs36
-rw-r--r--crates/ra_ide/src/syntax_highlighting/html.rs1
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs10
-rw-r--r--crates/ra_ide/src/typing/on_enter.rs11
-rw-r--r--crates/ra_syntax/Cargo.toml2
-rw-r--r--crates/ra_syntax/src/ast/tokens.rs5
-rw-r--r--crates/ra_syntax/src/validation.rs20
-rw-r--r--crates/rust-analyzer/Cargo.toml3
-rw-r--r--crates/rust-analyzer/src/caps.rs2
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs8
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs38
-rw-r--r--crates/rust-analyzer/src/main_loop.rs4
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs146
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs3
-rw-r--r--crates/rust-analyzer/src/to_proto.rs30
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs48
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/support.rs2
-rw-r--r--crates/test_utils/Cargo.toml4
-rw-r--r--crates/test_utils/src/lib.rs160
45 files changed, 912 insertions, 321 deletions
diff --git a/crates/ra_cfg/src/cfg_expr.rs b/crates/ra_cfg/src/cfg_expr.rs
index 39d71851c..85b100c6a 100644
--- a/crates/ra_cfg/src/cfg_expr.rs
+++ b/crates/ra_cfg/src/cfg_expr.rs
@@ -88,13 +88,17 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> {
88mod tests { 88mod tests {
89 use super::*; 89 use super::*;
90 90
91 use mbe::ast_to_token_tree; 91 use mbe::{ast_to_token_tree, TokenMap};
92 use ra_syntax::ast::{self, AstNode}; 92 use ra_syntax::ast::{self, AstNode};
93 93
94 fn assert_parse_result(input: &str, expected: CfgExpr) { 94 fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
95 let source_file = ast::SourceFile::parse(input).ok().unwrap(); 95 let source_file = ast::SourceFile::parse(input).ok().unwrap();
96 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); 96 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
97 let (tt, _) = ast_to_token_tree(&tt).unwrap(); 97 ast_to_token_tree(&tt).unwrap()
98 }
99
100 fn assert_parse_result(input: &str, expected: CfgExpr) {
101 let (tt, _) = get_token_tree_generated(input);
98 assert_eq!(parse_cfg(&tt), expected); 102 assert_eq!(parse_cfg(&tt), expected);
99 } 103 }
100 104
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs
index f8f767091..482a2f3e6 100644
--- a/crates/ra_db/src/fixture.rs
+++ b/crates/ra_db/src/fixture.rs
@@ -63,7 +63,7 @@ use std::sync::Arc;
63 63
64use ra_cfg::CfgOptions; 64use ra_cfg::CfgOptions;
65use rustc_hash::FxHashMap; 65use rustc_hash::FxHashMap;
66use test_utils::{extract_offset, parse_fixture, parse_single_fixture, CURSOR_MARKER}; 66use test_utils::{extract_offset, parse_fixture, parse_single_fixture, FixtureMeta, CURSOR_MARKER};
67 67
68use crate::{ 68use crate::{
69 input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, RelativePathBuf, 69 input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, RelativePathBuf,
@@ -113,7 +113,7 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId
113 let fixture = parse_single_fixture(ra_fixture); 113 let fixture = parse_single_fixture(ra_fixture);
114 114
115 let crate_graph = if let Some(entry) = fixture { 115 let crate_graph = if let Some(entry) = fixture {
116 let meta = match parse_meta(&entry.meta) { 116 let meta = match ParsedMeta::from(&entry.meta) {
117 ParsedMeta::File(it) => it, 117 ParsedMeta::File(it) => it,
118 _ => panic!("with_single_file only support file meta"), 118 _ => panic!("with_single_file only support file meta"),
119 }; 119 };
@@ -170,7 +170,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit
170 let mut file_position = None; 170 let mut file_position = None;
171 171
172 for entry in fixture.iter() { 172 for entry in fixture.iter() {
173 let meta = match parse_meta(&entry.meta) { 173 let meta = match ParsedMeta::from(&entry.meta) {
174 ParsedMeta::Root { path } => { 174 ParsedMeta::Root { path } => {
175 let source_root = std::mem::replace(&mut source_root, SourceRoot::new_local()); 175 let source_root = std::mem::replace(&mut source_root, SourceRoot::new_local());
176 db.set_source_root(source_root_id, Arc::new(source_root)); 176 db.set_source_root(source_root_id, Arc::new(source_root));
@@ -258,53 +258,25 @@ struct FileMeta {
258 env: Env, 258 env: Env,
259} 259}
260 260
261//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo) 261impl From<&FixtureMeta> for ParsedMeta {
262fn parse_meta(meta: &str) -> ParsedMeta { 262 fn from(meta: &FixtureMeta) -> Self {
263 let components = meta.split_ascii_whitespace().collect::<Vec<_>>(); 263 match meta {
264 264 FixtureMeta::Root { path } => {
265 if components[0] == "root" { 265 // `Self::Root` causes a false warning: 'variant is never constructed: `Root` '
266 let path: RelativePathBuf = components[1].into(); 266 // see https://github.com/rust-lang/rust/issues/69018
267 assert!(path.starts_with("/") && path.ends_with("/")); 267 ParsedMeta::Root { path: path.to_owned() }
268 return ParsedMeta::Root { path };
269 }
270
271 let path: RelativePathBuf = components[0].into();
272 assert!(path.starts_with("/"));
273
274 let mut krate = None;
275 let mut deps = Vec::new();
276 let mut edition = Edition::Edition2018;
277 let mut cfg = CfgOptions::default();
278 let mut env = Env::default();
279 for component in components[1..].iter() {
280 let (key, value) = split1(component, ':').unwrap();
281 match key {
282 "crate" => krate = Some(value.to_string()),
283 "deps" => deps = value.split(',').map(|it| it.to_string()).collect(),
284 "edition" => edition = Edition::from_str(&value).unwrap(),
285 "cfg" => {
286 for key in value.split(',') {
287 match split1(key, '=') {
288 None => cfg.insert_atom(key.into()),
289 Some((k, v)) => cfg.insert_key_value(k.into(), v.into()),
290 }
291 }
292 }
293 "env" => {
294 for key in value.split(',') {
295 if let Some((k, v)) = split1(key, '=') {
296 env.set(k, v.into());
297 }
298 }
299 } 268 }
300 _ => panic!("bad component: {:?}", component), 269 FixtureMeta::File(f) => Self::File(FileMeta {
270 path: f.path.to_owned(),
271 krate: f.crate_name.to_owned(),
272 deps: f.deps.to_owned(),
273 cfg: f.cfg.to_owned(),
274 edition: f
275 .edition
276 .as_ref()
277 .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()),
278 env: Env::from(f.env.iter()),
279 }),
301 } 280 }
302 } 281 }
303
304 ParsedMeta::File(FileMeta { path, krate, deps, edition, cfg, env })
305}
306
307fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> {
308 let idx = haystack.find(delim)?;
309 Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..]))
310} 282}
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index ab14e2d5e..4d2d3b48a 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -311,6 +311,21 @@ impl fmt::Display for Edition {
311 } 311 }
312} 312}
313 313
314impl<'a, T> From<T> for Env
315where
316 T: Iterator<Item = (&'a String, &'a String)>,
317{
318 fn from(iter: T) -> Self {
319 let mut result = Self::default();
320
321 for (k, v) in iter {
322 result.entries.insert(k.to_owned(), v.to_owned());
323 }
324
325 result
326 }
327}
328
314impl Env { 329impl Env {
315 pub fn set(&mut self, env: &str, value: String) { 330 pub fn set(&mut self, env: &str, value: String) {
316 self.entries.insert(env.to_owned(), value); 331 self.entries.insert(env.to_owned(), value);
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 7c015aa98..e40aeffbc 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -532,7 +532,7 @@ impl Adt {
532 Some(self.module(db).krate()) 532 Some(self.module(db).krate())
533 } 533 }
534 534
535 pub fn name(&self, db: &dyn HirDatabase) -> Name { 535 pub fn name(self, db: &dyn HirDatabase) -> Name {
536 match self { 536 match self {
537 Adt::Struct(s) => s.name(db), 537 Adt::Struct(s) => s.name(db),
538 Adt::Union(u) => u.name(db), 538 Adt::Union(u) => u.name(db),
@@ -1018,15 +1018,15 @@ impl ImplDef {
1018 impls.lookup_impl_defs_for_trait(trait_.id).map(Self::from).collect() 1018 impls.lookup_impl_defs_for_trait(trait_.id).map(Self::from).collect()
1019 } 1019 }
1020 1020
1021 pub fn target_trait(&self, db: &dyn HirDatabase) -> Option<TypeRef> { 1021 pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> {
1022 db.impl_data(self.id).target_trait.clone() 1022 db.impl_data(self.id).target_trait.clone()
1023 } 1023 }
1024 1024
1025 pub fn target_type(&self, db: &dyn HirDatabase) -> TypeRef { 1025 pub fn target_type(self, db: &dyn HirDatabase) -> TypeRef {
1026 db.impl_data(self.id).target_type.clone() 1026 db.impl_data(self.id).target_type.clone()
1027 } 1027 }
1028 1028
1029 pub fn target_ty(&self, db: &dyn HirDatabase) -> Type { 1029 pub fn target_ty(self, db: &dyn HirDatabase) -> Type {
1030 let impl_data = db.impl_data(self.id); 1030 let impl_data = db.impl_data(self.id);
1031 let resolver = self.id.resolver(db.upcast()); 1031 let resolver = self.id.resolver(db.upcast());
1032 let ctx = hir_ty::TyLoweringContext::new(db, &resolver); 1032 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
@@ -1038,23 +1038,23 @@ impl ImplDef {
1038 } 1038 }
1039 } 1039 }
1040 1040
1041 pub fn items(&self, db: &dyn HirDatabase) -> Vec<AssocItem> { 1041 pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
1042 db.impl_data(self.id).items.iter().map(|it| (*it).into()).collect() 1042 db.impl_data(self.id).items.iter().map(|it| (*it).into()).collect()
1043 } 1043 }
1044 1044
1045 pub fn is_negative(&self, db: &dyn HirDatabase) -> bool { 1045 pub fn is_negative(self, db: &dyn HirDatabase) -> bool {
1046 db.impl_data(self.id).is_negative 1046 db.impl_data(self.id).is_negative
1047 } 1047 }
1048 1048
1049 pub fn module(&self, db: &dyn HirDatabase) -> Module { 1049 pub fn module(self, db: &dyn HirDatabase) -> Module {
1050 self.id.lookup(db.upcast()).container.module(db.upcast()).into() 1050 self.id.lookup(db.upcast()).container.module(db.upcast()).into()
1051 } 1051 }
1052 1052
1053 pub fn krate(&self, db: &dyn HirDatabase) -> Crate { 1053 pub fn krate(self, db: &dyn HirDatabase) -> Crate {
1054 Crate { id: self.module(db).id.krate } 1054 Crate { id: self.module(db).id.krate }
1055 } 1055 }
1056 1056
1057 pub fn is_builtin_derive(&self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> { 1057 pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> {
1058 let src = self.source(db); 1058 let src = self.source(db);
1059 let item = src.file_id.is_builtin_derive(db.upcast())?; 1059 let item = src.file_id.is_builtin_derive(db.upcast())?;
1060 let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id); 1060 let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id);
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index c5df4ac24..3364a822f 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -62,6 +62,7 @@ pub use crate::{
62 62
63pub use hir_def::{ 63pub use hir_def::{
64 adt::StructKind, 64 adt::StructKind,
65 attr::Attrs,
65 body::scope::ExprScopes, 66 body::scope::ExprScopes,
66 builtin_type::BuiltinType, 67 builtin_type::BuiltinType,
67 docs::Documentation, 68 docs::Documentation,
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs
index 576cd0c65..8b6c0bede 100644
--- a/crates/ra_hir_def/src/attr.rs
+++ b/crates/ra_hir_def/src/attr.rs
@@ -81,7 +81,7 @@ impl Attrs {
81 } 81 }
82 } 82 }
83 83
84 fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Attrs { 84 pub fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Attrs {
85 let hygiene = Hygiene::new(db.upcast(), owner.file_id); 85 let hygiene = Hygiene::new(db.upcast(), owner.file_id);
86 Attrs::new(owner.value, &hygiene) 86 Attrs::new(owner.value, &hygiene)
87 } 87 }
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index f5a7305dc..273036cee 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -29,7 +29,7 @@ use crate::{
29 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, 29 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
30}; 30};
31 31
32/// A subset of Exander that only deals with cfg attributes. We only need it to 32/// A subset of Expander that only deals with cfg attributes. We only need it to
33/// avoid cyclic queries in crate def map during enum processing. 33/// avoid cyclic queries in crate def map during enum processing.
34pub(crate) struct CfgExpander { 34pub(crate) struct CfgExpander {
35 cfg_options: CfgOptions, 35 cfg_options: CfgOptions,
diff --git a/crates/ra_hir_def/src/lang_item.rs b/crates/ra_hir_def/src/lang_item.rs
index d962db3cc..3516784b8 100644
--- a/crates/ra_hir_def/src/lang_item.rs
+++ b/crates/ra_hir_def/src/lang_item.rs
@@ -164,7 +164,7 @@ impl LangItems {
164 T: Into<AttrDefId> + Copy, 164 T: Into<AttrDefId> + Copy,
165 { 165 {
166 if let Some(lang_item_name) = lang_attr(db, item) { 166 if let Some(lang_item_name) = lang_attr(db, item) {
167 self.items.entry(lang_item_name.clone()).or_insert_with(|| constructor(item)); 167 self.items.entry(lang_item_name).or_insert_with(|| constructor(item));
168 } 168 }
169 } 169 }
170} 170}
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs
index 4e628b14d..f44baa579 100644
--- a/crates/ra_hir_def/src/nameres/raw.rs
+++ b/crates/ra_hir_def/src/nameres/raw.rs
@@ -175,7 +175,7 @@ pub(super) enum DefKind {
175} 175}
176 176
177impl DefKind { 177impl DefKind {
178 pub fn ast_id(&self) -> FileAstId<ast::ModuleItem> { 178 pub fn ast_id(self) -> FileAstId<ast::ModuleItem> {
179 match self { 179 match self {
180 DefKind::Function(it) => it.upcast(), 180 DefKind::Function(it) => it.upcast(),
181 DefKind::Struct(it, _) => it.upcast(), 181 DefKind::Struct(it, _) => it.upcast(),
diff --git a/crates/ra_hir_expand/src/proc_macro.rs b/crates/ra_hir_expand/src/proc_macro.rs
index 4e0e069c8..04c026004 100644
--- a/crates/ra_hir_expand/src/proc_macro.rs
+++ b/crates/ra_hir_expand/src/proc_macro.rs
@@ -25,7 +25,7 @@ impl ProcMacroExpander {
25 } 25 }
26 26
27 pub fn expand( 27 pub fn expand(
28 &self, 28 self,
29 db: &dyn AstDatabase, 29 db: &dyn AstDatabase,
30 _id: LazyMacroId, 30 _id: LazyMacroId,
31 tt: &tt::Subtree, 31 tt: &tt::Subtree,
diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs
index ccab246bf..88a422d2c 100644
--- a/crates/ra_hir_ty/src/traits/builtin.rs
+++ b/crates/ra_hir_ty/src/traits/builtin.rs
@@ -290,8 +290,7 @@ fn trait_object_unsize_impl_datum(
290 let self_trait_ref = TraitRef { trait_, substs: self_substs }; 290 let self_trait_ref = TraitRef { trait_, substs: self_substs };
291 let where_clauses = vec![GenericPredicate::Implemented(self_trait_ref)]; 291 let where_clauses = vec![GenericPredicate::Implemented(self_trait_ref)];
292 292
293 let impl_substs = 293 let impl_substs = Substs::builder(2).push(self_ty).push(Ty::Dyn(target_bounds.into())).build();
294 Substs::builder(2).push(self_ty).push(Ty::Dyn(target_bounds.clone().into())).build();
295 294
296 let trait_ref = TraitRef { trait_: unsize_trait, substs: impl_substs }; 295 let trait_ref = TraitRef { trait_: unsize_trait, substs: impl_substs };
297 296
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs
index 85d1f0cb1..defd8176f 100644
--- a/crates/ra_ide/src/call_hierarchy.rs
+++ b/crates/ra_ide/src/call_hierarchy.rs
@@ -246,6 +246,35 @@ mod tests {
246 } 246 }
247 247
248 #[test] 248 #[test]
249 fn test_call_hierarchy_in_tests_mod() {
250 check_hierarchy(
251 r#"
252 //- /lib.rs cfg:test
253 fn callee() {}
254 fn caller1() {
255 call<|>ee();
256 }
257
258 #[cfg(test)]
259 mod tests {
260 use super::*;
261
262 #[test]
263 fn test_caller() {
264 callee();
265 }
266 }
267 "#,
268 "callee FN_DEF FileId(1) 0..14 3..9",
269 &[
270 "caller1 FN_DEF FileId(1) 15..45 18..25 : [34..40]",
271 "test_caller FN_DEF FileId(1) 93..147 108..119 : [132..138]",
272 ],
273 &[],
274 );
275 }
276
277 #[test]
249 fn test_call_hierarchy_in_different_files() { 278 fn test_call_hierarchy_in_different_files() {
250 check_hierarchy( 279 check_hierarchy(
251 r#" 280 r#"
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs
index f17266221..fb3f0b743 100644
--- a/crates/ra_ide/src/completion/complete_attribute.rs
+++ b/crates/ra_ide/src/completion/complete_attribute.rs
@@ -112,7 +112,7 @@ const ATTRIBUTES: &[AttrCompletion] = &[
112 AttrCompletion { label: "repr", snippet: Some("repr(${0:C})"), should_be_inner: false }, 112 AttrCompletion { label: "repr", snippet: Some("repr(${0:C})"), should_be_inner: false },
113 AttrCompletion { 113 AttrCompletion {
114 label: "should_panic", 114 label: "should_panic",
115 snippet: Some(r#"expected = "${0:reason}""#), 115 snippet: Some(r#"should_panic(expected = "${0:reason}")"#),
116 should_be_inner: false, 116 should_be_inner: false,
117 }, 117 },
118 AttrCompletion { 118 AttrCompletion {
@@ -571,7 +571,7 @@ mod tests {
571 label: "should_panic", 571 label: "should_panic",
572 source_range: 19..19, 572 source_range: 19..19,
573 delete: 19..19, 573 delete: 19..19,
574 insert: "expected = \"${0:reason}\"", 574 insert: "should_panic(expected = \"${0:reason}\")",
575 kind: Attribute, 575 kind: Attribute,
576 }, 576 },
577 CompletionItem { 577 CompletionItem {
@@ -810,7 +810,7 @@ mod tests {
810 label: "should_panic", 810 label: "should_panic",
811 source_range: 20..20, 811 source_range: 20..20,
812 delete: 20..20, 812 delete: 20..20,
813 insert: "expected = \"${0:reason}\"", 813 insert: "should_panic(expected = \"${0:reason}\")",
814 kind: Attribute, 814 kind: Attribute,
815 }, 815 },
816 CompletionItem { 816 CompletionItem {
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index f2a52a407..02e660ca8 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -184,6 +184,16 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
184 &format!("dbg!({})", receiver_text), 184 &format!("dbg!({})", receiver_text),
185 ) 185 )
186 .add_to(acc); 186 .add_to(acc);
187
188 postfix_snippet(
189 ctx,
190 cap,
191 &dot_receiver,
192 "call",
193 "function(expr)",
194 &format!("${{1}}({})", receiver_text),
195 )
196 .add_to(acc);
187} 197}
188 198
189fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String { 199fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
@@ -256,6 +266,13 @@ mod tests {
256 detail: "Box::new(expr)", 266 detail: "Box::new(expr)",
257 }, 267 },
258 CompletionItem { 268 CompletionItem {
269 label: "call",
270 source_range: 89..89,
271 delete: 85..89,
272 insert: "${1}(bar)",
273 detail: "function(expr)",
274 },
275 CompletionItem {
259 label: "dbg", 276 label: "dbg",
260 source_range: 89..89, 277 source_range: 89..89,
261 delete: 85..89, 278 delete: 85..89,
@@ -335,6 +352,13 @@ mod tests {
335 detail: "Box::new(expr)", 352 detail: "Box::new(expr)",
336 }, 353 },
337 CompletionItem { 354 CompletionItem {
355 label: "call",
356 source_range: 210..210,
357 delete: 206..210,
358 insert: "${1}(bar)",
359 detail: "function(expr)",
360 },
361 CompletionItem {
338 label: "dbg", 362 label: "dbg",
339 source_range: 210..210, 363 source_range: 210..210,
340 delete: 206..210, 364 delete: 206..210,
@@ -414,6 +438,13 @@ mod tests {
414 detail: "Box::new(expr)", 438 detail: "Box::new(expr)",
415 }, 439 },
416 CompletionItem { 440 CompletionItem {
441 label: "call",
442 source_range: 211..211,
443 delete: 207..211,
444 insert: "${1}(bar)",
445 detail: "function(expr)",
446 },
447 CompletionItem {
417 label: "dbg", 448 label: "dbg",
418 source_range: 211..211, 449 source_range: 211..211,
419 delete: 207..211, 450 delete: 207..211,
@@ -488,6 +519,13 @@ mod tests {
488 detail: "Box::new(expr)", 519 detail: "Box::new(expr)",
489 }, 520 },
490 CompletionItem { 521 CompletionItem {
522 label: "call",
523 source_range: 91..91,
524 delete: 87..91,
525 insert: "${1}(bar)",
526 detail: "function(expr)",
527 },
528 CompletionItem {
491 label: "dbg", 529 label: "dbg",
492 source_range: 91..91, 530 source_range: 91..91,
493 delete: 87..91, 531 delete: 87..91,
@@ -547,6 +585,13 @@ mod tests {
547 detail: "Box::new(expr)", 585 detail: "Box::new(expr)",
548 }, 586 },
549 CompletionItem { 587 CompletionItem {
588 label: "call",
589 source_range: 52..52,
590 delete: 49..52,
591 insert: "${1}(42)",
592 detail: "function(expr)",
593 },
594 CompletionItem {
550 label: "dbg", 595 label: "dbg",
551 source_range: 52..52, 596 source_range: 52..52,
552 delete: 49..52, 597 delete: 49..52,
@@ -608,6 +653,13 @@ mod tests {
608 detail: "Box::new(expr)", 653 detail: "Box::new(expr)",
609 }, 654 },
610 CompletionItem { 655 CompletionItem {
656 label: "call",
657 source_range: 149..150,
658 delete: 145..150,
659 insert: "${1}(bar)",
660 detail: "function(expr)",
661 },
662 CompletionItem {
611 label: "dbg", 663 label: "dbg",
612 source_range: 149..150, 664 source_range: 149..150,
613 delete: 145..150, 665 delete: 145..150,
@@ -667,6 +719,13 @@ mod tests {
667 detail: "Box::new(expr)", 719 detail: "Box::new(expr)",
668 }, 720 },
669 CompletionItem { 721 CompletionItem {
722 label: "call",
723 source_range: 56..56,
724 delete: 49..56,
725 insert: "${1}(&&&&42)",
726 detail: "function(expr)",
727 },
728 CompletionItem {
670 label: "dbg", 729 label: "dbg",
671 source_range: 56..56, 730 source_range: 56..56,
672 delete: 49..56, 731 delete: 49..56,
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index 039df03e0..21c9316e6 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -49,56 +49,53 @@ use crate::{
49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { 49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
50 if let Some((trigger, impl_def)) = completion_match(ctx) { 50 if let Some((trigger, impl_def)) = completion_match(ctx) {
51 match trigger.kind() { 51 match trigger.kind() {
52 SyntaxKind::NAME_REF => { 52 SyntaxKind::NAME_REF => get_missing_assoc_items(&ctx.sema, &impl_def)
53 get_missing_assoc_items(&ctx.sema, &impl_def).iter().for_each(|item| match item { 53 .into_iter()
54 .for_each(|item| match item {
54 hir::AssocItem::Function(fn_item) => { 55 hir::AssocItem::Function(fn_item) => {
55 add_function_impl(&trigger, acc, ctx, &fn_item) 56 add_function_impl(&trigger, acc, ctx, fn_item)
56 } 57 }
57 hir::AssocItem::TypeAlias(type_item) => { 58 hir::AssocItem::TypeAlias(type_item) => {
58 add_type_alias_impl(&trigger, acc, ctx, &type_item) 59 add_type_alias_impl(&trigger, acc, ctx, type_item)
59 } 60 }
60 hir::AssocItem::Const(const_item) => { 61 hir::AssocItem::Const(const_item) => {
61 add_const_impl(&trigger, acc, ctx, &const_item) 62 add_const_impl(&trigger, acc, ctx, const_item)
62 } 63 }
63 }) 64 }),
64 }
65 65
66 SyntaxKind::FN_DEF => { 66 SyntaxKind::FN_DEF => {
67 for missing_fn in 67 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
68 get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { 68 .into_iter()
69 match item { 69 .filter_map(|item| match item {
70 hir::AssocItem::Function(fn_item) => Some(fn_item), 70 hir::AssocItem::Function(fn_item) => Some(fn_item),
71 _ => None, 71 _ => None,
72 }
73 }) 72 })
74 { 73 {
75 add_function_impl(&trigger, acc, ctx, &missing_fn); 74 add_function_impl(&trigger, acc, ctx, missing_fn);
76 } 75 }
77 } 76 }
78 77
79 SyntaxKind::TYPE_ALIAS_DEF => { 78 SyntaxKind::TYPE_ALIAS_DEF => {
80 for missing_fn in 79 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
81 get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { 80 .into_iter()
82 match item { 81 .filter_map(|item| match item {
83 hir::AssocItem::TypeAlias(type_item) => Some(type_item), 82 hir::AssocItem::TypeAlias(type_item) => Some(type_item),
84 _ => None, 83 _ => None,
85 }
86 }) 84 })
87 { 85 {
88 add_type_alias_impl(&trigger, acc, ctx, &missing_fn); 86 add_type_alias_impl(&trigger, acc, ctx, missing_fn);
89 } 87 }
90 } 88 }
91 89
92 SyntaxKind::CONST_DEF => { 90 SyntaxKind::CONST_DEF => {
93 for missing_fn in 91 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
94 get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| { 92 .into_iter()
95 match item { 93 .filter_map(|item| match item {
96 hir::AssocItem::Const(const_item) => Some(const_item), 94 hir::AssocItem::Const(const_item) => Some(const_item),
97 _ => None, 95 _ => None,
98 }
99 }) 96 })
100 { 97 {
101 add_const_impl(&trigger, acc, ctx, &missing_fn); 98 add_const_impl(&trigger, acc, ctx, missing_fn);
102 } 99 }
103 } 100 }
104 101
@@ -126,9 +123,9 @@ fn add_function_impl(
126 fn_def_node: &SyntaxNode, 123 fn_def_node: &SyntaxNode,
127 acc: &mut Completions, 124 acc: &mut Completions,
128 ctx: &CompletionContext, 125 ctx: &CompletionContext,
129 func: &hir::Function, 126 func: hir::Function,
130) { 127) {
131 let signature = FunctionSignature::from_hir(ctx.db, *func); 128 let signature = FunctionSignature::from_hir(ctx.db, func);
132 129
133 let fn_name = func.name(ctx.db).to_string(); 130 let fn_name = func.name(ctx.db).to_string();
134 131
@@ -167,7 +164,7 @@ fn add_type_alias_impl(
167 type_def_node: &SyntaxNode, 164 type_def_node: &SyntaxNode,
168 acc: &mut Completions, 165 acc: &mut Completions,
169 ctx: &CompletionContext, 166 ctx: &CompletionContext,
170 type_alias: &hir::TypeAlias, 167 type_alias: hir::TypeAlias,
171) { 168) {
172 let alias_name = type_alias.name(ctx.db).to_string(); 169 let alias_name = type_alias.name(ctx.db).to_string();
173 170
@@ -187,7 +184,7 @@ fn add_const_impl(
187 const_def_node: &SyntaxNode, 184 const_def_node: &SyntaxNode,
188 acc: &mut Completions, 185 acc: &mut Completions,
189 ctx: &CompletionContext, 186 ctx: &CompletionContext,
190 const_: &hir::Const, 187 const_: hir::Const,
191) { 188) {
192 let const_name = const_.name(ctx.db).map(|n| n.to_string()); 189 let const_name = const_.name(ctx.db).map(|n| n.to_string());
193 190
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 440ffa31d..61565c84f 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -211,7 +211,7 @@ impl Completions {
211 .parameter_names 211 .parameter_names
212 .iter() 212 .iter()
213 .skip(if function_signature.has_self_param { 1 } else { 0 }) 213 .skip(if function_signature.has_self_param { 1 } else { 0 })
214 .cloned() 214 .map(|name| name.trim_start_matches('_').into())
215 .collect(); 215 .collect();
216 216
217 builder = builder.add_call_parens(ctx, name, Params::Named(params)); 217 builder = builder.add_call_parens(ctx, name, Params::Named(params));
@@ -672,6 +672,37 @@ mod tests {
672 assert_debug_snapshot!( 672 assert_debug_snapshot!(
673 do_reference_completion( 673 do_reference_completion(
674 r" 674 r"
675 fn with_ignored_args(_foo: i32, ___bar: bool, ho_ge_: String) {}
676 fn main() { with_<|> }
677 "
678 ),
679 @r###"
680 [
681 CompletionItem {
682 label: "main()",
683 source_range: 110..115,
684 delete: 110..115,
685 insert: "main()$0",
686 kind: Function,
687 lookup: "main",
688 detail: "fn main()",
689 },
690 CompletionItem {
691 label: "with_ignored_args(…)",
692 source_range: 110..115,
693 delete: 110..115,
694 insert: "with_ignored_args(${1:foo}, ${2:bar}, ${3:ho_ge_})$0",
695 kind: Function,
696 lookup: "with_ignored_args",
697 detail: "fn with_ignored_args(_foo: i32, ___bar: bool, ho_ge_: String)",
698 trigger_call_info: true,
699 },
700 ]
701 "###
702 );
703 assert_debug_snapshot!(
704 do_reference_completion(
705 r"
675 struct S {} 706 struct S {}
676 impl S { 707 impl S {
677 fn foo(&self) {} 708 fn foo(&self) {}
@@ -695,6 +726,33 @@ mod tests {
695 ] 726 ]
696 "### 727 "###
697 ); 728 );
729 assert_debug_snapshot!(
730 do_reference_completion(
731 r"
732 struct S {}
733 impl S {
734 fn foo_ignored_args(&self, _a: bool, b: i32) {}
735 }
736 fn bar(s: &S) {
737 s.f<|>
738 }
739 "
740 ),
741 @r###"
742 [
743 CompletionItem {
744 label: "foo_ignored_args(…)",
745 source_range: 194..195,
746 delete: 194..195,
747 insert: "foo_ignored_args(${1:a}, ${2:b})$0",
748 kind: Method,
749 lookup: "foo_ignored_args",
750 detail: "fn foo_ignored_args(&self, _a: bool, b: i32)",
751 trigger_call_info: true,
752 },
753 ]
754 "###
755 );
698 } 756 }
699 757
700 #[test] 758 #[test]
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
index 8bb312156..827c094e7 100644
--- a/crates/ra_ide/src/display.rs
+++ b/crates/ra_ide/src/display.rs
@@ -83,12 +83,13 @@ pub(crate) fn rust_code_markup_with_doc(
83 83
84 if let Some(mod_path) = mod_path { 84 if let Some(mod_path) = mod_path {
85 if !mod_path.is_empty() { 85 if !mod_path.is_empty() {
86 format_to!(buf, "{}\n___\n\n", mod_path); 86 format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
87 } 87 }
88 } 88 }
89 format_to!(buf, "```rust\n{}\n```", code); 89 format_to!(buf, "```rust\n{}\n```", code);
90 90
91 if let Some(doc) = doc { 91 if let Some(doc) = doc {
92 format_to!(buf, "\n___");
92 format_to!(buf, "\n\n{}", doc); 93 format_to!(buf, "\n\n{}", doc);
93 } 94 }
94 95
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 1f4f6b848..3e721dcca 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -405,7 +405,7 @@ mod tests {
405 }; 405 };
406 } 406 }
407 "#, 407 "#,
408 &["Foo\n___\n\n```rust\nfield_a: u32"], 408 &["Foo\n```\n\n```rust\nfield_a: u32"],
409 ); 409 );
410 410
411 // Hovering over the field in the definition 411 // Hovering over the field in the definition
@@ -422,7 +422,7 @@ mod tests {
422 }; 422 };
423 } 423 }
424 "#, 424 "#,
425 &["Foo\n___\n\n```rust\nfield_a: u32"], 425 &["Foo\n```\n\n```rust\nfield_a: u32"],
426 ); 426 );
427 } 427 }
428 428
@@ -475,7 +475,7 @@ fn main() {
475 ", 475 ",
476 ); 476 );
477 let hover = analysis.hover(position).unwrap().unwrap(); 477 let hover = analysis.hover(position).unwrap().unwrap();
478 assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\n___\n\n```rust\nSome")); 478 assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\n```\n\n```rust\nSome"));
479 479
480 let (analysis, position) = single_file_with_position( 480 let (analysis, position) = single_file_with_position(
481 " 481 "
@@ -503,11 +503,12 @@ fn main() {
503 "#, 503 "#,
504 &[" 504 &["
505Option 505Option
506___ 506```
507 507
508```rust 508```rust
509None 509None
510``` 510```
511___
511 512
512The None variant 513The None variant
513 " 514 "
@@ -527,11 +528,12 @@ The None variant
527 "#, 528 "#,
528 &[" 529 &["
529Option 530Option
530___ 531```
531 532
532```rust 533```rust
533Some 534Some
534``` 535```
536___
535 537
536The Some variant 538The Some variant
537 " 539 "
@@ -614,7 +616,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
614 let hover = analysis.hover(position).unwrap().unwrap(); 616 let hover = analysis.hover(position).unwrap().unwrap();
615 assert_eq!( 617 assert_eq!(
616 trim_markup_opt(hover.info.first()), 618 trim_markup_opt(hover.info.first()),
617 Some("wrapper::Thing\n___\n\n```rust\nfn new() -> Thing") 619 Some("wrapper::Thing\n```\n\n```rust\nfn new() -> Thing")
618 ); 620 );
619 } 621 }
620 622
@@ -891,7 +893,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
891 fo<|>o(); 893 fo<|>o();
892 } 894 }
893 ", 895 ",
894 &["fn foo()\n```\n\n<- `\u{3000}` here"], 896 &["fn foo()\n```\n___\n\n<- `\u{3000}` here"],
895 ); 897 );
896 } 898 }
897 899
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 5ac002d82..d983cd910 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -309,7 +309,8 @@ impl Analysis {
309 309
310 /// Returns an edit which should be applied when opening a new line, fixing 310 /// Returns an edit which should be applied when opening a new line, fixing
311 /// up minor stuff like continuing the comment. 311 /// up minor stuff like continuing the comment.
312 pub fn on_enter(&self, position: FilePosition) -> Cancelable<Option<SourceChange>> { 312 /// The edit will be a snippet (with `$0`).
313 pub fn on_enter(&self, position: FilePosition) -> Cancelable<Option<TextEdit>> {
313 self.with_db(|db| typing::on_enter(&db, position)) 314 self.with_db(|db| typing::on_enter(&db, position))
314 } 315 }
315 316
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs
index 2c13f206a..ad78d2d93 100644
--- a/crates/ra_ide/src/mock_analysis.rs
+++ b/crates/ra_ide/src/mock_analysis.rs
@@ -1,21 +1,81 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::str::FromStr;
3use std::sync::Arc; 4use std::sync::Arc;
4 5
5use ra_cfg::CfgOptions; 6use ra_cfg::CfgOptions;
6use ra_db::{CrateName, Env, RelativePathBuf}; 7use ra_db::{CrateName, Env, RelativePathBuf};
7use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; 8use test_utils::{extract_offset, extract_range, parse_fixture, FixtureEntry, CURSOR_MARKER};
8 9
9use crate::{ 10use crate::{
10 Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition::Edition2018, FileId, FilePosition, 11 Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange,
11 FileRange, SourceRootId, 12 SourceRootId,
12}; 13};
13 14
15#[derive(Debug)]
16enum MockFileData {
17 Plain { path: String, content: String },
18 Fixture(FixtureEntry),
19}
20
21impl MockFileData {
22 fn new(path: String, content: String) -> Self {
23 // `Self::Plain` causes a false warning: 'variant is never constructed: `Plain` '
24 // see https://github.com/rust-lang/rust/issues/69018
25 MockFileData::Plain { path, content }
26 }
27
28 fn path(&self) -> &str {
29 match self {
30 MockFileData::Plain { path, .. } => path.as_str(),
31 MockFileData::Fixture(f) => f.meta.path().as_str(),
32 }
33 }
34
35 fn content(&self) -> &str {
36 match self {
37 MockFileData::Plain { content, .. } => content,
38 MockFileData::Fixture(f) => f.text.as_str(),
39 }
40 }
41
42 fn cfg_options(&self) -> CfgOptions {
43 match self {
44 MockFileData::Fixture(f) => {
45 f.meta.cfg_options().map_or_else(Default::default, |o| o.clone())
46 }
47 _ => CfgOptions::default(),
48 }
49 }
50
51 fn edition(&self) -> Edition {
52 match self {
53 MockFileData::Fixture(f) => {
54 f.meta.edition().map_or(Edition::Edition2018, |v| Edition::from_str(v).unwrap())
55 }
56 _ => Edition::Edition2018,
57 }
58 }
59
60 fn env(&self) -> Env {
61 match self {
62 MockFileData::Fixture(f) => Env::from(f.meta.env()),
63 _ => Env::default(),
64 }
65 }
66}
67
68impl From<FixtureEntry> for MockFileData {
69 fn from(fixture: FixtureEntry) -> Self {
70 Self::Fixture(fixture)
71 }
72}
73
14/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis 74/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
15/// from a set of in-memory files. 75/// from a set of in-memory files.
16#[derive(Debug, Default)] 76#[derive(Debug, Default)]
17pub struct MockAnalysis { 77pub struct MockAnalysis {
18 files: Vec<(String, String)>, 78 files: Vec<MockFileData>,
19} 79}
20 80
21impl MockAnalysis { 81impl MockAnalysis {
@@ -35,7 +95,7 @@ impl MockAnalysis {
35 pub fn with_files(fixture: &str) -> MockAnalysis { 95 pub fn with_files(fixture: &str) -> MockAnalysis {
36 let mut res = MockAnalysis::new(); 96 let mut res = MockAnalysis::new();
37 for entry in parse_fixture(fixture) { 97 for entry in parse_fixture(fixture) {
38 res.add_file(&entry.meta, &entry.text); 98 res.add_file_fixture(entry);
39 } 99 }
40 res 100 res
41 } 101 }
@@ -48,30 +108,44 @@ impl MockAnalysis {
48 for entry in parse_fixture(fixture) { 108 for entry in parse_fixture(fixture) {
49 if entry.text.contains(CURSOR_MARKER) { 109 if entry.text.contains(CURSOR_MARKER) {
50 assert!(position.is_none(), "only one marker (<|>) per fixture is allowed"); 110 assert!(position.is_none(), "only one marker (<|>) per fixture is allowed");
51 position = Some(res.add_file_with_position(&entry.meta, &entry.text)); 111 position = Some(res.add_file_fixture_with_position(entry));
52 } else { 112 } else {
53 res.add_file(&entry.meta, &entry.text); 113 res.add_file_fixture(entry);
54 } 114 }
55 } 115 }
56 let position = position.expect("expected a marker (<|>)"); 116 let position = position.expect("expected a marker (<|>)");
57 (res, position) 117 (res, position)
58 } 118 }
59 119
120 pub fn add_file_fixture(&mut self, fixture: FixtureEntry) -> FileId {
121 let file_id = self.next_id();
122 self.files.push(MockFileData::from(fixture));
123 file_id
124 }
125
126 pub fn add_file_fixture_with_position(&mut self, mut fixture: FixtureEntry) -> FilePosition {
127 let (offset, text) = extract_offset(&fixture.text);
128 fixture.text = text;
129 let file_id = self.next_id();
130 self.files.push(MockFileData::from(fixture));
131 FilePosition { file_id, offset }
132 }
133
60 pub fn add_file(&mut self, path: &str, text: &str) -> FileId { 134 pub fn add_file(&mut self, path: &str, text: &str) -> FileId {
61 let file_id = FileId((self.files.len() + 1) as u32); 135 let file_id = self.next_id();
62 self.files.push((path.to_string(), text.to_string())); 136 self.files.push(MockFileData::new(path.to_string(), text.to_string()));
63 file_id 137 file_id
64 } 138 }
65 pub fn add_file_with_position(&mut self, path: &str, text: &str) -> FilePosition { 139 pub fn add_file_with_position(&mut self, path: &str, text: &str) -> FilePosition {
66 let (offset, text) = extract_offset(text); 140 let (offset, text) = extract_offset(text);
67 let file_id = FileId((self.files.len() + 1) as u32); 141 let file_id = self.next_id();
68 self.files.push((path.to_string(), text)); 142 self.files.push(MockFileData::new(path.to_string(), text));
69 FilePosition { file_id, offset } 143 FilePosition { file_id, offset }
70 } 144 }
71 pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange { 145 pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange {
72 let (range, text) = extract_range(text); 146 let (range, text) = extract_range(text);
73 let file_id = FileId((self.files.len() + 1) as u32); 147 let file_id = self.next_id();
74 self.files.push((path.to_string(), text)); 148 self.files.push(MockFileData::new(path.to_string(), text));
75 FileRange { file_id, range } 149 FileRange { file_id, range }
76 } 150 }
77 pub fn id_of(&self, path: &str) -> FileId { 151 pub fn id_of(&self, path: &str) -> FileId {
@@ -79,7 +153,7 @@ impl MockAnalysis {
79 .files 153 .files
80 .iter() 154 .iter()
81 .enumerate() 155 .enumerate()
82 .find(|(_, (p, _text))| path == p) 156 .find(|(_, data)| path == data.path())
83 .expect("no file in this mock"); 157 .expect("no file in this mock");
84 FileId(idx as u32 + 1) 158 FileId(idx as u32 + 1)
85 } 159 }
@@ -90,18 +164,21 @@ impl MockAnalysis {
90 change.add_root(source_root, true); 164 change.add_root(source_root, true);
91 let mut crate_graph = CrateGraph::default(); 165 let mut crate_graph = CrateGraph::default();
92 let mut root_crate = None; 166 let mut root_crate = None;
93 for (i, (path, contents)) in self.files.into_iter().enumerate() { 167 for (i, data) in self.files.into_iter().enumerate() {
168 let path = data.path();
94 assert!(path.starts_with('/')); 169 assert!(path.starts_with('/'));
95 let path = RelativePathBuf::from_path(&path[1..]).unwrap(); 170 let path = RelativePathBuf::from_path(&path[1..]).unwrap();
171 let cfg_options = data.cfg_options();
96 let file_id = FileId(i as u32 + 1); 172 let file_id = FileId(i as u32 + 1);
97 let cfg_options = CfgOptions::default(); 173 let edition = data.edition();
174 let env = data.env();
98 if path == "/lib.rs" || path == "/main.rs" { 175 if path == "/lib.rs" || path == "/main.rs" {
99 root_crate = Some(crate_graph.add_crate_root( 176 root_crate = Some(crate_graph.add_crate_root(
100 file_id, 177 file_id,
101 Edition2018, 178 edition,
102 None, 179 None,
103 cfg_options, 180 cfg_options,
104 Env::default(), 181 env,
105 Default::default(), 182 Default::default(),
106 Default::default(), 183 Default::default(),
107 )); 184 ));
@@ -109,10 +186,10 @@ impl MockAnalysis {
109 let crate_name = path.parent().unwrap().file_name().unwrap(); 186 let crate_name = path.parent().unwrap().file_name().unwrap();
110 let other_crate = crate_graph.add_crate_root( 187 let other_crate = crate_graph.add_crate_root(
111 file_id, 188 file_id,
112 Edition2018, 189 edition,
113 Some(CrateName::new(crate_name).unwrap()), 190 Some(CrateName::new(crate_name).unwrap()),
114 cfg_options, 191 cfg_options,
115 Env::default(), 192 env,
116 Default::default(), 193 Default::default(),
117 Default::default(), 194 Default::default(),
118 ); 195 );
@@ -122,7 +199,7 @@ impl MockAnalysis {
122 .unwrap(); 199 .unwrap();
123 } 200 }
124 } 201 }
125 change.add_file(source_root, file_id, path, Arc::new(contents)); 202 change.add_file(source_root, file_id, path, Arc::new(data.content().to_owned()));
126 } 203 }
127 change.set_crate_graph(crate_graph); 204 change.set_crate_graph(crate_graph);
128 host.apply_change(change); 205 host.apply_change(change);
@@ -131,6 +208,10 @@ impl MockAnalysis {
131 pub fn analysis(self) -> Analysis { 208 pub fn analysis(self) -> Analysis {
132 self.analysis_host().analysis() 209 self.analysis_host().analysis()
133 } 210 }
211
212 fn next_id(&self) -> FileId {
213 FileId((self.files.len() + 1) as u32)
214 }
134} 215}
135 216
136/// Creates analysis from a multi-file fixture, returns positions marked with <|>. 217/// Creates analysis from a multi-file fixture, returns positions marked with <|>.
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 131b8f307..6e7e47199 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,6 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{AsAssocItem, Semantics}; 3use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
4use itertools::Itertools; 4use itertools::Itertools;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
@@ -10,12 +10,14 @@ use ra_syntax::{
10 10
11use crate::FileId; 11use crate::FileId;
12use ast::DocCommentsOwner; 12use ast::DocCommentsOwner;
13use ra_cfg::CfgExpr;
13use std::fmt::Display; 14use std::fmt::Display;
14 15
15#[derive(Debug)] 16#[derive(Debug)]
16pub struct Runnable { 17pub struct Runnable {
17 pub range: TextRange, 18 pub range: TextRange,
18 pub kind: RunnableKind, 19 pub kind: RunnableKind,
20 pub cfg_exprs: Vec<CfgExpr>,
19} 21}
20 22
21#[derive(Debug)] 23#[derive(Debug)]
@@ -45,29 +47,33 @@ pub enum RunnableKind {
45pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 47pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
46 let sema = Semantics::new(db); 48 let sema = Semantics::new(db);
47 let source_file = sema.parse(file_id); 49 let source_file = sema.parse(file_id);
48 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i)).collect() 50 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect()
49} 51}
50 52
51fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { 53fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode, file_id: FileId) -> Option<Runnable> {
52 match_ast! { 54 match_ast! {
53 match item { 55 match item {
54 ast::FnDef(it) => runnable_fn(sema, it), 56 ast::FnDef(it) => runnable_fn(sema, it, file_id),
55 ast::Module(it) => runnable_mod(sema, it), 57 ast::Module(it) => runnable_mod(sema, it, file_id),
56 _ => None, 58 _ => None,
57 } 59 }
58 } 60 }
59} 61}
60 62
61fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Runnable> { 63fn runnable_fn(
64 sema: &Semantics<RootDatabase>,
65 fn_def: ast::FnDef,
66 file_id: FileId,
67) -> Option<Runnable> {
62 let name_string = fn_def.name()?.text().to_string(); 68 let name_string = fn_def.name()?.text().to_string();
63 69
64 let kind = if name_string == "main" { 70 let kind = if name_string == "main" {
65 RunnableKind::Bin 71 RunnableKind::Bin
66 } else { 72 } else {
67 let test_id = if let Some(module) = sema.to_def(&fn_def).map(|def| def.module(sema.db)) { 73 let test_id = match sema.to_def(&fn_def).map(|def| def.module(sema.db)) {
68 let def = sema.to_def(&fn_def)?; 74 Some(module) => {
69 let impl_trait_name = 75 let def = sema.to_def(&fn_def)?;
70 def.as_assoc_item(sema.db).and_then(|assoc_item| { 76 let impl_trait_name = def.as_assoc_item(sema.db).and_then(|assoc_item| {
71 match assoc_item.container(sema.db) { 77 match assoc_item.container(sema.db) {
72 hir::AssocItemContainer::Trait(trait_item) => { 78 hir::AssocItemContainer::Trait(trait_item) => {
73 Some(trait_item.name(sema.db).to_string()) 79 Some(trait_item.name(sema.db).to_string())
@@ -79,25 +85,25 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Run
79 } 85 }
80 }); 86 });
81 87
82 let path_iter = module 88 let path_iter = module
83 .path_to_root(sema.db) 89 .path_to_root(sema.db)
84 .into_iter() 90 .into_iter()
85 .rev() 91 .rev()
86 .filter_map(|it| it.name(sema.db)) 92 .filter_map(|it| it.name(sema.db))
87 .map(|name| name.to_string()); 93 .map(|name| name.to_string());
88 94
89 let path = if let Some(impl_trait_name) = impl_trait_name { 95 let path = if let Some(impl_trait_name) = impl_trait_name {
90 path_iter 96 path_iter
91 .chain(std::iter::once(impl_trait_name)) 97 .chain(std::iter::once(impl_trait_name))
92 .chain(std::iter::once(name_string)) 98 .chain(std::iter::once(name_string))
93 .join("::") 99 .join("::")
94 } else { 100 } else {
95 path_iter.chain(std::iter::once(name_string)).join("::") 101 path_iter.chain(std::iter::once(name_string)).join("::")
96 }; 102 };
97 103
98 TestId::Path(path) 104 TestId::Path(path)
99 } else { 105 }
100 TestId::Name(name_string) 106 None => TestId::Name(name_string),
101 }; 107 };
102 108
103 if has_test_related_attribute(&fn_def) { 109 if has_test_related_attribute(&fn_def) {
@@ -111,7 +117,12 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Run
111 return None; 117 return None;
112 } 118 }
113 }; 119 };
114 Some(Runnable { range: fn_def.syntax().text_range(), kind }) 120
121 let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def));
122 let cfg_exprs =
123 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
124
125 Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs })
115} 126}
116 127
117#[derive(Debug)] 128#[derive(Debug)]
@@ -147,7 +158,11 @@ fn has_doc_test(fn_def: &ast::FnDef) -> bool {
147 fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) 158 fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```"))
148} 159}
149 160
150fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { 161fn runnable_mod(
162 sema: &Semantics<RootDatabase>,
163 module: ast::Module,
164 file_id: FileId,
165) -> Option<Runnable> {
151 let has_test_function = module 166 let has_test_function = module
152 .item_list()? 167 .item_list()?
153 .items() 168 .items()
@@ -160,11 +175,20 @@ fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<R
160 return None; 175 return None;
161 } 176 }
162 let range = module.syntax().text_range(); 177 let range = module.syntax().text_range();
163 let module = sema.to_def(&module)?; 178 let module_def = sema.to_def(&module)?;
164 179
165 let path = 180 let path = module_def
166 module.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::"); 181 .path_to_root(sema.db)
167 Some(Runnable { range, kind: RunnableKind::TestMod { path } }) 182 .into_iter()
183 .rev()
184 .filter_map(|it| it.name(sema.db))
185 .join("::");
186
187 let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module));
188 let cfg_exprs =
189 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
190
191 Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs })
168} 192}
169 193
170#[cfg(test)] 194#[cfg(test)]
@@ -196,6 +220,7 @@ mod tests {
196 Runnable { 220 Runnable {
197 range: 1..21, 221 range: 1..21,
198 kind: Bin, 222 kind: Bin,
223 cfg_exprs: [],
199 }, 224 },
200 Runnable { 225 Runnable {
201 range: 22..46, 226 range: 22..46,
@@ -207,6 +232,7 @@ mod tests {
207 ignore: false, 232 ignore: false,
208 }, 233 },
209 }, 234 },
235 cfg_exprs: [],
210 }, 236 },
211 Runnable { 237 Runnable {
212 range: 47..81, 238 range: 47..81,
@@ -218,6 +244,7 @@ mod tests {
218 ignore: true, 244 ignore: true,
219 }, 245 },
220 }, 246 },
247 cfg_exprs: [],
221 }, 248 },
222 ] 249 ]
223 "### 250 "###
@@ -245,6 +272,7 @@ mod tests {
245 Runnable { 272 Runnable {
246 range: 1..21, 273 range: 1..21,
247 kind: Bin, 274 kind: Bin,
275 cfg_exprs: [],
248 }, 276 },
249 Runnable { 277 Runnable {
250 range: 22..64, 278 range: 22..64,
@@ -253,6 +281,7 @@ mod tests {
253 "foo", 281 "foo",
254 ), 282 ),
255 }, 283 },
284 cfg_exprs: [],
256 }, 285 },
257 ] 286 ]
258 "### 287 "###
@@ -283,6 +312,7 @@ mod tests {
283 Runnable { 312 Runnable {
284 range: 1..21, 313 range: 1..21,
285 kind: Bin, 314 kind: Bin,
315 cfg_exprs: [],
286 }, 316 },
287 Runnable { 317 Runnable {
288 range: 51..105, 318 range: 51..105,
@@ -291,6 +321,7 @@ mod tests {
291 "Data::foo", 321 "Data::foo",
292 ), 322 ),
293 }, 323 },
324 cfg_exprs: [],
294 }, 325 },
295 ] 326 ]
296 "### 327 "###
@@ -318,6 +349,7 @@ mod tests {
318 kind: TestMod { 349 kind: TestMod {
319 path: "test_mod", 350 path: "test_mod",
320 }, 351 },
352 cfg_exprs: [],
321 }, 353 },
322 Runnable { 354 Runnable {
323 range: 28..57, 355 range: 28..57,
@@ -329,6 +361,7 @@ mod tests {
329 ignore: false, 361 ignore: false,
330 }, 362 },
331 }, 363 },
364 cfg_exprs: [],
332 }, 365 },
333 ] 366 ]
334 "### 367 "###
@@ -358,6 +391,7 @@ mod tests {
358 kind: TestMod { 391 kind: TestMod {
359 path: "foo::test_mod", 392 path: "foo::test_mod",
360 }, 393 },
394 cfg_exprs: [],
361 }, 395 },
362 Runnable { 396 Runnable {
363 range: 46..79, 397 range: 46..79,
@@ -369,6 +403,7 @@ mod tests {
369 ignore: false, 403 ignore: false,
370 }, 404 },
371 }, 405 },
406 cfg_exprs: [],
372 }, 407 },
373 ] 408 ]
374 "### 409 "###
@@ -400,6 +435,7 @@ mod tests {
400 kind: TestMod { 435 kind: TestMod {
401 path: "foo::bar::test_mod", 436 path: "foo::bar::test_mod",
402 }, 437 },
438 cfg_exprs: [],
403 }, 439 },
404 Runnable { 440 Runnable {
405 range: 68..105, 441 range: 68..105,
@@ -411,6 +447,89 @@ mod tests {
411 ignore: false, 447 ignore: false,
412 }, 448 },
413 }, 449 },
450 cfg_exprs: [],
451 },
452 ]
453 "###
454 );
455 }
456
457 #[test]
458 fn test_runnables_with_feature() {
459 let (analysis, pos) = analysis_and_position(
460 r#"
461 //- /lib.rs crate:foo cfg:feature=foo
462 <|> //empty
463 #[test]
464 #[cfg(feature = "foo")]
465 fn test_foo1() {}
466 "#,
467 );
468 let runnables = analysis.runnables(pos.file_id).unwrap();
469 assert_debug_snapshot!(&runnables,
470 @r###"
471 [
472 Runnable {
473 range: 1..58,
474 kind: Test {
475 test_id: Path(
476 "test_foo1",
477 ),
478 attr: TestAttr {
479 ignore: false,
480 },
481 },
482 cfg_exprs: [
483 KeyValue {
484 key: "feature",
485 value: "foo",
486 },
487 ],
488 },
489 ]
490 "###
491 );
492 }
493
494 #[test]
495 fn test_runnables_with_features() {
496 let (analysis, pos) = analysis_and_position(
497 r#"
498 //- /lib.rs crate:foo cfg:feature=foo,feature=bar
499 <|> //empty
500 #[test]
501 #[cfg(all(feature = "foo", feature = "bar"))]
502 fn test_foo1() {}
503 "#,
504 );
505 let runnables = analysis.runnables(pos.file_id).unwrap();
506 assert_debug_snapshot!(&runnables,
507 @r###"
508 [
509 Runnable {
510 range: 1..80,
511 kind: Test {
512 test_id: Path(
513 "test_foo1",
514 ),
515 attr: TestAttr {
516 ignore: false,
517 },
518 },
519 cfg_exprs: [
520 All(
521 [
522 KeyValue {
523 key: "feature",
524 value: "foo",
525 },
526 KeyValue {
527 key: "feature",
528 value: "bar",
529 },
530 ],
531 ),
532 ],
414 }, 533 },
415 ] 534 ]
416 "### 535 "###
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html
index ea026d7a0..68fc589bc 100644
--- a/crates/ra_ide/src/snapshots/highlight_injection.html
+++ b/crates/ra_ide/src/snapshots/highlight_injection.html
@@ -17,6 +17,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
17.type_param { color: #DFAF8F; } 17.type_param { color: #DFAF8F; }
18.attribute { color: #94BFF3; } 18.attribute { color: #94BFF3; }
19.numeric_literal { color: #BFEBBF; } 19.numeric_literal { color: #BFEBBF; }
20.bool_literal { color: #BFE6EB; }
20.macro { color: #94BFF3; } 21.macro { color: #94BFF3; }
21.module { color: #AFD8AF; } 22.module { color: #AFD8AF; }
22.variable { color: #DCDCCC; } 23.variable { color: #DCDCCC; }
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html
index 752b487e8..326744361 100644
--- a/crates/ra_ide/src/snapshots/highlight_strings.html
+++ b/crates/ra_ide/src/snapshots/highlight_strings.html
@@ -17,6 +17,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
17.type_param { color: #DFAF8F; } 17.type_param { color: #DFAF8F; }
18.attribute { color: #94BFF3; } 18.attribute { color: #94BFF3; }
19.numeric_literal { color: #BFEBBF; } 19.numeric_literal { color: #BFEBBF; }
20.bool_literal { color: #BFE6EB; }
20.macro { color: #94BFF3; } 21.macro { color: #94BFF3; }
21.module { color: #AFD8AF; } 22.module { color: #AFD8AF; }
22.variable { color: #DCDCCC; } 23.variable { color: #DCDCCC; }
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index 635fe5cf9..352e35095 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -17,6 +17,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
17.type_param { color: #DFAF8F; } 17.type_param { color: #DFAF8F; }
18.attribute { color: #94BFF3; } 18.attribute { color: #94BFF3; }
19.numeric_literal { color: #BFEBBF; } 19.numeric_literal { color: #BFEBBF; }
20.bool_literal { color: #BFE6EB; }
20.macro { color: #94BFF3; } 21.macro { color: #94BFF3; }
21.module { color: #AFD8AF; } 22.module { color: #AFD8AF; }
22.variable { color: #DCDCCC; } 23.variable { color: #DCDCCC; }
@@ -27,19 +28,19 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
27.keyword.unsafe { color: #BC8383; font-weight: bold; } 28.keyword.unsafe { color: #BC8383; font-weight: bold; }
28.control { font-style: italic; } 29.control { font-style: italic; }
29</style> 30</style>
30<pre><code><span class="attribute">#[derive(Clone, Debug)]</span> 31<pre><code><span class="attribute">#[</span><span class="function attribute">derive</span><span class="attribute">(Clone, Debug)]</span>
31<span class="keyword">struct</span> <span class="struct declaration">Foo</span> { 32<span class="keyword">struct</span> <span class="struct declaration">Foo</span> {
32 <span class="keyword">pub</span> <span class="field declaration">x</span>: <span class="builtin_type">i32</span>, 33 <span class="keyword">pub</span> <span class="field declaration">x</span>: <span class="builtin_type">i32</span>,
33 <span class="keyword">pub</span> <span class="field declaration">y</span>: <span class="builtin_type">i32</span>, 34 <span class="keyword">pub</span> <span class="field declaration">y</span>: <span class="builtin_type">i32</span>,
34} 35}
35 36
36<span class="keyword">trait</span> <span class="trait declaration">Bar</span> { 37<span class="keyword">trait</span> <span class="trait declaration">Bar</span> {
37 <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="keyword">self</span>) -&gt; <span class="builtin_type">i32</span>; 38 <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">i32</span>;
38} 39}
39 40
40<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> { 41<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> {
41 <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="keyword">self</span>) -&gt; <span class="builtin_type">i32</span> { 42 <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">i32</span> {
42 <span class="keyword">self</span>.<span class="field">x</span> 43 <span class="self_keyword">self</span>.<span class="field">x</span>
43 } 44 }
44} 45}
45 46
@@ -64,7 +65,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
64 <span class="macro">println!</span>(<span class="string_literal">"Hello, {}!"</span>, <span class="numeric_literal">92</span>); 65 <span class="macro">println!</span>(<span class="string_literal">"Hello, {}!"</span>, <span class="numeric_literal">92</span>);
65 66
66 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> = <span class="unresolved_reference">Vec</span>::<span class="unresolved_reference">new</span>(); 67 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> = <span class="unresolved_reference">Vec</span>::<span class="unresolved_reference">new</span>();
67 <span class="keyword control">if</span> <span class="keyword">true</span> { 68 <span class="keyword control">if</span> <span class="bool_literal">true</span> {
68 <span class="keyword">let</span> <span class="variable declaration">x</span> = <span class="numeric_literal">92</span>; 69 <span class="keyword">let</span> <span class="variable declaration">x</span> = <span class="numeric_literal">92</span>;
69 <span class="variable mutable">vec</span>.<span class="unresolved_reference">push</span>(<span class="struct">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="numeric_literal">1</span> }); 70 <span class="variable mutable">vec</span>.<span class="unresolved_reference">push</span>(<span class="struct">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="numeric_literal">1</span> });
70 } 71 }
@@ -91,7 +92,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
91<span class="keyword">use</span> <span class="enum">Option</span>::*; 92<span class="keyword">use</span> <span class="enum">Option</span>::*;
92 93
93<span class="keyword">impl</span>&lt;<span class="type_param declaration">T</span>&gt; <span class="enum">Option</span>&lt;<span class="type_param">T</span>&gt; { 94<span class="keyword">impl</span>&lt;<span class="type_param declaration">T</span>&gt; <span class="enum">Option</span>&lt;<span class="type_param">T</span>&gt; {
94 <span class="keyword">fn</span> <span class="function declaration">and</span>&lt;<span class="type_param declaration">U</span>&gt;(<span class="keyword">self</span>, <span class="variable declaration">other</span>: <span class="enum">Option</span>&lt;<span class="type_param">U</span>&gt;) -&gt; <span class="enum">Option</span>&lt;(<span class="type_param">T</span>, <span class="type_param">U</span>)&gt; { 95 <span class="keyword">fn</span> <span class="function declaration">and</span>&lt;<span class="type_param declaration">U</span>&gt;(<span class="self_keyword">self</span>, <span class="variable declaration">other</span>: <span class="enum">Option</span>&lt;<span class="type_param">U</span>&gt;) -&gt; <span class="enum">Option</span>&lt;(<span class="type_param">T</span>, <span class="type_param">U</span>)&gt; {
95 <span class="keyword control">match</span> <span class="variable">other</span> { 96 <span class="keyword control">match</span> <span class="variable">other</span> {
96 <span class="enum_variant">None</span> =&gt; <span class="macro">unimplemented!</span>(), 97 <span class="enum_variant">None</span> =&gt; <span class="macro">unimplemented!</span>(),
97 <span class="variable declaration">Nope</span> =&gt; <span class="variable">Nope</span>, 98 <span class="variable declaration">Nope</span> =&gt; <span class="variable">Nope</span>,
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index 11e1f3e44..2a0294f71 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -17,6 +17,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
17.type_param { color: #DFAF8F; } 17.type_param { color: #DFAF8F; }
18.attribute { color: #94BFF3; } 18.attribute { color: #94BFF3; }
19.numeric_literal { color: #BFEBBF; } 19.numeric_literal { color: #BFEBBF; }
20.bool_literal { color: #BFE6EB; }
20.macro { color: #94BFF3; } 21.macro { color: #94BFF3; }
21.module { color: #AFD8AF; } 22.module { color: #AFD8AF; }
22.variable { color: #DCDCCC; } 23.variable { color: #DCDCCC; }
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 1873d1d0d..130d3b4c3 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -196,10 +196,10 @@ fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches {
196 ) -> Option<Match> { 196 ) -> Option<Match> {
197 let match_ = check_opt_nodes(pattern.path(), code.path(), placeholders, match_)?; 197 let match_ = check_opt_nodes(pattern.path(), code.path(), placeholders, match_)?;
198 198
199 let mut pattern_fields = 199 let mut pattern_fields: Vec<RecordField> =
200 pattern.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]); 200 pattern.record_field_list().map(|x| x.fields().collect()).unwrap_or_default();
201 let mut code_fields = 201 let mut code_fields: Vec<RecordField> =
202 code.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]); 202 code.record_field_list().map(|x| x.fields().collect()).unwrap_or_default();
203 203
204 if pattern_fields.len() != code_fields.len() { 204 if pattern_fields.len() != code_fields.len() {
205 return None; 205 return None;
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index be57eeb0a..8a995d779 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -361,7 +361,9 @@ fn highlight_element(
361 } 361 }
362 362
363 // Highlight references like the definitions they resolve to 363 // Highlight references like the definitions they resolve to
364 NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => return None, 364 NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => {
365 Highlight::from(HighlightTag::Function) | HighlightModifier::Attribute
366 }
365 NAME_REF => { 367 NAME_REF => {
366 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); 368 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
367 match classify_name_ref(sema, &name_ref) { 369 match classify_name_ref(sema, &name_ref) {
@@ -411,6 +413,8 @@ fn highlight_element(
411 | T![in] => h | HighlightModifier::ControlFlow, 413 | T![in] => h | HighlightModifier::ControlFlow,
412 T![for] if !is_child_of_impl(element) => h | HighlightModifier::ControlFlow, 414 T![for] if !is_child_of_impl(element) => h | HighlightModifier::ControlFlow,
413 T![unsafe] => h | HighlightModifier::Unsafe, 415 T![unsafe] => h | HighlightModifier::Unsafe,
416 T![true] | T![false] => HighlightTag::BoolLiteral.into(),
417 T![self] => HighlightTag::SelfKeyword.into(),
414 _ => h, 418 _ => h,
415 } 419 }
416 } 420 }
@@ -478,23 +482,31 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight {
478} 482}
479 483
480fn highlight_name_by_syntax(name: ast::Name) -> Highlight { 484fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
481 let default = HighlightTag::Function.into(); 485 let default = HighlightTag::UnresolvedReference;
482 486
483 let parent = match name.syntax().parent() { 487 let parent = match name.syntax().parent() {
484 Some(it) => it, 488 Some(it) => it,
485 _ => return default, 489 _ => return default.into(),
486 }; 490 };
487 491
488 match parent.kind() { 492 let tag = match parent.kind() {
489 STRUCT_DEF => HighlightTag::Struct.into(), 493 STRUCT_DEF => HighlightTag::Struct,
490 ENUM_DEF => HighlightTag::Enum.into(), 494 ENUM_DEF => HighlightTag::Enum,
491 UNION_DEF => HighlightTag::Union.into(), 495 UNION_DEF => HighlightTag::Union,
492 TRAIT_DEF => HighlightTag::Trait.into(), 496 TRAIT_DEF => HighlightTag::Trait,
493 TYPE_ALIAS_DEF => HighlightTag::TypeAlias.into(), 497 TYPE_ALIAS_DEF => HighlightTag::TypeAlias,
494 TYPE_PARAM => HighlightTag::TypeParam.into(), 498 TYPE_PARAM => HighlightTag::TypeParam,
495 RECORD_FIELD_DEF => HighlightTag::Field.into(), 499 RECORD_FIELD_DEF => HighlightTag::Field,
500 MODULE => HighlightTag::Module,
501 FN_DEF => HighlightTag::Function,
502 CONST_DEF => HighlightTag::Constant,
503 STATIC_DEF => HighlightTag::Static,
504 ENUM_VARIANT => HighlightTag::EnumVariant,
505 BIND_PAT => HighlightTag::Local,
496 _ => default, 506 _ => default,
497 } 507 };
508
509 tag.into()
498} 510}
499 511
500fn highlight_injection( 512fn highlight_injection(
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs
index ff0eeeb52..edfe61f39 100644
--- a/crates/ra_ide/src/syntax_highlighting/html.rs
+++ b/crates/ra_ide/src/syntax_highlighting/html.rs
@@ -76,6 +76,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
76.type_param { color: #DFAF8F; } 76.type_param { color: #DFAF8F; }
77.attribute { color: #94BFF3; } 77.attribute { color: #94BFF3; }
78.numeric_literal { color: #BFEBBF; } 78.numeric_literal { color: #BFEBBF; }
79.bool_literal { color: #BFE6EB; }
79.macro { color: #94BFF3; } 80.macro { color: #94BFF3; }
80.module { color: #AFD8AF; } 81.module { color: #AFD8AF; }
81.variable { color: #DCDCCC; } 82.variable { color: #DCDCCC; }
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index be1a0f12b..46c718c91 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -15,6 +15,7 @@ pub struct HighlightModifiers(u32);
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
16pub enum HighlightTag { 16pub enum HighlightTag {
17 Attribute, 17 Attribute,
18 BoolLiteral,
18 BuiltinType, 19 BuiltinType,
19 ByteLiteral, 20 ByteLiteral,
20 CharLiteral, 21 CharLiteral,
@@ -29,6 +30,7 @@ pub enum HighlightTag {
29 Macro, 30 Macro,
30 Module, 31 Module,
31 NumericLiteral, 32 NumericLiteral,
33 SelfKeyword,
32 SelfType, 34 SelfType,
33 Static, 35 Static,
34 StringLiteral, 36 StringLiteral,
@@ -45,8 +47,10 @@ pub enum HighlightTag {
45#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 47#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
46#[repr(u8)] 48#[repr(u8)]
47pub enum HighlightModifier { 49pub enum HighlightModifier {
50 /// Used to differentiate individual elements within attributes.
51 Attribute = 0,
48 /// Used with keywords like `if` and `break`. 52 /// Used with keywords like `if` and `break`.
49 ControlFlow = 0, 53 ControlFlow,
50 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is 54 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is
51 /// not. 55 /// not.
52 Definition, 56 Definition,
@@ -58,6 +62,7 @@ impl HighlightTag {
58 fn as_str(self) -> &'static str { 62 fn as_str(self) -> &'static str {
59 match self { 63 match self {
60 HighlightTag::Attribute => "attribute", 64 HighlightTag::Attribute => "attribute",
65 HighlightTag::BoolLiteral => "bool_literal",
61 HighlightTag::BuiltinType => "builtin_type", 66 HighlightTag::BuiltinType => "builtin_type",
62 HighlightTag::ByteLiteral => "byte_literal", 67 HighlightTag::ByteLiteral => "byte_literal",
63 HighlightTag::CharLiteral => "char_literal", 68 HighlightTag::CharLiteral => "char_literal",
@@ -72,6 +77,7 @@ impl HighlightTag {
72 HighlightTag::Macro => "macro", 77 HighlightTag::Macro => "macro",
73 HighlightTag::Module => "module", 78 HighlightTag::Module => "module",
74 HighlightTag::NumericLiteral => "numeric_literal", 79 HighlightTag::NumericLiteral => "numeric_literal",
80 HighlightTag::SelfKeyword => "self_keyword",
75 HighlightTag::SelfType => "self_type", 81 HighlightTag::SelfType => "self_type",
76 HighlightTag::Static => "static", 82 HighlightTag::Static => "static",
77 HighlightTag::StringLiteral => "string_literal", 83 HighlightTag::StringLiteral => "string_literal",
@@ -95,6 +101,7 @@ impl fmt::Display for HighlightTag {
95 101
96impl HighlightModifier { 102impl HighlightModifier {
97 const ALL: &'static [HighlightModifier] = &[ 103 const ALL: &'static [HighlightModifier] = &[
104 HighlightModifier::Attribute,
98 HighlightModifier::ControlFlow, 105 HighlightModifier::ControlFlow,
99 HighlightModifier::Definition, 106 HighlightModifier::Definition,
100 HighlightModifier::Mutable, 107 HighlightModifier::Mutable,
@@ -103,6 +110,7 @@ impl HighlightModifier {
103 110
104 fn as_str(self) -> &'static str { 111 fn as_str(self) -> &'static str {
105 match self { 112 match self {
113 HighlightModifier::Attribute => "attribute",
106 HighlightModifier::ControlFlow => "control", 114 HighlightModifier::ControlFlow => "control",
107 HighlightModifier::Definition => "declaration", 115 HighlightModifier::Definition => "declaration",
108 HighlightModifier::Mutable => "mutable", 116 HighlightModifier::Mutable => "mutable",
diff --git a/crates/ra_ide/src/typing/on_enter.rs b/crates/ra_ide/src/typing/on_enter.rs
index e7d64b4f6..a40d8af9c 100644
--- a/crates/ra_ide/src/typing/on_enter.rs
+++ b/crates/ra_ide/src/typing/on_enter.rs
@@ -11,9 +11,7 @@ use ra_syntax::{
11}; 11};
12use ra_text_edit::TextEdit; 12use ra_text_edit::TextEdit;
13 13
14use crate::{SourceChange, SourceFileEdit}; 14pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> {
15
16pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> {
17 let parse = db.parse(position.file_id); 15 let parse = db.parse(position.file_id);
18 let file = parse.tree(); 16 let file = parse.tree();
19 let comment = file 17 let comment = file
@@ -41,9 +39,7 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Sour
41 let inserted = format!("\n{}{} $0", indent, prefix); 39 let inserted = format!("\n{}{} $0", indent, prefix);
42 let edit = TextEdit::insert(position.offset, inserted); 40 let edit = TextEdit::insert(position.offset, inserted);
43 41
44 let mut res = SourceChange::from(SourceFileEdit { edit, file_id: position.file_id }); 42 Some(edit)
45 res.is_snippet = true;
46 Some(res)
47} 43}
48 44
49fn followed_by_comment(comment: &ast::Comment) -> bool { 45fn followed_by_comment(comment: &ast::Comment) -> bool {
@@ -90,9 +86,8 @@ mod tests {
90 let (analysis, file_id) = single_file(&before); 86 let (analysis, file_id) = single_file(&before);
91 let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?; 87 let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?;
92 88
93 assert_eq!(result.source_file_edits.len(), 1);
94 let mut actual = before.to_string(); 89 let mut actual = before.to_string();
95 result.source_file_edits[0].edit.apply(&mut actual); 90 result.apply(&mut actual);
96 Some(actual) 91 Some(actual)
97 } 92 }
98 93
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml
index c07ff488e..a8ff2e74f 100644
--- a/crates/ra_syntax/Cargo.toml
+++ b/crates/ra_syntax/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
13[dependencies] 13[dependencies]
14itertools = "0.9.0" 14itertools = "0.9.0"
15rowan = "0.10.0" 15rowan = "0.10.0"
16rustc_lexer = { version = "656.0.0", package = "rustc-ap-rustc_lexer" } 16rustc_lexer = { version = "661.0.0", package = "rustc-ap-rustc_lexer" }
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18arrayvec = "0.5.1" 18arrayvec = "0.5.1"
19once_cell = "1.3.1" 19once_cell = "1.3.1"
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs
index 74906d8a6..3cd6d99c3 100644
--- a/crates/ra_syntax/src/ast/tokens.rs
+++ b/crates/ra_syntax/src/ast/tokens.rs
@@ -6,6 +6,7 @@ use crate::{
6 ast::{AstToken, Comment, RawString, String, Whitespace}, 6 ast::{AstToken, Comment, RawString, String, Whitespace},
7 TextRange, TextSize, 7 TextRange, TextSize,
8}; 8};
9use rustc_lexer::unescape::{unescape_literal, Mode};
9 10
10impl Comment { 11impl Comment {
11 pub fn kind(&self) -> CommentKind { 12 pub fn kind(&self) -> CommentKind {
@@ -147,7 +148,7 @@ impl HasStringValue for String {
147 148
148 let mut buf = std::string::String::with_capacity(text.len()); 149 let mut buf = std::string::String::with_capacity(text.len());
149 let mut has_error = false; 150 let mut has_error = false;
150 rustc_lexer::unescape::unescape_str(text, &mut |_, unescaped_char| match unescaped_char { 151 unescape_literal(text, Mode::Str, &mut |_, unescaped_char| match unescaped_char {
151 Ok(c) => buf.push(c), 152 Ok(c) => buf.push(c),
152 Err(_) => has_error = true, 153 Err(_) => has_error = true,
153 }); 154 });
@@ -498,7 +499,7 @@ impl HasFormatSpecifier for String {
498 let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start(); 499 let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start();
499 500
500 let mut res = Vec::with_capacity(text.len()); 501 let mut res = Vec::with_capacity(text.len());
501 rustc_lexer::unescape::unescape_str(text, &mut |range, unescaped_char| { 502 unescape_literal(text, Mode::Str, &mut |range, unescaped_char| {
502 res.push(( 503 res.push((
503 TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap()) 504 TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap())
504 + offset, 505 + offset,
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs
index d68cf0a82..fdec48fb0 100644
--- a/crates/ra_syntax/src/validation.rs
+++ b/crates/ra_syntax/src/validation.rs
@@ -2,15 +2,15 @@
2 2
3mod block; 3mod block;
4 4
5use std::convert::TryFrom;
6
7use rustc_lexer::unescape;
8
9use crate::{ 5use crate::{
10 ast, match_ast, AstNode, SyntaxError, 6 ast, match_ast, AstNode, SyntaxError,
11 SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST_DEF, FN_DEF, INT_NUMBER, STRING, TYPE_ALIAS_DEF}, 7 SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST_DEF, FN_DEF, INT_NUMBER, STRING, TYPE_ALIAS_DEF},
12 SyntaxNode, SyntaxToken, TextSize, T, 8 SyntaxNode, SyntaxToken, TextSize, T,
13}; 9};
10use rustc_lexer::unescape::{
11 self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode,
12};
13use std::convert::TryFrom;
14 14
15fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str { 15fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str {
16 use unescape::EscapeError as EE; 16 use unescape::EscapeError as EE;
@@ -81,10 +81,8 @@ fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str {
81 81
82pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { 82pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
83 // FIXME: 83 // FIXME:
84 // * Add validation of character literal containing only a single char 84 // * Add unescape validation of raw string literals and raw byte string literals
85 // * Add validation of `crate` keyword not appearing in the middle of the symbol path
86 // * Add validation of doc comments are being attached to nodes 85 // * Add validation of doc comments are being attached to nodes
87 // * Remove validation of unterminated literals (it is already implemented in `tokenize()`)
88 86
89 let mut errors = Vec::new(); 87 let mut errors = Vec::new();
90 for node in root.descendants() { 88 for node in root.descendants() {
@@ -121,18 +119,18 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
121 119
122 match token.kind() { 120 match token.kind() {
123 BYTE => { 121 BYTE => {
124 if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape::unescape_byte) { 122 if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape_byte) {
125 push_err(2, e); 123 push_err(2, e);
126 } 124 }
127 } 125 }
128 CHAR => { 126 CHAR => {
129 if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape::unescape_char) { 127 if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape_char) {
130 push_err(1, e); 128 push_err(1, e);
131 } 129 }
132 } 130 }
133 BYTE_STRING => { 131 BYTE_STRING => {
134 if let Some(without_quotes) = unquote(text, 2, '"') { 132 if let Some(without_quotes) = unquote(text, 2, '"') {
135 unescape::unescape_byte_str(without_quotes, &mut |range, char| { 133 unescape_byte_literal(without_quotes, Mode::ByteStr, &mut |range, char| {
136 if let Err(err) = char { 134 if let Err(err) = char {
137 push_err(2, (range.start, err)); 135 push_err(2, (range.start, err));
138 } 136 }
@@ -141,7 +139,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
141 } 139 }
142 STRING => { 140 STRING => {
143 if let Some(without_quotes) = unquote(text, 1, '"') { 141 if let Some(without_quotes) = unquote(text, 1, '"') {
144 unescape::unescape_str(without_quotes, &mut |range, char| { 142 unescape_literal(without_quotes, Mode::Str, &mut |range, char| {
145 if let Err(err) = char { 143 if let Err(err) = char {
146 push_err(1, (range.start, err)); 144 push_err(1, (range.start, err));
147 } 145 }
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 9b2d29b1d..65b487db3 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -40,6 +40,7 @@ ra_project_model = { path = "../ra_project_model" }
40ra_syntax = { path = "../ra_syntax" } 40ra_syntax = { path = "../ra_syntax" }
41ra_text_edit = { path = "../ra_text_edit" } 41ra_text_edit = { path = "../ra_text_edit" }
42ra_vfs = "0.6.0" 42ra_vfs = "0.6.0"
43ra_cfg = { path = "../ra_cfg"}
43 44
44# This should only be used in CLI 45# This should only be used in CLI
45ra_db = { path = "../ra_db" } 46ra_db = { path = "../ra_db" }
@@ -55,6 +56,8 @@ winapi = "0.3.8"
55tempfile = "3.1.0" 56tempfile = "3.1.0"
56insta = "0.16.0" 57insta = "0.16.0"
57test_utils = { path = "../test_utils" } 58test_utils = { path = "../test_utils" }
59mbe = { path = "../ra_mbe", package = "ra_mbe" }
60tt = { path = "../ra_tt", package = "ra_tt" }
58 61
59[features] 62[features]
60jemalloc = [ "ra_prof/jemalloc" ] 63jemalloc = [ "ra_prof/jemalloc" ]
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 780fc9317..345693524 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -85,6 +85,8 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
85 experimental: Some(json!({ 85 experimental: Some(json!({
86 "joinLines": true, 86 "joinLines": true,
87 "ssr": true, 87 "ssr": true,
88 "onEnter": true,
89 "parentModule": true,
88 })), 90 })),
89 } 91 }
90} 92}
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index 5e5a17943..441fb61df 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -4,6 +4,7 @@ use ra_ide::{FileId, RunnableKind, TestId};
4use ra_project_model::{self, ProjectWorkspace, TargetKind}; 4use ra_project_model::{self, ProjectWorkspace, TargetKind};
5 5
6use crate::{world::WorldSnapshot, Result}; 6use crate::{world::WorldSnapshot, Result};
7use ra_syntax::SmolStr;
7 8
8/// Abstract representation of Cargo target. 9/// Abstract representation of Cargo target.
9/// 10///
@@ -20,6 +21,7 @@ impl CargoTargetSpec {
20 pub(crate) fn runnable_args( 21 pub(crate) fn runnable_args(
21 spec: Option<CargoTargetSpec>, 22 spec: Option<CargoTargetSpec>,
22 kind: &RunnableKind, 23 kind: &RunnableKind,
24 features_needed: &Vec<SmolStr>,
23 ) -> Result<(Vec<String>, Vec<String>)> { 25 ) -> Result<(Vec<String>, Vec<String>)> {
24 let mut args = Vec::new(); 26 let mut args = Vec::new();
25 let mut extra_args = Vec::new(); 27 let mut extra_args = Vec::new();
@@ -73,6 +75,12 @@ impl CargoTargetSpec {
73 } 75 }
74 } 76 }
75 } 77 }
78
79 features_needed.iter().for_each(|feature| {
80 args.push("--features".to_string());
81 args.push(feature.to_string());
82 });
83
76 Ok((args, extra_args)) 84 Ok((args, extra_args))
77 } 85 }
78 86
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index c25d90a50..acb1dacb6 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -3,7 +3,7 @@
3use std::{collections::HashMap, path::PathBuf}; 3use std::{collections::HashMap, path::PathBuf};
4 4
5use lsp_types::request::Request; 5use lsp_types::request::Request;
6use lsp_types::{Location, Position, Range, TextDocumentIdentifier}; 6use lsp_types::{Position, Range, TextDocumentIdentifier};
7use rustc_hash::FxHashMap; 7use rustc_hash::FxHashMap;
8use serde::{Deserialize, Serialize}; 8use serde::{Deserialize, Serialize};
9 9
@@ -38,13 +38,6 @@ pub struct SyntaxTreeParams {
38 pub range: Option<Range>, 38 pub range: Option<Range>,
39} 39}
40 40
41#[derive(Deserialize, Serialize, Debug)]
42#[serde(rename_all = "camelCase")]
43pub struct ExpandedMacro {
44 pub name: String,
45 pub expansion: String,
46}
47
48pub enum ExpandMacro {} 41pub enum ExpandMacro {}
49 42
50impl Request for ExpandMacro { 43impl Request for ExpandMacro {
@@ -57,30 +50,37 @@ impl Request for ExpandMacro {
57#[serde(rename_all = "camelCase")] 50#[serde(rename_all = "camelCase")]
58pub struct ExpandMacroParams { 51pub struct ExpandMacroParams {
59 pub text_document: TextDocumentIdentifier, 52 pub text_document: TextDocumentIdentifier,
60 pub position: Option<Position>, 53 pub position: Position,
54}
55
56#[derive(Deserialize, Serialize, Debug)]
57#[serde(rename_all = "camelCase")]
58pub struct ExpandedMacro {
59 pub name: String,
60 pub expansion: String,
61} 61}
62 62
63pub enum FindMatchingBrace {} 63pub enum MatchingBrace {}
64 64
65impl Request for FindMatchingBrace { 65impl Request for MatchingBrace {
66 type Params = FindMatchingBraceParams; 66 type Params = MatchingBraceParams;
67 type Result = Vec<Position>; 67 type Result = Vec<Position>;
68 const METHOD: &'static str = "rust-analyzer/findMatchingBrace"; 68 const METHOD: &'static str = "experimental/matchingBrace";
69} 69}
70 70
71#[derive(Deserialize, Serialize, Debug)] 71#[derive(Deserialize, Serialize, Debug)]
72#[serde(rename_all = "camelCase")] 72#[serde(rename_all = "camelCase")]
73pub struct FindMatchingBraceParams { 73pub struct MatchingBraceParams {
74 pub text_document: TextDocumentIdentifier, 74 pub text_document: TextDocumentIdentifier,
75 pub offsets: Vec<Position>, 75 pub positions: Vec<Position>,
76} 76}
77 77
78pub enum ParentModule {} 78pub enum ParentModule {}
79 79
80impl Request for ParentModule { 80impl Request for ParentModule {
81 type Params = lsp_types::TextDocumentPositionParams; 81 type Params = lsp_types::TextDocumentPositionParams;
82 type Result = Vec<Location>; 82 type Result = Option<lsp_types::GotoDefinitionResponse>;
83 const METHOD: &'static str = "rust-analyzer/parentModule"; 83 const METHOD: &'static str = "experimental/parentModule";
84} 84}
85 85
86pub enum JoinLines {} 86pub enum JoinLines {}
@@ -102,8 +102,8 @@ pub enum OnEnter {}
102 102
103impl Request for OnEnter { 103impl Request for OnEnter {
104 type Params = lsp_types::TextDocumentPositionParams; 104 type Params = lsp_types::TextDocumentPositionParams;
105 type Result = Option<SnippetWorkspaceEdit>; 105 type Result = Option<Vec<SnippetTextEdit>>;
106 const METHOD: &'static str = "rust-analyzer/onEnter"; 106 const METHOD: &'static str = "experimental/onEnter";
107} 107}
108 108
109pub enum Runnables {} 109pub enum Runnables {}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 87795fffb..f1287d52c 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -509,9 +509,7 @@ fn on_request(
509 .on_sync::<lsp_types::request::SelectionRangeRequest>(|s, p| { 509 .on_sync::<lsp_types::request::SelectionRangeRequest>(|s, p| {
510 handlers::handle_selection_range(s.snapshot(), p) 510 handlers::handle_selection_range(s.snapshot(), p)
511 })? 511 })?
512 .on_sync::<lsp_ext::FindMatchingBrace>(|s, p| { 512 .on_sync::<lsp_ext::MatchingBrace>(|s, p| handlers::handle_matching_brace(s.snapshot(), p))?
513 handlers::handle_find_matching_brace(s.snapshot(), p)
514 })?
515 .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)? 513 .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)?
516 .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)? 514 .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)?
517 .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro)? 515 .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro)?
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index ba6857556..1f910ff82 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -17,13 +17,14 @@ use lsp_types::{
17 SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, 17 SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
18 SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, 18 SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit,
19}; 19};
20use ra_cfg::CfgExpr;
20use ra_ide::{ 21use ra_ide::{
21 FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, 22 FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
22 TextEdit, 23 TextEdit,
23}; 24};
24use ra_prof::profile; 25use ra_prof::profile;
25use ra_project_model::TargetKind; 26use ra_project_model::TargetKind;
26use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; 27use ra_syntax::{AstNode, SmolStr, SyntaxKind, TextRange, TextSize};
27use rustc_hash::FxHashMap; 28use rustc_hash::FxHashMap;
28use serde::{Deserialize, Serialize}; 29use serde::{Deserialize, Serialize};
29use serde_json::to_value; 30use serde_json::to_value;
@@ -71,15 +72,10 @@ pub fn handle_expand_macro(
71 let _p = profile("handle_expand_macro"); 72 let _p = profile("handle_expand_macro");
72 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 73 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
73 let line_index = world.analysis().file_line_index(file_id)?; 74 let line_index = world.analysis().file_line_index(file_id)?;
74 let offset = params.position.map(|p| from_proto::offset(&line_index, p)); 75 let offset = from_proto::offset(&line_index, params.position);
75 76
76 match offset { 77 let res = world.analysis().expand_macro(FilePosition { file_id, offset })?;
77 None => Ok(None), 78 Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion }))
78 Some(offset) => {
79 let res = world.analysis().expand_macro(FilePosition { file_id, offset })?;
80 Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion }))
81 }
82 }
83} 79}
84 80
85pub fn handle_selection_range( 81pub fn handle_selection_range(
@@ -125,15 +121,15 @@ pub fn handle_selection_range(
125 Ok(Some(res?)) 121 Ok(Some(res?))
126} 122}
127 123
128pub fn handle_find_matching_brace( 124pub fn handle_matching_brace(
129 world: WorldSnapshot, 125 world: WorldSnapshot,
130 params: lsp_ext::FindMatchingBraceParams, 126 params: lsp_ext::MatchingBraceParams,
131) -> Result<Vec<Position>> { 127) -> Result<Vec<Position>> {
132 let _p = profile("handle_find_matching_brace"); 128 let _p = profile("handle_matching_brace");
133 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 129 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
134 let line_index = world.analysis().file_line_index(file_id)?; 130 let line_index = world.analysis().file_line_index(file_id)?;
135 let res = params 131 let res = params
136 .offsets 132 .positions
137 .into_iter() 133 .into_iter()
138 .map(|position| { 134 .map(|position| {
139 let offset = from_proto::offset(&line_index, position); 135 let offset = from_proto::offset(&line_index, position);
@@ -173,13 +169,17 @@ pub fn handle_join_lines(
173pub fn handle_on_enter( 169pub fn handle_on_enter(
174 world: WorldSnapshot, 170 world: WorldSnapshot,
175 params: lsp_types::TextDocumentPositionParams, 171 params: lsp_types::TextDocumentPositionParams,
176) -> Result<Option<lsp_ext::SnippetWorkspaceEdit>> { 172) -> Result<Option<Vec<lsp_ext::SnippetTextEdit>>> {
177 let _p = profile("handle_on_enter"); 173 let _p = profile("handle_on_enter");
178 let position = from_proto::file_position(&world, params)?; 174 let position = from_proto::file_position(&world, params)?;
179 match world.analysis().on_enter(position)? { 175 let edit = match world.analysis().on_enter(position)? {
180 None => Ok(None), 176 None => return Ok(None),
181 Some(source_change) => to_proto::snippet_workspace_edit(&world, source_change).map(Some), 177 Some(it) => it,
182 } 178 };
179 let line_index = world.analysis().file_line_index(position.file_id)?;
180 let line_endings = world.file_line_endings(position.file_id);
181 let edit = to_proto::snippet_text_edit_vec(&line_index, line_endings, true, edit);
182 Ok(Some(edit))
183} 183}
184 184
185// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`. 185// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`.
@@ -344,11 +344,8 @@ pub fn handle_goto_definition(
344 None => return Ok(None), 344 None => return Ok(None),
345 Some(it) => it, 345 Some(it) => it,
346 }; 346 };
347 let res = to_proto::goto_definition_response( 347 let src = FileRange { file_id: position.file_id, range: nav_info.range };
348 &world, 348 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?;
349 FileRange { file_id: position.file_id, range: nav_info.range },
350 nav_info.info,
351 )?;
352 Ok(Some(res)) 349 Ok(Some(res))
353} 350}
354 351
@@ -362,11 +359,8 @@ pub fn handle_goto_implementation(
362 None => return Ok(None), 359 None => return Ok(None),
363 Some(it) => it, 360 Some(it) => it,
364 }; 361 };
365 let res = to_proto::goto_definition_response( 362 let src = FileRange { file_id: position.file_id, range: nav_info.range };
366 &world, 363 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?;
367 FileRange { file_id: position.file_id, range: nav_info.range },
368 nav_info.info,
369 )?;
370 Ok(Some(res)) 364 Ok(Some(res))
371} 365}
372 366
@@ -380,26 +374,20 @@ pub fn handle_goto_type_definition(
380 None => return Ok(None), 374 None => return Ok(None),
381 Some(it) => it, 375 Some(it) => it,
382 }; 376 };
383 let res = to_proto::goto_definition_response( 377 let src = FileRange { file_id: position.file_id, range: nav_info.range };
384 &world, 378 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?;
385 FileRange { file_id: position.file_id, range: nav_info.range },
386 nav_info.info,
387 )?;
388 Ok(Some(res)) 379 Ok(Some(res))
389} 380}
390 381
391pub fn handle_parent_module( 382pub fn handle_parent_module(
392 world: WorldSnapshot, 383 world: WorldSnapshot,
393 params: lsp_types::TextDocumentPositionParams, 384 params: lsp_types::TextDocumentPositionParams,
394) -> Result<Vec<Location>> { 385) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
395 let _p = profile("handle_parent_module"); 386 let _p = profile("handle_parent_module");
396 let position = from_proto::file_position(&world, params)?; 387 let position = from_proto::file_position(&world, params)?;
397 world 388 let navs = world.analysis().parent_module(position)?;
398 .analysis() 389 let res = to_proto::goto_definition_response(&world, None, navs)?;
399 .parent_module(position)? 390 Ok(Some(res))
400 .into_iter()
401 .map(|it| to_proto::location(&world, it.file_range()))
402 .collect::<Result<Vec<_>>>()
403} 391}
404 392
405pub fn handle_runnables( 393pub fn handle_runnables(
@@ -978,7 +966,12 @@ fn to_lsp_runnable(
978) -> Result<lsp_ext::Runnable> { 966) -> Result<lsp_ext::Runnable> {
979 let spec = CargoTargetSpec::for_file(world, file_id)?; 967 let spec = CargoTargetSpec::for_file(world, file_id)?;
980 let target = spec.as_ref().map(|s| s.target.clone()); 968 let target = spec.as_ref().map(|s| s.target.clone());
981 let (args, extra_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind)?; 969 let mut features_needed = vec![];
970 for cfg_expr in &runnable.cfg_exprs {
971 collect_minimal_features_needed(cfg_expr, &mut features_needed);
972 }
973 let (args, extra_args) =
974 CargoTargetSpec::runnable_args(spec, &runnable.kind, &features_needed)?;
982 let line_index = world.analysis().file_line_index(file_id)?; 975 let line_index = world.analysis().file_line_index(file_id)?;
983 let label = match &runnable.kind { 976 let label = match &runnable.kind {
984 RunnableKind::Test { test_id, .. } => format!("test {}", test_id), 977 RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
@@ -1004,6 +997,26 @@ fn to_lsp_runnable(
1004 }) 997 })
1005} 998}
1006 999
1000/// Fill minimal features needed
1001fn collect_minimal_features_needed(cfg_expr: &CfgExpr, features: &mut Vec<SmolStr>) {
1002 match cfg_expr {
1003 CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()),
1004 CfgExpr::All(preds) => {
1005 preds.iter().for_each(|cfg| collect_minimal_features_needed(cfg, features));
1006 }
1007 CfgExpr::Any(preds) => {
1008 for cfg in preds {
1009 let len_features = features.len();
1010 collect_minimal_features_needed(cfg, features);
1011 if len_features != features.len() {
1012 break;
1013 }
1014 }
1015 }
1016 _ => {}
1017 }
1018}
1019
1007pub fn handle_inlay_hints( 1020pub fn handle_inlay_hints(
1008 world: WorldSnapshot, 1021 world: WorldSnapshot,
1009 params: InlayHintsParams, 1022 params: InlayHintsParams,
@@ -1140,3 +1153,54 @@ pub fn handle_semantic_tokens_range(
1140 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); 1153 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1141 Ok(Some(semantic_tokens.into())) 1154 Ok(Some(semantic_tokens.into()))
1142} 1155}
1156
1157#[cfg(test)]
1158mod tests {
1159 use super::*;
1160
1161 use mbe::{ast_to_token_tree, TokenMap};
1162 use ra_cfg::parse_cfg;
1163 use ra_syntax::{
1164 ast::{self, AstNode},
1165 SmolStr,
1166 };
1167
1168 fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
1169 let source_file = ast::SourceFile::parse(input).ok().unwrap();
1170 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
1171 ast_to_token_tree(&tt).unwrap()
1172 }
1173
1174 #[test]
1175 fn test_cfg_expr_minimal_features_needed() {
1176 let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
1177 let cfg_expr = parse_cfg(&subtree);
1178 let mut min_features = vec![];
1179 collect_minimal_features_needed(&cfg_expr, &mut min_features);
1180
1181 assert_eq!(min_features, vec![SmolStr::new("baz")]);
1182
1183 let (subtree, _) =
1184 get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
1185 let cfg_expr = parse_cfg(&subtree);
1186
1187 let mut min_features = vec![];
1188 collect_minimal_features_needed(&cfg_expr, &mut min_features);
1189 assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]);
1190
1191 let (subtree, _) =
1192 get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
1193 let cfg_expr = parse_cfg(&subtree);
1194
1195 let mut min_features = vec![];
1196 collect_minimal_features_needed(&cfg_expr, &mut min_features);
1197 assert_eq!(min_features, vec![SmolStr::new("baz")]);
1198
1199 let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
1200 let cfg_expr = parse_cfg(&subtree);
1201
1202 let mut min_features = vec![];
1203 collect_minimal_features_needed(&cfg_expr, &mut min_features);
1204 assert!(min_features.is_empty());
1205 }
1206}
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 2dc5cb119..6f125c37c 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -36,9 +36,11 @@ macro_rules! define_semantic_token_types {
36 36
37define_semantic_token_types![ 37define_semantic_token_types![
38 (ATTRIBUTE, "attribute"), 38 (ATTRIBUTE, "attribute"),
39 (BOOLEAN, "boolean"),
39 (BUILTIN_TYPE, "builtinType"), 40 (BUILTIN_TYPE, "builtinType"),
40 (ENUM_MEMBER, "enumMember"), 41 (ENUM_MEMBER, "enumMember"),
41 (LIFETIME, "lifetime"), 42 (LIFETIME, "lifetime"),
43 (SELF_KEYWORD, "selfKeyword"),
42 (TYPE_ALIAS, "typeAlias"), 44 (TYPE_ALIAS, "typeAlias"),
43 (UNION, "union"), 45 (UNION, "union"),
44 (UNRESOLVED_REFERENCE, "unresolvedReference"), 46 (UNRESOLVED_REFERENCE, "unresolvedReference"),
@@ -67,6 +69,7 @@ define_semantic_token_modifiers![
67 (CONTROL_FLOW, "controlFlow"), 69 (CONTROL_FLOW, "controlFlow"),
68 (MUTABLE, "mutable"), 70 (MUTABLE, "mutable"),
69 (UNSAFE, "unsafe"), 71 (UNSAFE, "unsafe"),
72 (ATTRIBUTE_MODIFIER, "attribute"),
70]; 73];
71 74
72#[derive(Default)] 75#[derive(Default)]
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 7dd7d9416..8e8e7033d 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -135,6 +135,18 @@ pub(crate) fn text_edit_vec(
135 text_edit.into_iter().map(|indel| self::text_edit(line_index, line_endings, indel)).collect() 135 text_edit.into_iter().map(|indel| self::text_edit(line_index, line_endings, indel)).collect()
136} 136}
137 137
138pub(crate) fn snippet_text_edit_vec(
139 line_index: &LineIndex,
140 line_endings: LineEndings,
141 is_snippet: bool,
142 text_edit: TextEdit,
143) -> Vec<lsp_ext::SnippetTextEdit> {
144 text_edit
145 .into_iter()
146 .map(|indel| self::snippet_text_edit(line_index, line_endings, is_snippet, indel))
147 .collect()
148}
149
138pub(crate) fn completion_item( 150pub(crate) fn completion_item(
139 line_index: &LineIndex, 151 line_index: &LineIndex,
140 line_endings: LineEndings, 152 line_endings: LineEndings,
@@ -274,6 +286,7 @@ fn semantic_token_type_and_modifiers(
274 HighlightTag::TypeAlias => semantic_tokens::TYPE_ALIAS, 286 HighlightTag::TypeAlias => semantic_tokens::TYPE_ALIAS,
275 HighlightTag::Trait => lsp_types::SemanticTokenType::INTERFACE, 287 HighlightTag::Trait => lsp_types::SemanticTokenType::INTERFACE,
276 HighlightTag::BuiltinType => semantic_tokens::BUILTIN_TYPE, 288 HighlightTag::BuiltinType => semantic_tokens::BUILTIN_TYPE,
289 HighlightTag::SelfKeyword => semantic_tokens::SELF_KEYWORD,
277 HighlightTag::SelfType => lsp_types::SemanticTokenType::TYPE, 290 HighlightTag::SelfType => lsp_types::SemanticTokenType::TYPE,
278 HighlightTag::Field => lsp_types::SemanticTokenType::PROPERTY, 291 HighlightTag::Field => lsp_types::SemanticTokenType::PROPERTY,
279 HighlightTag::Function => lsp_types::SemanticTokenType::FUNCTION, 292 HighlightTag::Function => lsp_types::SemanticTokenType::FUNCTION,
@@ -295,6 +308,7 @@ fn semantic_token_type_and_modifiers(
295 HighlightTag::ByteLiteral | HighlightTag::NumericLiteral => { 308 HighlightTag::ByteLiteral | HighlightTag::NumericLiteral => {
296 lsp_types::SemanticTokenType::NUMBER 309 lsp_types::SemanticTokenType::NUMBER
297 } 310 }
311 HighlightTag::BoolLiteral => semantic_tokens::BOOLEAN,
298 HighlightTag::CharLiteral | HighlightTag::StringLiteral => { 312 HighlightTag::CharLiteral | HighlightTag::StringLiteral => {
299 lsp_types::SemanticTokenType::STRING 313 lsp_types::SemanticTokenType::STRING
300 } 314 }
@@ -307,6 +321,7 @@ fn semantic_token_type_and_modifiers(
307 321
308 for modifier in highlight.modifiers.iter() { 322 for modifier in highlight.modifiers.iter() {
309 let modifier = match modifier { 323 let modifier = match modifier {
324 HighlightModifier::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER,
310 HighlightModifier::Definition => lsp_types::SemanticTokenModifier::DECLARATION, 325 HighlightModifier::Definition => lsp_types::SemanticTokenModifier::DECLARATION,
311 HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW, 326 HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW,
312 HighlightModifier::Mutable => semantic_tokens::MUTABLE, 327 HighlightModifier::Mutable => semantic_tokens::MUTABLE,
@@ -389,13 +404,20 @@ pub(crate) fn location(world: &WorldSnapshot, frange: FileRange) -> Result<lsp_t
389 404
390pub(crate) fn location_link( 405pub(crate) fn location_link(
391 world: &WorldSnapshot, 406 world: &WorldSnapshot,
392 src: FileRange, 407 src: Option<FileRange>,
393 target: NavigationTarget, 408 target: NavigationTarget,
394) -> Result<lsp_types::LocationLink> { 409) -> Result<lsp_types::LocationLink> {
395 let src_location = location(world, src)?; 410 let origin_selection_range = match src {
411 Some(src) => {
412 let line_index = world.analysis().file_line_index(src.file_id)?;
413 let range = range(&line_index, src.range);
414 Some(range)
415 }
416 None => None,
417 };
396 let (target_uri, target_range, target_selection_range) = location_info(world, target)?; 418 let (target_uri, target_range, target_selection_range) = location_info(world, target)?;
397 let res = lsp_types::LocationLink { 419 let res = lsp_types::LocationLink {
398 origin_selection_range: Some(src_location.range), 420 origin_selection_range,
399 target_uri, 421 target_uri,
400 target_range, 422 target_range,
401 target_selection_range, 423 target_selection_range,
@@ -418,7 +440,7 @@ fn location_info(
418 440
419pub(crate) fn goto_definition_response( 441pub(crate) fn goto_definition_response(
420 world: &WorldSnapshot, 442 world: &WorldSnapshot,
421 src: FileRange, 443 src: Option<FileRange>,
422 targets: Vec<NavigationTarget>, 444 targets: Vec<NavigationTarget>,
423) -> Result<lsp_types::GotoDefinitionResponse> { 445) -> Result<lsp_types::GotoDefinitionResponse> {
424 if world.config.client_caps.location_link { 446 if world.config.client_caps.location_link {
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 738a9a8e3..405ddb362 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -473,23 +473,14 @@ fn main() {{}}
473 text_document: server.doc_id("src/m0.rs"), 473 text_document: server.doc_id("src/m0.rs"),
474 position: Position { line: 0, character: 5 }, 474 position: Position { line: 0, character: 5 },
475 }, 475 },
476 json!({ 476 json!([{
477 "documentChanges": [ 477 "insertTextFormat": 2,
478 { 478 "newText": "\n/// $0",
479 "edits": [ 479 "range": {
480 { 480 "end": { "character": 5, "line": 0 },
481 "insertTextFormat": 2, 481 "start": { "character": 5, "line": 0 }
482 "newText": "\n/// $0",
483 "range": {
484 "end": { "character": 5, "line": 0 },
485 "start": { "character": 5, "line": 0 }
486 }
487 }
488 ],
489 "textDocument": { "uri": "file:///[..]src/m0.rs", "version": null }
490 } 482 }
491 ] 483 }]),
492 }),
493 ); 484 );
494 let elapsed = start.elapsed(); 485 let elapsed = start.elapsed();
495 assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed); 486 assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed);
@@ -519,23 +510,14 @@ version = \"0.0.0\"
519 text_document: server.doc_id("src/main.rs"), 510 text_document: server.doc_id("src/main.rs"),
520 position: Position { line: 0, character: 8 }, 511 position: Position { line: 0, character: 8 },
521 }, 512 },
522 json!({ 513 json!([{
523 "documentChanges": [ 514 "insertTextFormat": 2,
524 { 515 "newText": "\r\n/// $0",
525 "edits": [ 516 "range": {
526 { 517 "end": { "line": 0, "character": 8 },
527 "insertTextFormat": 2, 518 "start": { "line": 0, "character": 8 }
528 "newText": "\r\n/// $0",
529 "range": {
530 "end": { "line": 0, "character": 8 },
531 "start": { "line": 0, "character": 8 }
532 }
533 }
534 ],
535 "textDocument": { "uri": "file:///[..]src/main.rs", "version": null }
536 } 519 }
537 ] 520 }]),
538 }),
539 ); 521 );
540} 522}
541 523
@@ -774,5 +756,5 @@ pub fn foo(_input: TokenStream) -> TokenStream {
774 }); 756 });
775 757
776 let value = res.get("contents").unwrap().get("value").unwrap().to_string(); 758 let value = res.get("contents").unwrap().get("value").unwrap().to_string();
777 assert_eq!(value, r#""foo::Bar\n___\n\n```rust\nfn bar()\n```""#) 759 assert_eq!(value, r#""```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#)
778} 760}
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs
index 9acbae066..66a6f4d54 100644
--- a/crates/rust-analyzer/tests/heavy_tests/support.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/support.rs
@@ -68,7 +68,7 @@ impl<'a> Project<'a> {
68 let mut paths = vec![]; 68 let mut paths = vec![];
69 69
70 for entry in parse_fixture(self.fixture) { 70 for entry in parse_fixture(self.fixture) {
71 let path = tmp_dir.path().join(entry.meta); 71 let path = tmp_dir.path().join(entry.meta.path().as_str());
72 fs::create_dir_all(path.parent().unwrap()).unwrap(); 72 fs::create_dir_all(path.parent().unwrap()).unwrap();
73 fs::write(path.as_path(), entry.text.as_bytes()).unwrap(); 73 fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
74 paths.push((path, entry.text)); 74 paths.push((path, entry.text));
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml
index 8ec986bcb..4d185b01c 100644
--- a/crates/test_utils/Cargo.toml
+++ b/crates/test_utils/Cargo.toml
@@ -11,3 +11,7 @@ doctest = false
11difference = "2.0.0" 11difference = "2.0.0"
12text-size = "1.0.0" 12text-size = "1.0.0"
13serde_json = "1.0.48" 13serde_json = "1.0.48"
14relative-path = "1.0.0"
15rustc-hash = "1.1.0"
16
17ra_cfg = { path = "../ra_cfg" } \ No newline at end of file
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index be2cfbaa2..1bd97215c 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -14,6 +14,10 @@ use std::{
14 path::{Path, PathBuf}, 14 path::{Path, PathBuf},
15}; 15};
16 16
17pub use ra_cfg::CfgOptions;
18
19pub use relative_path::{RelativePath, RelativePathBuf};
20pub use rustc_hash::FxHashMap;
17use serde_json::Value; 21use serde_json::Value;
18use text_size::{TextRange, TextSize}; 22use text_size::{TextRange, TextSize};
19 23
@@ -157,10 +161,82 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String {
157 161
158#[derive(Debug, Eq, PartialEq)] 162#[derive(Debug, Eq, PartialEq)]
159pub struct FixtureEntry { 163pub struct FixtureEntry {
160 pub meta: String, 164 pub meta: FixtureMeta,
161 pub text: String, 165 pub text: String,
162} 166}
163 167
168#[derive(Debug, Eq, PartialEq)]
169pub enum FixtureMeta {
170 Root { path: RelativePathBuf },
171 File(FileMeta),
172}
173
174#[derive(Debug, Eq, PartialEq)]
175pub struct FileMeta {
176 pub path: RelativePathBuf,
177 pub crate_name: Option<String>,
178 pub deps: Vec<String>,
179 pub cfg: CfgOptions,
180 pub edition: Option<String>,
181 pub env: FxHashMap<String, String>,
182}
183
184impl FixtureMeta {
185 pub fn path(&self) -> &RelativePath {
186 match self {
187 FixtureMeta::Root { path } => &path,
188 FixtureMeta::File(f) => &f.path,
189 }
190 }
191
192 pub fn crate_name(&self) -> Option<&String> {
193 match self {
194 FixtureMeta::File(f) => f.crate_name.as_ref(),
195 _ => None,
196 }
197 }
198
199 pub fn cfg_options(&self) -> Option<&CfgOptions> {
200 match self {
201 FixtureMeta::File(f) => Some(&f.cfg),
202 _ => None,
203 }
204 }
205
206 pub fn edition(&self) -> Option<&String> {
207 match self {
208 FixtureMeta::File(f) => f.edition.as_ref(),
209 _ => None,
210 }
211 }
212
213 pub fn env(&self) -> impl Iterator<Item = (&String, &String)> {
214 struct EnvIter<'a> {
215 iter: Option<std::collections::hash_map::Iter<'a, String, String>>,
216 }
217
218 impl<'a> EnvIter<'a> {
219 fn new(meta: &'a FixtureMeta) -> Self {
220 Self {
221 iter: match meta {
222 FixtureMeta::File(f) => Some(f.env.iter()),
223 _ => None,
224 },
225 }
226 }
227 }
228
229 impl<'a> Iterator for EnvIter<'a> {
230 type Item = (&'a String, &'a String);
231 fn next(&mut self) -> Option<Self::Item> {
232 self.iter.as_mut().and_then(|i| i.next())
233 }
234 }
235
236 EnvIter::new(self)
237 }
238}
239
164/// Parses text which looks like this: 240/// Parses text which looks like this:
165/// 241///
166/// ```not_rust 242/// ```not_rust
@@ -169,8 +245,8 @@ pub struct FixtureEntry {
169/// line 2 245/// line 2
170/// // - other meta 246/// // - other meta
171/// ``` 247/// ```
172pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> { 248pub fn parse_fixture(ra_fixture: &str) -> Vec<FixtureEntry> {
173 let fixture = indent_first_line(fixture); 249 let fixture = indent_first_line(ra_fixture);
174 let margin = fixture_margin(&fixture); 250 let margin = fixture_margin(&fixture);
175 251
176 let mut lines = fixture 252 let mut lines = fixture
@@ -200,6 +276,7 @@ The offending line: {:?}"#,
200 for line in lines.by_ref() { 276 for line in lines.by_ref() {
201 if line.starts_with("//-") { 277 if line.starts_with("//-") {
202 let meta = line["//-".len()..].trim().to_string(); 278 let meta = line["//-".len()..].trim().to_string();
279 let meta = parse_meta(&meta);
203 res.push(FixtureEntry { meta, text: String::new() }) 280 res.push(FixtureEntry { meta, text: String::new() })
204 } else if let Some(entry) = res.last_mut() { 281 } else if let Some(entry) = res.last_mut() {
205 entry.text.push_str(line); 282 entry.text.push_str(line);
@@ -209,6 +286,57 @@ The offending line: {:?}"#,
209 res 286 res
210} 287}
211 288
289//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
290fn parse_meta(meta: &str) -> FixtureMeta {
291 let components = meta.split_ascii_whitespace().collect::<Vec<_>>();
292
293 if components[0] == "root" {
294 let path: RelativePathBuf = components[1].into();
295 assert!(path.starts_with("/") && path.ends_with("/"));
296 return FixtureMeta::Root { path };
297 }
298
299 let path: RelativePathBuf = components[0].into();
300 assert!(path.starts_with("/"));
301
302 let mut krate = None;
303 let mut deps = Vec::new();
304 let mut edition = None;
305 let mut cfg = CfgOptions::default();
306 let mut env = FxHashMap::default();
307 for component in components[1..].iter() {
308 let (key, value) = split1(component, ':').unwrap();
309 match key {
310 "crate" => krate = Some(value.to_string()),
311 "deps" => deps = value.split(',').map(|it| it.to_string()).collect(),
312 "edition" => edition = Some(value.to_string()),
313 "cfg" => {
314 for key in value.split(',') {
315 match split1(key, '=') {
316 None => cfg.insert_atom(key.into()),
317 Some((k, v)) => cfg.insert_key_value(k.into(), v.into()),
318 }
319 }
320 }
321 "env" => {
322 for key in value.split(',') {
323 if let Some((k, v)) = split1(key, '=') {
324 env.insert(k.into(), v.into());
325 }
326 }
327 }
328 _ => panic!("bad component: {:?}", component),
329 }
330 }
331
332 FixtureMeta::File(FileMeta { path, crate_name: krate, deps, edition, cfg, env })
333}
334
335fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> {
336 let idx = haystack.find(delim)?;
337 Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..]))
338}
339
212/// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines. 340/// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines.
213/// This allows fixtures to start off in a different indentation, e.g. to align the first line with 341/// This allows fixtures to start off in a different indentation, e.g. to align the first line with
214/// the other lines visually: 342/// the other lines visually:
@@ -288,13 +416,33 @@ struct Bar;
288 ) 416 )
289} 417}
290 418
419#[test]
420fn parse_fixture_gets_full_meta() {
421 let parsed = parse_fixture(
422 r"
423 //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo
424 mod m;
425 ",
426 );
427 assert_eq!(1, parsed.len());
428
429 let parsed = &parsed[0];
430 assert_eq!("mod m;\n\n", parsed.text);
431
432 let meta = &parsed.meta;
433 assert_eq!("foo", meta.crate_name().unwrap());
434 assert_eq!("/lib.rs", meta.path());
435 assert!(meta.cfg_options().is_some());
436 assert_eq!(2, meta.env().count());
437}
438
291/// Same as `parse_fixture`, except it allow empty fixture 439/// Same as `parse_fixture`, except it allow empty fixture
292pub fn parse_single_fixture(fixture: &str) -> Option<FixtureEntry> { 440pub fn parse_single_fixture(ra_fixture: &str) -> Option<FixtureEntry> {
293 if !fixture.lines().any(|it| it.trim_start().starts_with("//-")) { 441 if !ra_fixture.lines().any(|it| it.trim_start().starts_with("//-")) {
294 return None; 442 return None;
295 } 443 }
296 444
297 let fixtures = parse_fixture(fixture); 445 let fixtures = parse_fixture(ra_fixture);
298 if fixtures.len() > 1 { 446 if fixtures.len() > 1 {
299 panic!("too many fixtures"); 447 panic!("too many fixtures");
300 } 448 }