88use crate :: html:: escape:: Escape ;
99use crate :: html:: render:: Context ;
1010
11+ use std:: collections:: VecDeque ;
1112use std:: fmt:: { Display , Write } ;
12- use std:: iter:: Peekable ;
1313
1414use rustc_lexer:: { LiteralKind , TokenKind } ;
1515use rustc_span:: edition:: Edition ;
@@ -199,10 +199,57 @@ fn get_real_ident_class(text: &str, edition: Edition, allow_path_keywords: bool)
199199 } )
200200}
201201
202+ /// This iterator comes from the same idea than "Peekable" except that it allows to "peek" more than
203+ /// just the next item by using `peek_next`. The `peek` method always returns the next item after
204+ /// the current one whereas `peek_next` will return the next item after the last one peeked.
205+ ///
206+ /// You can use both `peek` and `peek_next` at the same time without problem.
207+ struct PeekIter < ' a > {
208+ stored : VecDeque < ( TokenKind , & ' a str ) > ,
209+ /// This position is reinitialized when using `next`. It is used in `peek_next`.
210+ peek_pos : usize ,
211+ iter : TokenIter < ' a > ,
212+ }
213+
214+ impl PeekIter < ' a > {
215+ fn new ( iter : TokenIter < ' a > ) -> Self {
216+ Self { stored : VecDeque :: new ( ) , peek_pos : 0 , iter }
217+ }
218+ /// Returns the next item after the current one. It doesn't interfer with `peek_next` output.
219+ fn peek ( & mut self ) -> Option < & ( TokenKind , & ' a str ) > {
220+ if self . stored . is_empty ( ) {
221+ if let Some ( next) = self . iter . next ( ) {
222+ self . stored . push_back ( next) ;
223+ }
224+ }
225+ self . stored . front ( )
226+ }
227+ /// Returns the next item after the last one peeked. It doesn't interfer with `peek` output.
228+ fn peek_next ( & mut self ) -> Option < & ( TokenKind , & ' a str ) > {
229+ self . peek_pos += 1 ;
230+ if self . peek_pos - 1 < self . stored . len ( ) {
231+ self . stored . get ( self . peek_pos - 1 )
232+ } else if let Some ( next) = self . iter . next ( ) {
233+ self . stored . push_back ( next) ;
234+ self . stored . back ( )
235+ } else {
236+ None
237+ }
238+ }
239+ }
240+
241+ impl Iterator for PeekIter < ' a > {
242+ type Item = ( TokenKind , & ' a str ) ;
243+ fn next ( & mut self ) -> Option < Self :: Item > {
244+ self . peek_pos = 0 ;
245+ if let Some ( first) = self . stored . pop_front ( ) { Some ( first) } else { self . iter . next ( ) }
246+ }
247+ }
248+
202249/// Processes program tokens, classifying strings of text by highlighting
203250/// category (`Class`).
204251struct Classifier < ' a > {
205- tokens : Peekable < TokenIter < ' a > > ,
252+ tokens : PeekIter < ' a > ,
206253 in_attribute : bool ,
207254 in_macro : bool ,
208255 in_macro_nonterminal : bool ,
@@ -216,7 +263,7 @@ impl<'a> Classifier<'a> {
216263 /// Takes as argument the source code to HTML-ify, the rust edition to use and the source code
217264 /// file span which will be used later on by the `span_correspondance_map`.
218265 fn new ( src : & str , edition : Edition , file_span : Span ) -> Classifier < ' _ > {
219- let tokens = TokenIter { src } . peekable ( ) ;
266+ let tokens = PeekIter :: new ( TokenIter { src } ) ;
220267 Classifier {
221268 tokens,
222269 in_attribute : false ,
@@ -367,7 +414,7 @@ impl<'a> Classifier<'a> {
367414 // Assume that '&' or '*' is the reference or dereference operator
368415 // or a reference or pointer type. Unless, of course, it looks like
369416 // a logical and or a multiplication operator: `&&` or `* `.
370- TokenKind :: Star => match lookahead {
417+ TokenKind :: Star => match self . peek ( ) {
371418 Some ( TokenKind :: Whitespace ) => Class :: Op ,
372419 _ => Class :: RefKeyWord ,
373420 } ,
@@ -478,6 +525,9 @@ impl<'a> Classifier<'a> {
478525 None => match text {
479526 "Option" | "Result" => Class :: PreludeTy ,
480527 "Some" | "None" | "Ok" | "Err" => Class :: PreludeVal ,
528+ // "union" is a weak keyword and is only considered as a keyword when declaring
529+ // a union type.
530+ "union" if self . check_if_is_union_keyword ( ) => Class :: KeyWord ,
481531 _ if self . in_macro_nonterminal => {
482532 self . in_macro_nonterminal = false ;
483533 Class :: MacroNonTerminal
@@ -498,7 +548,17 @@ impl<'a> Classifier<'a> {
498548 }
499549
500550 fn peek ( & mut self ) -> Option < TokenKind > {
501- self . tokens . peek ( ) . map ( |( toke_kind, _text) | * toke_kind)
551+ self . tokens . peek ( ) . map ( |( token_kind, _text) | * token_kind)
552+ }
553+
554+ fn check_if_is_union_keyword ( & mut self ) -> bool {
555+ while let Some ( kind) = self . tokens . peek_next ( ) . map ( |( token_kind, _text) | token_kind) {
556+ if * kind == TokenKind :: Whitespace {
557+ continue ;
558+ }
559+ return * kind == TokenKind :: Ident ;
560+ }
561+ false
502562 }
503563}
504564
0 commit comments