Skip to content

Commit 2ee97bd

Browse files
Merge idents if they are part of a path
1 parent 1c158b6 commit 2ee97bd

File tree

1 file changed

+107
-18
lines changed

1 file changed

+107
-18
lines changed

src/librustdoc/html/highlight.rs

+107-18
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,16 @@ impl Iterator for TokenIter<'a> {
136136
}
137137
}
138138

139+
fn get_real_ident_class(text: &str, edition: Edition) -> Class {
140+
match text {
141+
"ref" | "mut" => Class::RefKeyWord,
142+
"self" | "Self" => Class::Self_,
143+
"false" | "true" => Class::Bool,
144+
_ if Symbol::intern(text).is_reserved(|| edition) => Class::KeyWord,
145+
_ => Class::Ident,
146+
}
147+
}
148+
139149
/// Processes program tokens, classifying strings of text by highlighting
140150
/// category (`Class`).
141151
struct Classifier<'a> {
@@ -144,6 +154,8 @@ struct Classifier<'a> {
144154
in_macro: bool,
145155
in_macro_nonterminal: bool,
146156
edition: Edition,
157+
byte_pos: u32,
158+
src: &'a str,
147159
}
148160

149161
impl<'a> Classifier<'a> {
@@ -155,6 +167,68 @@ impl<'a> Classifier<'a> {
155167
in_macro: false,
156168
in_macro_nonterminal: false,
157169
edition,
170+
byte_pos: 0,
171+
src,
172+
}
173+
}
174+
175+
/// Concatenate colons and idents as one when possible.
176+
fn get_full_ident_path(&mut self) -> Vec<(TokenKind, usize, usize)> {
177+
let start = self.byte_pos as usize;
178+
let mut pos = start;
179+
let mut has_ident = false;
180+
let edition = self.edition;
181+
182+
loop {
183+
let mut nb = 0;
184+
while let Some((TokenKind::Colon, _)) = self.tokens.peek() {
185+
self.tokens.next();
186+
nb += 1;
187+
}
188+
// Ident path can start with "::" but if we already have content in the ident path,
189+
// the "::" is mandatory.
190+
if has_ident && nb == 0 {
191+
return vec![(TokenKind::Ident, start, pos)];
192+
} else if nb != 0 && nb != 2 {
193+
if has_ident {
194+
return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)];
195+
} else {
196+
return vec![(TokenKind::Colon, pos, pos + nb)];
197+
}
198+
}
199+
200+
if let Some((Class::Ident, text)) = self.tokens.peek().map(|(token, text)| {
201+
if *token == TokenKind::Ident {
202+
let class = get_real_ident_class(text, edition);
203+
(class, text)
204+
} else {
205+
// Doesn't matter which Class we put in here...
206+
(Class::Comment, text)
207+
}
208+
}) {
209+
// We only "add" the colon if there is an ident behind.
210+
pos += text.len() + nb;
211+
has_ident = true;
212+
self.tokens.next();
213+
} else if nb > 0 && has_ident {
214+
return vec![(TokenKind::Ident, start, pos), (TokenKind::Colon, pos, pos + nb)];
215+
} else if nb > 0 {
216+
return vec![(TokenKind::Colon, pos, pos + nb)];
217+
} else if has_ident {
218+
return vec![(TokenKind::Ident, start, pos)];
219+
} else {
220+
return Vec::new();
221+
}
222+
}
223+
}
224+
225+
/// Wraps the tokens iteration to ensure that the byte_pos is always correct.
226+
fn next(&mut self) -> Option<(TokenKind, &'a str)> {
227+
if let Some((kind, text)) = self.tokens.next() {
228+
self.byte_pos += text.len() as u32;
229+
Some((kind, text))
230+
} else {
231+
None
158232
}
159233
}
160234

@@ -165,8 +239,25 @@ impl<'a> Classifier<'a> {
165239
/// token is used.
166240
fn highlight(mut self, sink: &mut dyn FnMut(Highlight<'a>)) {
167241
with_default_session_globals(|| {
168-
while let Some((token, text)) = self.tokens.next() {
169-
self.advance(token, text, sink);
242+
loop {
243+
if self
244+
.tokens
245+
.peek()
246+
.map(|t| matches!(t.0, TokenKind::Colon | TokenKind::Ident))
247+
.unwrap_or(false)
248+
{
249+
let tokens = self.get_full_ident_path();
250+
for (token, start, end) in tokens {
251+
let text = &self.src[start..end];
252+
self.advance(token, text, sink);
253+
self.byte_pos += text.len() as u32;
254+
}
255+
}
256+
if let Some((token, text)) = self.next() {
257+
self.advance(token, text, sink);
258+
} else {
259+
break;
260+
}
170261
}
171262
})
172263
}
@@ -203,12 +294,12 @@ impl<'a> Classifier<'a> {
203294
},
204295
TokenKind::And => match lookahead {
205296
Some(TokenKind::And) => {
206-
let _and = self.tokens.next();
297+
self.next();
207298
sink(Highlight::Token { text: "&&", class: Some(Class::Op) });
208299
return;
209300
}
210301
Some(TokenKind::Eq) => {
211-
let _eq = self.tokens.next();
302+
self.next();
212303
sink(Highlight::Token { text: "&=", class: Some(Class::Op) });
213304
return;
214305
}
@@ -260,7 +351,7 @@ impl<'a> Classifier<'a> {
260351
match lookahead {
261352
// Case 1: #![inner_attribute]
262353
Some(TokenKind::Bang) => {
263-
let _not = self.tokens.next().unwrap();
354+
self.next();
264355
if let Some(TokenKind::OpenBracket) = self.peek() {
265356
self.in_attribute = true;
266357
sink(Highlight::EnterSpan { class: Class::Attribute });
@@ -304,19 +395,17 @@ impl<'a> Classifier<'a> {
304395
sink(Highlight::Token { text, class: None });
305396
return;
306397
}
307-
TokenKind::Ident => match text {
308-
"ref" | "mut" => Class::RefKeyWord,
309-
"self" | "Self" => Class::Self_,
310-
"false" | "true" => Class::Bool,
311-
"Option" | "Result" => Class::PreludeTy,
312-
"Some" | "None" | "Ok" | "Err" => Class::PreludeVal,
313-
// Keywords are also included in the identifier set.
314-
_ if Symbol::intern(text).is_reserved(|| self.edition) => Class::KeyWord,
315-
_ if self.in_macro_nonterminal => {
316-
self.in_macro_nonterminal = false;
317-
Class::MacroNonTerminal
318-
}
319-
_ => Class::Ident,
398+
TokenKind::Ident => match get_real_ident_class(text, self.edition) {
399+
Class::Ident => match text {
400+
"Option" | "Result" => Class::PreludeTy,
401+
"Some" | "None" | "Ok" | "Err" => Class::PreludeVal,
402+
_ if self.in_macro_nonterminal => {
403+
self.in_macro_nonterminal = false;
404+
Class::MacroNonTerminal
405+
}
406+
_ => Class::Ident,
407+
},
408+
c => c,
320409
},
321410
TokenKind::RawIdent => Class::Ident,
322411
TokenKind::Lifetime { .. } => Class::Lifetime,

0 commit comments

Comments
 (0)