diff options
Diffstat (limited to 'crates')
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> { | |||
88 | mod tests { | 88 | mod 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 | ||
64 | use ra_cfg::CfgOptions; | 64 | use ra_cfg::CfgOptions; |
65 | use rustc_hash::FxHashMap; | 65 | use rustc_hash::FxHashMap; |
66 | use test_utils::{extract_offset, parse_fixture, parse_single_fixture, CURSOR_MARKER}; | 66 | use test_utils::{extract_offset, parse_fixture, parse_single_fixture, FixtureMeta, CURSOR_MARKER}; |
67 | 67 | ||
68 | use crate::{ | 68 | use 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) | 261 | impl From<&FixtureMeta> for ParsedMeta { |
262 | fn 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 | |||
307 | fn 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 | ||
314 | impl<'a, T> From<T> for Env | ||
315 | where | ||
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 | |||
314 | impl Env { | 329 | impl 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 | ||
63 | pub use hir_def::{ | 63 | pub 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. |
34 | pub(crate) struct CfgExpander { | 34 | pub(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 | ||
177 | impl DefKind { | 177 | impl 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 | ||
189 | fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String { | 199 | fn 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::{ | |||
49 | pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { | 49 | pub(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 | &[" |
505 | Option | 505 | Option |
506 | ___ | 506 | ``` |
507 | 507 | ||
508 | ```rust | 508 | ```rust |
509 | None | 509 | None |
510 | ``` | 510 | ``` |
511 | ___ | ||
511 | 512 | ||
512 | The None variant | 513 | The None variant |
513 | " | 514 | " |
@@ -527,11 +528,12 @@ The None variant | |||
527 | "#, | 528 | "#, |
528 | &[" | 529 | &[" |
529 | Option | 530 | Option |
530 | ___ | 531 | ``` |
531 | 532 | ||
532 | ```rust | 533 | ```rust |
533 | Some | 534 | Some |
534 | ``` | 535 | ``` |
536 | ___ | ||
535 | 537 | ||
536 | The Some variant | 538 | The 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 | ||
3 | use std::str::FromStr; | ||
3 | use std::sync::Arc; | 4 | use std::sync::Arc; |
4 | 5 | ||
5 | use ra_cfg::CfgOptions; | 6 | use ra_cfg::CfgOptions; |
6 | use ra_db::{CrateName, Env, RelativePathBuf}; | 7 | use ra_db::{CrateName, Env, RelativePathBuf}; |
7 | use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; | 8 | use test_utils::{extract_offset, extract_range, parse_fixture, FixtureEntry, CURSOR_MARKER}; |
8 | 9 | ||
9 | use crate::{ | 10 | use 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)] | ||
16 | enum MockFileData { | ||
17 | Plain { path: String, content: String }, | ||
18 | Fixture(FixtureEntry), | ||
19 | } | ||
20 | |||
21 | impl 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 | |||
68 | impl 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)] |
17 | pub struct MockAnalysis { | 77 | pub struct MockAnalysis { |
18 | files: Vec<(String, String)>, | 78 | files: Vec<MockFileData>, |
19 | } | 79 | } |
20 | 80 | ||
21 | impl MockAnalysis { | 81 | impl 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 | ||
3 | use hir::{AsAssocItem, Semantics}; | 3 | use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; |
4 | use itertools::Itertools; | 4 | use itertools::Itertools; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
@@ -10,12 +10,14 @@ use ra_syntax::{ | |||
10 | 10 | ||
11 | use crate::FileId; | 11 | use crate::FileId; |
12 | use ast::DocCommentsOwner; | 12 | use ast::DocCommentsOwner; |
13 | use ra_cfg::CfgExpr; | ||
13 | use std::fmt::Display; | 14 | use std::fmt::Display; |
14 | 15 | ||
15 | #[derive(Debug)] | 16 | #[derive(Debug)] |
16 | pub struct Runnable { | 17 | pub 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 { | |||
45 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { | 47 | pub(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 | ||
51 | fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { | 53 | fn 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 | ||
61 | fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Runnable> { | 63 | fn 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 | ||
150 | fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { | 161 | fn 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>) -> <span class="builtin_type">i32</span>; | 38 | <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -> <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>) -> <span class="builtin_type">i32</span> { | 42 | <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -> <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><<span class="type_param declaration">T</span>> <span class="enum">Option</span><<span class="type_param">T</span>> { | 94 | <span class="keyword">impl</span><<span class="type_param declaration">T</span>> <span class="enum">Option</span><<span class="type_param">T</span>> { |
94 | <span class="keyword">fn</span> <span class="function declaration">and</span><<span class="type_param declaration">U</span>>(<span class="keyword">self</span>, <span class="variable declaration">other</span>: <span class="enum">Option</span><<span class="type_param">U</span>>) -> <span class="enum">Option</span><(<span class="type_param">T</span>, <span class="type_param">U</span>)> { | 95 | <span class="keyword">fn</span> <span class="function declaration">and</span><<span class="type_param declaration">U</span>>(<span class="self_keyword">self</span>, <span class="variable declaration">other</span>: <span class="enum">Option</span><<span class="type_param">U</span>>) -> <span class="enum">Option</span><(<span class="type_param">T</span>, <span class="type_param">U</span>)> { |
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> => <span class="macro">unimplemented!</span>(), | 97 | <span class="enum_variant">None</span> => <span class="macro">unimplemented!</span>(), |
97 | <span class="variable declaration">Nope</span> => <span class="variable">Nope</span>, | 98 | <span class="variable declaration">Nope</span> => <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 | ||
480 | fn highlight_name_by_syntax(name: ast::Name) -> Highlight { | 484 | fn 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 | ||
500 | fn highlight_injection( | 512 | fn 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)] |
16 | pub enum HighlightTag { | 16 | pub 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)] |
47 | pub enum HighlightModifier { | 49 | pub 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 | ||
96 | impl HighlightModifier { | 102 | impl 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 | }; |
12 | use ra_text_edit::TextEdit; | 12 | use ra_text_edit::TextEdit; |
13 | 13 | ||
14 | use crate::{SourceChange, SourceFileEdit}; | 14 | pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> { |
15 | |||
16 | pub(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 | ||
49 | fn followed_by_comment(comment: &ast::Comment) -> bool { | 45 | fn 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] |
14 | itertools = "0.9.0" | 14 | itertools = "0.9.0" |
15 | rowan = "0.10.0" | 15 | rowan = "0.10.0" |
16 | rustc_lexer = { version = "656.0.0", package = "rustc-ap-rustc_lexer" } | 16 | rustc_lexer = { version = "661.0.0", package = "rustc-ap-rustc_lexer" } |
17 | rustc-hash = "1.1.0" | 17 | rustc-hash = "1.1.0" |
18 | arrayvec = "0.5.1" | 18 | arrayvec = "0.5.1" |
19 | once_cell = "1.3.1" | 19 | once_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 | }; |
9 | use rustc_lexer::unescape::{unescape_literal, Mode}; | ||
9 | 10 | ||
10 | impl Comment { | 11 | impl 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 | ||
3 | mod block; | 3 | mod block; |
4 | 4 | ||
5 | use std::convert::TryFrom; | ||
6 | |||
7 | use rustc_lexer::unescape; | ||
8 | |||
9 | use crate::{ | 5 | use 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 | }; |
10 | use rustc_lexer::unescape::{ | ||
11 | self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode, | ||
12 | }; | ||
13 | use std::convert::TryFrom; | ||
14 | 14 | ||
15 | fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str { | 15 | fn 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 | ||
82 | pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { | 82 | pub(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" } | |||
40 | ra_syntax = { path = "../ra_syntax" } | 40 | ra_syntax = { path = "../ra_syntax" } |
41 | ra_text_edit = { path = "../ra_text_edit" } | 41 | ra_text_edit = { path = "../ra_text_edit" } |
42 | ra_vfs = "0.6.0" | 42 | ra_vfs = "0.6.0" |
43 | ra_cfg = { path = "../ra_cfg"} | ||
43 | 44 | ||
44 | # This should only be used in CLI | 45 | # This should only be used in CLI |
45 | ra_db = { path = "../ra_db" } | 46 | ra_db = { path = "../ra_db" } |
@@ -55,6 +56,8 @@ winapi = "0.3.8" | |||
55 | tempfile = "3.1.0" | 56 | tempfile = "3.1.0" |
56 | insta = "0.16.0" | 57 | insta = "0.16.0" |
57 | test_utils = { path = "../test_utils" } | 58 | test_utils = { path = "../test_utils" } |
59 | mbe = { path = "../ra_mbe", package = "ra_mbe" } | ||
60 | tt = { path = "../ra_tt", package = "ra_tt" } | ||
58 | 61 | ||
59 | [features] | 62 | [features] |
60 | jemalloc = [ "ra_prof/jemalloc" ] | 63 | jemalloc = [ "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}; | |||
4 | use ra_project_model::{self, ProjectWorkspace, TargetKind}; | 4 | use ra_project_model::{self, ProjectWorkspace, TargetKind}; |
5 | 5 | ||
6 | use crate::{world::WorldSnapshot, Result}; | 6 | use crate::{world::WorldSnapshot, Result}; |
7 | use 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 @@ | |||
3 | use std::{collections::HashMap, path::PathBuf}; | 3 | use std::{collections::HashMap, path::PathBuf}; |
4 | 4 | ||
5 | use lsp_types::request::Request; | 5 | use lsp_types::request::Request; |
6 | use lsp_types::{Location, Position, Range, TextDocumentIdentifier}; | 6 | use lsp_types::{Position, Range, TextDocumentIdentifier}; |
7 | use rustc_hash::FxHashMap; | 7 | use rustc_hash::FxHashMap; |
8 | use serde::{Deserialize, Serialize}; | 8 | use 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")] | ||
43 | pub struct ExpandedMacro { | ||
44 | pub name: String, | ||
45 | pub expansion: String, | ||
46 | } | ||
47 | |||
48 | pub enum ExpandMacro {} | 41 | pub enum ExpandMacro {} |
49 | 42 | ||
50 | impl Request for ExpandMacro { | 43 | impl Request for ExpandMacro { |
@@ -57,30 +50,37 @@ impl Request for ExpandMacro { | |||
57 | #[serde(rename_all = "camelCase")] | 50 | #[serde(rename_all = "camelCase")] |
58 | pub struct ExpandMacroParams { | 51 | pub 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")] | ||
58 | pub struct ExpandedMacro { | ||
59 | pub name: String, | ||
60 | pub expansion: String, | ||
61 | } | 61 | } |
62 | 62 | ||
63 | pub enum FindMatchingBrace {} | 63 | pub enum MatchingBrace {} |
64 | 64 | ||
65 | impl Request for FindMatchingBrace { | 65 | impl 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")] |
73 | pub struct FindMatchingBraceParams { | 73 | pub 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 | ||
78 | pub enum ParentModule {} | 78 | pub enum ParentModule {} |
79 | 79 | ||
80 | impl Request for ParentModule { | 80 | impl 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 | ||
86 | pub enum JoinLines {} | 86 | pub enum JoinLines {} |
@@ -102,8 +102,8 @@ pub enum OnEnter {} | |||
102 | 102 | ||
103 | impl Request for OnEnter { | 103 | impl 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 | ||
109 | pub enum Runnables {} | 109 | pub 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 | }; |
20 | use ra_cfg::CfgExpr; | ||
20 | use ra_ide::{ | 21 | use 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 | }; |
24 | use ra_prof::profile; | 25 | use ra_prof::profile; |
25 | use ra_project_model::TargetKind; | 26 | use ra_project_model::TargetKind; |
26 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; | 27 | use ra_syntax::{AstNode, SmolStr, SyntaxKind, TextRange, TextSize}; |
27 | use rustc_hash::FxHashMap; | 28 | use rustc_hash::FxHashMap; |
28 | use serde::{Deserialize, Serialize}; | 29 | use serde::{Deserialize, Serialize}; |
29 | use serde_json::to_value; | 30 | use 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, ¶ms.text_document.uri)?; | 73 | let file_id = from_proto::file_id(&world, ¶ms.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 | ||
85 | pub fn handle_selection_range( | 81 | pub 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 | ||
128 | pub fn handle_find_matching_brace( | 124 | pub 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, ¶ms.text_document.uri)?; | 129 | let file_id = from_proto::file_id(&world, ¶ms.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( | |||
173 | pub fn handle_on_enter( | 169 | pub 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 | ||
391 | pub fn handle_parent_module( | 382 | pub 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 | ||
405 | pub fn handle_runnables( | 393 | pub 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 | ||
1001 | fn 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 | |||
1007 | pub fn handle_inlay_hints( | 1020 | pub 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)] | ||
1158 | mod 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 | ||
37 | define_semantic_token_types![ | 37 | define_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 | ||
138 | pub(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 | |||
138 | pub(crate) fn completion_item( | 150 | pub(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 | ||
390 | pub(crate) fn location_link( | 405 | pub(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 | ||
419 | pub(crate) fn goto_definition_response( | 441 | pub(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 | |||
11 | difference = "2.0.0" | 11 | difference = "2.0.0" |
12 | text-size = "1.0.0" | 12 | text-size = "1.0.0" |
13 | serde_json = "1.0.48" | 13 | serde_json = "1.0.48" |
14 | relative-path = "1.0.0" | ||
15 | rustc-hash = "1.1.0" | ||
16 | |||
17 | ra_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 | ||
17 | pub use ra_cfg::CfgOptions; | ||
18 | |||
19 | pub use relative_path::{RelativePath, RelativePathBuf}; | ||
20 | pub use rustc_hash::FxHashMap; | ||
17 | use serde_json::Value; | 21 | use serde_json::Value; |
18 | use text_size::{TextRange, TextSize}; | 22 | use 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)] |
159 | pub struct FixtureEntry { | 163 | pub 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)] | ||
169 | pub enum FixtureMeta { | ||
170 | Root { path: RelativePathBuf }, | ||
171 | File(FileMeta), | ||
172 | } | ||
173 | |||
174 | #[derive(Debug, Eq, PartialEq)] | ||
175 | pub 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 | |||
184 | impl 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 | /// ``` |
172 | pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> { | 248 | pub 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 | ||
290 | fn 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 | |||
335 | fn 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] | ||
420 | fn 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 |
292 | pub fn parse_single_fixture(fixture: &str) -> Option<FixtureEntry> { | 440 | pub 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 | } |