Skip to content

Commit 78af272

Browse files
committed
Properly handle doc attributes in doc-comment highlight injection
1 parent 3daa302 commit 78af272

File tree

5 files changed

+84
-8
lines changed

5 files changed

+84
-8
lines changed

crates/hir/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ pub use crate::{
8989
pub use {
9090
hir_def::{
9191
adt::StructKind,
92-
attr::{Attrs, Documentation},
92+
attr::{Attr, Attrs, Documentation},
9393
body::scope::ExprScopes,
9494
find_path::PrefixKind,
9595
import_map,

crates/hir_def/src/attr.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@ impl RawAttrs {
162162
let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?;
163163
// FIXME hygiene
164164
let hygiene = Hygiene::new_unhygienic();
165-
// FIXME same index is assigned to multiple attributes
166165
Attr::from_src(attr, &hygiene).map(|attr| Attr { index, ..attr })
167166
});
168167

@@ -450,6 +449,13 @@ impl Attr {
450449
_ => None,
451450
}
452451
}
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+
}
453459
}
454460

455461
#[derive(Debug, Clone, Copy)]

crates/ide/src/syntax_highlighting/inject.rs

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use hir::{HasAttrs, Semantics};
55
use ide_db::call_info::ActiveParameter;
66
use syntax::{
77
ast::{self, AstNode, AttrsOwner, DocCommentsOwner},
8-
match_ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize,
8+
match_ast, AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize,
99
};
1010

1111
use crate::{Analysis, HlMod, HlRange, HlTag, RootDatabase};
@@ -153,7 +153,6 @@ pub(super) fn doc_comment(hl: &mut Highlights, sema: &Semantics<RootDatabase>, n
153153
if attributes.docs().map_or(true, |docs| !String::from(docs).contains(RUSTDOC_FENCE)) {
154154
return;
155155
}
156-
let doc_comments = attributes.by_key("doc").attrs().map(|attr| attr.to_src(&owner));
157156

158157
let mut inj = Injector::default();
159158
inj.add_unmapped("fn doctest() {\n");
@@ -164,13 +163,28 @@ pub(super) fn doc_comment(hl: &mut Highlights, sema: &Semantics<RootDatabase>, n
164163
// Replace the original, line-spanning comment ranges by new, only comment-prefix
165164
// spanning comment ranges.
166165
let mut new_comments = Vec::new();
167-
for comment in doc_comments {
168-
let (line, range, prefix) = match &comment {
169-
Either::Left(_) => continue, // FIXME
166+
let mut string;
167+
for attr in attributes.by_key("doc").attrs() {
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+
}
170183
Either::Right(comment) => {
171184
(comment.text(), comment.syntax().text_range(), comment.prefix())
172185
}
173186
};
187+
174188
match line.find(RUSTDOC_FENCE) {
175189
Some(idx) => {
176190
is_codeblock = !is_codeblock;
@@ -222,3 +236,27 @@ pub(super) fn doc_comment(hl: &mut Highlights, sema: &Semantics<RootDatabase>, n
222236
});
223237
}
224238
}
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+
}

crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,20 @@
105105
<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">&gt;</span> <span class="brace">{</span>
106106
<span class="punctuation">$</span>expr
107107
<span class="brace">}</span>
108-
<span class="brace">}</span></code></pre>
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>

crates/ide/src/syntax_highlighting/tests.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,22 @@ macro_rules! noop {
541541
$expr
542542
}
543543
}
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() {}
544560
"#
545561
.trim(),
546562
expect_file!["./test_data/highlight_doctest.html"],

0 commit comments

Comments
 (0)