@@ -12,6 +12,7 @@ use crate::html::render::Context;
12
12
use std:: collections:: VecDeque ;
13
13
use std:: fmt:: { Display , Write } ;
14
14
15
+ use rustc_data_structures:: fx:: FxHashMap ;
15
16
use rustc_lexer:: { LiteralKind , TokenKind } ;
16
17
use rustc_span:: edition:: Edition ;
17
18
use rustc_span:: symbol:: Symbol ;
@@ -30,6 +31,10 @@ crate struct ContextInfo<'a, 'b, 'c> {
30
31
crate root_path : & ' c str ,
31
32
}
32
33
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
+
33
38
/// Highlights `src`, returning the HTML output.
34
39
crate fn render_with_highlighting (
35
40
src : & str ,
@@ -40,6 +45,7 @@ crate fn render_with_highlighting(
40
45
edition : Edition ,
41
46
extra_content : Option < Buffer > ,
42
47
context_info : Option < ContextInfo < ' _ , ' _ , ' _ > > ,
48
+ decoration_info : Option < DecorationInfo > ,
43
49
) {
44
50
debug ! ( "highlighting: ================\n {}\n ==============" , src) ;
45
51
if let Some ( ( edition_info, class) ) = tooltip {
@@ -56,7 +62,7 @@ crate fn render_with_highlighting(
56
62
}
57
63
58
64
write_header ( out, class, extra_content) ;
59
- write_code ( out, & src, edition, context_info) ;
65
+ write_code ( out, & src, edition, context_info, decoration_info ) ;
60
66
write_footer ( out, playground_button) ;
61
67
}
62
68
@@ -89,17 +95,23 @@ fn write_code(
89
95
src : & str ,
90
96
edition : Edition ,
91
97
context_info : Option < ContextInfo < ' _ , ' _ , ' _ > > ,
98
+ decoration_info : Option < DecorationInfo > ,
92
99
) {
93
100
// This replace allows to fix how the code source with DOS backline characters is displayed.
94
101
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
+ } ) ;
103
115
}
104
116
105
117
fn write_footer ( out : & mut Buffer , playground_button : Option < & str > ) {
@@ -127,6 +139,7 @@ enum Class {
127
139
PreludeTy ,
128
140
PreludeVal ,
129
141
QuestionMark ,
142
+ Decoration ( & ' static str ) ,
130
143
}
131
144
132
145
impl Class {
@@ -150,6 +163,7 @@ impl Class {
150
163
Class :: PreludeTy => "prelude-ty" ,
151
164
Class :: PreludeVal => "prelude-val" ,
152
165
Class :: QuestionMark => "question-mark" ,
166
+ Class :: Decoration ( kind) => kind,
153
167
}
154
168
}
155
169
@@ -248,6 +262,24 @@ impl Iterator for PeekIter<'a> {
248
262
}
249
263
}
250
264
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
+
251
283
/// Processes program tokens, classifying strings of text by highlighting
252
284
/// category (`Class`).
253
285
struct Classifier < ' a > {
@@ -259,13 +291,20 @@ struct Classifier<'a> {
259
291
byte_pos : u32 ,
260
292
file_span : Span ,
261
293
src : & ' a str ,
294
+ decorations : Option < Decorations > ,
262
295
}
263
296
264
297
impl < ' a > Classifier < ' a > {
265
298
/// Takes as argument the source code to HTML-ify, the rust edition to use and the source code
266
299
/// 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 < ' _ > {
268
306
let tokens = PeekIter :: new ( TokenIter { src } ) ;
307
+ let decorations = decoration_info. map ( Decorations :: new) ;
269
308
Classifier {
270
309
tokens,
271
310
in_attribute : false ,
@@ -275,6 +314,7 @@ impl<'a> Classifier<'a> {
275
314
byte_pos : 0 ,
276
315
file_span,
277
316
src,
317
+ decorations,
278
318
}
279
319
}
280
320
@@ -356,6 +396,19 @@ impl<'a> Classifier<'a> {
356
396
/// token is used.
357
397
fn highlight ( mut self , sink : & mut dyn FnMut ( Highlight < ' a > ) ) {
358
398
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
+
359
412
if self
360
413
. tokens
361
414
. peek ( )
@@ -657,7 +710,7 @@ fn string<T: Display>(
657
710
// https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338
658
711
match href {
659
712
LinkFromSrc :: Local ( span) => context
660
- . href_from_span ( * span)
713
+ . href_from_span ( * span, true )
661
714
. map ( |s| format ! ( "{}{}" , context_info. root_path, s) ) ,
662
715
LinkFromSrc :: External ( def_id) => {
663
716
format:: href_with_root_path ( * def_id, context, Some ( context_info. root_path ) )
0 commit comments