@@ -9,8 +9,8 @@ use crate::clean::PrimitiveType;
99use crate :: html:: escape:: Escape ;
1010use crate :: html:: render:: Context ;
1111
12+ use std:: collections:: VecDeque ;
1213use std:: fmt:: { Display , Write } ;
13- use std:: iter:: Peekable ;
1414
1515use rustc_lexer:: { LiteralKind , TokenKind } ;
1616use rustc_span:: edition:: Edition ;
@@ -201,10 +201,57 @@ fn get_real_ident_class(text: &str, edition: Edition, allow_path_keywords: bool)
201201 } )
202202}
203203
204+ /// This iterator comes from the same idea than "Peekable" except that it allows to "peek" more than
205+ /// just the next item by using `peek_next`. The `peek` method always returns the next item after
206+ /// the current one whereas `peek_next` will return the next item after the last one peeked.
207+ ///
208+ /// You can use both `peek` and `peek_next` at the same time without problem.
209+ struct PeekIter < ' a > {
210+ stored : VecDeque < ( TokenKind , & ' a str ) > ,
211+ /// This position is reinitialized when using `next`. It is used in `peek_next`.
212+ peek_pos : usize ,
213+ iter : TokenIter < ' a > ,
214+ }
215+
216+ impl PeekIter < ' a > {
217+ fn new ( iter : TokenIter < ' a > ) -> Self {
218+ Self { stored : VecDeque :: new ( ) , peek_pos : 0 , iter }
219+ }
220+ /// Returns the next item after the current one. It doesn't interfer with `peek_next` output.
221+ fn peek ( & mut self ) -> Option < & ( TokenKind , & ' a str ) > {
222+ if self . stored . is_empty ( ) {
223+ if let Some ( next) = self . iter . next ( ) {
224+ self . stored . push_back ( next) ;
225+ }
226+ }
227+ self . stored . front ( )
228+ }
229+ /// Returns the next item after the last one peeked. It doesn't interfer with `peek` output.
230+ fn peek_next ( & mut self ) -> Option < & ( TokenKind , & ' a str ) > {
231+ self . peek_pos += 1 ;
232+ if self . peek_pos - 1 < self . stored . len ( ) {
233+ self . stored . get ( self . peek_pos - 1 )
234+ } else if let Some ( next) = self . iter . next ( ) {
235+ self . stored . push_back ( next) ;
236+ self . stored . back ( )
237+ } else {
238+ None
239+ }
240+ }
241+ }
242+
243+ impl Iterator for PeekIter < ' a > {
244+ type Item = ( TokenKind , & ' a str ) ;
245+ fn next ( & mut self ) -> Option < Self :: Item > {
246+ self . peek_pos = 0 ;
247+ if let Some ( first) = self . stored . pop_front ( ) { Some ( first) } else { self . iter . next ( ) }
248+ }
249+ }
250+
204251/// Processes program tokens, classifying strings of text by highlighting
205252/// category (`Class`).
206253struct Classifier < ' a > {
207- tokens : Peekable < TokenIter < ' a > > ,
254+ tokens : PeekIter < ' a > ,
208255 in_attribute : bool ,
209256 in_macro : bool ,
210257 in_macro_nonterminal : bool ,
@@ -218,7 +265,7 @@ impl<'a> Classifier<'a> {
218265 /// Takes as argument the source code to HTML-ify, the rust edition to use and the source code
219266 /// file span which will be used later on by the `span_correspondance_map`.
220267 fn new ( src : & str , edition : Edition , file_span : Span ) -> Classifier < ' _ > {
221- let tokens = TokenIter { src } . peekable ( ) ;
268+ let tokens = PeekIter :: new ( TokenIter { src } ) ;
222269 Classifier {
223270 tokens,
224271 in_attribute : false ,
@@ -369,7 +416,7 @@ impl<'a> Classifier<'a> {
369416 // Assume that '&' or '*' is the reference or dereference operator
370417 // or a reference or pointer type. Unless, of course, it looks like
371418 // a logical and or a multiplication operator: `&&` or `* `.
372- TokenKind :: Star => match lookahead {
419+ TokenKind :: Star => match self . peek ( ) {
373420 Some ( TokenKind :: Whitespace ) => Class :: Op ,
374421 _ => Class :: RefKeyWord ,
375422 } ,
@@ -480,6 +527,9 @@ impl<'a> Classifier<'a> {
480527 None => match text {
481528 "Option" | "Result" => Class :: PreludeTy ,
482529 "Some" | "None" | "Ok" | "Err" => Class :: PreludeVal ,
530+ // "union" is a weak keyword and is only considered as a keyword when declaring
531+ // a union type.
532+ "union" if self . check_if_is_union_keyword ( ) => Class :: KeyWord ,
483533 _ if self . in_macro_nonterminal => {
484534 self . in_macro_nonterminal = false ;
485535 Class :: MacroNonTerminal
@@ -500,7 +550,17 @@ impl<'a> Classifier<'a> {
500550 }
501551
502552 fn peek ( & mut self ) -> Option < TokenKind > {
503- self . tokens . peek ( ) . map ( |( toke_kind, _text) | * toke_kind)
553+ self . tokens . peek ( ) . map ( |( token_kind, _text) | * token_kind)
554+ }
555+
556+ fn check_if_is_union_keyword ( & mut self ) -> bool {
557+ while let Some ( kind) = self . tokens . peek_next ( ) . map ( |( token_kind, _text) | token_kind) {
558+ if * kind == TokenKind :: Whitespace {
559+ continue ;
560+ }
561+ return * kind == TokenKind :: Ident ;
562+ }
563+ false
504564 }
505565}
506566
0 commit comments