55//!
66//! Use the `render_with_highlighting` to highlight some rust code.
77
8+ use std:: borrow:: Cow ;
89use std:: collections:: VecDeque ;
910use std:: fmt:: { self , Display , Write } ;
10- use std:: { cmp , iter} ;
11+ use std:: iter;
1112
13+ use itertools:: Either ;
1214use rustc_data_structures:: fx:: FxIndexMap ;
1315use rustc_lexer:: { Cursor , FrontmatterAllowed , LiteralKind , TokenKind } ;
1416use rustc_span:: BytePos ;
@@ -134,24 +136,24 @@ fn can_merge(class1: Option<Class>, class2: Option<Class>, text: &str) -> bool {
134136}
135137
136138#[ derive( Debug ) ]
137- struct Content {
138- text : String ,
139+ struct Content < ' a > {
140+ text : Cow < ' a , str > ,
139141 /// If `Some` and the `span` is different from the parent, then it might generate a link so we
140142 /// need to keep this information.
141143 class : Option < Class > ,
142144 needs_escape : bool ,
143145}
144146
145147#[ derive( Debug ) ]
146- struct Element {
148+ struct Element < ' a > {
147149 /// If `class` is `None`, then it's just plain text with no HTML tag.
148150 class : Option < Class > ,
149151 /// Content for the current element.
150- content : Vec < Content > ,
152+ content : Vec < Content < ' a > > ,
151153}
152154
153- impl Element {
154- fn new ( class : Option < Class > , text : String , needs_escape : bool ) -> Self {
155+ impl < ' a > Element < ' a > {
156+ fn new ( class : Option < Class > , text : Cow < ' a , str > , needs_escape : bool ) -> Self {
155157 Self { class, content : vec ! [ Content { text, class, needs_escape } ] }
156158 }
157159
@@ -168,8 +170,11 @@ impl Element {
168170 let mut prev = parent_class;
169171 let mut closing_tag = None ;
170172 for part in & self . content {
171- let text: & dyn Display =
172- if part. needs_escape { & EscapeBodyText ( & part. text ) } else { & part. text } ;
173+ let text = if part. needs_escape {
174+ Either :: Left ( & EscapeBodyText ( & part. text ) )
175+ } else {
176+ Either :: Right ( & part. text )
177+ } ;
173178 if part. class . is_some ( ) {
174179 // We only try to generate links as the `<span>` should have already be generated
175180 // by the caller of `write_elem_to`.
@@ -199,9 +204,9 @@ impl Element {
199204}
200205
201206#[ derive( Debug ) ]
202- enum ElementOrStack {
203- Element ( Element ) ,
204- Stack ( ElementStack ) ,
207+ enum ElementOrStack < ' a > {
208+ Element ( Element < ' a > ) ,
209+ Stack ( ElementStack < ' a > ) ,
205210}
206211
207212/// This represents the stack of HTML elements. For example a macro expansion
@@ -211,14 +216,14 @@ enum ElementOrStack {
211216/// This allows to easily handle HTML tags instead of having a more complicated
212217/// state machine to keep track of which tags are open.
213218#[ derive( Debug ) ]
214- struct ElementStack {
215- elements : Vec < ElementOrStack > ,
216- parent : Option < Box < ElementStack > > ,
219+ struct ElementStack < ' a > {
220+ elements : Vec < ElementOrStack < ' a > > ,
221+ parent : Option < Box < ElementStack < ' a > > > ,
217222 class : Option < Class > ,
218223 pending_exit : bool ,
219224}
220225
221- impl ElementStack {
226+ impl < ' a > ElementStack < ' a > {
222227 fn new ( ) -> Self {
223228 Self :: new_with_class ( None )
224229 }
@@ -227,7 +232,7 @@ impl ElementStack {
227232 Self { elements : Vec :: new ( ) , parent : None , class, pending_exit : false }
228233 }
229234
230- fn push_element ( & mut self , mut elem : Element ) {
235+ fn push_element ( & mut self , mut elem : Element < ' a > ) {
231236 if self . pending_exit
232237 && !can_merge ( self . class , elem. class , elem. content . first ( ) . map_or ( "" , |c| & c. text ) )
233238 {
@@ -236,15 +241,13 @@ impl ElementStack {
236241 if let Some ( ElementOrStack :: Element ( last) ) = self . elements . last_mut ( )
237242 && last. can_merge ( & elem)
238243 {
239- for part in elem. content . drain ( ..) {
240- last. content . push ( part) ;
241- }
244+ last. content . append ( & mut elem. content ) ;
242245 } else {
243246 self . elements . push ( ElementOrStack :: Element ( elem) ) ;
244247 }
245248 }
246249
247- fn empty_stack_and_set_new_heads ( & mut self , class : Class , element : Element ) {
250+ fn empty_stack_and_set_new_heads ( & mut self , class : Class , element : Element < ' a > ) {
248251 self . elements . clear ( ) ;
249252 if let Some ( parent) = & mut self . parent {
250253 parent. empty_stack_and_set_new_heads ( class, element) ;
@@ -257,14 +260,12 @@ impl ElementStack {
257260
258261 fn enter_stack (
259262 & mut self ,
260- ElementStack { elements, parent, class, pending_exit } : ElementStack ,
263+ ElementStack { elements, parent, class, pending_exit } : ElementStack < ' a > ,
261264 ) {
262265 if self . pending_exit {
263266 if can_merge ( self . class , class, "" ) {
264267 self . pending_exit = false ;
265- for elem in elements {
266- self . elements . push ( elem) ;
267- }
268+ self . elements . extend ( elements) ;
268269 // Compatible stacks, nothing to be done here!
269270 return ;
270271 }
@@ -382,7 +383,7 @@ impl ElementStack {
382383/// the various functions (which became its methods).
383384struct TokenHandler < ' a , ' tcx , F : Write > {
384385 out : & ' a mut F ,
385- element_stack : ElementStack ,
386+ element_stack : ElementStack < ' a > ,
386387 /// We need to keep the `Class` for each element because it could contain a `Span` which is
387388 /// used to generate links.
388389 href_context : Option < HrefContext < ' a , ' tcx > > ,
@@ -397,7 +398,7 @@ impl<F: Write> std::fmt::Debug for TokenHandler<'_, '_, F> {
397398 }
398399}
399400
400- impl < F : Write > TokenHandler < ' _ , ' _ , F > {
401+ impl < ' a , F : Write > TokenHandler < ' a , ' _ , F > {
401402 fn handle_backline ( & mut self ) -> Option < String > {
402403 self . line += 1 ;
403404 if self . line < self . max_lines {
@@ -409,20 +410,21 @@ impl<F: Write> TokenHandler<'_, '_, F> {
409410 fn push_element_without_backline_check (
410411 & mut self ,
411412 class : Option < Class > ,
412- text : String ,
413+ text : Cow < ' a , str > ,
413414 needs_escape : bool ,
414415 ) {
415416 self . element_stack . push_element ( Element :: new ( class, text, needs_escape) )
416417 }
417418
418- fn push_element ( & mut self , class : Option < Class > , mut text : String ) {
419- let needs_escape = if text == "\n "
419+ fn push_element ( & mut self , class : Option < Class > , text : Cow < ' a , str > ) {
420+ let ( needs_escape, text ) = if text == "\n "
420421 && let Some ( backline) = self . handle_backline ( )
421422 {
423+ let mut text = text. into_owned ( ) ;
422424 text. push_str ( & backline) ;
423- false
425+ ( false , Cow :: Owned ( text ) )
424426 } else {
425- true
427+ ( true , text )
426428 } ;
427429
428430 self . push_element_without_backline_check ( class, text, needs_escape) ;
@@ -438,14 +440,14 @@ impl<F: Write> TokenHandler<'_, '_, F> {
438440 Element {
439441 class : None ,
440442 content : vec ! [ Content {
441- text: format!(
443+ text: Cow :: Owned ( format!(
442444 "<input id=expand-{} \
443445 tabindex=0 \
444446 type=checkbox \
445447 aria-label=\" Collapse/expand macro\" \
446448 title=\" Collapse/expand macro\" >",
447449 self . line,
448- ) ,
450+ ) ) ,
449451 class: None ,
450452 needs_escape: false ,
451453 } ] ,
@@ -456,7 +458,7 @@ impl<F: Write> TokenHandler<'_, '_, F> {
456458 fn add_expanded_code ( & mut self , expanded_code : & ExpandedCode ) {
457459 self . element_stack . push_element ( Element :: new (
458460 None ,
459- format ! ( "<span class=expanded>{}</span>" , expanded_code. code) ,
461+ Cow :: Owned ( format ! ( "<span class=expanded>{}</span>" , expanded_code. code) ) ,
460462 false ,
461463 ) ) ;
462464 self . element_stack . enter_stack ( ElementStack :: new_with_class ( Some ( Class :: Original ) ) ) ;
@@ -615,7 +617,7 @@ pub(super) fn write_code(
615617 token_handler. line = line_info. start_line - 1 ;
616618 token_handler. max_lines = line_info. max_lines ;
617619 if let Some ( text) = token_handler. handle_backline ( ) {
618- token_handler. push_element_without_backline_check ( None , text, false ) ;
620+ token_handler. push_element_without_backline_check ( None , Cow :: Owned ( text) , false ) ;
619621 }
620622 }
621623
@@ -635,7 +637,7 @@ pub(super) fn write_code(
635637 )
636638 . highlight ( & mut |span, highlight| match highlight {
637639 Highlight :: Token { text, class } => {
638- token_handler. push_element ( class, text . to_string ( ) ) ;
640+ token_handler. push_element ( class, Cow :: Borrowed ( text ) ) ;
639641
640642 if text == "\n " {
641643 if current_expansion. is_none ( ) {
0 commit comments