diff options
Diffstat (limited to 'crates/ide/src')
-rw-r--r-- | crates/ide/src/syntax_highlighting.rs | 8 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/inject.rs (renamed from crates/ide/src/syntax_highlighting/injection.rs) | 130 |
2 files changed, 52 insertions, 86 deletions
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index ad456bc00..079248511 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs | |||
@@ -4,7 +4,7 @@ mod highlights; | |||
4 | mod injector; | 4 | mod injector; |
5 | 5 | ||
6 | mod format; | 6 | mod format; |
7 | mod injection; | 7 | mod inject; |
8 | mod macro_rules; | 8 | mod macro_rules; |
9 | 9 | ||
10 | mod html; | 10 | mod html; |
@@ -135,9 +135,7 @@ pub(crate) fn highlight( | |||
135 | if ast::Attr::can_cast(node.kind()) { | 135 | if ast::Attr::can_cast(node.kind()) { |
136 | inside_attribute = false | 136 | inside_attribute = false |
137 | } | 137 | } |
138 | if let Some((new_comments, inj)) = injection::extract_doc_comments(node) { | 138 | inject::doc_comment(&mut hl, node); |
139 | injection::highlight_doc_comment(new_comments, inj, &mut hl); | ||
140 | } | ||
141 | } | 139 | } |
142 | WalkEvent::Enter(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => { | 140 | WalkEvent::Enter(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => { |
143 | inside_attribute = true | 141 | inside_attribute = true |
@@ -181,7 +179,7 @@ pub(crate) fn highlight( | |||
181 | if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) { | 179 | if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) { |
182 | if token.is_raw() { | 180 | if token.is_raw() { |
183 | let expanded = element_to_highlight.as_token().unwrap().clone(); | 181 | let expanded = element_to_highlight.as_token().unwrap().clone(); |
184 | if injection::highlight_injection(&mut hl, &sema, token, expanded).is_some() { | 182 | if inject::ra_fixture(&mut hl, &sema, token, expanded).is_some() { |
185 | continue; | 183 | continue; |
186 | } | 184 | } |
187 | } | 185 | } |
diff --git a/crates/ide/src/syntax_highlighting/injection.rs b/crates/ide/src/syntax_highlighting/inject.rs index 008d5ce24..4647a72c2 100644 --- a/crates/ide/src/syntax_highlighting/injection.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -1,17 +1,14 @@ | |||
1 | //! Syntax highlighting injections such as highlighting of documentation tests. | 1 | //! Syntax highlighting injections such as highlighting of documentation tests. |
2 | 2 | ||
3 | use std::convert::TryFrom; | ||
4 | |||
5 | use hir::Semantics; | 3 | use hir::Semantics; |
6 | use ide_db::call_info::ActiveParameter; | 4 | use ide_db::call_info::ActiveParameter; |
7 | use itertools::Itertools; | ||
8 | use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize}; | 5 | use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize}; |
9 | 6 | ||
10 | use crate::{Analysis, HlMod, HlRange, HlTag, RootDatabase}; | 7 | use crate::{Analysis, HlMod, HlRange, HlTag, RootDatabase}; |
11 | 8 | ||
12 | use super::{highlights::Highlights, injector::Injector}; | 9 | use super::{highlights::Highlights, injector::Injector}; |
13 | 10 | ||
14 | pub(super) fn highlight_injection( | 11 | pub(super) fn ra_fixture( |
15 | hl: &mut Highlights, | 12 | hl: &mut Highlights, |
16 | sema: &Semantics<RootDatabase>, | 13 | sema: &Semantics<RootDatabase>, |
17 | literal: ast::String, | 14 | literal: ast::String, |
@@ -84,107 +81,78 @@ const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[ | |||
84 | "edition2021", | 81 | "edition2021", |
85 | ]; | 82 | ]; |
86 | 83 | ||
87 | /// Extracts Rust code from documentation comments as well as a mapping from | 84 | /// Injection of syntax highlighting of doctests. |
88 | /// the extracted source code back to the original source ranges. | 85 | pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) { |
89 | /// Lastly, a vector of new comment highlight ranges (spanning only the | 86 | let doc_comments = node |
90 | /// comment prefix) is returned which is used in the syntax highlighting | 87 | .children_with_tokens() |
91 | /// injection to replace the previous (line-spanning) comment ranges. | 88 | .filter_map(|it| it.into_token().and_then(ast::Comment::cast)) |
92 | pub(super) fn extract_doc_comments(node: &SyntaxNode) -> Option<(Vec<HlRange>, Injector)> { | 89 | .filter(|it| it.kind().doc.is_some()); |
90 | |||
91 | if !doc_comments.clone().any(|it| it.text().contains(RUSTDOC_FENCE)) { | ||
92 | return; | ||
93 | } | ||
94 | |||
93 | let mut inj = Injector::default(); | 95 | let mut inj = Injector::default(); |
94 | // wrap the doctest into function body to get correct syntax highlighting | 96 | inj.add_unmapped("fn doctest() {\n"); |
95 | let prefix = "fn doctest() {\n"; | ||
96 | let suffix = "}\n"; | ||
97 | 97 | ||
98 | let mut line_start = TextSize::of(prefix); | ||
99 | let mut is_codeblock = false; | 98 | let mut is_codeblock = false; |
100 | let mut is_doctest = false; | 99 | let mut is_doctest = false; |
100 | |||
101 | // Replace the original, line-spanning comment ranges by new, only comment-prefix | 101 | // Replace the original, line-spanning comment ranges by new, only comment-prefix |
102 | // spanning comment ranges. | 102 | // spanning comment ranges. |
103 | let mut new_comments = Vec::new(); | 103 | let mut new_comments = Vec::new(); |
104 | 104 | for comment in doc_comments { | |
105 | inj.add_unmapped(prefix); | 105 | match comment.text().find(RUSTDOC_FENCE) { |
106 | let doctest = node | 106 | Some(idx) => { |
107 | .children_with_tokens() | ||
108 | .filter_map(|el| el.into_token().and_then(ast::Comment::cast)) | ||
109 | .filter(|comment| comment.kind().doc.is_some()) | ||
110 | .filter(|comment| { | ||
111 | if let Some(idx) = comment.text().find(RUSTDOC_FENCE) { | ||
112 | is_codeblock = !is_codeblock; | 107 | is_codeblock = !is_codeblock; |
113 | // Check whether code is rust by inspecting fence guards | 108 | // Check whether code is rust by inspecting fence guards |
114 | let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..]; | 109 | let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..]; |
115 | let is_rust = | 110 | let is_rust = |
116 | guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim())); | 111 | guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim())); |
117 | is_doctest = is_codeblock && is_rust; | 112 | is_doctest = is_codeblock && is_rust; |
118 | false | 113 | continue; |
119 | } else { | ||
120 | is_doctest | ||
121 | } | 114 | } |
122 | }) | 115 | None if !is_doctest => continue, |
123 | .map(|comment| { | 116 | None => (), |
124 | let prefix_len = comment.prefix().len(); | 117 | } |
125 | let line: &str = comment.text().as_str(); | ||
126 | let range = comment.syntax().text_range(); | ||
127 | |||
128 | // whitespace after comment is ignored | ||
129 | let pos = if let Some(ws) = line.chars().nth(prefix_len).filter(|c| c.is_whitespace()) { | ||
130 | prefix_len + ws.len_utf8() | ||
131 | } else { | ||
132 | prefix_len | ||
133 | }; | ||
134 | |||
135 | // lines marked with `#` should be ignored in output, we skip the `#` char | ||
136 | let pos = if let Some(ws) = line.chars().nth(pos).filter(|&c| c == '#') { | ||
137 | pos + ws.len_utf8() | ||
138 | } else { | ||
139 | pos | ||
140 | }; | ||
141 | |||
142 | new_comments.push(HlRange { | ||
143 | range: TextRange::new( | ||
144 | range.start(), | ||
145 | range.start() + TextSize::try_from(pos).unwrap(), | ||
146 | ), | ||
147 | highlight: HlTag::Comment | HlMod::Documentation, | ||
148 | binding_hash: None, | ||
149 | }); | ||
150 | line_start += range.len() - TextSize::try_from(pos).unwrap(); | ||
151 | line_start += TextSize::of("\n"); | ||
152 | |||
153 | inj.add( | ||
154 | &line[pos..], | ||
155 | TextRange::new(range.start() + TextSize::try_from(pos).unwrap(), range.end()), | ||
156 | ); | ||
157 | inj.add_unmapped("\n"); | ||
158 | line[pos..].to_owned() | ||
159 | }) | ||
160 | .join("\n"); | ||
161 | inj.add_unmapped(suffix); | ||
162 | |||
163 | if doctest.is_empty() { | ||
164 | return None; | ||
165 | } | ||
166 | 118 | ||
167 | Some((new_comments, inj)) | 119 | let line: &str = comment.text().as_str(); |
168 | } | 120 | let range = comment.syntax().text_range(); |
169 | 121 | ||
170 | /// Injection of syntax highlighting of doctests. | 122 | let mut pos = TextSize::of(comment.prefix()); |
171 | pub(super) fn highlight_doc_comment( | 123 | // whitespace after comment is ignored |
172 | new_comments: Vec<HlRange>, | 124 | if let Some(ws) = line[pos.into()..].chars().next().filter(|c| c.is_whitespace()) { |
173 | inj: Injector, | 125 | pos += TextSize::of(ws); |
174 | stack: &mut Highlights, | 126 | } |
175 | ) { | 127 | // lines marked with `#` should be ignored in output, we skip the `#` char |
176 | let (analysis, tmp_file_id) = Analysis::from_single_file(inj.text().to_string()); | 128 | if let Some(ws) = line[pos.into()..].chars().next().filter(|&c| c == '#') { |
177 | for comment in new_comments { | 129 | pos += TextSize::of(ws); |
178 | stack.add(comment); | 130 | } |
131 | |||
132 | new_comments.push(TextRange::at(range.start(), pos)); | ||
133 | |||
134 | inj.add(&line[pos.into()..], TextRange::new(range.start() + pos, range.end())); | ||
135 | inj.add_unmapped("\n"); | ||
179 | } | 136 | } |
137 | inj.add_unmapped("\n}"); | ||
138 | |||
139 | let (analysis, tmp_file_id) = Analysis::from_single_file(inj.text().to_string()); | ||
180 | 140 | ||
181 | for h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() { | 141 | for h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() { |
182 | for r in inj.map_range_up(h.range) { | 142 | for r in inj.map_range_up(h.range) { |
183 | stack.add(HlRange { | 143 | hl.add(HlRange { |
184 | range: r, | 144 | range: r, |
185 | highlight: h.highlight | HlMod::Injected, | 145 | highlight: h.highlight | HlMod::Injected, |
186 | binding_hash: h.binding_hash, | 146 | binding_hash: h.binding_hash, |
187 | }); | 147 | }); |
188 | } | 148 | } |
189 | } | 149 | } |
150 | |||
151 | for range in new_comments { | ||
152 | hl.add(HlRange { | ||
153 | range, | ||
154 | highlight: HlTag::Comment | HlMod::Documentation, | ||
155 | binding_hash: None, | ||
156 | }); | ||
157 | } | ||
190 | } | 158 | } |