Skip to content

Commit 47c5056

Browse files
Fix highlighting for union keyword
1 parent 1f94abc commit 47c5056

File tree

1 file changed

+65
-5
lines changed

1 file changed

+65
-5
lines changed

src/librustdoc/html/highlight.rs

+65-5
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
use crate::html::escape::Escape;
99
use crate::html::render::Context;
1010

11+
use std::collections::VecDeque;
1112
use std::fmt::{Display, Write};
12-
use std::iter::Peekable;
1313

1414
use rustc_lexer::{LiteralKind, TokenKind};
1515
use 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`).
204251
struct 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

Comments
 (0)