aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/syntax_highlighting/injection.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-01-07 22:39:02 +0000
committerAleksey Kladov <[email protected]>2021-01-08 20:47:35 +0000
commite30c1c3fbf8f70336d985b2b73e5b0f45f3b95f5 (patch)
treea3cdc2d2f667ab5a122758152eb338a654d387cd /crates/ide/src/syntax_highlighting/injection.rs
parent981a0d708ec352969f9ca075a3e0e50c6da48197 (diff)
Simplify highlighting infra
This also fixes the killer whale bug
Diffstat (limited to 'crates/ide/src/syntax_highlighting/injection.rs')
-rw-r--r--crates/ide/src/syntax_highlighting/injection.rs85
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
3use std::{collections::BTreeMap, convert::TryFrom}; 3use std::convert::TryFrom;
4 4
5use hir::Semantics; 5use hir::Semantics;
6use ide_db::call_info::ActiveParameter; 6use ide_db::call_info::ActiveParameter;
7use itertools::Itertools; 7use itertools::Itertools;
8use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize}; 8use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize};
9 9
10use crate::{Analysis, Highlight, HighlightModifier, HighlightTag, HighlightedRange, RootDatabase}; 10use crate::{Analysis, HighlightModifier, HighlightTag, HighlightedRange, RootDatabase};
11 11
12use super::HighlightedRangeStack; 12use super::{highlights::Highlights, injector::Injector};
13 13
14pub(super) fn highlight_injection( 14pub(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
102type RangesMap = BTreeMap<TextSize, TextSize>;
103
104const RUSTDOC_FENCE: &'static str = "```"; 101const RUSTDOC_FENCE: &'static str = "```";
105const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[ 102const 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.
122pub(super) fn extract_doc_comments( 119pub(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.
197pub(super) fn highlight_doc_comment( 198pub(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}