diff options
author | Lukas Wirth <[email protected]> | 2021-03-16 20:05:07 +0000 |
---|---|---|
committer | Lukas Wirth <[email protected]> | 2021-03-16 20:15:26 +0000 |
commit | c766492d2625dba65c3bd933841c71938f6dc747 (patch) | |
tree | 79fc22c7f6018bafa8856fa52494646ebf9781d3 /crates | |
parent | 3daa302cd39c779cae0b096972f2fdc3e67e214c (diff) |
Properly handle doc attributes in doc-comment highlight injection
Diffstat (limited to 'crates')
-rw-r--r-- | crates/hir/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/hir_def/src/attr.rs | 8 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/inject.rs | 48 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html | 18 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/tests.rs | 16 |
5 files changed, 84 insertions, 8 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index ad79a79f8..265a084f3 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -89,7 +89,7 @@ pub use crate::{ | |||
89 | pub use { | 89 | pub use { |
90 | hir_def::{ | 90 | hir_def::{ |
91 | adt::StructKind, | 91 | adt::StructKind, |
92 | attr::{Attrs, Documentation}, | 92 | attr::{Attr, Attrs, Documentation}, |
93 | body::scope::ExprScopes, | 93 | body::scope::ExprScopes, |
94 | find_path::PrefixKind, | 94 | find_path::PrefixKind, |
95 | import_map, | 95 | import_map, |
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 683c37023..e7019e0c9 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -162,7 +162,6 @@ impl RawAttrs { | |||
162 | let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; | 162 | let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; |
163 | // FIXME hygiene | 163 | // FIXME hygiene |
164 | let hygiene = Hygiene::new_unhygienic(); | 164 | let hygiene = Hygiene::new_unhygienic(); |
165 | // FIXME same index is assigned to multiple attributes | ||
166 | Attr::from_src(attr, &hygiene).map(|attr| Attr { index, ..attr }) | 165 | Attr::from_src(attr, &hygiene).map(|attr| Attr { index, ..attr }) |
167 | }); | 166 | }); |
168 | 167 | ||
@@ -450,6 +449,13 @@ impl Attr { | |||
450 | _ => None, | 449 | _ => None, |
451 | } | 450 | } |
452 | } | 451 | } |
452 | |||
453 | pub fn string_value(&self) -> Option<&SmolStr> { | ||
454 | match self.input.as_ref()? { | ||
455 | AttrInput::Literal(it) => Some(it), | ||
456 | _ => None, | ||
457 | } | ||
458 | } | ||
453 | } | 459 | } |
454 | 460 | ||
455 | #[derive(Debug, Clone, Copy)] | 461 | #[derive(Debug, Clone, Copy)] |
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 086db40e5..0f1de4fb8 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -5,7 +5,7 @@ use hir::{HasAttrs, Semantics}; | |||
5 | use ide_db::call_info::ActiveParameter; | 5 | use ide_db::call_info::ActiveParameter; |
6 | use syntax::{ | 6 | use syntax::{ |
7 | ast::{self, AstNode, AttrsOwner, DocCommentsOwner}, | 7 | ast::{self, AstNode, AttrsOwner, DocCommentsOwner}, |
8 | match_ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize, | 8 | match_ast, AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, |
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crate::{Analysis, HlMod, HlRange, HlTag, RootDatabase}; | 11 | use crate::{Analysis, HlMod, HlRange, HlTag, RootDatabase}; |
@@ -153,7 +153,6 @@ pub(super) fn doc_comment(hl: &mut Highlights, sema: &Semantics<RootDatabase>, n | |||
153 | if attributes.docs().map_or(true, |docs| !String::from(docs).contains(RUSTDOC_FENCE)) { | 153 | if attributes.docs().map_or(true, |docs| !String::from(docs).contains(RUSTDOC_FENCE)) { |
154 | return; | 154 | return; |
155 | } | 155 | } |
156 | let doc_comments = attributes.by_key("doc").attrs().map(|attr| attr.to_src(&owner)); | ||
157 | 156 | ||
158 | let mut inj = Injector::default(); | 157 | let mut inj = Injector::default(); |
159 | inj.add_unmapped("fn doctest() {\n"); | 158 | inj.add_unmapped("fn doctest() {\n"); |
@@ -164,13 +163,28 @@ pub(super) fn doc_comment(hl: &mut Highlights, sema: &Semantics<RootDatabase>, n | |||
164 | // Replace the original, line-spanning comment ranges by new, only comment-prefix | 163 | // Replace the original, line-spanning comment ranges by new, only comment-prefix |
165 | // spanning comment ranges. | 164 | // spanning comment ranges. |
166 | let mut new_comments = Vec::new(); | 165 | let mut new_comments = Vec::new(); |
167 | for comment in doc_comments { | 166 | let mut string; |
168 | let (line, range, prefix) = match &comment { | 167 | for attr in attributes.by_key("doc").attrs() { |
169 | Either::Left(_) => continue, // FIXME | 168 | let src = attr.to_src(&owner); |
169 | let (line, range, prefix) = match &src { | ||
170 | Either::Left(it) => { | ||
171 | string = match find_doc_string_in_attr(attr, it) { | ||
172 | Some(it) => it, | ||
173 | None => continue, | ||
174 | }; | ||
175 | let text_range = string.syntax().text_range(); | ||
176 | let text_range = TextRange::new( | ||
177 | text_range.start() + TextSize::from(1), | ||
178 | text_range.end() - TextSize::from(1), | ||
179 | ); | ||
180 | let text = string.text(); | ||
181 | (&text[1..text.len() - 1], text_range, "") | ||
182 | } | ||
170 | Either::Right(comment) => { | 183 | Either::Right(comment) => { |
171 | (comment.text(), comment.syntax().text_range(), comment.prefix()) | 184 | (comment.text(), comment.syntax().text_range(), comment.prefix()) |
172 | } | 185 | } |
173 | }; | 186 | }; |
187 | |||
174 | match line.find(RUSTDOC_FENCE) { | 188 | match line.find(RUSTDOC_FENCE) { |
175 | Some(idx) => { | 189 | Some(idx) => { |
176 | is_codeblock = !is_codeblock; | 190 | is_codeblock = !is_codeblock; |
@@ -222,3 +236,27 @@ pub(super) fn doc_comment(hl: &mut Highlights, sema: &Semantics<RootDatabase>, n | |||
222 | }); | 236 | }); |
223 | } | 237 | } |
224 | } | 238 | } |
239 | |||
240 | fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::String> { | ||
241 | match it.literal() { | ||
242 | // #[doc = lit] | ||
243 | Some(lit) => match lit.kind() { | ||
244 | ast::LiteralKind::String(it) => Some(it), | ||
245 | _ => None, | ||
246 | }, | ||
247 | // #[cfg_attr(..., doc = "", ...)] | ||
248 | None => { | ||
249 | // We gotta hunt the string token manually here | ||
250 | let text = attr.string_value()?; | ||
251 | // FIXME: We just pick the first string literal that has the same text as the doc attribute | ||
252 | // This means technically we might highlight the wrong one | ||
253 | it.syntax() | ||
254 | .descendants_with_tokens() | ||
255 | .filter_map(NodeOrToken::into_token) | ||
256 | .filter_map(ast::String::cast) | ||
257 | .find(|string| { | ||
258 | string.text().get(1..string.text().len() - 1).map_or(false, |it| it == text) | ||
259 | }) | ||
260 | } | ||
261 | } | ||
262 | } | ||
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 5e877df88..45817faf9 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html | |||
@@ -105,4 +105,20 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
105 | <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="brace">{</span> | 105 | <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">></span> <span class="brace">{</span> |
106 | <span class="punctuation">$</span>expr | 106 | <span class="punctuation">$</span>expr |
107 | <span class="brace">}</span> | 107 | <span class="brace">}</span> |
108 | <span class="brace">}</span></code></pre> \ No newline at end of file | 108 | <span class="brace">}</span> |
109 | |||
110 | <span class="comment documentation">/// ```rust</span> | ||
111 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> | ||
112 | <span class="comment documentation">/// ```</span> | ||
113 | <span class="comment documentation">///</span> | ||
114 | <span class="comment documentation">/// ```</span> | ||
115 | <span class="comment documentation">/// </span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span> | ||
116 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="attribute attribute">not</span><span class="parenthesis attribute">(</span><span class="attribute attribute">feature </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"false"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span><span class="attribute attribute"> doc </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span> | ||
117 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">doc</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="attribute attribute">]</span> | ||
118 | <span class="comment documentation">/// ```</span> | ||
119 | <span class="comment documentation">///</span> | ||
120 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="attribute attribute">feature </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span><span class="attribute attribute"> doc </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span> | ||
121 | <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="attribute attribute">not</span><span class="parenthesis attribute">(</span><span class="attribute attribute">feature </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span><span class="attribute attribute"> doc </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span> | ||
122 | <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="none injected">alloc::</span><span class="macro injected">vec!</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> | ||
123 | <span class="comment documentation">/// ```</span> | ||
124 | <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">mix_and_match</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre> \ No newline at end of file | ||
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 9d0cd1af5..a5ef2d29b 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -541,6 +541,22 @@ macro_rules! noop { | |||
541 | $expr | 541 | $expr |
542 | } | 542 | } |
543 | } | 543 | } |
544 | |||
545 | /// ```rust | ||
546 | /// let _ = example(&[1, 2, 3]); | ||
547 | /// ``` | ||
548 | /// | ||
549 | /// ``` | ||
550 | /// loop {} | ||
551 | #[cfg_attr(not(feature = "false"), doc = "loop {}")] | ||
552 | #[doc = "loop {}"] | ||
553 | /// ``` | ||
554 | /// | ||
555 | #[cfg_attr(feature = "alloc", doc = "```rust")] | ||
556 | #[cfg_attr(not(feature = "alloc"), doc = "```ignore")] | ||
557 | /// let _ = example(&alloc::vec![1, 2, 3]); | ||
558 | /// ``` | ||
559 | pub fn mix_and_match() {} | ||
544 | "# | 560 | "# |
545 | .trim(), | 561 | .trim(), |
546 | expect_file!["./test_data/highlight_doctest.html"], | 562 | expect_file!["./test_data/highlight_doctest.html"], |