@@ -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