Skip to content

Commit b28eabc

Browse files
Improve performance
1 parent c6e26f9 commit b28eabc

File tree

1 file changed

+39
-37
lines changed

1 file changed

+39
-37
lines changed

src/librustdoc/html/highlight.rs

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
//!
66
//! Use the `render_with_highlighting` to highlight some rust code.
77
8+
use std::borrow::Cow;
89
use std::collections::VecDeque;
910
use std::fmt::{self, Display, Write};
10-
use std::{cmp, iter};
11+
use std::iter;
1112

13+
use itertools::Either;
1214
use rustc_data_structures::fx::FxIndexMap;
1315
use rustc_lexer::{Cursor, FrontmatterAllowed, LiteralKind, TokenKind};
1416
use 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).
383384
struct 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

Comments
 (0)