diff options
Diffstat (limited to 'crates/ide/src/goto_definition.rs')
-rw-r--r-- | crates/ide/src/goto_definition.rs | 88 |
1 files changed, 84 insertions, 4 deletions
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 95b4cb9e3..c20185b16 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -1,14 +1,20 @@ | |||
1 | use either::Either; | 1 | use either::Either; |
2 | use hir::Semantics; | 2 | use hir::{HasAttrs, ModuleDef, Semantics}; |
3 | use ide_db::{ | 3 | use ide_db::{ |
4 | base_db::FileId, | 4 | base_db::FileId, |
5 | defs::{NameClass, NameRefClass}, | 5 | defs::{Definition, NameClass, NameRefClass}, |
6 | symbol_index, RootDatabase, | 6 | symbol_index, RootDatabase, |
7 | }; | 7 | }; |
8 | use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; | 8 | use syntax::{ |
9 | ast::{self, NameOwner}, | ||
10 | match_ast, AstNode, AstToken, | ||
11 | SyntaxKind::*, | ||
12 | SyntaxToken, TextSize, TokenAtOffset, T, | ||
13 | }; | ||
9 | 14 | ||
10 | use crate::{ | 15 | use crate::{ |
11 | display::{ToNav, TryToNav}, | 16 | display::{ToNav, TryToNav}, |
17 | doc_links::extract_definitions_from_markdown, | ||
12 | FilePosition, NavigationTarget, RangeInfo, SymbolKind, | 18 | FilePosition, NavigationTarget, RangeInfo, SymbolKind, |
13 | }; | 19 | }; |
14 | 20 | ||
@@ -30,6 +36,10 @@ pub(crate) fn goto_definition( | |||
30 | let original_token = pick_best(file.token_at_offset(position.offset))?; | 36 | let original_token = pick_best(file.token_at_offset(position.offset))?; |
31 | let token = sema.descend_into_macros(original_token.clone()); | 37 | let token = sema.descend_into_macros(original_token.clone()); |
32 | let parent = token.parent(); | 38 | let parent = token.parent(); |
39 | if let Some(comment) = ast::Comment::cast(token.clone()) { | ||
40 | let nav = def_for_doc_comment(&sema, position, &comment)?.try_to_nav(db)?; | ||
41 | return Some(RangeInfo::new(original_token.text_range(), vec![nav])); | ||
42 | } | ||
33 | 43 | ||
34 | let nav_targets = match_ast! { | 44 | let nav_targets = match_ast! { |
35 | match parent { | 45 | match parent { |
@@ -68,11 +78,66 @@ pub(crate) fn goto_definition( | |||
68 | Some(RangeInfo::new(original_token.text_range(), nav_targets)) | 78 | Some(RangeInfo::new(original_token.text_range(), nav_targets)) |
69 | } | 79 | } |
70 | 80 | ||
81 | fn def_for_doc_comment( | ||
82 | sema: &Semantics<RootDatabase>, | ||
83 | position: FilePosition, | ||
84 | doc_comment: &ast::Comment, | ||
85 | ) -> Option<hir::ModuleDef> { | ||
86 | let parent = doc_comment.syntax().parent(); | ||
87 | let db = sema.db; | ||
88 | let (link, ns) = extract_positioned_link_from_comment(position, doc_comment)?; | ||
89 | let link = &link; | ||
90 | let name = match_ast! { | ||
91 | match parent { | ||
92 | ast::Name(name) => Some(name), | ||
93 | ast::Fn(func) => func.name(), | ||
94 | _ => None, | ||
95 | } | ||
96 | }?; | ||
97 | let definition = NameClass::classify(&sema, &name).and_then(|d| d.defined(sema.db))?; | ||
98 | match definition { | ||
99 | Definition::ModuleDef(def) => match def { | ||
100 | ModuleDef::Module(it) => it.resolve_doc_path(db, link, ns), | ||
101 | ModuleDef::Function(it) => it.resolve_doc_path(db, link, ns), | ||
102 | ModuleDef::Adt(it) => it.resolve_doc_path(db, link, ns), | ||
103 | ModuleDef::Variant(it) => it.resolve_doc_path(db, link, ns), | ||
104 | ModuleDef::Const(it) => it.resolve_doc_path(db, link, ns), | ||
105 | ModuleDef::Static(it) => it.resolve_doc_path(db, link, ns), | ||
106 | ModuleDef::Trait(it) => it.resolve_doc_path(db, link, ns), | ||
107 | ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, link, ns), | ||
108 | ModuleDef::BuiltinType(_) => return None, | ||
109 | }, | ||
110 | Definition::Macro(it) => it.resolve_doc_path(db, link, ns), | ||
111 | Definition::Field(it) => it.resolve_doc_path(db, link, ns), | ||
112 | Definition::SelfType(_) | ||
113 | | Definition::Local(_) | ||
114 | | Definition::GenericParam(_) | ||
115 | | Definition::Label(_) => return None, | ||
116 | } | ||
117 | } | ||
118 | |||
119 | fn extract_positioned_link_from_comment( | ||
120 | position: FilePosition, | ||
121 | comment: &ast::Comment, | ||
122 | ) -> Option<(String, Option<hir::Namespace>)> { | ||
123 | let comment_range = comment.syntax().text_range(); | ||
124 | let doc_comment = comment.doc_comment()?; | ||
125 | let def_links = extract_definitions_from_markdown(doc_comment); | ||
126 | let (def_link, ns, _) = def_links.iter().min_by_key(|(_, _, def_link_range)| { | ||
127 | let matched_position = comment_range.start() + TextSize::from(def_link_range.start as u32); | ||
128 | match position.offset.checked_sub(matched_position) { | ||
129 | Some(distance) => distance, | ||
130 | None => comment_range.end(), | ||
131 | } | ||
132 | })?; | ||
133 | Some((def_link.to_string(), ns.clone())) | ||
134 | } | ||
135 | |||
71 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | 136 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { |
72 | return tokens.max_by_key(priority); | 137 | return tokens.max_by_key(priority); |
73 | fn priority(n: &SyntaxToken) -> usize { | 138 | fn priority(n: &SyntaxToken) -> usize { |
74 | match n.kind() { | 139 | match n.kind() { |
75 | IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 2, | 140 | IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | COMMENT => 2, |
76 | kind if kind.is_trivia() => 0, | 141 | kind if kind.is_trivia() => 0, |
77 | _ => 1, | 142 | _ => 1, |
78 | } | 143 | } |
@@ -1145,4 +1210,19 @@ fn foo<'foo>(_: &'foo ()) { | |||
1145 | }"#, | 1210 | }"#, |
1146 | ) | 1211 | ) |
1147 | } | 1212 | } |
1213 | |||
1214 | #[test] | ||
1215 | fn goto_def_for_intra_rustdoc_link_same_file() { | ||
1216 | check( | ||
1217 | r#" | ||
1218 | /// Blah, [`bar`](bar) .. [`foo`](foo)$0 has [`bar`](bar) | ||
1219 | pub fn bar() { } | ||
1220 | |||
1221 | /// You might want to see [`std::fs::read()`] too. | ||
1222 | pub fn foo() { } | ||
1223 | //^^^ | ||
1224 | |||
1225 | }"#, | ||
1226 | ) | ||
1227 | } | ||
1148 | } | 1228 | } |