diff options
Diffstat (limited to 'crates/ide/src/syntax_highlighting/injection.rs')
-rw-r--r-- | crates/ide/src/syntax_highlighting/injection.rs | 85 |
1 files changed, 31 insertions, 54 deletions
diff --git a/crates/ide/src/syntax_highlighting/injection.rs b/crates/ide/src/syntax_highlighting/injection.rs index d6be9708d..98ee03e0d 100644 --- a/crates/ide/src/syntax_highlighting/injection.rs +++ b/crates/ide/src/syntax_highlighting/injection.rs | |||
@@ -1,18 +1,18 @@ | |||
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::{collections::BTreeMap, convert::TryFrom}; | 3 | use std::convert::TryFrom; |
4 | 4 | ||
5 | use hir::Semantics; | 5 | use hir::Semantics; |
6 | use ide_db::call_info::ActiveParameter; | 6 | use ide_db::call_info::ActiveParameter; |
7 | use itertools::Itertools; | 7 | use itertools::Itertools; |
8 | use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize}; | 8 | use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize}; |
9 | 9 | ||
10 | use crate::{Analysis, Highlight, HighlightModifier, HighlightTag, HighlightedRange, RootDatabase}; | 10 | use crate::{Analysis, HighlightModifier, HighlightTag, HighlightedRange, RootDatabase}; |
11 | 11 | ||
12 | use super::HighlightedRangeStack; | 12 | use super::{highlights::Highlights, injector::Injector}; |
13 | 13 | ||
14 | pub(super) fn highlight_injection( | 14 | pub(super) fn highlight_injection( |
15 | acc: &mut HighlightedRangeStack, | 15 | acc: &mut Highlights, |
16 | sema: &Semantics<RootDatabase>, | 16 | sema: &Semantics<RootDatabase>, |
17 | literal: ast::String, | 17 | literal: ast::String, |
18 | expanded: SyntaxToken, | 18 | expanded: SyntaxToken, |
@@ -98,9 +98,6 @@ impl MarkerInfo { | |||
98 | } | 98 | } |
99 | } | 99 | } |
100 | 100 | ||
101 | /// Mapping from extracted documentation code to original code | ||
102 | type RangesMap = BTreeMap<TextSize, TextSize>; | ||
103 | |||
104 | const RUSTDOC_FENCE: &'static str = "```"; | 101 | const RUSTDOC_FENCE: &'static str = "```"; |
105 | const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[ | 102 | const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[ |
106 | "", | 103 | "", |
@@ -119,20 +116,20 @@ const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[ | |||
119 | /// Lastly, a vector of new comment highlight ranges (spanning only the | 116 | /// Lastly, a vector of new comment highlight ranges (spanning only the |
120 | /// comment prefix) is returned which is used in the syntax highlighting | 117 | /// comment prefix) is returned which is used in the syntax highlighting |
121 | /// injection to replace the previous (line-spanning) comment ranges. | 118 | /// injection to replace the previous (line-spanning) comment ranges. |
122 | pub(super) fn extract_doc_comments( | 119 | pub(super) fn extract_doc_comments(node: &SyntaxNode) -> Option<(Vec<HighlightedRange>, Injector)> { |
123 | node: &SyntaxNode, | 120 | let mut inj = Injector::default(); |
124 | ) -> Option<(String, RangesMap, Vec<HighlightedRange>)> { | ||
125 | // wrap the doctest into function body to get correct syntax highlighting | 121 | // wrap the doctest into function body to get correct syntax highlighting |
126 | let prefix = "fn doctest() {\n"; | 122 | let prefix = "fn doctest() {\n"; |
127 | let suffix = "}\n"; | 123 | let suffix = "}\n"; |
128 | // Mapping from extracted documentation code to original code | 124 | |
129 | let mut range_mapping: RangesMap = BTreeMap::new(); | 125 | let mut line_start = TextSize::of(prefix); |
130 | let mut line_start = TextSize::try_from(prefix.len()).unwrap(); | ||
131 | let mut is_codeblock = false; | 126 | let mut is_codeblock = false; |
132 | let mut is_doctest = false; | 127 | let mut is_doctest = false; |
133 | // Replace the original, line-spanning comment ranges by new, only comment-prefix | 128 | // Replace the original, line-spanning comment ranges by new, only comment-prefix |
134 | // spanning comment ranges. | 129 | // spanning comment ranges. |
135 | let mut new_comments = Vec::new(); | 130 | let mut new_comments = Vec::new(); |
131 | |||
132 | inj.add_unmapped(prefix); | ||
136 | let doctest = node | 133 | let doctest = node |
137 | .children_with_tokens() | 134 | .children_with_tokens() |
138 | .filter_map(|el| el.into_token().and_then(ast::Comment::cast)) | 135 | .filter_map(|el| el.into_token().and_then(ast::Comment::cast)) |
@@ -169,7 +166,6 @@ pub(super) fn extract_doc_comments( | |||
169 | pos | 166 | pos |
170 | }; | 167 | }; |
171 | 168 | ||
172 | range_mapping.insert(line_start, range.start() + TextSize::try_from(pos).unwrap()); | ||
173 | new_comments.push(HighlightedRange { | 169 | new_comments.push(HighlightedRange { |
174 | range: TextRange::new( | 170 | range: TextRange::new( |
175 | range.start(), | 171 | range.start(), |
@@ -179,62 +175,43 @@ pub(super) fn extract_doc_comments( | |||
179 | binding_hash: None, | 175 | binding_hash: None, |
180 | }); | 176 | }); |
181 | line_start += range.len() - TextSize::try_from(pos).unwrap(); | 177 | line_start += range.len() - TextSize::try_from(pos).unwrap(); |
182 | line_start += TextSize::try_from('\n'.len_utf8()).unwrap(); | 178 | line_start += TextSize::of("\n"); |
183 | 179 | ||
180 | inj.add( | ||
181 | &line[pos..], | ||
182 | TextRange::new(range.start() + TextSize::try_from(pos).unwrap(), range.end()), | ||
183 | ); | ||
184 | inj.add_unmapped("\n"); | ||
184 | line[pos..].to_owned() | 185 | line[pos..].to_owned() |
185 | }) | 186 | }) |
186 | .join("\n"); | 187 | .join("\n"); |
188 | inj.add_unmapped(suffix); | ||
187 | 189 | ||
188 | if doctest.is_empty() { | 190 | if doctest.is_empty() { |
189 | return None; | 191 | return None; |
190 | } | 192 | } |
191 | 193 | ||
192 | let doctest = format!("{}{}{}", prefix, doctest, suffix); | 194 | Some((new_comments, inj)) |
193 | Some((doctest, range_mapping, new_comments)) | ||
194 | } | 195 | } |
195 | 196 | ||
196 | /// Injection of syntax highlighting of doctests. | 197 | /// Injection of syntax highlighting of doctests. |
197 | pub(super) fn highlight_doc_comment( | 198 | pub(super) fn highlight_doc_comment( |
198 | text: String, | ||
199 | range_mapping: RangesMap, | ||
200 | new_comments: Vec<HighlightedRange>, | 199 | new_comments: Vec<HighlightedRange>, |
201 | stack: &mut HighlightedRangeStack, | 200 | inj: Injector, |
201 | stack: &mut Highlights, | ||
202 | ) { | 202 | ) { |
203 | let (analysis, tmp_file_id) = Analysis::from_single_file(text); | 203 | let (analysis, tmp_file_id) = Analysis::from_single_file(inj.text().to_string()); |
204 | |||
205 | stack.push(); | ||
206 | for mut h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() { | ||
207 | // Determine start offset and end offset in case of multi-line ranges | ||
208 | let mut start_offset = None; | ||
209 | let mut end_offset = None; | ||
210 | for (line_start, orig_line_start) in range_mapping.range(..h.range.end()).rev() { | ||
211 | // It's possible for orig_line_start - line_start to be negative. Add h.range.start() | ||
212 | // here and remove it from the end range after the loop below so that the values are | ||
213 | // always non-negative. | ||
214 | let offset = h.range.start() + orig_line_start - line_start; | ||
215 | if line_start <= &h.range.start() { | ||
216 | start_offset.get_or_insert(offset); | ||
217 | break; | ||
218 | } else { | ||
219 | end_offset.get_or_insert(offset); | ||
220 | } | ||
221 | } | ||
222 | if let Some(start_offset) = start_offset { | ||
223 | h.range = TextRange::new( | ||
224 | start_offset, | ||
225 | h.range.end() + end_offset.unwrap_or(start_offset) - h.range.start(), | ||
226 | ); | ||
227 | |||
228 | h.highlight |= HighlightModifier::Injected; | ||
229 | stack.add(h); | ||
230 | } | ||
231 | } | ||
232 | |||
233 | // Inject the comment prefix highlight ranges | ||
234 | stack.push(); | ||
235 | for comment in new_comments { | 204 | for comment in new_comments { |
236 | stack.add(comment); | 205 | stack.add(comment); |
237 | } | 206 | } |
238 | stack.pop_and_inject(None); | 207 | |
239 | stack.pop_and_inject(Some(Highlight::from(HighlightTag::Dummy) | HighlightModifier::Injected)); | 208 | for h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() { |
209 | for r in inj.map_range_up(h.range) { | ||
210 | stack.add(HighlightedRange { | ||
211 | range: r, | ||
212 | highlight: h.highlight | HighlightModifier::Injected, | ||
213 | binding_hash: h.binding_hash, | ||
214 | }); | ||
215 | } | ||
216 | } | ||
240 | } | 217 | } |