diff options
Diffstat (limited to 'crates/ide/src/syntax_highlighting/inject.rs')
-rw-r--r-- | crates/ide/src/syntax_highlighting/inject.rs | 118 |
1 files changed, 28 insertions, 90 deletions
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 963c3fb59..04fafd244 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -1,17 +1,18 @@ | |||
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, Analysis, HlMod, HlRange, HlTag, RootDatabase, | 14 | doc_links::{doc_attributes, extract_definitions_from_markdown, resolve_doc_path_for_def}, |
15 | Analysis, HlMod, HlRange, HlTag, RootDatabase, | ||
15 | }; | 16 | }; |
16 | 17 | ||
17 | use super::{highlights::Highlights, injector::Injector}; | 18 | use super::{highlights::Highlights, injector::Injector}; |
@@ -89,33 +90,6 @@ const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[ | |||
89 | "edition2021", | 90 | "edition2021", |
90 | ]; | 91 | ]; |
91 | 92 | ||
92 | fn doc_attributes<'node>( | ||
93 | sema: &Semantics<RootDatabase>, | ||
94 | node: &'node SyntaxNode, | ||
95 | ) -> Option<(hir::AttrsWithOwner, Definition)> { | ||
96 | match_ast! { | ||
97 | match node { | ||
98 | ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), | ||
99 | ast::Module(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))), | ||
100 | ast::Fn(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Function(def)))), | ||
101 | ast::Struct(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(def))))), | ||
102 | ast::Union(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Union(def))))), | ||
103 | ast::Enum(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(def))))), | ||
104 | ast::Variant(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Variant(def)))), | ||
105 | ast::Trait(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Trait(def)))), | ||
106 | ast::Static(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Static(def)))), | ||
107 | ast::Const(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Const(def)))), | ||
108 | ast::TypeAlias(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::TypeAlias(def)))), | ||
109 | ast::Impl(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::SelfType(def))), | ||
110 | ast::RecordField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), | ||
111 | ast::TupleField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))), | ||
112 | ast::Macro(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))), | ||
113 | // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))), | ||
114 | _ => return None | ||
115 | } | ||
116 | } | ||
117 | } | ||
118 | |||
119 | /// Injection of syntax highlighting of doctests. | 93 | /// Injection of syntax highlighting of doctests. |
120 | pub(super) fn doc_comment( | 94 | pub(super) fn doc_comment( |
121 | hl: &mut Highlights, | 95 | hl: &mut Highlights, |
@@ -138,8 +112,28 @@ pub(super) fn doc_comment( | |||
138 | // 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 |
139 | // spanning comment ranges. | 113 | // spanning comment ranges. |
140 | let mut new_comments = Vec::new(); | 114 | let mut new_comments = Vec::new(); |
141 | let mut intra_doc_links = Vec::new(); | ||
142 | 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 | |||
143 | for attr in attributes.by_key("doc").attrs() { | 137 | for attr in attributes.by_key("doc").attrs() { |
144 | 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); |
145 | if file_id != node.file_id { | 139 | if file_id != node.file_id { |
@@ -185,25 +179,7 @@ pub(super) fn doc_comment( | |||
185 | is_doctest = is_codeblock && is_rust; | 179 | is_doctest = is_codeblock && is_rust; |
186 | continue; | 180 | continue; |
187 | } | 181 | } |
188 | None if !is_doctest => { | 182 | None if !is_doctest => continue, |
189 | intra_doc_links.extend( | ||
190 | extract_definitions_from_markdown(line) | ||
191 | .into_iter() | ||
192 | .filter_map(|(range, link, ns)| { | ||
193 | Some(range).zip(validate_intra_doc_link(sema.db, &def, &link, ns)) | ||
194 | }) | ||
195 | .map(|(Range { start, end }, def)| { | ||
196 | ( | ||
197 | def, | ||
198 | TextRange::at( | ||
199 | prev_range_start + TextSize::from(start as u32), | ||
200 | TextSize::from((end - start) as u32), | ||
201 | ), | ||
202 | ) | ||
203 | }), | ||
204 | ); | ||
205 | continue; | ||
206 | } | ||
207 | None => (), | 183 | None => (), |
208 | } | 184 | } |
209 | 185 | ||
@@ -222,17 +198,6 @@ pub(super) fn doc_comment( | |||
222 | } | 198 | } |
223 | } | 199 | } |
224 | 200 | ||
225 | for (def, range) in intra_doc_links { | ||
226 | hl.add(HlRange { | ||
227 | range, | ||
228 | highlight: module_def_to_hl_tag(def) | ||
229 | | HlMod::Documentation | ||
230 | | HlMod::Injected | ||
231 | | HlMod::IntraDocLink, | ||
232 | binding_hash: None, | ||
233 | }); | ||
234 | } | ||
235 | |||
236 | if new_comments.is_empty() { | 201 | if new_comments.is_empty() { |
237 | return; // no need to run an analysis on an empty file | 202 | return; // no need to run an analysis on an empty file |
238 | } | 203 | } |
@@ -283,33 +248,6 @@ fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::Stri | |||
283 | } | 248 | } |
284 | } | 249 | } |
285 | 250 | ||
286 | fn validate_intra_doc_link( | ||
287 | db: &RootDatabase, | ||
288 | def: &Definition, | ||
289 | link: &str, | ||
290 | ns: Option<hir::Namespace>, | ||
291 | ) -> Option<hir::ModuleDef> { | ||
292 | match def { | ||
293 | Definition::ModuleDef(def) => match def { | ||
294 | hir::ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns), | ||
295 | hir::ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns), | ||
296 | hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns), | ||
297 | hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns), | ||
298 | hir::ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns), | ||
299 | hir::ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns), | ||
300 | hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns), | ||
301 | hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns), | ||
302 | hir::ModuleDef::BuiltinType(_) => None, | ||
303 | }, | ||
304 | Definition::Macro(it) => it.resolve_doc_path(db, &link, ns), | ||
305 | Definition::Field(it) => it.resolve_doc_path(db, &link, ns), | ||
306 | Definition::SelfType(_) | ||
307 | | Definition::Local(_) | ||
308 | | Definition::GenericParam(_) | ||
309 | | Definition::Label(_) => None, | ||
310 | } | ||
311 | } | ||
312 | |||
313 | fn module_def_to_hl_tag(def: hir::ModuleDef) -> HlTag { | 251 | fn module_def_to_hl_tag(def: hir::ModuleDef) -> HlTag { |
314 | let symbol = match def { | 252 | let symbol = match def { |
315 | hir::ModuleDef::Module(_) => SymbolKind::Module, | 253 | hir::ModuleDef::Module(_) => SymbolKind::Module, |