@@ -12,15 +12,17 @@ use std::io;
1212use std:: io:: prelude:: * ;
1313
1414use rustc_ast:: token:: { self , Token } ;
15+ use rustc_data_structures:: sync:: Lrc ;
1516use rustc_parse:: lexer;
1617use rustc_session:: parse:: ParseSess ;
18+ use rustc_span:: hygiene:: SyntaxContext ;
1719use rustc_span:: source_map:: SourceMap ;
1820use rustc_span:: symbol:: { kw, sym} ;
19- use rustc_span:: { FileName , Span } ;
21+ use rustc_span:: { BytePos , FileName , SourceFile , Span } ;
2022
2123/// Highlights `src`, returning the HTML output.
2224pub fn render_with_highlighting (
23- src : & str ,
25+ src : String ,
2426 class : Option < & str > ,
2527 playground_button : Option < & str > ,
2628 tooltip : Option < ( & str , & str ) > ,
@@ -38,12 +40,13 @@ pub fn render_with_highlighting(
3840 }
3941
4042 let sess = ParseSess :: with_silent_emitter ( ) ;
41- let sf = sess
43+ let source_file = sess
4244 . source_map ( )
43- . new_source_file ( FileName :: Custom ( String :: from ( "rustdoc-highlighting" ) ) , src. to_owned ( ) ) ;
45+ . new_source_file ( FileName :: Custom ( String :: from ( "rustdoc-highlighting" ) ) , src) ;
46+
47+ let classifier_source_file = Lrc :: clone ( & source_file) ;
4448 let highlight_result = rustc_driver:: catch_fatal_errors ( || {
45- let lexer = lexer:: StringReader :: new ( & sess, sf, None ) ;
46- let mut classifier = Classifier :: new ( lexer, sess. source_map ( ) ) ;
49+ let mut classifier = Classifier :: new ( & sess, classifier_source_file) ;
4750
4851 let mut highlighted_source = vec ! [ ] ;
4952 if classifier. write_source ( & mut highlighted_source) . is_err ( ) {
@@ -61,9 +64,17 @@ pub fn render_with_highlighting(
6164 write_footer ( & mut out, playground_button) . unwrap ( ) ;
6265 }
6366 Err ( ( ) ) => {
67+ // Get the source back out of the source map to avoid a copy in the happy path.
68+ let span =
69+ Span :: new ( BytePos ( 0 ) , BytePos ( source_file. byte_length ( ) ) , SyntaxContext :: root ( ) ) ;
70+ let src = sess
71+ . source_map ( )
72+ . span_to_snippet ( span)
73+ . expect ( "could not retrieve snippet from artificial source file" ) ;
74+
6475 // If errors are encountered while trying to highlight, just emit
6576 // the unhighlighted source.
66- write ! ( out, "<pre><code>{}</code></pre>" , Escape ( src) ) . unwrap ( ) ;
77+ write ! ( out, "<pre><code>{}</code></pre>" , Escape ( & src) ) . unwrap ( ) ;
6778 }
6879 }
6980
@@ -73,10 +84,10 @@ pub fn render_with_highlighting(
7384/// Processes a program (nested in the internal `lexer`), classifying strings of
7485/// text by highlighting category (`Class`). Calls out to a `Writer` to write
7586/// each span of text in sequence.
76- struct Classifier < ' a > {
77- lexer : lexer:: StringReader < ' a > ,
87+ struct Classifier < ' sess > {
88+ lexer : lexer:: StringReader < ' sess > ,
7889 peek_token : Option < Token > ,
79- source_map : & ' a SourceMap ,
90+ source_map : & ' sess SourceMap ,
8091
8192 // State of the classifier.
8293 in_attribute : bool ,
@@ -154,6 +165,7 @@ impl<U: Write> Writer for U {
154165 }
155166}
156167
168+ #[ derive( Debug ) ]
157169enum HighlightError {
158170 LexError ,
159171 IoError ( io:: Error ) ,
@@ -165,12 +177,14 @@ impl From<io::Error> for HighlightError {
165177 }
166178}
167179
168- impl < ' a > Classifier < ' a > {
169- fn new ( lexer : lexer:: StringReader < ' a > , source_map : & ' a SourceMap ) -> Classifier < ' a > {
180+ impl < ' sess > Classifier < ' sess > {
181+ fn new ( sess : & ParseSess , source_file : Lrc < SourceFile > ) -> Classifier < ' _ > {
182+ let lexer = lexer:: StringReader :: new ( sess, source_file, None ) ;
183+
170184 Classifier {
171185 lexer,
172186 peek_token : None ,
173- source_map,
187+ source_map : sess . source_map ( ) ,
174188 in_attribute : false ,
175189 in_macro : false ,
176190 in_macro_nonterminal : false ,
@@ -209,11 +223,17 @@ impl<'a> Classifier<'a> {
209223 /// source.
210224 fn write_source < W : Writer > ( & mut self , out : & mut W ) -> Result < ( ) , HighlightError > {
211225 loop {
212- let next = self . try_next_token ( ) ?;
226+ let mut next = self . try_next_token ( ) ?;
213227 if next == token:: Eof {
214228 break ;
215229 }
216230
231+ // Glue any tokens that need to be glued.
232+ if let Some ( joint) = next. glue ( self . peek ( ) ?) {
233+ next = joint;
234+ let _ = self . try_next_token ( ) ?;
235+ }
236+
217237 self . write_token ( out, next) ?;
218238 }
219239
@@ -429,3 +449,6 @@ fn write_header(class: Option<&str>, out: &mut dyn Write) -> io::Result<()> {
429449fn write_footer ( out : & mut dyn Write , playground_button : Option < & str > ) -> io:: Result < ( ) > {
430450 write ! ( out, "</pre>{}</div>\n " , if let Some ( button) = playground_button { button } else { "" } )
431451}
452+
453+ #[ cfg( test) ]
454+ mod tests;
0 commit comments