@@ -12,6 +12,7 @@ use crate::html::render::Context;
1212use  std:: collections:: VecDeque ; 
1313use  std:: fmt:: { Display ,  Write } ; 
1414
15+ use  rustc_data_structures:: fx:: FxHashMap ; 
1516use  rustc_lexer:: { LiteralKind ,  TokenKind } ; 
1617use  rustc_span:: edition:: Edition ; 
1718use  rustc_span:: symbol:: Symbol ; 
@@ -30,6 +31,10 @@ crate struct ContextInfo<'a, 'b, 'c> {
3031     crate  root_path :  & ' c  str , 
3132} 
3233
34+ /// Decorations are represented as a map from CSS class to vector of character ranges. 
35+ /// Each range will be wrapped in a span with that class. 
36+ crate  struct  DecorationInfo ( crate  FxHashMap < & ' static  str ,  Vec < ( u32 ,  u32 ) > > ) ; 
37+ 
3338/// Highlights `src`, returning the HTML output. 
3439crate  fn  render_with_highlighting ( 
3540    src :  & str , 
@@ -40,6 +45,7 @@ crate fn render_with_highlighting(
4045    edition :  Edition , 
4146    extra_content :  Option < Buffer > , 
4247    context_info :  Option < ContextInfo < ' _ ,  ' _ ,  ' _ > > , 
48+     decoration_info :  Option < DecorationInfo > , 
4349)  { 
4450    debug ! ( "highlighting: ================\n {}\n ==============" ,  src) ; 
4551    if  let  Some ( ( edition_info,  class) )  = tooltip { 
@@ -56,7 +62,7 @@ crate fn render_with_highlighting(
5662    } 
5763
5864    write_header ( out,  class,  extra_content) ; 
59-     write_code ( out,  & src,  edition,  context_info) ; 
65+     write_code ( out,  & src,  edition,  context_info,  decoration_info ) ; 
6066    write_footer ( out,  playground_button) ; 
6167} 
6268
@@ -89,17 +95,23 @@ fn write_code(
8995    src :  & str , 
9096    edition :  Edition , 
9197    context_info :  Option < ContextInfo < ' _ ,  ' _ ,  ' _ > > , 
98+     decoration_info :  Option < DecorationInfo > , 
9299)  { 
93100    // This replace allows to fix how the code source with DOS backline characters is displayed. 
94101    let  src = src. replace ( "\r \n " ,  "\n " ) ; 
95-     Classifier :: new ( & src,  edition,  context_info. as_ref ( ) . map ( |c| c. file_span ) . unwrap_or ( DUMMY_SP ) ) 
96-         . highlight ( & mut  |highlight| { 
97-             match  highlight { 
98-                 Highlight :: Token  {  text,  class }  => string ( out,  Escape ( text) ,  class,  & context_info) , 
99-                 Highlight :: EnterSpan  {  class }  => enter_span ( out,  class) , 
100-                 Highlight :: ExitSpan  => exit_span ( out) , 
101-             } ; 
102-         } ) ; 
102+     Classifier :: new ( 
103+         & src, 
104+         edition, 
105+         context_info. as_ref ( ) . map ( |c| c. file_span ) . unwrap_or ( DUMMY_SP ) , 
106+         decoration_info, 
107+     ) 
108+     . highlight ( & mut  |highlight| { 
109+         match  highlight { 
110+             Highlight :: Token  {  text,  class }  => string ( out,  Escape ( text) ,  class,  & context_info) , 
111+             Highlight :: EnterSpan  {  class }  => enter_span ( out,  class) , 
112+             Highlight :: ExitSpan  => exit_span ( out) , 
113+         } ; 
114+     } ) ; 
103115} 
104116
105117fn  write_footer ( out :  & mut  Buffer ,  playground_button :  Option < & str > )  { 
@@ -127,6 +139,7 @@ enum Class {
127139    PreludeTy , 
128140    PreludeVal , 
129141    QuestionMark , 
142+     Decoration ( & ' static  str ) , 
130143} 
131144
132145impl  Class  { 
@@ -150,6 +163,7 @@ impl Class {
150163            Class :: PreludeTy  => "prelude-ty" , 
151164            Class :: PreludeVal  => "prelude-val" , 
152165            Class :: QuestionMark  => "question-mark" , 
166+             Class :: Decoration ( kind)  => kind, 
153167        } 
154168    } 
155169
@@ -248,6 +262,24 @@ impl Iterator for PeekIter<'a> {
248262    } 
249263} 
250264
265+ /// Custom spans inserted into the source. Eg --scrape-examples uses this to highlight function calls 
266+ struct  Decorations  { 
267+     starts :  Vec < ( u32 ,  & ' static  str ) > , 
268+     ends :  Vec < u32 > , 
269+ } 
270+ 
271+ impl  Decorations  { 
272+     fn  new ( info :  DecorationInfo )  -> Self  { 
273+         let  ( starts,  ends)  = info
274+             . 0 
275+             . into_iter ( ) 
276+             . map ( |( kind,  ranges) | ranges. into_iter ( ) . map ( move  |( lo,  hi) | ( ( lo,  kind) ,  hi) ) ) 
277+             . flatten ( ) 
278+             . unzip ( ) ; 
279+         Decorations  {  starts,  ends } 
280+     } 
281+ } 
282+ 
251283/// Processes program tokens, classifying strings of text by highlighting 
252284/// category (`Class`). 
253285struct  Classifier < ' a >  { 
@@ -259,13 +291,20 @@ struct Classifier<'a> {
259291    byte_pos :  u32 , 
260292    file_span :  Span , 
261293    src :  & ' a  str , 
294+     decorations :  Option < Decorations > , 
262295} 
263296
264297impl < ' a >  Classifier < ' a >  { 
265298    /// Takes as argument the source code to HTML-ify, the rust edition to use and the source code 
266299     /// file span which will be used later on by the `span_correspondance_map`. 
267-      fn  new ( src :  & str ,  edition :  Edition ,  file_span :  Span )  -> Classifier < ' _ >  { 
300+      fn  new ( 
301+         src :  & str , 
302+         edition :  Edition , 
303+         file_span :  Span , 
304+         decoration_info :  Option < DecorationInfo > , 
305+     )  -> Classifier < ' _ >  { 
268306        let  tokens = PeekIter :: new ( TokenIter  {  src } ) ; 
307+         let  decorations = decoration_info. map ( Decorations :: new) ; 
269308        Classifier  { 
270309            tokens, 
271310            in_attribute :  false , 
@@ -275,6 +314,7 @@ impl<'a> Classifier<'a> {
275314            byte_pos :  0 , 
276315            file_span, 
277316            src, 
317+             decorations, 
278318        } 
279319    } 
280320
@@ -356,6 +396,19 @@ impl<'a> Classifier<'a> {
356396     /// token is used. 
357397     fn  highlight ( mut  self ,  sink :  & mut  dyn  FnMut ( Highlight < ' a > ) )  { 
358398        loop  { 
399+             if  let  Some ( decs)  = self . decorations . as_mut ( )  { 
400+                 let  byte_pos = self . byte_pos ; 
401+                 let  n_starts = decs. starts . iter ( ) . filter ( |( i,  _) | byte_pos >= * i) . count ( ) ; 
402+                 for  ( _,  kind)  in  decs. starts . drain ( 0 ..n_starts)  { 
403+                     sink ( Highlight :: EnterSpan  {  class :  Class :: Decoration ( kind)  } ) ; 
404+                 } 
405+ 
406+                 let  n_ends = decs. ends . iter ( ) . filter ( |i| byte_pos >= * * i) . count ( ) ; 
407+                 for  _ in  decs. ends . drain ( 0 ..n_ends)  { 
408+                     sink ( Highlight :: ExitSpan ) ; 
409+                 } 
410+             } 
411+ 
359412            if  self 
360413                . tokens 
361414                . peek ( ) 
@@ -657,7 +710,7 @@ fn string<T: Display>(
657710                // https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338 
658711                match  href { 
659712                    LinkFromSrc :: Local ( span)  => context
660-                         . href_from_span ( * span) 
713+                         . href_from_span ( * span,   true ) 
661714                        . map ( |s| format ! ( "{}{}" ,  context_info. root_path,  s) ) , 
662715                    LinkFromSrc :: External ( def_id)  => { 
663716                        format:: href_with_root_path ( * def_id,  context,  Some ( context_info. root_path ) ) 
0 commit comments