diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-04-05 13:30:20 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2021-04-05 13:30:20 +0100 |
commit | c2be91dcd826e1529ac6ac431b3f871ec72abebc (patch) | |
tree | e267eed3fc8966093fbd79389e47a051c435cd7d /crates/ide/src/syntax_highlighting | |
parent | d8ee25bb976f50c0c0c8c247ca8bb030d9167bdb (diff) | |
parent | 8d786dc4c3ce26dbb3432023c7461bd879993bfd (diff) |
Merge #8245
8245: Properly resolve intra doc links in hover and goto_definition r=matklad a=Veykril
Unfortunately involves a bit of weird workarounds due to pulldown_cmark's incorrect lifetimes on `BrokenLinkCallback`... I should probably open an issue there asking for the fixes to be pushed to a release since they already exist in the repo for quite some time it seems.
Fixes #8258, Fixes #8238
Co-authored-by: Lukas Wirth <[email protected]>
Diffstat (limited to 'crates/ide/src/syntax_highlighting')
-rw-r--r-- | crates/ide/src/syntax_highlighting/inject.rs | 90 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html | 10 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/tests.rs | 10 |
3 files changed, 45 insertions, 65 deletions
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index b62d43256..04fafd244 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -1,17 +1,17 @@ | |||
1 | //! "Recursive" Syntax highlighting for code in doctests and fixtures. | 1 | //! "Recursive" Syntax highlighting for code in doctests and fixtures. |
2 | 2 | ||
3 | use std::{mem, ops::Range}; | 3 | use std::mem; |
4 | 4 | ||
5 | use either::Either; | 5 | use either::Either; |
6 | use hir::{HasAttrs, InFile, Semantics}; | 6 | use hir::{InFile, Semantics}; |
7 | use ide_db::{call_info::ActiveParameter, defs::Definition, SymbolKind}; | 7 | use ide_db::{call_info::ActiveParameter, SymbolKind}; |
8 | use syntax::{ | 8 | use syntax::{ |
9 | ast::{self, AstNode}, | 9 | ast::{self, AstNode}, |
10 | match_ast, AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, | 10 | AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
14 | doc_links::{extract_definitions_from_markdown, resolve_doc_path_for_def}, | 14 | doc_links::{doc_attributes, extract_definitions_from_markdown, resolve_doc_path_for_def}, |
15 | Analysis, HlMod, HlRange, HlTag, RootDatabase, | 15 | Analysis, HlMod, HlRange, HlTag, RootDatabase, |
16 | }; | 16 | }; |
17 | 17 | ||
@@ -90,33 +90,6 @@ const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[ | |||
90 | "edition2021", | 90 | "edition2021", |
91 | ]; | 91 | ]; |
92 | 92 | ||
93 | fn doc_attributes<'node>( | ||
94 | sema: &Semantics<RootDatabase>, | ||
95 | node: &'node SyntaxNode, | ||
96 | ) -> Option<(hir::AttrsWithOwner, Definition)> { | ||
97 | match_ast! { | ||
98 | match node { | ||
99 | ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), | ||
100 | ast::Module(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), | ||
101 | ast::Fn(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Function(def)))), | ||
102 | ast::Struct(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(def))))), | ||
103 | ast::Union(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Union(def))))), | ||
104 | ast::Enum(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(def))))), | ||
105 | ast::Variant(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Variant(def)))), | ||
106 | ast::Trait(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Trait(def)))), | ||
107 | ast::Static(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Static(def)))), | ||
108 | ast::Const(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Const(def)))), | ||
109 | ast::TypeAlias(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::TypeAlias(def)))), | ||
110 | ast::Impl(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::SelfType(def))), | ||
111 | ast::RecordField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), | ||
112 | ast::TupleField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), | ||
113 | ast::Macro(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))), | ||
114 | // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), | ||
115 | _ => return None | ||
116 | } | ||
117 | } | ||
118 | } | ||
119 | |||
120 | /// Injection of syntax highlighting of doctests. | 93 | /// Injection of syntax highlighting of doctests. |
121 | pub(super) fn doc_comment( | 94 | pub(super) fn doc_comment( |
122 | hl: &mut Highlights, | 95 | hl: &mut Highlights, |
@@ -139,8 +112,28 @@ pub(super) fn doc_comment( | |||
139 | // Replace the original, line-spanning comment ranges by new, only comment-prefix | 112 | // Replace the original, line-spanning comment ranges by new, only comment-prefix |
140 | // spanning comment ranges. | 113 | // spanning comment ranges. |
141 | let mut new_comments = Vec::new(); | 114 | let mut new_comments = Vec::new(); |
142 | let mut intra_doc_links = Vec::new(); | ||
143 | let mut string; | 115 | let mut string; |
116 | |||
117 | if let Some((docs, doc_mapping)) = attributes.docs_with_rangemap(sema.db) { | ||
118 | extract_definitions_from_markdown(docs.as_str()) | ||
119 | .into_iter() | ||
120 | .filter_map(|(range, link, ns)| { | ||
121 | let def = resolve_doc_path_for_def(sema.db, def, &link, ns)?; | ||
122 | let InFile { file_id, value: range } = doc_mapping.map(range)?; | ||
123 | (file_id == node.file_id).then(|| (range, def)) | ||
124 | }) | ||
125 | .for_each(|(range, def)| { | ||
126 | hl.add(HlRange { | ||
127 | range, | ||
128 | highlight: module_def_to_hl_tag(def) | ||
129 | | HlMod::Documentation | ||
130 | | HlMod::Injected | ||
131 | | HlMod::IntraDocLink, | ||
132 | binding_hash: None, | ||
133 | }) | ||
134 | }); | ||
135 | } | ||
136 | |||
144 | for attr in attributes.by_key("doc").attrs() { | 137 | for attr in attributes.by_key("doc").attrs() { |
145 | let InFile { file_id, value: src } = attrs_source_map.source_of(&attr); | 138 | let InFile { file_id, value: src } = attrs_source_map.source_of(&attr); |
146 | if file_id != node.file_id { | 139 | if file_id != node.file_id { |
@@ -186,25 +179,7 @@ pub(super) fn doc_comment( | |||
186 | is_doctest = is_codeblock && is_rust; | 179 | is_doctest = is_codeblock && is_rust; |
187 | continue; | 180 | continue; |
188 | } | 181 | } |
189 | None if !is_doctest => { | 182 | None if !is_doctest => continue, |
190 | intra_doc_links.extend( | ||
191 | extract_definitions_from_markdown(line) | ||
192 | .into_iter() | ||
193 | .filter_map(|(range, link, ns)| { | ||
194 | Some(range).zip(resolve_doc_path_for_def(sema.db, def, &link, ns)) | ||
195 | }) | ||
196 | .map(|(Range { start, end }, def)| { | ||
197 | ( | ||
198 | def, | ||
199 | TextRange::at( | ||
200 | prev_range_start + TextSize::from(start as u32), | ||
201 | TextSize::from((end - start) as u32), | ||
202 | ), | ||
203 | ) | ||
204 | }), | ||
205 | ); | ||
206 | continue; | ||
207 | } | ||
208 | None => (), | 183 | None => (), |
209 | } | 184 | } |
210 | 185 | ||
@@ -223,17 +198,6 @@ pub(super) fn doc_comment( | |||
223 | } | 198 | } |
224 | } | 199 | } |
225 | 200 | ||
226 | for (def, range) in intra_doc_links { | ||
227 | hl.add(HlRange { | ||
228 | range, | ||
229 | highlight: module_def_to_hl_tag(def) | ||
230 | | HlMod::Documentation | ||
231 | | HlMod::Injected | ||
232 | | HlMod::IntraDocLink, | ||
233 | binding_hash: None, | ||
234 | }); | ||
235 | } | ||
236 | |||
237 | if new_comments.is_empty() { | 201 | if new_comments.is_empty() { |
238 | return; // no need to run an analysis on an empty file | 202 | return; // no need to run an analysis on an empty file |
239 | } | 203 | } |
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 045162eb8..b6d1cac4e 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html | |||
@@ -100,10 +100,18 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
100 | <span class="brace">}</span> | 100 | <span class="brace">}</span> |
101 | 101 | ||
102 | <span class="comment documentation">/// </span><span class="struct documentation intra_doc_link injected">[`Foo`](Foo)</span><span class="comment documentation"> is a struct</span> | 102 | <span class="comment documentation">/// </span><span class="struct documentation intra_doc_link injected">[`Foo`](Foo)</span><span class="comment documentation"> is a struct</span> |
103 | <span class="comment documentation">/// </span><span class="function documentation intra_doc_link injected">[`all_the_links`](all_the_links)</span><span class="comment documentation"> is this function</span> | 103 | <span class="comment documentation">/// This function is > </span><span class="function documentation intra_doc_link injected">[`all_the_links`](all_the_links)</span><span class="comment documentation"> <</span> |
104 | <span class="comment documentation">/// [`noop`](noop) is a macro below</span> | 104 | <span class="comment documentation">/// [`noop`](noop) is a macro below</span> |
105 | <span class="comment documentation">/// </span><span class="struct documentation intra_doc_link injected">[`Item`]</span><span class="comment documentation"> is a struct in the module </span><span class="module documentation intra_doc_link injected">[`module`]</span> | ||
106 | <span class="comment documentation">///</span> | ||
107 | <span class="comment documentation">/// [`Item`]: module::Item</span> | ||
108 | <span class="comment documentation">/// [mix_and_match]: ThisShouldntResolve</span> | ||
105 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">all_the_links</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> | 109 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">all_the_links</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> |
106 | 110 | ||
111 | <span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">module</span> <span class="brace">{</span> | ||
112 | <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="struct declaration">Item</span><span class="semicolon">;</span> | ||
113 | <span class="brace">}</span> | ||
114 | |||
107 | <span class="comment documentation">/// ```</span> | 115 | <span class="comment documentation">/// ```</span> |
108 | <span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> | 116 | <span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> |
109 | <span class="comment documentation">/// ```</span> | 117 | <span class="comment documentation">/// ```</span> |
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 369ae0972..1b02857ec 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -544,10 +544,18 @@ impl Foo { | |||
544 | } | 544 | } |
545 | 545 | ||
546 | /// [`Foo`](Foo) is a struct | 546 | /// [`Foo`](Foo) is a struct |
547 | /// [`all_the_links`](all_the_links) is this function | 547 | /// This function is > [`all_the_links`](all_the_links) < |
548 | /// [`noop`](noop) is a macro below | 548 | /// [`noop`](noop) is a macro below |
549 | /// [`Item`] is a struct in the module [`module`] | ||
550 | /// | ||
551 | /// [`Item`]: module::Item | ||
552 | /// [mix_and_match]: ThisShouldntResolve | ||
549 | pub fn all_the_links() {} | 553 | pub fn all_the_links() {} |
550 | 554 | ||
555 | pub mod module { | ||
556 | pub struct Item; | ||
557 | } | ||
558 | |||
551 | /// ``` | 559 | /// ``` |
552 | /// noop!(1); | 560 | /// noop!(1); |
553 | /// ``` | 561 | /// ``` |