diff options
Diffstat (limited to 'crates/ide')
-rw-r--r-- | crates/ide/src/display/navigation_target.rs | 14 | ||||
-rw-r--r-- | crates/ide/src/doc_links.rs | 30 | ||||
-rw-r--r-- | crates/ide/src/goto_definition.rs | 88 | ||||
-rw-r--r-- | crates/ide/src/hover.rs | 16 | ||||
-rw-r--r-- | crates/ide/src/inlay_hints.rs | 26 | ||||
-rw-r--r-- | crates/ide/src/references.rs | 5 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/highlight.rs | 8 |
7 files changed, 158 insertions, 29 deletions
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs index e24c78301..4eecae697 100644 --- a/crates/ide/src/display/navigation_target.rs +++ b/crates/ide/src/display/navigation_target.rs | |||
@@ -215,10 +215,8 @@ impl TryToNav for Definition { | |||
215 | Definition::ModuleDef(it) => it.try_to_nav(db), | 215 | Definition::ModuleDef(it) => it.try_to_nav(db), |
216 | Definition::SelfType(it) => it.try_to_nav(db), | 216 | Definition::SelfType(it) => it.try_to_nav(db), |
217 | Definition::Local(it) => Some(it.to_nav(db)), | 217 | Definition::Local(it) => Some(it.to_nav(db)), |
218 | Definition::TypeParam(it) => it.try_to_nav(db), | 218 | Definition::GenericParam(it) => it.try_to_nav(db), |
219 | Definition::LifetimeParam(it) => it.try_to_nav(db), | ||
220 | Definition::Label(it) => Some(it.to_nav(db)), | 219 | Definition::Label(it) => Some(it.to_nav(db)), |
221 | Definition::ConstParam(it) => it.try_to_nav(db), | ||
222 | } | 220 | } |
223 | } | 221 | } |
224 | } | 222 | } |
@@ -389,6 +387,16 @@ impl TryToNav for hir::AssocItem { | |||
389 | } | 387 | } |
390 | } | 388 | } |
391 | 389 | ||
390 | impl TryToNav for hir::GenericParam { | ||
391 | fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { | ||
392 | match self { | ||
393 | hir::GenericParam::TypeParam(it) => it.try_to_nav(db), | ||
394 | hir::GenericParam::ConstParam(it) => it.try_to_nav(db), | ||
395 | hir::GenericParam::LifetimeParam(it) => it.try_to_nav(db), | ||
396 | } | ||
397 | } | ||
398 | } | ||
399 | |||
392 | impl ToNav for hir::Local { | 400 | impl ToNav for hir::Local { |
393 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { | 401 | fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { |
394 | let src = self.source(db); | 402 | let src = self.source(db); |
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 1ff818de2..91f4241f9 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! Resolves and rewrites links in markdown documentation. | 1 | //! Resolves and rewrites links in markdown documentation. |
2 | 2 | ||
3 | use std::{convert::TryFrom, iter::once}; | 3 | use std::{convert::TryFrom, iter::once, ops::Range}; |
4 | 4 | ||
5 | use itertools::Itertools; | 5 | use itertools::Itertools; |
6 | use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag}; | 6 | use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag}; |
@@ -61,6 +61,30 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Defi | |||
61 | out | 61 | out |
62 | } | 62 | } |
63 | 63 | ||
64 | pub(crate) fn extract_definitions_from_markdown( | ||
65 | markdown: &str, | ||
66 | ) -> Vec<(String, Option<hir::Namespace>, Range<usize>)> { | ||
67 | let mut res = vec![]; | ||
68 | let mut cb = |link: BrokenLink| { | ||
69 | Some(( | ||
70 | /*url*/ link.reference.to_owned().into(), | ||
71 | /*title*/ link.reference.to_owned().into(), | ||
72 | )) | ||
73 | }; | ||
74 | let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb)); | ||
75 | for (event, range) in doc.into_offset_iter() { | ||
76 | match event { | ||
77 | Event::Start(Tag::Link(_link_type, ref target, ref title)) => { | ||
78 | let link = if target.is_empty() { title } else { target }; | ||
79 | let (link, ns) = parse_link(link); | ||
80 | res.push((link.to_string(), ns, range)); | ||
81 | } | ||
82 | _ => {} | ||
83 | } | ||
84 | } | ||
85 | res | ||
86 | } | ||
87 | |||
64 | /// Remove all links in markdown documentation. | 88 | /// Remove all links in markdown documentation. |
65 | pub(crate) fn remove_links(markdown: &str) -> String { | 89 | pub(crate) fn remove_links(markdown: &str) -> String { |
66 | let mut drop_link = false; | 90 | let mut drop_link = false; |
@@ -192,9 +216,7 @@ fn rewrite_intra_doc_link( | |||
192 | Definition::Field(it) => it.resolve_doc_path(db, link, ns), | 216 | Definition::Field(it) => it.resolve_doc_path(db, link, ns), |
193 | Definition::SelfType(_) | 217 | Definition::SelfType(_) |
194 | | Definition::Local(_) | 218 | | Definition::Local(_) |
195 | | Definition::TypeParam(_) | 219 | | Definition::GenericParam(_) |
196 | | Definition::ConstParam(_) | ||
197 | | Definition::LifetimeParam(_) | ||
198 | | Definition::Label(_) => return None, | 220 | | Definition::Label(_) => return None, |
199 | }?; | 221 | }?; |
200 | let krate = resolved.module(db)?.krate(); | 222 | let krate = resolved.module(db)?.krate(); |
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 | } |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 939efa43f..e331f8886 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use hir::{ | 1 | use hir::{ |
2 | Adt, AsAssocItem, AssocItemContainer, FieldSource, HasAttrs, HasSource, HirDisplay, Module, | 2 | Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource, |
3 | ModuleDef, ModuleSource, Semantics, | 3 | HirDisplay, Module, ModuleDef, ModuleSource, Semantics, |
4 | }; | 4 | }; |
5 | use ide_db::base_db::SourceDatabase; | 5 | use ide_db::base_db::SourceDatabase; |
6 | use ide_db::{ | 6 | use ide_db::{ |
@@ -220,12 +220,12 @@ fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { | |||
220 | } | 220 | } |
221 | }; | 221 | }; |
222 | 222 | ||
223 | if let Definition::TypeParam(it) = def { | 223 | if let Definition::GenericParam(GenericParam::TypeParam(it)) = def { |
224 | it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into())); | 224 | it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into())); |
225 | } else { | 225 | } else { |
226 | let ty = match def { | 226 | let ty = match def { |
227 | Definition::Local(it) => it.ty(db), | 227 | Definition::Local(it) => it.ty(db), |
228 | Definition::ConstParam(it) => it.ty(db), | 228 | Definition::GenericParam(GenericParam::ConstParam(it)) => it.ty(db), |
229 | _ => return None, | 229 | _ => return None, |
230 | }; | 230 | }; |
231 | 231 | ||
@@ -357,9 +357,11 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> { | |||
357 | }) | 357 | }) |
358 | } | 358 | } |
359 | Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))), | 359 | Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))), |
360 | Definition::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))), | 360 | Definition::GenericParam(it) => match it { |
361 | Definition::TypeParam(type_param) => Some(Markup::fenced_block(&type_param.display(db))), | 361 | GenericParam::TypeParam(it) => Some(Markup::fenced_block(&it.display(db))), |
362 | Definition::ConstParam(it) => from_def_source(db, it, None), | 362 | GenericParam::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))), |
363 | GenericParam::ConstParam(it) => from_def_source(db, it, None), | ||
364 | }, | ||
363 | }; | 365 | }; |
364 | 366 | ||
365 | fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup> | 367 | fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup> |
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index a74829cd0..3e9a65d9c 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs | |||
@@ -353,13 +353,25 @@ fn is_argument_similar_to_param_name( | |||
353 | } | 353 | } |
354 | match get_string_representation(argument) { | 354 | match get_string_representation(argument) { |
355 | None => false, | 355 | None => false, |
356 | Some(mut repr) => { | 356 | Some(argument_string) => { |
357 | let param_name = param_name.to_ascii_lowercase(); | 357 | let num_leading_underscores = |
358 | let argument_string = { | 358 | argument_string.bytes().take_while(|&c| c == b'_').count(); |
359 | repr.make_ascii_lowercase(); | 359 | |
360 | repr.trim_start_matches('_') | 360 | // Does the argument name begin with the parameter name? Ignore leading underscores. |
361 | }; | 361 | let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores); |
362 | argument_string.starts_with(¶m_name) || argument_string.ends_with(¶m_name) | 362 | let starts_with_pattern = param_name.bytes().all( |
363 | |expected| matches!(arg_bytes.next(), Some(actual) if expected.eq_ignore_ascii_case(&actual)), | ||
364 | ); | ||
365 | |||
366 | if starts_with_pattern { | ||
367 | return true; | ||
368 | } | ||
369 | |||
370 | // Does the argument name end with the parameter name? | ||
371 | let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores); | ||
372 | param_name.bytes().rev().all( | ||
373 | |expected| matches!(arg_bytes.next_back(), Some(actual) if expected.eq_ignore_ascii_case(&actual)), | ||
374 | ) | ||
363 | } | 375 | } |
364 | } | 376 | } |
365 | } | 377 | } |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index c95ed669c..0d5cd5f9a 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -130,7 +130,10 @@ pub(crate) fn find_all_refs( | |||
130 | kind = ReferenceKind::FieldShorthandForLocal; | 130 | kind = ReferenceKind::FieldShorthandForLocal; |
131 | } | 131 | } |
132 | } | 132 | } |
133 | } else if matches!(def, Definition::LifetimeParam(_) | Definition::Label(_)) { | 133 | } else if matches!( |
134 | def, | ||
135 | Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) | ||
136 | ) { | ||
134 | kind = ReferenceKind::Lifetime; | 137 | kind = ReferenceKind::Lifetime; |
135 | }; | 138 | }; |
136 | 139 | ||
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 1a88975d2..20eccf3c6 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs | |||
@@ -328,8 +328,11 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { | |||
328 | } | 328 | } |
329 | }, | 329 | }, |
330 | Definition::SelfType(_) => HlTag::Symbol(SymbolKind::Impl), | 330 | Definition::SelfType(_) => HlTag::Symbol(SymbolKind::Impl), |
331 | Definition::TypeParam(_) => HlTag::Symbol(SymbolKind::TypeParam), | 331 | Definition::GenericParam(it) => match it { |
332 | Definition::ConstParam(_) => HlTag::Symbol(SymbolKind::ConstParam), | 332 | hir::GenericParam::TypeParam(_) => HlTag::Symbol(SymbolKind::TypeParam), |
333 | hir::GenericParam::ConstParam(_) => HlTag::Symbol(SymbolKind::ConstParam), | ||
334 | hir::GenericParam::LifetimeParam(_) => HlTag::Symbol(SymbolKind::LifetimeParam), | ||
335 | }, | ||
333 | Definition::Local(local) => { | 336 | Definition::Local(local) => { |
334 | let tag = if local.is_param(db) { | 337 | let tag = if local.is_param(db) { |
335 | HlTag::Symbol(SymbolKind::ValueParam) | 338 | HlTag::Symbol(SymbolKind::ValueParam) |
@@ -345,7 +348,6 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { | |||
345 | } | 348 | } |
346 | return h; | 349 | return h; |
347 | } | 350 | } |
348 | Definition::LifetimeParam(_) => HlTag::Symbol(SymbolKind::LifetimeParam), | ||
349 | Definition::Label(_) => HlTag::Symbol(SymbolKind::Label), | 351 | Definition::Label(_) => HlTag::Symbol(SymbolKind::Label), |
350 | } | 352 | } |
351 | .into() | 353 | .into() |