diff options
Diffstat (limited to 'crates/ide/src')
-rw-r--r-- | crates/ide/src/expand_macro.rs | 34 | ||||
-rw-r--r-- | crates/ide/src/goto_definition.rs | 89 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting.rs | 30 |
3 files changed, 143 insertions, 10 deletions
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index e0d01fa96..3f38e2145 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs | |||
@@ -3,8 +3,7 @@ use std::iter; | |||
3 | use hir::Semantics; | 3 | use hir::Semantics; |
4 | use ide_db::RootDatabase; | 4 | use ide_db::RootDatabase; |
5 | use syntax::{ | 5 | use syntax::{ |
6 | algo::find_node_at_offset, ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, | 6 | ast, match_ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, SyntaxNode, WalkEvent, T, |
7 | SyntaxNode, WalkEvent, T, | ||
8 | }; | 7 | }; |
9 | 8 | ||
10 | use crate::FilePosition; | 9 | use crate::FilePosition; |
@@ -28,16 +27,37 @@ pub struct ExpandedMacro { | |||
28 | pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { | 27 | pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { |
29 | let sema = Semantics::new(db); | 28 | let sema = Semantics::new(db); |
30 | let file = sema.parse(position.file_id); | 29 | let file = sema.parse(position.file_id); |
31 | let mac = find_node_at_offset::<ast::MacroCall>(file.syntax(), position.offset)?; | ||
32 | let name = mac.path()?.segment()?.name_ref()?; | ||
33 | 30 | ||
34 | let expanded = expand_macro_recur(&sema, &mac)?; | 31 | let tok = file.syntax().token_at_offset(position.offset).left_biased()?; |
32 | let mut expanded = None; | ||
33 | let mut name = None; | ||
34 | for node in tok.ancestors() { | ||
35 | match_ast! { | ||
36 | match node { | ||
37 | ast::MacroCall(mac) => { | ||
38 | name = Some(mac.path()?.segment()?.name_ref()?.to_string()); | ||
39 | expanded = expand_macro_recur(&sema, &mac); | ||
40 | break; | ||
41 | }, | ||
42 | ast::Item(item) => { | ||
43 | // FIXME: add the macro name | ||
44 | // FIXME: make this recursive too | ||
45 | name = Some("?".to_string()); | ||
46 | expanded = sema.expand_attr_macro(&item); | ||
47 | if expanded.is_some() { | ||
48 | break; | ||
49 | } | ||
50 | }, | ||
51 | _ => {} | ||
52 | } | ||
53 | } | ||
54 | } | ||
35 | 55 | ||
36 | // FIXME: | 56 | // FIXME: |
37 | // macro expansion may lose all white space information | 57 | // macro expansion may lose all white space information |
38 | // But we hope someday we can use ra_fmt for that | 58 | // But we hope someday we can use ra_fmt for that |
39 | let expansion = insert_whitespaces(expanded); | 59 | let expansion = insert_whitespaces(expanded?); |
40 | Some(ExpandedMacro { name: name.to_string(), expansion }) | 60 | Some(ExpandedMacro { name: name?, expansion }) |
41 | } | 61 | } |
42 | 62 | ||
43 | fn expand_macro_recur( | 63 | fn expand_macro_recur( |
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index b0bfd646e..2d36c34e9 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -1,10 +1,10 @@ | |||
1 | use std::convert::TryInto; | 1 | use std::convert::TryInto; |
2 | 2 | ||
3 | use either::Either; | 3 | use either::Either; |
4 | use hir::{InFile, Semantics}; | 4 | use hir::{AsAssocItem, InFile, ModuleDef, Semantics}; |
5 | use ide_db::{ | 5 | use ide_db::{ |
6 | base_db::{AnchoredPath, FileId, FileLoader}, | 6 | base_db::{AnchoredPath, FileId, FileLoader}, |
7 | defs::{NameClass, NameRefClass}, | 7 | defs::{Definition, NameClass, NameRefClass}, |
8 | RootDatabase, | 8 | RootDatabase, |
9 | }; | 9 | }; |
10 | use syntax::{ | 10 | use syntax::{ |
@@ -57,7 +57,8 @@ pub(crate) fn goto_definition( | |||
57 | }, | 57 | }, |
58 | ast::Name(name) => { | 58 | ast::Name(name) => { |
59 | let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); | 59 | let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); |
60 | def.try_to_nav(sema.db) | 60 | try_find_trait_item_definition(&sema.db, &def) |
61 | .or_else(|| def.try_to_nav(sema.db)) | ||
61 | }, | 62 | }, |
62 | ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) { | 63 | ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) { |
63 | let def = name_class.referenced_or_defined(sema.db); | 64 | let def = name_class.referenced_or_defined(sema.db); |
@@ -99,6 +100,34 @@ fn try_lookup_include_path( | |||
99 | }) | 100 | }) |
100 | } | 101 | } |
101 | 102 | ||
103 | /// finds the trait definition of an impl'd item | ||
104 | /// e.g. | ||
105 | /// ```rust | ||
106 | /// trait A { fn a(); } | ||
107 | /// struct S; | ||
108 | /// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait | ||
109 | /// ``` | ||
110 | fn try_find_trait_item_definition(db: &RootDatabase, def: &Definition) -> Option<NavigationTarget> { | ||
111 | let name = def.name(db)?; | ||
112 | let assoc = match def { | ||
113 | Definition::ModuleDef(ModuleDef::Function(f)) => f.as_assoc_item(db), | ||
114 | Definition::ModuleDef(ModuleDef::Const(c)) => c.as_assoc_item(db), | ||
115 | Definition::ModuleDef(ModuleDef::TypeAlias(ty)) => ty.as_assoc_item(db), | ||
116 | _ => None, | ||
117 | }?; | ||
118 | |||
119 | let imp = match assoc.container(db) { | ||
120 | hir::AssocItemContainer::Impl(imp) => imp, | ||
121 | _ => return None, | ||
122 | }; | ||
123 | |||
124 | let trait_ = imp.trait_(db)?; | ||
125 | trait_ | ||
126 | .items(db) | ||
127 | .iter() | ||
128 | .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten()) | ||
129 | } | ||
130 | |||
102 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | 131 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { |
103 | return tokens.max_by_key(priority); | 132 | return tokens.max_by_key(priority); |
104 | fn priority(n: &SyntaxToken) -> usize { | 133 | fn priority(n: &SyntaxToken) -> usize { |
@@ -1262,4 +1291,58 @@ fn main() { | |||
1262 | "#, | 1291 | "#, |
1263 | ); | 1292 | ); |
1264 | } | 1293 | } |
1294 | |||
1295 | #[test] | ||
1296 | fn goto_def_of_trait_impl_fn() { | ||
1297 | check( | ||
1298 | r#" | ||
1299 | trait Twait { | ||
1300 | fn a(); | ||
1301 | // ^ | ||
1302 | } | ||
1303 | |||
1304 | struct Stwuct; | ||
1305 | |||
1306 | impl Twait for Stwuct { | ||
1307 | fn a$0(); | ||
1308 | } | ||
1309 | "#, | ||
1310 | ); | ||
1311 | } | ||
1312 | |||
1313 | #[test] | ||
1314 | fn goto_def_of_trait_impl_const() { | ||
1315 | check( | ||
1316 | r#" | ||
1317 | trait Twait { | ||
1318 | const NOMS: bool; | ||
1319 | // ^^^^ | ||
1320 | } | ||
1321 | |||
1322 | struct Stwuct; | ||
1323 | |||
1324 | impl Twait for Stwuct { | ||
1325 | const NOMS$0: bool = true; | ||
1326 | } | ||
1327 | "#, | ||
1328 | ); | ||
1329 | } | ||
1330 | |||
1331 | #[test] | ||
1332 | fn goto_def_of_trait_impl_type_alias() { | ||
1333 | check( | ||
1334 | r#" | ||
1335 | trait Twait { | ||
1336 | type IsBad; | ||
1337 | // ^^^^^ | ||
1338 | } | ||
1339 | |||
1340 | struct Stwuct; | ||
1341 | |||
1342 | impl Twait for Stwuct { | ||
1343 | type IsBad$0 = !; | ||
1344 | } | ||
1345 | "#, | ||
1346 | ); | ||
1347 | } | ||
1265 | } | 1348 | } |
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 79c2f4a1e..b03f1c71f 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs | |||
@@ -192,6 +192,7 @@ fn traverse( | |||
192 | let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); | 192 | let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); |
193 | 193 | ||
194 | let mut current_macro_call: Option<ast::MacroCall> = None; | 194 | let mut current_macro_call: Option<ast::MacroCall> = None; |
195 | let mut current_attr_macro_call = None; | ||
195 | let mut current_macro: Option<ast::Macro> = None; | 196 | let mut current_macro: Option<ast::Macro> = None; |
196 | let mut macro_highlighter = MacroHighlighter::default(); | 197 | let mut macro_highlighter = MacroHighlighter::default(); |
197 | let mut inside_attribute = false; | 198 | let mut inside_attribute = false; |
@@ -227,6 +228,19 @@ fn traverse( | |||
227 | } | 228 | } |
228 | _ => (), | 229 | _ => (), |
229 | } | 230 | } |
231 | match event.clone().map(|it| it.into_node().and_then(ast::Item::cast)) { | ||
232 | WalkEvent::Enter(Some(item)) => { | ||
233 | if sema.is_attr_macro_call(&item) { | ||
234 | current_attr_macro_call = Some(item); | ||
235 | } | ||
236 | } | ||
237 | WalkEvent::Leave(Some(item)) => { | ||
238 | if current_attr_macro_call == Some(item) { | ||
239 | current_attr_macro_call = None; | ||
240 | } | ||
241 | } | ||
242 | _ => (), | ||
243 | } | ||
230 | 244 | ||
231 | match event.clone().map(|it| it.into_node().and_then(ast::Macro::cast)) { | 245 | match event.clone().map(|it| it.into_node().and_then(ast::Macro::cast)) { |
232 | WalkEvent::Enter(Some(mac)) => { | 246 | WalkEvent::Enter(Some(mac)) => { |
@@ -286,6 +300,22 @@ fn traverse( | |||
286 | } | 300 | } |
287 | None => token.into(), | 301 | None => token.into(), |
288 | } | 302 | } |
303 | } else if current_attr_macro_call.is_some() { | ||
304 | let token = match element.clone().into_token() { | ||
305 | Some(it) => it, | ||
306 | _ => continue, | ||
307 | }; | ||
308 | let token = sema.descend_into_macros(token.clone()); | ||
309 | match token.parent() { | ||
310 | Some(parent) => { | ||
311 | // We only care Name and Name_ref | ||
312 | match (token.kind(), parent.kind()) { | ||
313 | (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(), | ||
314 | _ => token.into(), | ||
315 | } | ||
316 | } | ||
317 | None => token.into(), | ||
318 | } | ||
289 | } else { | 319 | } else { |
290 | element.clone() | 320 | element.clone() |
291 | }; | 321 | }; |