11mod tags;
22mod html;
3+ mod injection;
34#[ cfg( test) ]
45mod tests;
56
@@ -10,14 +11,14 @@ use ra_ide_db::{
1011} ;
1112use ra_prof:: profile;
1213use ra_syntax:: {
13- ast:: { self , HasFormatSpecifier , HasQuotes , HasStringValue } ,
14+ ast:: { self , HasFormatSpecifier } ,
1415 AstNode , AstToken , Direction , NodeOrToken , SyntaxElement ,
1516 SyntaxKind :: * ,
16- SyntaxToken , TextRange , WalkEvent , T ,
17+ TextRange , WalkEvent , T ,
1718} ;
1819use rustc_hash:: FxHashMap ;
1920
20- use crate :: { call_info :: ActiveParameter , Analysis , FileId } ;
21+ use crate :: FileId ;
2122
2223use ast:: FormatSpecifier ;
2324pub ( crate ) use html:: highlight_as_html;
@@ -123,6 +124,23 @@ pub(crate) fn highlight(
123124 _ => ( ) ,
124125 }
125126
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+
126144 let element = match event {
127145 WalkEvent :: Enter ( it) => it,
128146 WalkEvent :: Leave ( _) => continue ,
@@ -173,7 +191,7 @@ pub(crate) fn highlight(
173191
174192 if let Some ( token) = element. as_token ( ) . cloned ( ) . and_then ( ast:: RawString :: cast) {
175193 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 ( ) {
177195 continue ;
178196 }
179197 }
@@ -259,9 +277,8 @@ impl HighlightedRangeStack {
259277 let mut parent = prev. pop ( ) . unwrap ( ) ;
260278 for ele in children {
261279 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) ;
265282 if !parent. range . is_empty ( ) {
266283 prev. push ( parent) ;
267284 }
@@ -274,6 +291,62 @@ impl HighlightedRangeStack {
274291 }
275292 }
276293
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+
277350 fn add ( & mut self , range : HighlightedRange ) {
278351 self . stack
279352 . last_mut ( )
@@ -539,42 +612,3 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
539612
540613 tag. into ( )
541614}
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