aboutsummaryrefslogtreecommitdiff
path: root/crates/ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide')
-rw-r--r--crates/ide/src/syntax_highlighting.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs65
2 files changed, 52 insertions, 15 deletions
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 870146d24..ba3447b3a 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -150,7 +150,7 @@ fn traverse(
150 WalkEvent::Enter(it) => it, 150 WalkEvent::Enter(it) => it,
151 WalkEvent::Leave(it) => { 151 WalkEvent::Leave(it) => {
152 if let Some(node) = it.as_node() { 152 if let Some(node) = it.as_node() {
153 inject::doc_comment(hl, node); 153 inject::doc_comment(hl, sema, node);
154 } 154 }
155 continue; 155 continue;
156 } 156 }
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 8cdc3688f..5b065c09f 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -1,8 +1,12 @@
1//! "Recursive" Syntax highlighting for code in doctests and fixtures. 1//! "Recursive" Syntax highlighting for code in doctests and fixtures.
2 2
3use hir::Semantics; 3use either::Either;
4use hir::{HasAttrs, Semantics};
4use ide_db::call_info::ActiveParameter; 5use ide_db::call_info::ActiveParameter;
5use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize}; 6use syntax::{
7 ast::{self, AstNode, AttrsOwner},
8 match_ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize,
9};
6 10
7use crate::{Analysis, HlMod, HlRange, HlTag, RootDatabase}; 11use crate::{Analysis, HlMod, HlRange, HlTag, RootDatabase};
8 12
@@ -81,16 +85,46 @@ const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[
81 "edition2021", 85 "edition2021",
82]; 86];
83 87
88fn doc_attributes<'node>(
89 sema: &Semantics<RootDatabase>,
90 node: &'node SyntaxNode,
91) -> Option<(Box<dyn AttrsOwner>, hir::Attrs)> {
92 match_ast! {
93 match node {
94 ast::SourceFile(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
95 ast::Fn(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
96 ast::Struct(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
97 ast::Union(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
98 ast::RecordField(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
99 ast::TupleField(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
100 ast::Enum(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
101 ast::Variant(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
102 ast::Trait(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
103 ast::Module(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
104 ast::Static(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
105 ast::Const(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
106 ast::TypeAlias(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
107 ast::Impl(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
108 ast::MacroRules(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
109 ast::MacroRules(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
110 // ast::MacroDef(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
111 // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
112 _ => return None
113 }
114 }
115}
116
84/// Injection of syntax highlighting of doctests. 117/// Injection of syntax highlighting of doctests.
85pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) { 118pub(super) fn doc_comment(hl: &mut Highlights, sema: &Semantics<RootDatabase>, node: &SyntaxNode) {
86 let doc_comments = node 119 let (owner, attributes) = match doc_attributes(sema, node) {
87 .children_with_tokens() 120 Some(it) => it,
88 .filter_map(|it| it.into_token().and_then(ast::Comment::cast)) 121 None => return,
89 .filter(|it| it.kind().doc.is_some()); 122 };
90 123
91 if !doc_comments.clone().any(|it| it.text().contains(RUSTDOC_FENCE)) { 124 if attributes.docs().map_or(true, |docs| !String::from(docs).contains(RUSTDOC_FENCE)) {
92 return; 125 return;
93 } 126 }
127 let doc_comments = attributes.by_key("doc").attrs().map(|attr| attr.to_src(&*owner));
94 128
95 let mut inj = Injector::default(); 129 let mut inj = Injector::default();
96 inj.add_unmapped("fn doctest() {\n"); 130 inj.add_unmapped("fn doctest() {\n");
@@ -102,11 +136,17 @@ pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) {
102 // spanning comment ranges. 136 // spanning comment ranges.
103 let mut new_comments = Vec::new(); 137 let mut new_comments = Vec::new();
104 for comment in doc_comments { 138 for comment in doc_comments {
105 match comment.text().find(RUSTDOC_FENCE) { 139 let (line, range, prefix) = match &comment {
140 Either::Left(_) => continue, // FIXME
141 Either::Right(comment) => {
142 (comment.text(), comment.syntax().text_range(), comment.prefix())
143 }
144 };
145 match line.find(RUSTDOC_FENCE) {
106 Some(idx) => { 146 Some(idx) => {
107 is_codeblock = !is_codeblock; 147 is_codeblock = !is_codeblock;
108 // Check whether code is rust by inspecting fence guards 148 // Check whether code is rust by inspecting fence guards
109 let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..]; 149 let guards = &line[idx + RUSTDOC_FENCE.len()..];
110 let is_rust = 150 let is_rust =
111 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim())); 151 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim()));
112 is_doctest = is_codeblock && is_rust; 152 is_doctest = is_codeblock && is_rust;
@@ -116,10 +156,7 @@ pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) {
116 None => (), 156 None => (),
117 } 157 }
118 158
119 let line: &str = comment.text(); 159 let mut pos = TextSize::of(prefix);
120 let range = comment.syntax().text_range();
121
122 let mut pos = TextSize::of(comment.prefix());
123 // whitespace after comment is ignored 160 // whitespace after comment is ignored
124 if let Some(ws) = line[pos.into()..].chars().next().filter(|c| c.is_whitespace()) { 161 if let Some(ws) = line[pos.into()..].chars().next().filter(|c| c.is_whitespace()) {
125 pos += TextSize::of(ws); 162 pos += TextSize::of(ws);