5
5
//!
6
6
//! Use the `render_with_highlighting` to highlight some rust code.
7
7
8
+ use crate :: clean;
8
9
use crate :: html:: escape:: Escape ;
10
+ use crate :: html:: render:: Context ;
9
11
10
- use std:: fmt:: Display ;
12
+ use std:: fmt:: { Display , Write } ;
11
13
use std:: iter:: Peekable ;
12
14
13
15
use rustc_lexer:: { LiteralKind , TokenKind } ;
14
16
use rustc_span:: edition:: Edition ;
15
17
use rustc_span:: symbol:: Symbol ;
16
18
17
- use super :: format:: Buffer ;
19
+ use super :: format:: { self , Buffer } ;
20
+ use super :: render:: LinkFromSrc ;
18
21
19
22
/// Highlights `src`, returning the HTML output.
20
23
crate fn render_with_highlighting (
@@ -25,6 +28,9 @@ crate fn render_with_highlighting(
25
28
tooltip : Option < ( Option < Edition > , & str ) > ,
26
29
edition : Edition ,
27
30
extra_content : Option < Buffer > ,
31
+ file_span_lo : u32 ,
32
+ context : Option < & Context < ' _ > > ,
33
+ root_path : & str ,
28
34
) {
29
35
debug ! ( "highlighting: ================\n {}\n ==============" , src) ;
30
36
if let Some ( ( edition_info, class) ) = tooltip {
@@ -41,7 +47,7 @@ crate fn render_with_highlighting(
41
47
}
42
48
43
49
write_header ( out, class, extra_content) ;
44
- write_code ( out, & src, edition) ;
50
+ write_code ( out, & src, edition, file_span_lo , context , root_path ) ;
45
51
write_footer ( out, playground_button) ;
46
52
}
47
53
@@ -57,12 +63,21 @@ fn write_header(out: &mut Buffer, class: Option<&str>, extra_content: Option<Buf
57
63
}
58
64
}
59
65
60
- fn write_code ( out : & mut Buffer , src : & str , edition : Edition ) {
66
+ fn write_code (
67
+ out : & mut Buffer ,
68
+ src : & str ,
69
+ edition : Edition ,
70
+ file_span_lo : u32 ,
71
+ context : Option < & Context < ' _ > > ,
72
+ root_path : & str ,
73
+ ) {
61
74
// This replace allows to fix how the code source with DOS backline characters is displayed.
62
75
let src = src. replace ( "\r \n " , "\n " ) ;
63
- Classifier :: new ( & src, edition) . highlight ( & mut |highlight| {
76
+ Classifier :: new ( & src, edition, file_span_lo ) . highlight ( & mut |highlight| {
64
77
match highlight {
65
- Highlight :: Token { text, class } => string ( out, Escape ( text) , class) ,
78
+ Highlight :: Token { text, class } => {
79
+ string ( out, Escape ( text) , class, context, root_path)
80
+ }
66
81
Highlight :: EnterSpan { class } => enter_span ( out, class) ,
67
82
Highlight :: ExitSpan => exit_span ( out) ,
68
83
} ;
@@ -82,14 +97,14 @@ enum Class {
82
97
KeyWord ,
83
98
// Keywords that do pointer/reference stuff.
84
99
RefKeyWord ,
85
- Self_ ,
100
+ Self_ ( ( u32 , u32 ) ) ,
86
101
Op ,
87
102
Macro ,
88
103
MacroNonTerminal ,
89
104
String ,
90
105
Number ,
91
106
Bool ,
92
- Ident ,
107
+ Ident ( ( u32 , u32 ) ) ,
93
108
Lifetime ,
94
109
PreludeTy ,
95
110
PreludeVal ,
@@ -105,20 +120,27 @@ impl Class {
105
120
Class :: Attribute => "attribute" ,
106
121
Class :: KeyWord => "kw" ,
107
122
Class :: RefKeyWord => "kw-2" ,
108
- Class :: Self_ => "self" ,
123
+ Class :: Self_ ( _ ) => "self" ,
109
124
Class :: Op => "op" ,
110
125
Class :: Macro => "macro" ,
111
126
Class :: MacroNonTerminal => "macro-nonterminal" ,
112
127
Class :: String => "string" ,
113
128
Class :: Number => "number" ,
114
129
Class :: Bool => "bool-val" ,
115
- Class :: Ident => "ident" ,
130
+ Class :: Ident ( _ ) => "ident" ,
116
131
Class :: Lifetime => "lifetime" ,
117
132
Class :: PreludeTy => "prelude-ty" ,
118
133
Class :: PreludeVal => "prelude-val" ,
119
134
Class :: QuestionMark => "question-mark" ,
120
135
}
121
136
}
137
+
138
+ fn get_span ( self ) -> Option < ( u32 , u32 ) > {
139
+ match self {
140
+ Self :: Ident ( sp) | Self :: Self_ ( sp) => Some ( sp) ,
141
+ _ => None ,
142
+ }
143
+ }
122
144
}
123
145
124
146
enum Highlight < ' a > {
@@ -144,14 +166,23 @@ impl Iterator for TokenIter<'a> {
144
166
}
145
167
}
146
168
147
- fn get_real_ident_class ( text : & str , edition : Edition ) -> Class {
148
- match text {
169
+ /// Returns `None` if this is a `Class::Ident`.
170
+ fn get_real_ident_class ( text : & str , edition : Edition , allow_path_keywords : bool ) -> Option < Class > {
171
+ let ignore: & [ & str ] =
172
+ if allow_path_keywords { & [ "self" , "Self" , "super" , "crate" ] } else { & [ "self" , "Self" ] } ;
173
+ if ignore. iter ( ) . any ( |k| * k == text) {
174
+ return None ;
175
+ }
176
+ Some ( match text {
149
177
"ref" | "mut" => Class :: RefKeyWord ,
150
- "self" | "Self" => Class :: Self_ ,
151
178
"false" | "true" => Class :: Bool ,
152
179
_ if Symbol :: intern ( text) . is_reserved ( || edition) => Class :: KeyWord ,
153
- _ => Class :: Ident ,
154
- }
180
+ _ => return None ,
181
+ } )
182
+ }
183
+
184
+ fn move_span ( file_span_lo : u32 , start : u32 , end : u32 ) -> ( u32 , u32 ) {
185
+ ( start + file_span_lo, end + file_span_lo)
155
186
}
156
187
157
188
/// Processes program tokens, classifying strings of text by highlighting
@@ -163,11 +194,12 @@ struct Classifier<'a> {
163
194
in_macro_nonterminal : bool ,
164
195
edition : Edition ,
165
196
byte_pos : u32 ,
197
+ file_span_lo : u32 ,
166
198
src : & ' a str ,
167
199
}
168
200
169
201
impl < ' a > Classifier < ' a > {
170
- fn new ( src : & str , edition : Edition ) -> Classifier < ' _ > {
202
+ fn new ( src : & str , edition : Edition , file_span_lo : u32 ) -> Classifier < ' _ > {
171
203
let tokens = TokenIter { src } . peekable ( ) ;
172
204
Classifier {
173
205
tokens,
@@ -176,6 +208,7 @@ impl<'a> Classifier<'a> {
176
208
in_macro_nonterminal : false ,
177
209
edition,
178
210
byte_pos : 0 ,
211
+ file_span_lo,
179
212
src,
180
213
}
181
214
}
@@ -201,17 +234,17 @@ impl<'a> Classifier<'a> {
201
234
if has_ident {
202
235
return vec ! [ ( TokenKind :: Ident , start, pos) , ( TokenKind :: Colon , pos, pos + nb) ] ;
203
236
} else {
204
- return vec ! [ ( TokenKind :: Colon , pos , pos + nb) ] ;
237
+ return vec ! [ ( TokenKind :: Colon , start , pos + nb) ] ;
205
238
}
206
239
}
207
240
208
- if let Some ( ( Class :: Ident , text) ) = self . tokens . peek ( ) . map ( |( token, text) | {
241
+ if let Some ( ( None , text) ) = self . tokens . peek ( ) . map ( |( token, text) | {
209
242
if * token == TokenKind :: Ident {
210
- let class = get_real_ident_class ( text, edition) ;
243
+ let class = get_real_ident_class ( text, edition, true ) ;
211
244
( class, text)
212
245
} else {
213
246
// Doesn't matter which Class we put in here...
214
- ( Class :: Comment , text)
247
+ ( Some ( Class :: Comment ) , text)
215
248
}
216
249
} ) {
217
250
// We only "add" the colon if there is an ident behind.
@@ -221,7 +254,7 @@ impl<'a> Classifier<'a> {
221
254
} else if nb > 0 && has_ident {
222
255
return vec ! [ ( TokenKind :: Ident , start, pos) , ( TokenKind :: Colon , pos, pos + nb) ] ;
223
256
} else if nb > 0 {
224
- return vec ! [ ( TokenKind :: Colon , pos , pos + nb) ] ;
257
+ return vec ! [ ( TokenKind :: Colon , start , start + nb) ] ;
225
258
} else if has_ident {
226
259
return vec ! [ ( TokenKind :: Ident , start, pos) ] ;
227
260
} else {
@@ -231,10 +264,11 @@ impl<'a> Classifier<'a> {
231
264
}
232
265
233
266
/// Wraps the tokens iteration to ensure that the byte_pos is always correct.
234
- fn next ( & mut self ) -> Option < ( TokenKind , & ' a str ) > {
267
+ fn next ( & mut self ) -> Option < ( TokenKind , & ' a str , u32 ) > {
235
268
if let Some ( ( kind, text) ) = self . tokens . next ( ) {
269
+ let before = self . byte_pos ;
236
270
self . byte_pos += text. len ( ) as u32 ;
237
- Some ( ( kind, text) )
271
+ Some ( ( kind, text, before ) )
238
272
} else {
239
273
None
240
274
}
@@ -254,14 +288,18 @@ impl<'a> Classifier<'a> {
254
288
. unwrap_or ( false )
255
289
{
256
290
let tokens = self . get_full_ident_path ( ) ;
291
+ let skip = !tokens. is_empty ( ) ;
257
292
for ( token, start, end) in tokens {
258
293
let text = & self . src [ start..end] ;
259
- self . advance ( token, text, sink) ;
294
+ self . advance ( token, text, sink, start as u32 ) ;
260
295
self . byte_pos += text. len ( ) as u32 ;
261
296
}
297
+ if skip {
298
+ continue ;
299
+ }
262
300
}
263
- if let Some ( ( token, text) ) = self . next ( ) {
264
- self . advance ( token, text, sink) ;
301
+ if let Some ( ( token, text, before ) ) = self . next ( ) {
302
+ self . advance ( token, text, sink, before ) ;
265
303
} else {
266
304
break ;
267
305
}
@@ -270,7 +308,13 @@ impl<'a> Classifier<'a> {
270
308
271
309
/// Single step of highlighting. This will classify `token`, but maybe also
272
310
/// a couple of following ones as well.
273
- fn advance ( & mut self , token : TokenKind , text : & ' a str , sink : & mut dyn FnMut ( Highlight < ' a > ) ) {
311
+ fn advance (
312
+ & mut self ,
313
+ token : TokenKind ,
314
+ text : & ' a str ,
315
+ sink : & mut dyn FnMut ( Highlight < ' a > ) ,
316
+ before : u32 ,
317
+ ) {
274
318
let lookahead = self . peek ( ) ;
275
319
let no_highlight = |sink : & mut dyn FnMut ( _) | sink ( Highlight :: Token { text, class : None } ) ;
276
320
let class = match token {
@@ -401,19 +445,30 @@ impl<'a> Classifier<'a> {
401
445
sink ( Highlight :: Token { text, class : None } ) ;
402
446
return ;
403
447
}
404
- TokenKind :: Ident => match get_real_ident_class ( text, self . edition ) {
405
- Class :: Ident => match text {
448
+ TokenKind :: Ident => match get_real_ident_class ( text, self . edition , false ) {
449
+ None => match text {
406
450
"Option" | "Result" => Class :: PreludeTy ,
407
451
"Some" | "None" | "Ok" | "Err" => Class :: PreludeVal ,
408
452
_ if self . in_macro_nonterminal => {
409
453
self . in_macro_nonterminal = false ;
410
454
Class :: MacroNonTerminal
411
455
}
412
- _ => Class :: Ident ,
456
+ "self" | "Self" => Class :: Self_ ( move_span (
457
+ self . file_span_lo ,
458
+ before,
459
+ before + text. len ( ) as u32 ,
460
+ ) ) ,
461
+ _ => Class :: Ident ( move_span (
462
+ self . file_span_lo ,
463
+ before,
464
+ before + text. len ( ) as u32 ,
465
+ ) ) ,
413
466
} ,
414
- c => c,
467
+ Some ( c ) => c,
415
468
} ,
416
- TokenKind :: RawIdent | TokenKind :: UnknownPrefix => Class :: Ident ,
469
+ TokenKind :: RawIdent | TokenKind :: UnknownPrefix => {
470
+ Class :: Ident ( move_span ( self . file_span_lo , before, before + text. len ( ) as u32 ) )
471
+ }
417
472
TokenKind :: Lifetime { .. } => Class :: Lifetime ,
418
473
} ;
419
474
// Anything that didn't return above is the simple case where we the
@@ -448,11 +503,74 @@ fn exit_span(out: &mut Buffer) {
448
503
/// ```
449
504
/// The latter can be thought of as a shorthand for the former, which is more
450
505
/// flexible.
451
- fn string < T : Display > ( out : & mut Buffer , text : T , klass : Option < Class > ) {
506
+ fn string < T : Display > (
507
+ out : & mut Buffer ,
508
+ text : T ,
509
+ klass : Option < Class > ,
510
+ context : Option < & Context < ' _ > > ,
511
+ root_path : & str ,
512
+ ) {
452
513
match klass {
453
514
None => write ! ( out, "{}" , text) ,
454
- Some ( klass) => write ! ( out, "<span class=\" {}\" >{}</span>" , klass. as_html( ) , text) ,
515
+ Some ( klass) => {
516
+ if let Some ( def_span) = klass. get_span ( ) {
517
+ let mut text = text. to_string ( ) ;
518
+ if text. contains ( "::" ) {
519
+ text =
520
+ text. split ( "::" ) . enumerate ( ) . fold ( String :: new ( ) , |mut path, ( pos, t) | {
521
+ let pre = if pos != 0 { "::" } else { "" } ;
522
+ match t {
523
+ "self" | "Self" => write ! (
524
+ & mut path,
525
+ "{}<span class=\" {}\" >{}</span>" ,
526
+ pre,
527
+ Class :: Self_ ( ( 0 , 0 ) ) . as_html( ) ,
528
+ t
529
+ ) ,
530
+ "crate" | "super" => write ! (
531
+ & mut path,
532
+ "{}<span class=\" {}\" >{}</span>" ,
533
+ pre,
534
+ Class :: KeyWord . as_html( ) ,
535
+ t
536
+ ) ,
537
+ t => write ! ( & mut path, "{}{}" , pre, t) ,
538
+ }
539
+ . expect ( "Failed to build source HTML path" ) ;
540
+ path
541
+ } ) ;
542
+ }
543
+ if let Some ( context) = context {
544
+ if let Some ( href) =
545
+ context. shared . span_correspondance_map . get ( & def_span) . and_then ( |href| {
546
+ match href {
547
+ LinkFromSrc :: Local ( span) => {
548
+ eprintln ! ( "==> {:?}:{:?}" , span. lo( ) , span. hi( ) ) ;
549
+ context
550
+ . href_from_span ( clean:: Span :: wrap ( * span) )
551
+ . map ( |s| format ! ( "{}{}" , root_path, s) )
552
+ } ,
553
+ LinkFromSrc :: External ( def_id) => {
554
+ format:: href ( * def_id, context) . map ( |( url, _, _) | url)
555
+ }
556
+ }
557
+ } )
558
+ {
559
+ write ! (
560
+ out,
561
+ "<a class=\" {}\" href=\" {}\" >{}</a>" ,
562
+ klass. as_html( ) ,
563
+ href,
564
+ text
565
+ ) ;
566
+ return ;
567
+ }
568
+ }
569
+ }
570
+ write ! ( out, "<span class=\" {}\" >{}</span>" , klass. as_html( ) , text) ;
571
+ }
455
572
}
573
+ write ! ( out, "<span class=\" {}\" >{}</span>" , klass. as_html( ) , text) ;
456
574
}
457
575
458
576
#[ cfg( test) ]
0 commit comments