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/inject.rs | |
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/inject.rs')
-rw-r--r-- | crates/ide/src/syntax_highlighting/inject.rs | 90 |
1 files changed, 27 insertions, 63 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 | } |