diff options
Diffstat (limited to 'crates/ide')
-rw-r--r-- | crates/ide/src/display/navigation_target.rs | 65 | ||||
-rw-r--r-- | crates/ide/src/doc_links.rs | 7 | ||||
-rw-r--r-- | crates/ide/src/extend_selection.rs | 2 | ||||
-rw-r--r-- | crates/ide/src/goto_definition.rs | 57 | ||||
-rw-r--r-- | crates/ide/src/goto_implementation.rs | 8 | ||||
-rw-r--r-- | crates/ide/src/hover.rs | 4 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 7 | ||||
-rw-r--r-- | crates/ide/src/references.rs | 91 | ||||
-rw-r--r-- | crates/ide/src/references/rename.rs | 104 | ||||
-rw-r--r-- | crates/ide/src/runnables.rs | 203 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting.rs | 1 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/test_data/highlighting.html | 5 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/tests.rs | 3 |
13 files changed, 374 insertions, 183 deletions
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs index 73fc73619..48acb8c93 100644 --- a/crates/ide/src/display/navigation_target.rs +++ b/crates/ide/src/display/navigation_target.rs | |||
@@ -1,15 +1,13 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use either::Either; | 3 | use either::Either; |
4 | use hir::{ | 4 | use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource}; |
5 | AssocItem, Documentation, FieldSource, HasAttrs, HasSource, HirFileId, InFile, ModuleSource, | ||
6 | }; | ||
7 | use ide_db::base_db::{FileId, SourceDatabase}; | 5 | use ide_db::base_db::{FileId, SourceDatabase}; |
8 | use ide_db::{defs::Definition, RootDatabase}; | 6 | use ide_db::{defs::Definition, RootDatabase}; |
9 | use syntax::{ | 7 | use syntax::{ |
10 | ast::{self, NameOwner}, | 8 | ast::{self, NameOwner}, |
11 | match_ast, AstNode, SmolStr, | 9 | match_ast, AstNode, SmolStr, |
12 | SyntaxKind::{self, IDENT_PAT, TYPE_PARAM}, | 10 | SyntaxKind::{self, IDENT_PAT, LIFETIME_PARAM, TYPE_PARAM}, |
13 | TextRange, | 11 | TextRange, |
14 | }; | 12 | }; |
15 | 13 | ||
@@ -119,25 +117,6 @@ impl NavigationTarget { | |||
119 | ) | 117 | ) |
120 | } | 118 | } |
121 | 119 | ||
122 | /// Allows `NavigationTarget` to be created from a `DocCommentsOwner` and a `NameOwner` | ||
123 | pub(crate) fn from_doc_commented( | ||
124 | db: &RootDatabase, | ||
125 | named: InFile<&dyn ast::NameOwner>, | ||
126 | node: InFile<&dyn ast::DocCommentsOwner>, | ||
127 | ) -> NavigationTarget { | ||
128 | let name = | ||
129 | named.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_")); | ||
130 | let frange = node.map(|it| it.syntax()).original_file_range(db); | ||
131 | |||
132 | NavigationTarget::from_syntax( | ||
133 | frange.file_id, | ||
134 | name, | ||
135 | None, | ||
136 | frange.range, | ||
137 | node.value.syntax().kind(), | ||
138 | ) | ||
139 | } | ||
140 | |||
141 | fn from_syntax( | 120 | fn from_syntax( |
142 | file_id: FileId, | 121 | file_id: FileId, |
143 | name: SmolStr, | 122 | name: SmolStr, |
@@ -168,7 +147,7 @@ impl ToNav for FileSymbol { | |||
168 | focus_range: self.name_range, | 147 | focus_range: self.name_range, |
169 | container_name: self.container_name.clone(), | 148 | container_name: self.container_name.clone(), |
170 | description: description_from_symbol(db, self), | 149 | description: description_from_symbol(db, self), |
171 | docs: docs_from_symbol(db, self), | 150 | docs: None, |
172 | } | 151 | } |
173 | } | 152 | } |
174 | } | 153 | } |
@@ -190,6 +169,7 @@ impl TryToNav for Definition { | |||
190 | Definition::SelfType(it) => Some(it.to_nav(db)), | 169 | Definition::SelfType(it) => Some(it.to_nav(db)), |
191 | Definition::Local(it) => Some(it.to_nav(db)), | 170 | Definition::Local(it) => Some(it.to_nav(db)), |
192 | Definition::TypeParam(it) => Some(it.to_nav(db)), | 171 | Definition::TypeParam(it) => Some(it.to_nav(db)), |
172 | Definition::LifetimeParam(it) => Some(it.to_nav(db)), | ||
193 | } | 173 | } |
194 | } | 174 | } |
195 | } | 175 | } |
@@ -252,7 +232,7 @@ impl ToNav for hir::Module { | |||
252 | } | 232 | } |
253 | } | 233 | } |
254 | 234 | ||
255 | impl ToNav for hir::ImplDef { | 235 | impl ToNav for hir::Impl { |
256 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | 236 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { |
257 | let src = self.source(db); | 237 | let src = self.source(db); |
258 | let derive_attr = self.is_builtin_derive(db); | 238 | let derive_attr = self.is_builtin_derive(db); |
@@ -384,28 +364,21 @@ impl ToNav for hir::TypeParam { | |||
384 | } | 364 | } |
385 | } | 365 | } |
386 | 366 | ||
387 | pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<Documentation> { | 367 | impl ToNav for hir::LifetimeParam { |
388 | let parse = db.parse(symbol.file_id); | 368 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { |
389 | let node = symbol.ptr.to_node(parse.tree().syntax()); | 369 | let src = self.source(db); |
390 | let file_id = HirFileId::from(symbol.file_id); | 370 | let full_range = src.value.syntax().text_range(); |
391 | 371 | NavigationTarget { | |
392 | let it = match_ast! { | 372 | file_id: src.file_id.original_file(db), |
393 | match node { | 373 | name: self.name(db).to_string().into(), |
394 | ast::Fn(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)), | 374 | kind: LIFETIME_PARAM, |
395 | ast::Struct(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)), | 375 | full_range, |
396 | ast::Enum(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)), | 376 | focus_range: Some(full_range), |
397 | ast::Trait(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)), | 377 | container_name: None, |
398 | ast::Module(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)), | 378 | description: None, |
399 | ast::TypeAlias(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)), | 379 | docs: None, |
400 | ast::Const(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)), | ||
401 | ast::Static(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)), | ||
402 | ast::RecordField(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)), | ||
403 | ast::Variant(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)), | ||
404 | ast::MacroCall(it) => hir::Attrs::from_attrs_owner(db, InFile::new(file_id, &it)), | ||
405 | _ => return None, | ||
406 | } | 380 | } |
407 | }; | 381 | } |
408 | it.docs() | ||
409 | } | 382 | } |
410 | 383 | ||
411 | /// Get a description of a symbol. | 384 | /// Get a description of a symbol. |
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 10263537a..79c081cac 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs | |||
@@ -112,7 +112,7 @@ fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> { | |||
112 | .as_assoc_item(db) | 112 | .as_assoc_item(db) |
113 | .and_then(|assoc| match assoc.container(db) { | 113 | .and_then(|assoc| match assoc.container(db) { |
114 | AssocItemContainer::Trait(t) => Some(t.into()), | 114 | AssocItemContainer::Trait(t) => Some(t.into()), |
115 | AssocItemContainer::ImplDef(impld) => { | 115 | AssocItemContainer::Impl(impld) => { |
116 | impld.target_ty(db).as_adt().map(|adt| adt.into()) | 116 | impld.target_ty(db).as_adt().map(|adt| adt.into()) |
117 | } | 117 | } |
118 | }) | 118 | }) |
@@ -190,7 +190,10 @@ fn rewrite_intra_doc_link( | |||
190 | }, | 190 | }, |
191 | Definition::Macro(it) => it.resolve_doc_path(db, link, ns), | 191 | Definition::Macro(it) => it.resolve_doc_path(db, link, ns), |
192 | Definition::Field(it) => it.resolve_doc_path(db, link, ns), | 192 | Definition::Field(it) => it.resolve_doc_path(db, link, ns), |
193 | Definition::SelfType(_) | Definition::Local(_) | Definition::TypeParam(_) => return None, | 193 | Definition::SelfType(_) |
194 | | Definition::Local(_) | ||
195 | | Definition::TypeParam(_) | ||
196 | | Definition::LifetimeParam(_) => return None, | ||
194 | }?; | 197 | }?; |
195 | let krate = resolved.module(db)?.krate(); | 198 | let krate = resolved.module(db)?.krate(); |
196 | let canonical_path = resolved.canonical_path(db)?; | 199 | let canonical_path = resolved.canonical_path(db)?; |
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs index 0971f7701..6f3022dfd 100644 --- a/crates/ide/src/extend_selection.rs +++ b/crates/ide/src/extend_selection.rs | |||
@@ -237,7 +237,7 @@ fn pick_best(l: SyntaxToken, r: SyntaxToken) -> SyntaxToken { | |||
237 | fn priority(n: &SyntaxToken) -> usize { | 237 | fn priority(n: &SyntaxToken) -> usize { |
238 | match n.kind() { | 238 | match n.kind() { |
239 | WHITESPACE => 0, | 239 | WHITESPACE => 0, |
240 | IDENT | T![self] | T![super] | T![crate] | LIFETIME => 2, | 240 | IDENT | T![self] | T![super] | T![crate] | LIFETIME_IDENT => 2, |
241 | _ => 1, | 241 | _ => 1, |
242 | } | 242 | } |
243 | } | 243 | } |
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index b9810457f..173509b08 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -1,3 +1,4 @@ | |||
1 | use either::Either; | ||
1 | use hir::Semantics; | 2 | use hir::Semantics; |
2 | use ide_db::{ | 3 | use ide_db::{ |
3 | base_db::FileId, | 4 | base_db::FileId, |
@@ -33,7 +34,7 @@ pub(crate) fn goto_definition( | |||
33 | let nav_targets = match_ast! { | 34 | let nav_targets = match_ast! { |
34 | match parent { | 35 | match parent { |
35 | ast::NameRef(name_ref) => { | 36 | ast::NameRef(name_ref) => { |
36 | reference_definition(&sema, &name_ref).to_vec() | 37 | reference_definition(&sema, Either::Right(&name_ref)).to_vec() |
37 | }, | 38 | }, |
38 | ast::Name(name) => { | 39 | ast::Name(name) => { |
39 | let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); | 40 | let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); |
@@ -53,6 +54,13 @@ pub(crate) fn goto_definition( | |||
53 | let self_param = func.param_list()?.self_param()?; | 54 | let self_param = func.param_list()?.self_param()?; |
54 | vec![self_to_nav_target(self_param, position.file_id)?] | 55 | vec![self_to_nav_target(self_param, position.file_id)?] |
55 | }, | 56 | }, |
57 | ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) { | ||
58 | let def = name_class.referenced_or_defined(sema.db); | ||
59 | let nav = def.try_to_nav(sema.db)?; | ||
60 | vec![nav] | ||
61 | } else { | ||
62 | reference_definition(&sema, Either::Left(<)).to_vec() | ||
63 | }, | ||
56 | _ => return None, | 64 | _ => return None, |
57 | } | 65 | } |
58 | }; | 66 | }; |
@@ -64,7 +72,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | |||
64 | return tokens.max_by_key(priority); | 72 | return tokens.max_by_key(priority); |
65 | fn priority(n: &SyntaxToken) -> usize { | 73 | fn priority(n: &SyntaxToken) -> usize { |
66 | match n.kind() { | 74 | match n.kind() { |
67 | IDENT | INT_NUMBER | T![self] => 2, | 75 | IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 2, |
68 | kind if kind.is_trivia() => 0, | 76 | kind if kind.is_trivia() => 0, |
69 | _ => 1, | 77 | _ => 1, |
70 | } | 78 | } |
@@ -102,9 +110,12 @@ impl ReferenceResult { | |||
102 | 110 | ||
103 | pub(crate) fn reference_definition( | 111 | pub(crate) fn reference_definition( |
104 | sema: &Semantics<RootDatabase>, | 112 | sema: &Semantics<RootDatabase>, |
105 | name_ref: &ast::NameRef, | 113 | name_ref: Either<&ast::Lifetime, &ast::NameRef>, |
106 | ) -> ReferenceResult { | 114 | ) -> ReferenceResult { |
107 | let name_kind = NameRefClass::classify(sema, name_ref); | 115 | let name_kind = name_ref.either( |
116 | |lifetime| NameRefClass::classify_lifetime(sema, lifetime), | ||
117 | |name_ref| NameRefClass::classify(sema, name_ref), | ||
118 | ); | ||
108 | if let Some(def) = name_kind { | 119 | if let Some(def) = name_kind { |
109 | let def = def.referenced(sema.db); | 120 | let def = def.referenced(sema.db); |
110 | return match def.try_to_nav(sema.db) { | 121 | return match def.try_to_nav(sema.db) { |
@@ -114,10 +125,9 @@ pub(crate) fn reference_definition( | |||
114 | } | 125 | } |
115 | 126 | ||
116 | // Fallback index based approach: | 127 | // Fallback index based approach: |
117 | let navs = symbol_index::index_resolve(sema.db, name_ref) | 128 | let name = name_ref.either(ast::Lifetime::text, ast::NameRef::text); |
118 | .into_iter() | 129 | let navs = |
119 | .map(|s| s.to_nav(sema.db)) | 130 | symbol_index::index_resolve(sema.db, name).into_iter().map(|s| s.to_nav(sema.db)).collect(); |
120 | .collect(); | ||
121 | ReferenceResult::Approximate(navs) | 131 | ReferenceResult::Approximate(navs) |
122 | } | 132 | } |
123 | 133 | ||
@@ -1036,4 +1046,35 @@ impl Foo { | |||
1036 | }"#, | 1046 | }"#, |
1037 | ) | 1047 | ) |
1038 | } | 1048 | } |
1049 | |||
1050 | #[test] | ||
1051 | fn goto_lifetime_param_on_decl() { | ||
1052 | check( | ||
1053 | r#" | ||
1054 | fn foo<'foobar<|>>(_: &'foobar ()) { | ||
1055 | //^^^^^^^ | ||
1056 | }"#, | ||
1057 | ) | ||
1058 | } | ||
1059 | |||
1060 | #[test] | ||
1061 | fn goto_lifetime_param_decl() { | ||
1062 | check( | ||
1063 | r#" | ||
1064 | fn foo<'foobar>(_: &'foobar<|> ()) { | ||
1065 | //^^^^^^^ | ||
1066 | }"#, | ||
1067 | ) | ||
1068 | } | ||
1069 | |||
1070 | #[test] | ||
1071 | fn goto_lifetime_param_decl_nested() { | ||
1072 | check( | ||
1073 | r#" | ||
1074 | fn foo<'foobar>(_: &'foobar ()) { | ||
1075 | fn foo<'foobar>(_: &'foobar<|> ()) {} | ||
1076 | //^^^^^^^ | ||
1077 | }"#, | ||
1078 | ) | ||
1079 | } | ||
1039 | } | 1080 | } |
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index 529004878..6eac39639 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use hir::{Crate, ImplDef, Semantics}; | 1 | use hir::{Crate, Impl, Semantics}; |
2 | use ide_db::RootDatabase; | 2 | use ide_db::RootDatabase; |
3 | use syntax::{algo::find_node_at_offset, ast, AstNode}; | 3 | use syntax::{algo::find_node_at_offset, ast, AstNode}; |
4 | 4 | ||
@@ -49,7 +49,7 @@ fn impls_for_def( | |||
49 | ast::AdtDef::Union(def) => sema.to_def(def)?.ty(sema.db), | 49 | ast::AdtDef::Union(def) => sema.to_def(def)?.ty(sema.db), |
50 | }; | 50 | }; |
51 | 51 | ||
52 | let impls = ImplDef::all_in_crate(sema.db, krate); | 52 | let impls = Impl::all_in_crate(sema.db, krate); |
53 | 53 | ||
54 | Some( | 54 | Some( |
55 | impls | 55 | impls |
@@ -67,7 +67,7 @@ fn impls_for_trait( | |||
67 | ) -> Option<Vec<NavigationTarget>> { | 67 | ) -> Option<Vec<NavigationTarget>> { |
68 | let tr = sema.to_def(node)?; | 68 | let tr = sema.to_def(node)?; |
69 | 69 | ||
70 | let impls = ImplDef::for_trait(sema.db, krate, tr); | 70 | let impls = Impl::for_trait(sema.db, krate, tr); |
71 | 71 | ||
72 | Some(impls.into_iter().map(|imp| imp.to_nav(sema.db)).collect()) | 72 | Some(impls.into_iter().map(|imp| imp.to_nav(sema.db)).collect()) |
73 | } | 73 | } |
@@ -221,6 +221,8 @@ struct Foo<|>; | |||
221 | mod marker { | 221 | mod marker { |
222 | trait Copy {} | 222 | trait Copy {} |
223 | } | 223 | } |
224 | #[rustc_builtin_macro] | ||
225 | macro Copy {} | ||
224 | "#, | 226 | "#, |
225 | ); | 227 | ); |
226 | } | 228 | } |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index c03dd74e4..da6bb726a 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -295,7 +295,7 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String> | |||
295 | Definition::ModuleDef(md) => match md { | 295 | Definition::ModuleDef(md) => match md { |
296 | ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) { | 296 | ModuleDef::Function(f) => match f.as_assoc_item(db)?.container(db) { |
297 | AssocItemContainer::Trait(t) => Some(t.name(db)), | 297 | AssocItemContainer::Trait(t) => Some(t.name(db)), |
298 | AssocItemContainer::ImplDef(i) => i.target_ty(db).as_adt().map(|adt| adt.name(db)), | 298 | AssocItemContainer::Impl(i) => i.target_ty(db).as_adt().map(|adt| adt.name(db)), |
299 | }, | 299 | }, |
300 | ModuleDef::EnumVariant(e) => Some(e.parent_enum(db).name(db)), | 300 | ModuleDef::EnumVariant(e) => Some(e.parent_enum(db).name(db)), |
301 | _ => None, | 301 | _ => None, |
@@ -370,7 +370,7 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> { | |||
370 | Adt::Enum(it) => from_def_source(db, it, mod_path), | 370 | Adt::Enum(it) => from_def_source(db, it, mod_path), |
371 | }) | 371 | }) |
372 | } | 372 | } |
373 | Definition::TypeParam(_) => { | 373 | Definition::TypeParam(_) | Definition::LifetimeParam(_) => { |
374 | // FIXME: Hover for generic param | 374 | // FIXME: Hover for generic param |
375 | None | 375 | None |
376 | } | 376 | } |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 71068cac2..c5c652cda 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -528,6 +528,13 @@ impl Analysis { | |||
528 | self.with_db(|db| references::rename::rename(db, position, new_name)) | 528 | self.with_db(|db| references::rename::rename(db, position, new_name)) |
529 | } | 529 | } |
530 | 530 | ||
531 | pub fn prepare_rename( | ||
532 | &self, | ||
533 | position: FilePosition, | ||
534 | ) -> Cancelable<Result<RangeInfo<()>, RenameError>> { | ||
535 | self.with_db(|db| references::rename::prepare_rename(db, position)) | ||
536 | } | ||
537 | |||
531 | pub fn structural_search_replace( | 538 | pub fn structural_search_replace( |
532 | &self, | 539 | &self, |
533 | query: &str, | 540 | query: &str, |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 675957fff..98190a86b 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -130,6 +130,8 @@ pub(crate) fn find_all_refs( | |||
130 | kind = ReferenceKind::FieldShorthandForLocal; | 130 | kind = ReferenceKind::FieldShorthandForLocal; |
131 | } | 131 | } |
132 | } | 132 | } |
133 | } else if let Definition::LifetimeParam(_) = def { | ||
134 | kind = ReferenceKind::Lifetime; | ||
133 | }; | 135 | }; |
134 | 136 | ||
135 | let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; | 137 | let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; |
@@ -148,11 +150,29 @@ fn find_name( | |||
148 | let range = name.syntax().text_range(); | 150 | let range = name.syntax().text_range(); |
149 | return Some(RangeInfo::new(range, def)); | 151 | return Some(RangeInfo::new(range, def)); |
150 | } | 152 | } |
151 | let name_ref = | 153 | |
152 | sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?; | 154 | let (text_range, def) = if let Some(lifetime) = |
153 | let def = NameRefClass::classify(sema, &name_ref)?.referenced(sema.db); | 155 | sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset) |
154 | let range = name_ref.syntax().text_range(); | 156 | { |
155 | Some(RangeInfo::new(range, def)) | 157 | if let Some(def) = NameRefClass::classify_lifetime(sema, &lifetime) |
158 | .map(|class| NameRefClass::referenced(class, sema.db)) | ||
159 | { | ||
160 | (lifetime.syntax().text_range(), def) | ||
161 | } else { | ||
162 | ( | ||
163 | lifetime.syntax().text_range(), | ||
164 | NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db), | ||
165 | ) | ||
166 | } | ||
167 | } else { | ||
168 | let name_ref = | ||
169 | sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?; | ||
170 | ( | ||
171 | name_ref.syntax().text_range(), | ||
172 | NameRefClass::classify(sema, &name_ref)?.referenced(sema.db), | ||
173 | ) | ||
174 | }; | ||
175 | Some(RangeInfo::new(text_range, def)) | ||
156 | } | 176 | } |
157 | 177 | ||
158 | fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { | 178 | fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { |
@@ -1005,4 +1025,65 @@ impl Foo { | |||
1005 | } | 1025 | } |
1006 | expect.assert_eq(&actual) | 1026 | expect.assert_eq(&actual) |
1007 | } | 1027 | } |
1028 | |||
1029 | #[test] | ||
1030 | fn test_find_lifetimes_function() { | ||
1031 | check( | ||
1032 | r#" | ||
1033 | trait Foo<'a> {} | ||
1034 | impl<'a> Foo<'a> for &'a () {} | ||
1035 | fn foo<'a, 'b: 'a>(x: &'a<|> ()) -> &'a () where &'a (): Foo<'a> { | ||
1036 | fn bar<'a>(_: &'a ()) {} | ||
1037 | x | ||
1038 | } | ||
1039 | "#, | ||
1040 | expect![[r#" | ||
1041 | 'a LIFETIME_PARAM FileId(0) 55..57 55..57 Lifetime | ||
1042 | |||
1043 | FileId(0) 63..65 Lifetime | ||
1044 | FileId(0) 71..73 Lifetime | ||
1045 | FileId(0) 82..84 Lifetime | ||
1046 | FileId(0) 95..97 Lifetime | ||
1047 | FileId(0) 106..108 Lifetime | ||
1048 | "#]], | ||
1049 | ); | ||
1050 | } | ||
1051 | |||
1052 | #[test] | ||
1053 | fn test_find_lifetimes_type_alias() { | ||
1054 | check( | ||
1055 | r#" | ||
1056 | type Foo<'a, T> where T: 'a<|> = &'a T; | ||
1057 | "#, | ||
1058 | expect![[r#" | ||
1059 | 'a LIFETIME_PARAM FileId(0) 9..11 9..11 Lifetime | ||
1060 | |||
1061 | FileId(0) 25..27 Lifetime | ||
1062 | FileId(0) 31..33 Lifetime | ||
1063 | "#]], | ||
1064 | ); | ||
1065 | } | ||
1066 | |||
1067 | #[test] | ||
1068 | fn test_find_lifetimes_trait_impl() { | ||
1069 | check( | ||
1070 | r#" | ||
1071 | trait Foo<'a> { | ||
1072 | fn foo() -> &'a (); | ||
1073 | } | ||
1074 | impl<'a> Foo<'a> for &'a () { | ||
1075 | fn foo() -> &'a<|> () { | ||
1076 | unimplemented!() | ||
1077 | } | ||
1078 | } | ||
1079 | "#, | ||
1080 | expect![[r#" | ||
1081 | 'a LIFETIME_PARAM FileId(0) 47..49 47..49 Lifetime | ||
1082 | |||
1083 | FileId(0) 55..57 Lifetime | ||
1084 | FileId(0) 64..66 Lifetime | ||
1085 | FileId(0) 89..91 Lifetime | ||
1086 | "#]], | ||
1087 | ); | ||
1088 | } | ||
1008 | } | 1089 | } |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 44081f210..56e923841 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -35,6 +35,29 @@ impl fmt::Display for RenameError { | |||
35 | 35 | ||
36 | impl Error for RenameError {} | 36 | impl Error for RenameError {} |
37 | 37 | ||
38 | pub(crate) fn prepare_rename( | ||
39 | db: &RootDatabase, | ||
40 | position: FilePosition, | ||
41 | ) -> Result<RangeInfo<()>, RenameError> { | ||
42 | let sema = Semantics::new(db); | ||
43 | let source_file = sema.parse(position.file_id); | ||
44 | let syntax = source_file.syntax(); | ||
45 | if let Some(module) = find_module_at_offset(&sema, position, syntax) { | ||
46 | rename_mod(&sema, position, module, "dummy") | ||
47 | } else if let Some(self_token) = | ||
48 | syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) | ||
49 | { | ||
50 | rename_self_to_param(&sema, position, self_token, "dummy") | ||
51 | } else { | ||
52 | let range = match find_all_refs(&sema, position, None) { | ||
53 | Some(RangeInfo { range, .. }) => range, | ||
54 | None => return Err(RenameError("No references found at position".to_string())), | ||
55 | }; | ||
56 | Ok(RangeInfo::new(range, SourceChange::from(vec![]))) | ||
57 | } | ||
58 | .map(|info| RangeInfo::new(info.range, ())) | ||
59 | } | ||
60 | |||
38 | pub(crate) fn rename( | 61 | pub(crate) fn rename( |
39 | db: &RootDatabase, | 62 | db: &RootDatabase, |
40 | position: FilePosition, | 63 | position: FilePosition, |
@@ -49,11 +72,18 @@ pub(crate) fn rename_with_semantics( | |||
49 | position: FilePosition, | 72 | position: FilePosition, |
50 | new_name: &str, | 73 | new_name: &str, |
51 | ) -> Result<RangeInfo<SourceChange>, RenameError> { | 74 | ) -> Result<RangeInfo<SourceChange>, RenameError> { |
52 | match lex_single_syntax_kind(new_name) { | 75 | let is_lifetime_name = match lex_single_syntax_kind(new_name) { |
53 | Some(res) => match res { | 76 | Some(res) => match res { |
54 | (SyntaxKind::IDENT, _) => (), | 77 | (SyntaxKind::IDENT, _) => false, |
55 | (SyntaxKind::UNDERSCORE, _) => (), | 78 | (SyntaxKind::UNDERSCORE, _) => false, |
56 | (SyntaxKind::SELF_KW, _) => return rename_to_self(&sema, position), | 79 | (SyntaxKind::SELF_KW, _) => return rename_to_self(&sema, position), |
80 | (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => true, | ||
81 | (SyntaxKind::LIFETIME_IDENT, _) => { | ||
82 | return Err(RenameError(format!( | ||
83 | "Invalid name `{0}`: Cannot rename lifetime to {0}", | ||
84 | new_name | ||
85 | ))) | ||
86 | } | ||
57 | (_, Some(syntax_error)) => { | 87 | (_, Some(syntax_error)) => { |
58 | return Err(RenameError(format!("Invalid name `{}`: {}", new_name, syntax_error))) | 88 | return Err(RenameError(format!("Invalid name `{}`: {}", new_name, syntax_error))) |
59 | } | 89 | } |
@@ -62,18 +92,21 @@ pub(crate) fn rename_with_semantics( | |||
62 | } | 92 | } |
63 | }, | 93 | }, |
64 | None => return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))), | 94 | None => return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))), |
65 | } | 95 | }; |
66 | 96 | ||
67 | let source_file = sema.parse(position.file_id); | 97 | let source_file = sema.parse(position.file_id); |
68 | let syntax = source_file.syntax(); | 98 | let syntax = source_file.syntax(); |
69 | if let Some(module) = find_module_at_offset(&sema, position, syntax) { | 99 | // this is here to prevent lifetime renames from happening on modules and self |
100 | if is_lifetime_name { | ||
101 | rename_reference(&sema, position, new_name, is_lifetime_name) | ||
102 | } else if let Some(module) = find_module_at_offset(&sema, position, syntax) { | ||
70 | rename_mod(&sema, position, module, new_name) | 103 | rename_mod(&sema, position, module, new_name) |
71 | } else if let Some(self_token) = | 104 | } else if let Some(self_token) = |
72 | syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) | 105 | syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) |
73 | { | 106 | { |
74 | rename_self_to_param(&sema, position, self_token, new_name) | 107 | rename_self_to_param(&sema, position, self_token, new_name) |
75 | } else { | 108 | } else { |
76 | rename_reference(&sema, position, new_name) | 109 | rename_reference(&sema, position, new_name, is_lifetime_name) |
77 | } | 110 | } |
78 | } | 111 | } |
79 | 112 | ||
@@ -355,12 +388,26 @@ fn rename_reference( | |||
355 | sema: &Semantics<RootDatabase>, | 388 | sema: &Semantics<RootDatabase>, |
356 | position: FilePosition, | 389 | position: FilePosition, |
357 | new_name: &str, | 390 | new_name: &str, |
391 | is_lifetime_name: bool, | ||
358 | ) -> Result<RangeInfo<SourceChange>, RenameError> { | 392 | ) -> Result<RangeInfo<SourceChange>, RenameError> { |
359 | let RangeInfo { range, info: refs } = match find_all_refs(sema, position, None) { | 393 | let RangeInfo { range, info: refs } = match find_all_refs(sema, position, None) { |
360 | Some(range_info) => range_info, | 394 | Some(range_info) => range_info, |
361 | None => return Err(RenameError("No references found at position".to_string())), | 395 | None => return Err(RenameError("No references found at position".to_string())), |
362 | }; | 396 | }; |
363 | 397 | ||
398 | match (refs.declaration.kind == ReferenceKind::Lifetime, is_lifetime_name) { | ||
399 | (true, false) => { | ||
400 | return Err(RenameError(format!( | ||
401 | "Invalid name `{}`: not a lifetime identifier", | ||
402 | new_name | ||
403 | ))) | ||
404 | } | ||
405 | (false, true) => { | ||
406 | return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))) | ||
407 | } | ||
408 | _ => (), | ||
409 | } | ||
410 | |||
364 | let edit = refs | 411 | let edit = refs |
365 | .into_iter() | 412 | .into_iter() |
366 | .map(|reference| source_edit_from_reference(sema, reference, new_name)) | 413 | .map(|reference| source_edit_from_reference(sema, reference, new_name)) |
@@ -465,6 +512,24 @@ mod tests { | |||
465 | } | 512 | } |
466 | 513 | ||
467 | #[test] | 514 | #[test] |
515 | fn test_rename_to_invalid_identifier_lifetime() { | ||
516 | check( | ||
517 | "'foo", | ||
518 | r#"fn main() { let i<|> = 1; }"#, | ||
519 | "error: Invalid name `'foo`: not an identifier", | ||
520 | ); | ||
521 | } | ||
522 | |||
523 | #[test] | ||
524 | fn test_rename_to_invalid_identifier_lifetime2() { | ||
525 | check( | ||
526 | "foo", | ||
527 | r#"fn main<'a>(_: &'a<|> ()) {}"#, | ||
528 | "error: Invalid name `foo`: not a lifetime identifier", | ||
529 | ); | ||
530 | } | ||
531 | |||
532 | #[test] | ||
468 | fn test_rename_for_local() { | 533 | fn test_rename_for_local() { |
469 | check( | 534 | check( |
470 | "k", | 535 | "k", |
@@ -1396,4 +1461,31 @@ fn foo(Foo { i: bar }: foo) -> i32 { | |||
1396 | "#, | 1461 | "#, |
1397 | ) | 1462 | ) |
1398 | } | 1463 | } |
1464 | |||
1465 | #[test] | ||
1466 | fn test_rename_lifetimes() { | ||
1467 | check( | ||
1468 | "'yeeee", | ||
1469 | r#" | ||
1470 | trait Foo<'a> { | ||
1471 | fn foo() -> &'a (); | ||
1472 | } | ||
1473 | impl<'a> Foo<'a> for &'a () { | ||
1474 | fn foo() -> &'a<|> () { | ||
1475 | unimplemented!() | ||
1476 | } | ||
1477 | } | ||
1478 | "#, | ||
1479 | r#" | ||
1480 | trait Foo<'a> { | ||
1481 | fn foo() -> &'a (); | ||
1482 | } | ||
1483 | impl<'yeeee> Foo<'yeeee> for &'yeeee () { | ||
1484 | fn foo() -> &'yeeee () { | ||
1485 | unimplemented!() | ||
1486 | } | ||
1487 | } | ||
1488 | "#, | ||
1489 | ) | ||
1490 | } | ||
1399 | } | 1491 | } |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 646f63704..2f465c195 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -2,7 +2,7 @@ use std::fmt; | |||
2 | 2 | ||
3 | use assists::utils::test_related_attribute; | 3 | use assists::utils::test_related_attribute; |
4 | use cfg::CfgExpr; | 4 | use cfg::CfgExpr; |
5 | use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; | 5 | use hir::{AsAssocItem, HasAttrs, InFile, Semantics}; |
6 | use ide_db::RootDatabase; | 6 | use ide_db::RootDatabase; |
7 | use itertools::Itertools; | 7 | use itertools::Itertools; |
8 | use syntax::{ | 8 | use syntax::{ |
@@ -10,7 +10,10 @@ use syntax::{ | |||
10 | match_ast, SyntaxNode, | 10 | match_ast, SyntaxNode, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crate::{display::ToNav, FileId, NavigationTarget}; | 13 | use crate::{ |
14 | display::{ToNav, TryToNav}, | ||
15 | FileId, NavigationTarget, | ||
16 | }; | ||
14 | 17 | ||
15 | #[derive(Debug, Clone)] | 18 | #[derive(Debug, Clone)] |
16 | pub struct Runnable { | 19 | pub struct Runnable { |
@@ -101,124 +104,109 @@ pub(crate) fn runnable( | |||
101 | item: SyntaxNode, | 104 | item: SyntaxNode, |
102 | file_id: FileId, | 105 | file_id: FileId, |
103 | ) -> Option<Runnable> { | 106 | ) -> Option<Runnable> { |
104 | match_ast! { | 107 | let runnable_item = match_ast! { |
105 | match item { | 108 | match (item.clone()) { |
106 | ast::Struct(it) => runnable_struct(sema, it, file_id), | ||
107 | ast::Fn(it) => runnable_fn(sema, it, file_id), | 109 | ast::Fn(it) => runnable_fn(sema, it, file_id), |
108 | ast::Module(it) => runnable_mod(sema, it, file_id), | 110 | ast::Module(it) => runnable_mod(sema, it), |
109 | _ => None, | 111 | _ => None, |
110 | } | 112 | } |
111 | } | 113 | }; |
114 | runnable_item.or_else(|| runnable_doctest(sema, item)) | ||
112 | } | 115 | } |
113 | 116 | ||
114 | fn runnable_fn( | 117 | fn runnable_fn(sema: &Semantics<RootDatabase>, func: ast::Fn, file_id: FileId) -> Option<Runnable> { |
115 | sema: &Semantics<RootDatabase>, | 118 | let def = sema.to_def(&func)?; |
116 | fn_def: ast::Fn, | 119 | let name_string = func.name()?.text().to_string(); |
117 | file_id: FileId, | ||
118 | ) -> Option<Runnable> { | ||
119 | let name_string = fn_def.name()?.text().to_string(); | ||
120 | 120 | ||
121 | let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def)); | ||
122 | let kind = if name_string == "main" { | 121 | let kind = if name_string == "main" { |
123 | RunnableKind::Bin | 122 | RunnableKind::Bin |
124 | } else { | 123 | } else { |
125 | let test_id = match sema.to_def(&fn_def).map(|def| def.module(sema.db)) { | 124 | let canonical_path = sema.to_def(&func).and_then(|def| { |
126 | Some(module) => { | 125 | let def: hir::ModuleDef = def.into(); |
127 | let def = sema.to_def(&fn_def)?; | 126 | def.canonical_path(sema.db) |
128 | let impl_trait_name = def.as_assoc_item(sema.db).and_then(|assoc_item| { | 127 | }); |
129 | match assoc_item.container(sema.db) { | 128 | let test_id = canonical_path.map(TestId::Path).unwrap_or(TestId::Name(name_string)); |
130 | hir::AssocItemContainer::Trait(trait_item) => { | 129 | |
131 | Some(trait_item.name(sema.db).to_string()) | 130 | if test_related_attribute(&func).is_some() { |
132 | } | 131 | let attr = TestAttr::from_fn(&func); |
133 | hir::AssocItemContainer::ImplDef(impl_def) => impl_def | ||
134 | .target_ty(sema.db) | ||
135 | .as_adt() | ||
136 | .map(|adt| adt.name(sema.db).to_string()), | ||
137 | } | ||
138 | }); | ||
139 | |||
140 | let path_iter = module | ||
141 | .path_to_root(sema.db) | ||
142 | .into_iter() | ||
143 | .rev() | ||
144 | .filter_map(|it| it.name(sema.db)) | ||
145 | .map(|name| name.to_string()); | ||
146 | |||
147 | let path = if let Some(impl_trait_name) = impl_trait_name { | ||
148 | path_iter | ||
149 | .chain(std::iter::once(impl_trait_name)) | ||
150 | .chain(std::iter::once(name_string)) | ||
151 | .join("::") | ||
152 | } else { | ||
153 | path_iter.chain(std::iter::once(name_string)).join("::") | ||
154 | }; | ||
155 | |||
156 | TestId::Path(path) | ||
157 | } | ||
158 | None => TestId::Name(name_string), | ||
159 | }; | ||
160 | |||
161 | if test_related_attribute(&fn_def).is_some() { | ||
162 | let attr = TestAttr::from_fn(&fn_def); | ||
163 | RunnableKind::Test { test_id, attr } | 132 | RunnableKind::Test { test_id, attr } |
164 | } else if fn_def.has_atom_attr("bench") { | 133 | } else if func.has_atom_attr("bench") { |
165 | RunnableKind::Bench { test_id } | 134 | RunnableKind::Bench { test_id } |
166 | } else if has_runnable_doc_test(&attrs) { | ||
167 | RunnableKind::DocTest { test_id } | ||
168 | } else { | 135 | } else { |
169 | return None; | 136 | return None; |
170 | } | 137 | } |
171 | }; | 138 | }; |
172 | 139 | ||
173 | let cfg = attrs.cfg(); | 140 | let nav = NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &func)); |
174 | 141 | let cfg = def.attrs(sema.db).cfg(); | |
175 | let nav = if let RunnableKind::DocTest { .. } = kind { | ||
176 | NavigationTarget::from_doc_commented( | ||
177 | sema.db, | ||
178 | InFile::new(file_id.into(), &fn_def), | ||
179 | InFile::new(file_id.into(), &fn_def), | ||
180 | ) | ||
181 | } else { | ||
182 | NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &fn_def)) | ||
183 | }; | ||
184 | Some(Runnable { nav, kind, cfg }) | 142 | Some(Runnable { nav, kind, cfg }) |
185 | } | 143 | } |
186 | 144 | ||
187 | fn runnable_struct( | 145 | fn runnable_doctest(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { |
188 | sema: &Semantics<RootDatabase>, | 146 | match_ast! { |
189 | struct_def: ast::Struct, | 147 | match item { |
190 | file_id: FileId, | 148 | ast::Fn(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), |
191 | ) -> Option<Runnable> { | 149 | ast::Struct(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), |
192 | let name_string = struct_def.name()?.text().to_string(); | 150 | ast::Enum(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), |
151 | ast::Union(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), | ||
152 | ast::Trait(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), | ||
153 | ast::Const(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), | ||
154 | ast::Static(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), | ||
155 | ast::TypeAlias(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), | ||
156 | _ => None, | ||
157 | } | ||
158 | } | ||
159 | } | ||
193 | 160 | ||
194 | let attrs = | 161 | fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Option<Runnable> { |
195 | Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &struct_def)); | 162 | let attrs = match def { |
163 | hir::ModuleDef::Module(it) => it.attrs(sema.db), | ||
164 | hir::ModuleDef::Function(it) => it.attrs(sema.db), | ||
165 | hir::ModuleDef::Adt(it) => it.attrs(sema.db), | ||
166 | hir::ModuleDef::EnumVariant(it) => it.attrs(sema.db), | ||
167 | hir::ModuleDef::Const(it) => it.attrs(sema.db), | ||
168 | hir::ModuleDef::Static(it) => it.attrs(sema.db), | ||
169 | hir::ModuleDef::Trait(it) => it.attrs(sema.db), | ||
170 | hir::ModuleDef::TypeAlias(it) => it.attrs(sema.db), | ||
171 | hir::ModuleDef::BuiltinType(_) => return None, | ||
172 | }; | ||
196 | if !has_runnable_doc_test(&attrs) { | 173 | if !has_runnable_doc_test(&attrs) { |
197 | return None; | 174 | return None; |
198 | } | 175 | } |
199 | let cfg = attrs.cfg(); | 176 | let def_name = def.name(sema.db).map(|it| it.to_string()); |
200 | 177 | let test_id = def | |
201 | let test_id = match sema.to_def(&struct_def).map(|def| def.module(sema.db)) { | 178 | .canonical_path(sema.db) |
202 | Some(module) => { | 179 | // This probably belongs to canonical path? |
203 | let path_iter = module | 180 | .map(|path| { |
204 | .path_to_root(sema.db) | 181 | let assoc_def = match def { |
205 | .into_iter() | 182 | hir::ModuleDef::Function(it) => it.as_assoc_item(sema.db), |
206 | .rev() | 183 | hir::ModuleDef::Const(it) => it.as_assoc_item(sema.db), |
207 | .filter_map(|it| it.name(sema.db)) | 184 | hir::ModuleDef::TypeAlias(it) => it.as_assoc_item(sema.db), |
208 | .map(|name| name.to_string()); | 185 | _ => None, |
209 | let path = path_iter.chain(std::iter::once(name_string)).join("::"); | 186 | }; |
210 | 187 | // FIXME: this also looks very wrong | |
211 | TestId::Path(path) | 188 | if let Some(assoc_def) = assoc_def { |
212 | } | 189 | if let hir::AssocItemContainer::Impl(imp) = assoc_def.container(sema.db) { |
213 | None => TestId::Name(name_string), | 190 | if let Some(adt) = imp.target_ty(sema.db).as_adt() { |
214 | }; | 191 | let name = adt.name(sema.db).to_string(); |
215 | 192 | let idx = path.rfind(':').unwrap_or(0); | |
216 | let nav = NavigationTarget::from_doc_commented( | 193 | let (prefix, suffix) = path.split_at(idx); |
217 | sema.db, | 194 | return format!("{}{}::{}", prefix, name, suffix); |
218 | InFile::new(file_id.into(), &struct_def), | 195 | } |
219 | InFile::new(file_id.into(), &struct_def), | 196 | } |
220 | ); | 197 | } |
221 | Some(Runnable { nav, kind: RunnableKind::DocTest { test_id }, cfg }) | 198 | path |
199 | }) | ||
200 | .map(TestId::Path) | ||
201 | .or_else(|| def_name.clone().map(TestId::Name))?; | ||
202 | |||
203 | let mut nav = def.try_to_nav(sema.db)?; | ||
204 | nav.focus_range = None; | ||
205 | nav.description = None; | ||
206 | nav.docs = None; | ||
207 | nav.kind = syntax::SyntaxKind::COMMENT; | ||
208 | let res = Runnable { nav, kind: RunnableKind::DocTest { test_id }, cfg: attrs.cfg() }; | ||
209 | Some(res) | ||
222 | } | 210 | } |
223 | 211 | ||
224 | #[derive(Debug, Copy, Clone)] | 212 | #[derive(Debug, Copy, Clone)] |
@@ -262,11 +250,7 @@ fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool { | |||
262 | }) | 250 | }) |
263 | } | 251 | } |
264 | 252 | ||
265 | fn runnable_mod( | 253 | fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { |
266 | sema: &Semantics<RootDatabase>, | ||
267 | module: ast::Module, | ||
268 | file_id: FileId, | ||
269 | ) -> Option<Runnable> { | ||
270 | if !has_test_function_or_multiple_test_submodules(&module) { | 254 | if !has_test_function_or_multiple_test_submodules(&module) { |
271 | return None; | 255 | return None; |
272 | } | 256 | } |
@@ -279,7 +263,8 @@ fn runnable_mod( | |||
279 | .filter_map(|it| it.name(sema.db)) | 263 | .filter_map(|it| it.name(sema.db)) |
280 | .join("::"); | 264 | .join("::"); |
281 | 265 | ||
282 | let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module)); | 266 | let def = sema.to_def(&module)?; |
267 | let attrs = def.attrs(sema.db); | ||
283 | let cfg = attrs.cfg(); | 268 | let cfg = attrs.cfg(); |
284 | let nav = module_def.to_nav(sema.db); | 269 | let nav = module_def.to_nav(sema.db); |
285 | Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg }) | 270 | Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg }) |
@@ -319,7 +304,7 @@ mod tests { | |||
319 | 304 | ||
320 | use crate::fixture; | 305 | use crate::fixture; |
321 | 306 | ||
322 | use super::{RunnableAction, BENCH, BIN, DOCTEST, TEST}; | 307 | use super::*; |
323 | 308 | ||
324 | fn check( | 309 | fn check( |
325 | ra_fixture: &str, | 310 | ra_fixture: &str, |
@@ -548,7 +533,7 @@ struct StructWithRunnable(String); | |||
548 | full_range: 15..74, | 533 | full_range: 15..74, |
549 | focus_range: None, | 534 | focus_range: None, |
550 | name: "should_have_runnable", | 535 | name: "should_have_runnable", |
551 | kind: FN, | 536 | kind: COMMENT, |
552 | container_name: None, | 537 | container_name: None, |
553 | description: None, | 538 | description: None, |
554 | docs: None, | 539 | docs: None, |
@@ -568,7 +553,7 @@ struct StructWithRunnable(String); | |||
568 | full_range: 76..148, | 553 | full_range: 76..148, |
569 | focus_range: None, | 554 | focus_range: None, |
570 | name: "should_have_runnable_1", | 555 | name: "should_have_runnable_1", |
571 | kind: FN, | 556 | kind: COMMENT, |
572 | container_name: None, | 557 | container_name: None, |
573 | description: None, | 558 | description: None, |
574 | docs: None, | 559 | docs: None, |
@@ -588,7 +573,7 @@ struct StructWithRunnable(String); | |||
588 | full_range: 150..254, | 573 | full_range: 150..254, |
589 | focus_range: None, | 574 | focus_range: None, |
590 | name: "should_have_runnable_2", | 575 | name: "should_have_runnable_2", |
591 | kind: FN, | 576 | kind: COMMENT, |
592 | container_name: None, | 577 | container_name: None, |
593 | description: None, | 578 | description: None, |
594 | docs: None, | 579 | docs: None, |
@@ -608,7 +593,7 @@ struct StructWithRunnable(String); | |||
608 | full_range: 756..821, | 593 | full_range: 756..821, |
609 | focus_range: None, | 594 | focus_range: None, |
610 | name: "StructWithRunnable", | 595 | name: "StructWithRunnable", |
611 | kind: STRUCT, | 596 | kind: COMMENT, |
612 | container_name: None, | 597 | container_name: None, |
613 | description: None, | 598 | description: None, |
614 | docs: None, | 599 | docs: None, |
@@ -670,7 +655,7 @@ impl Data { | |||
670 | full_range: 44..98, | 655 | full_range: 44..98, |
671 | focus_range: None, | 656 | focus_range: None, |
672 | name: "foo", | 657 | name: "foo", |
673 | kind: FN, | 658 | kind: COMMENT, |
674 | container_name: None, | 659 | container_name: None, |
675 | description: None, | 660 | description: None, |
676 | docs: None, | 661 | docs: None, |
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 990b0f7d9..488969f1a 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs | |||
@@ -806,6 +806,7 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { | |||
806 | } | 806 | } |
807 | return h; | 807 | return h; |
808 | } | 808 | } |
809 | Definition::LifetimeParam(_) => HighlightTag::Lifetime, | ||
809 | } | 810 | } |
810 | .into() | 811 | .into() |
811 | } | 812 | } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html index 0569cf1e5..3530a5fdb 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html | |||
@@ -38,6 +38,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
38 | <pre><code><span class="keyword">use</span> <span class="module">inner</span><span class="operator">::</span><span class="punctuation">{</span><span class="self_keyword">self</span> <span class="keyword">as</span> <span class="module declaration">inner_mod</span><span class="punctuation">}</span><span class="punctuation">;</span> | 38 | <pre><code><span class="keyword">use</span> <span class="module">inner</span><span class="operator">::</span><span class="punctuation">{</span><span class="self_keyword">self</span> <span class="keyword">as</span> <span class="module declaration">inner_mod</span><span class="punctuation">}</span><span class="punctuation">;</span> |
39 | <span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 39 | <span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="punctuation">{</span><span class="punctuation">}</span> |
40 | 40 | ||
41 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span> | ||
42 | <span class="keyword">macro</span> <span class="unresolved_reference declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> | ||
43 | |||
41 | <span class="comment">// Needed for function consuming vs normal</span> | 44 | <span class="comment">// Needed for function consuming vs normal</span> |
42 | <span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="punctuation">{</span> | 45 | <span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="punctuation">{</span> |
43 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"copy"</span><span class="attribute attribute">]</span> | 46 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"copy"</span><span class="attribute attribute">]</span> |
@@ -119,7 +122,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
119 | <span class="value_param callable">f</span><span class="punctuation">(</span><span class="punctuation">)</span> | 122 | <span class="value_param callable">f</span><span class="punctuation">(</span><span class="punctuation">)</span> |
120 | <span class="punctuation">}</span> | 123 | <span class="punctuation">}</span> |
121 | 124 | ||
122 | <span class="keyword">fn</span> <span class="function declaration">foobar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="keyword">impl</span> <span class="unresolved_reference">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> | 125 | <span class="keyword">fn</span> <span class="function declaration">foobar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-></span> <span class="keyword">impl</span> <span class="macro">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> |
123 | 126 | ||
124 | <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> | 127 | <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> |
125 | <span class="keyword">let</span> <span class="variable declaration">bar</span> <span class="operator">=</span> <span class="function">foobar</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> | 128 | <span class="keyword">let</span> <span class="variable declaration">bar</span> <span class="operator">=</span> <span class="function">foobar</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> |
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 1dc018a16..f53d2c3ba 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -12,6 +12,9 @@ fn test_highlighting() { | |||
12 | use inner::{self as inner_mod}; | 12 | use inner::{self as inner_mod}; |
13 | mod inner {} | 13 | mod inner {} |
14 | 14 | ||
15 | #[rustc_builtin_macro] | ||
16 | macro Copy {} | ||
17 | |||
15 | // Needed for function consuming vs normal | 18 | // Needed for function consuming vs normal |
16 | pub mod marker { | 19 | pub mod marker { |
17 | #[lang = "copy"] | 20 | #[lang = "copy"] |