aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/goto_definition.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/goto_definition.rs')
-rw-r--r--crates/ide/src/goto_definition.rs88
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 @@
1use either::Either; 1use either::Either;
2use hir::Semantics; 2use hir::{HasAttrs, ModuleDef, Semantics};
3use ide_db::{ 3use 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};
8use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 8use syntax::{
9 ast::{self, NameOwner},
10 match_ast, AstNode, AstToken,
11 SyntaxKind::*,
12 SyntaxToken, TextSize, TokenAtOffset, T,
13};
9 14
10use crate::{ 15use 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
81fn 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
119fn 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
71fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 136fn 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)
1219pub fn bar() { }
1220
1221/// You might want to see [`std::fs::read()`] too.
1222pub fn foo() { }
1223 //^^^
1224
1225}"#,
1226 )
1227 }
1148} 1228}