1
1
mod tags;
2
2
mod html;
3
+ mod injection;
3
4
#[ cfg( test) ]
4
5
mod tests;
5
6
@@ -10,14 +11,14 @@ use ra_ide_db::{
10
11
} ;
11
12
use ra_prof:: profile;
12
13
use ra_syntax:: {
13
- ast:: { self , HasFormatSpecifier , HasQuotes , HasStringValue } ,
14
+ ast:: { self , HasFormatSpecifier } ,
14
15
AstNode , AstToken , Direction , NodeOrToken , SyntaxElement ,
15
16
SyntaxKind :: * ,
16
- SyntaxToken , TextRange , WalkEvent , T ,
17
+ TextRange , WalkEvent , T ,
17
18
} ;
18
19
use rustc_hash:: FxHashMap ;
19
20
20
- use crate :: { call_info :: ActiveParameter , Analysis , FileId } ;
21
+ use crate :: FileId ;
21
22
22
23
use ast:: FormatSpecifier ;
23
24
pub ( crate ) use html:: highlight_as_html;
@@ -123,6 +124,23 @@ pub(crate) fn highlight(
123
124
_ => ( ) ,
124
125
}
125
126
127
+ // Check for Rust code in documentation
128
+ match & event {
129
+ WalkEvent :: Leave ( NodeOrToken :: Node ( node) ) => {
130
+ if let Some ( ( doctest, range_mapping, new_comments) ) =
131
+ injection:: extract_doc_comments ( node)
132
+ {
133
+ injection:: highlight_doc_comment (
134
+ doctest,
135
+ range_mapping,
136
+ new_comments,
137
+ & mut stack,
138
+ ) ;
139
+ }
140
+ }
141
+ _ => ( ) ,
142
+ }
143
+
126
144
let element = match event {
127
145
WalkEvent :: Enter ( it) => it,
128
146
WalkEvent :: Leave ( _) => continue ,
@@ -173,7 +191,7 @@ pub(crate) fn highlight(
173
191
174
192
if let Some ( token) = element. as_token ( ) . cloned ( ) . and_then ( ast:: RawString :: cast) {
175
193
let expanded = element_to_highlight. as_token ( ) . unwrap ( ) . clone ( ) ;
176
- if highlight_injection ( & mut stack, & sema, token, expanded) . is_some ( ) {
194
+ if injection :: highlight_injection ( & mut stack, & sema, token, expanded) . is_some ( ) {
177
195
continue ;
178
196
}
179
197
}
@@ -259,9 +277,8 @@ impl HighlightedRangeStack {
259
277
let mut parent = prev. pop ( ) . unwrap ( ) ;
260
278
for ele in children {
261
279
assert ! ( parent. range. contains_range( ele. range) ) ;
262
- let mut cloned = parent. clone ( ) ;
263
- parent. range = TextRange :: new ( parent. range . start ( ) , ele. range . start ( ) ) ;
264
- cloned. range = TextRange :: new ( ele. range . end ( ) , cloned. range . end ( ) ) ;
280
+
281
+ let cloned = Self :: intersect ( & mut parent, & ele) ;
265
282
if !parent. range . is_empty ( ) {
266
283
prev. push ( parent) ;
267
284
}
@@ -274,6 +291,62 @@ impl HighlightedRangeStack {
274
291
}
275
292
}
276
293
294
+ /// Intersects the `HighlightedRange` `parent` with `child`.
295
+ /// `parent` is mutated in place, becoming the range before `child`.
296
+ /// Returns the range (of the same type as `parent`) *after* `child`.
297
+ fn intersect ( parent : & mut HighlightedRange , child : & HighlightedRange ) -> HighlightedRange {
298
+ assert ! ( parent. range. contains_range( child. range) ) ;
299
+
300
+ let mut cloned = parent. clone ( ) ;
301
+ parent. range = TextRange :: new ( parent. range . start ( ) , child. range . start ( ) ) ;
302
+ cloned. range = TextRange :: new ( child. range . end ( ) , cloned. range . end ( ) ) ;
303
+
304
+ cloned
305
+ }
306
+
307
+ /// Similar to `pop`, but can modify arbitrary prior ranges (where `pop`)
308
+ /// can only modify the last range currently on the stack.
309
+ /// Can be used to do injections that span multiple ranges, like the
310
+ /// doctest injection below.
311
+ /// If `delete` is set to true, the parent range is deleted instead of
312
+ /// intersected.
313
+ ///
314
+ /// Note that `pop` can be simulated by `pop_and_inject(false)` but the
315
+ /// latter is computationally more expensive.
316
+ fn pop_and_inject ( & mut self , delete : bool ) {
317
+ let mut children = self . stack . pop ( ) . unwrap ( ) ;
318
+ let prev = self . stack . last_mut ( ) . unwrap ( ) ;
319
+ children. sort_by_key ( |range| range. range . start ( ) ) ;
320
+ prev. sort_by_key ( |range| range. range . start ( ) ) ;
321
+
322
+ for child in children {
323
+ if let Some ( idx) =
324
+ prev. iter ( ) . position ( |parent| parent. range . contains_range ( child. range ) )
325
+ {
326
+ let cloned = Self :: intersect ( & mut prev[ idx] , & child) ;
327
+ let insert_idx = if delete || prev[ idx] . range . is_empty ( ) {
328
+ prev. remove ( idx) ;
329
+ idx
330
+ } else {
331
+ idx + 1
332
+ } ;
333
+ prev. insert ( insert_idx, child) ;
334
+ if !delete && !cloned. range . is_empty ( ) {
335
+ prev. insert ( insert_idx + 1 , cloned) ;
336
+ }
337
+ } else if let Some ( _idx) =
338
+ prev. iter ( ) . position ( |parent| parent. range . contains ( child. range . start ( ) ) )
339
+ {
340
+ unreachable ! ( "child range should be completely contained in parent range" ) ;
341
+ } else {
342
+ let idx = prev
343
+ . binary_search_by_key ( & child. range . start ( ) , |range| range. range . start ( ) )
344
+ . unwrap_or_else ( |x| x) ;
345
+ prev. insert ( idx, child) ;
346
+ }
347
+ }
348
+ }
349
+
277
350
fn add ( & mut self , range : HighlightedRange ) {
278
351
self . stack
279
352
. last_mut ( )
@@ -539,42 +612,3 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
539
612
540
613
tag. into ( )
541
614
}
542
-
543
- fn highlight_injection (
544
- acc : & mut HighlightedRangeStack ,
545
- sema : & Semantics < RootDatabase > ,
546
- literal : ast:: RawString ,
547
- expanded : SyntaxToken ,
548
- ) -> Option < ( ) > {
549
- let active_parameter = ActiveParameter :: at_token ( & sema, expanded) ?;
550
- if !active_parameter. name . starts_with ( "ra_fixture" ) {
551
- return None ;
552
- }
553
- let value = literal. value ( ) ?;
554
- let ( analysis, tmp_file_id) = Analysis :: from_single_file ( value) ;
555
-
556
- if let Some ( range) = literal. open_quote_text_range ( ) {
557
- acc. add ( HighlightedRange {
558
- range,
559
- highlight : HighlightTag :: StringLiteral . into ( ) ,
560
- binding_hash : None ,
561
- } )
562
- }
563
-
564
- for mut h in analysis. highlight ( tmp_file_id) . unwrap ( ) {
565
- if let Some ( r) = literal. map_range_up ( h. range ) {
566
- h. range = r;
567
- acc. add ( h)
568
- }
569
- }
570
-
571
- if let Some ( range) = literal. close_quote_text_range ( ) {
572
- acc. add ( HighlightedRange {
573
- range,
574
- highlight : HighlightTag :: StringLiteral . into ( ) ,
575
- binding_hash : None ,
576
- } )
577
- }
578
-
579
- Some ( ( ) )
580
- }
0 commit comments