Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 55bb517

Browse files
committed
Move highlighting logic from JS to Rust
Continue migrating JS functionality Cleanup Fix compile error Clean up the diff Set toggle font to sans-serif
1 parent eea8f0a commit 55bb517

File tree

10 files changed

+189
-187
lines changed

10 files changed

+189
-187
lines changed

src/librustdoc/doctest.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ crate struct TestOptions {
4545
crate attrs: Vec<String>,
4646
}
4747

48-
crate fn make_rustc_config(options: &Options) -> interface::Config {
48+
crate fn run(options: Options) -> Result<(), ErrorReported> {
4949
let input = config::Input::File(options.input.clone());
5050

5151
let invalid_codeblock_attributes_name = crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name;
@@ -87,7 +87,7 @@ crate fn make_rustc_config(options: &Options) -> interface::Config {
8787
let mut cfgs = options.cfgs.clone();
8888
cfgs.push("doc".to_owned());
8989
cfgs.push("doctest".to_owned());
90-
interface::Config {
90+
let config = interface::Config {
9191
opts: sessopts,
9292
crate_cfg: interface::parse_cfgspecs(cfgs),
9393
input,
@@ -103,11 +103,7 @@ crate fn make_rustc_config(options: &Options) -> interface::Config {
103103
override_queries: None,
104104
make_codegen_backend: None,
105105
registry: rustc_driver::diagnostics_registry(),
106-
}
107-
}
108-
109-
crate fn run(options: Options) -> Result<(), ErrorReported> {
110-
let config = make_rustc_config(&options);
106+
};
111107

112108
let test_args = options.test_args.clone();
113109
let display_doctest_warnings = options.display_doctest_warnings;

src/librustdoc/html/highlight.rs

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::html::render::Context;
1212
use std::collections::VecDeque;
1313
use std::fmt::{Display, Write};
1414

15+
use rustc_data_structures::fx::FxHashMap;
1516
use rustc_lexer::{LiteralKind, TokenKind};
1617
use rustc_span::edition::Edition;
1718
use rustc_span::symbol::Symbol;
@@ -30,6 +31,8 @@ crate struct ContextInfo<'a, 'b, 'c> {
3031
crate root_path: &'c str,
3132
}
3233

34+
crate type DecorationInfo = FxHashMap<&'static str, Vec<(u32, u32)>>;
35+
3336
/// Highlights `src`, returning the HTML output.
3437
crate fn render_with_highlighting(
3538
src: &str,
@@ -40,6 +43,7 @@ crate fn render_with_highlighting(
4043
edition: Edition,
4144
extra_content: Option<Buffer>,
4245
context_info: Option<ContextInfo<'_, '_, '_>>,
46+
decoration_info: Option<DecorationInfo>,
4347
) {
4448
debug!("highlighting: ================\n{}\n==============", src);
4549
if let Some((edition_info, class)) = tooltip {
@@ -56,7 +60,7 @@ crate fn render_with_highlighting(
5660
}
5761

5862
write_header(out, class, extra_content);
59-
write_code(out, &src, edition, context_info);
63+
write_code(out, &src, edition, context_info, decoration_info);
6064
write_footer(out, playground_button);
6165
}
6266

@@ -89,17 +93,23 @@ fn write_code(
8993
src: &str,
9094
edition: Edition,
9195
context_info: Option<ContextInfo<'_, '_, '_>>,
96+
decoration_info: Option<DecorationInfo>,
9297
) {
9398
// This replace allows to fix how the code source with DOS backline characters is displayed.
9499
let src = src.replace("\r\n", "\n");
95-
Classifier::new(&src, edition, context_info.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP))
96-
.highlight(&mut |highlight| {
97-
match highlight {
98-
Highlight::Token { text, class } => string(out, Escape(text), class, &context_info),
99-
Highlight::EnterSpan { class } => enter_span(out, class),
100-
Highlight::ExitSpan => exit_span(out),
101-
};
102-
});
100+
Classifier::new(
101+
&src,
102+
edition,
103+
context_info.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP),
104+
decoration_info,
105+
)
106+
.highlight(&mut |highlight| {
107+
match highlight {
108+
Highlight::Token { text, class } => string(out, Escape(text), class, &context_info),
109+
Highlight::EnterSpan { class } => enter_span(out, class),
110+
Highlight::ExitSpan => exit_span(out),
111+
};
112+
});
103113
}
104114

105115
fn write_footer(out: &mut Buffer, playground_button: Option<&str>) {
@@ -127,6 +137,7 @@ enum Class {
127137
PreludeTy,
128138
PreludeVal,
129139
QuestionMark,
140+
Decoration(&'static str),
130141
}
131142

132143
impl Class {
@@ -150,6 +161,7 @@ impl Class {
150161
Class::PreludeTy => "prelude-ty",
151162
Class::PreludeVal => "prelude-val",
152163
Class::QuestionMark => "question-mark",
164+
Class::Decoration(kind) => kind,
153165
}
154166
}
155167

@@ -244,7 +256,28 @@ impl Iterator for PeekIter<'a> {
244256
type Item = (TokenKind, &'a str);
245257
fn next(&mut self) -> Option<Self::Item> {
246258
self.peek_pos = 0;
247-
if let Some(first) = self.stored.pop_front() { Some(first) } else { self.iter.next() }
259+
if let Some(first) = self.stored.pop_front() {
260+
Some(first)
261+
} else {
262+
self.iter.next()
263+
}
264+
}
265+
}
266+
267+
/// Custom spans inserted into the source. Eg --scrape-examples uses this to highlight function calls
268+
struct Decorations {
269+
starts: Vec<(u32, &'static str)>,
270+
ends: Vec<u32>,
271+
}
272+
273+
impl Decorations {
274+
fn new(info: DecorationInfo) -> Self {
275+
let (starts, ends) = info
276+
.into_iter()
277+
.map(|(kind, ranges)| ranges.into_iter().map(move |(lo, hi)| ((lo, kind), hi)))
278+
.flatten()
279+
.unzip();
280+
Decorations { starts, ends }
248281
}
249282
}
250283

@@ -259,12 +292,18 @@ struct Classifier<'a> {
259292
byte_pos: u32,
260293
file_span: Span,
261294
src: &'a str,
295+
decorations: Option<Decorations>,
262296
}
263297

264298
impl<'a> Classifier<'a> {
265299
/// Takes as argument the source code to HTML-ify, the rust edition to use and the source code
266300
/// file span which will be used later on by the `span_correspondance_map`.
267-
fn new(src: &str, edition: Edition, file_span: Span) -> Classifier<'_> {
301+
fn new(
302+
src: &str,
303+
edition: Edition,
304+
file_span: Span,
305+
decoration_info: Option<DecorationInfo>,
306+
) -> Classifier<'_> {
268307
let tokens = PeekIter::new(TokenIter { src });
269308
Classifier {
270309
tokens,
@@ -275,6 +314,7 @@ impl<'a> Classifier<'a> {
275314
byte_pos: 0,
276315
file_span,
277316
src,
317+
decorations,
278318
}
279319
}
280320

@@ -356,6 +396,19 @@ impl<'a> Classifier<'a> {
356396
/// token is used.
357397
fn highlight(mut self, sink: &mut dyn FnMut(Highlight<'a>)) {
358398
loop {
399+
if let Some(decs) = self.decorations.as_mut() {
400+
let byte_pos = self.byte_pos;
401+
let n_starts = decs.starts.iter().filter(|(i, _)| byte_pos >= *i).count();
402+
for (_, kind) in decs.starts.drain(0..n_starts) {
403+
sink(Highlight::EnterSpan { class: Class::Decoration(kind) });
404+
}
405+
406+
let n_ends = decs.ends.iter().filter(|i| byte_pos >= **i).count();
407+
for _ in decs.ends.drain(0..n_ends) {
408+
sink(Highlight::ExitSpan);
409+
}
410+
}
411+
359412
if self
360413
.tokens
361414
.peek()

src/librustdoc/html/highlight/tests.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ fn test_html_highlighting() {
2222
let src = include_str!("fixtures/sample.rs");
2323
let html = {
2424
let mut out = Buffer::new();
25-
write_code(&mut out, src, Edition::Edition2018, None);
25+
write_code(&mut out, src, Edition::Edition2018, None, None);
2626
format!("{}<pre><code>{}</code></pre>\n", STYLE, out.into_inner())
2727
};
2828
expect_file!["fixtures/sample.html"].assert_eq(&html);
@@ -36,7 +36,7 @@ fn test_dos_backline() {
3636
println!(\"foo\");\r\n\
3737
}\r\n";
3838
let mut html = Buffer::new();
39-
write_code(&mut html, src, Edition::Edition2018, None);
39+
write_code(&mut html, src, Edition::Edition2018, None, None);
4040
expect_file!["fixtures/dos_line.html"].assert_eq(&html.into_inner());
4141
});
4242
}
@@ -50,7 +50,7 @@ let x = super::b::foo;
5050
let y = Self::whatever;";
5151

5252
let mut html = Buffer::new();
53-
write_code(&mut html, src, Edition::Edition2018, None);
53+
write_code(&mut html, src, Edition::Edition2018, None, None);
5454
expect_file!["fixtures/highlight.html"].assert_eq(&html.into_inner());
5555
});
5656
}

src/librustdoc/html/markdown.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
360360
edition,
361361
None,
362362
None,
363+
None,
363364
);
364365
Some(Event::Html(s.into_inner().into()))
365366
}

src/librustdoc/html/render/mod.rs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ use std::string::ToString;
4646

4747
use rustc_ast_pretty::pprust;
4848
use rustc_attr::{ConstStability, Deprecation, StabilityLevel};
49-
use rustc_data_structures::fx::FxHashSet;
49+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
5050
use rustc_hir as hir;
5151
use rustc_hir::def::CtorKind;
5252
use rustc_hir::def_id::DefId;
@@ -2496,23 +2496,28 @@ fn render_call_locations(
24962496

24972497
// To reduce file sizes, we only want to embed the source code needed to understand the example, not
24982498
// the entire file. So we find the smallest byte range that covers all items enclosing examples.
2499+
assert!(call_data.locations.len() > 0);
24992500
let min_loc =
2500-
call_data.locations.iter().min_by_key(|loc| loc.enclosing_item_span.0).unwrap();
2501-
let min_byte = min_loc.enclosing_item_span.0;
2502-
let min_line = min_loc.enclosing_item_lines.0;
2501+
call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
2502+
let min_byte = min_loc.enclosing_item.byte_span.0;
2503+
let min_line = min_loc.enclosing_item.line_span.0;
25032504
let max_byte =
2504-
call_data.locations.iter().map(|loc| loc.enclosing_item_span.1).max().unwrap();
2505+
call_data.locations.iter().map(|loc| loc.enclosing_item.byte_span.1).max().unwrap();
25052506

25062507
// The output code is limited to that byte range.
2507-
let contents_subset = &contents[min_byte..max_byte];
2508+
let contents_subset = &contents[(min_byte as usize)..(max_byte as usize)];
25082509

25092510
// The call locations need to be updated to reflect that the size of the program has changed.
25102511
// Specifically, the ranges are all subtracted by `min_byte` since that's the new zero point.
2511-
let locations = call_data
2512+
let (byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
25122513
.locations
25132514
.iter()
2514-
.map(|loc| (loc.call_span.0 - min_byte, loc.call_span.1 - min_byte))
2515-
.collect::<Vec<_>>();
2515+
.map(|loc| {
2516+
let (byte_lo, byte_hi) = loc.call_expr.byte_span;
2517+
let (line_lo, line_hi) = loc.call_expr.line_span;
2518+
((byte_lo - min_byte, byte_hi - min_byte), (line_lo - min_line, line_hi - min_line))
2519+
})
2520+
.unzip();
25162521

25172522
let edition = cx.shared.edition();
25182523
write!(
@@ -2524,15 +2529,26 @@ fn render_call_locations(
25242529
// The code and locations are encoded as data attributes, so they can be read
25252530
// later by the JS for interactions.
25262531
code = contents_subset.replace("\"", "&quot;"),
2527-
locations = serde_json::to_string(&locations).unwrap(),
2532+
locations = serde_json::to_string(&line_ranges).unwrap(),
25282533
);
25292534
write!(w, r#"<span class="prev">&pr;</span> <span class="next">&sc;</span>"#);
25302535
write!(w, r#"<span class="expand">&varr;</span>"#);
25312536

25322537
// FIXME(wcrichto): where should file_span and root_path come from?
25332538
let file_span = rustc_span::DUMMY_SP;
25342539
let root_path = "".to_string();
2535-
sources::print_src(w, contents_subset, edition, file_span, cx, &root_path, Some(min_line));
2540+
let mut decoration_info = FxHashMap::default();
2541+
decoration_info.insert("highlight", byte_ranges);
2542+
sources::print_src(
2543+
w,
2544+
contents_subset,
2545+
edition,
2546+
file_span,
2547+
cx,
2548+
&root_path,
2549+
Some(min_line),
2550+
Some(decoration_info),
2551+
);
25362552
write!(w, "</div></div>");
25372553
};
25382554

@@ -2542,7 +2558,8 @@ fn render_call_locations(
25422558
// understand at a glance.
25432559
let ordered_locations = {
25442560
let sort_criterion = |(_, call_data): &(_, &CallData)| {
2545-
let (lo, hi) = call_data.locations[0].enclosing_item_span;
2561+
// Use the first location because that's what the user will see initially
2562+
let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
25462563
hi - lo
25472564
};
25482565

src/librustdoc/html/render/print_item.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,7 @@ fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Mac
11171117
it.span(cx.tcx()).inner().edition(),
11181118
None,
11191119
None,
1120+
None,
11201121
);
11211122
});
11221123
document(w, cx, it, None, HeadingOffset::H2)

src/librustdoc/html/sources.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ impl SourceCollector<'_, 'tcx> {
212212
&self.cx,
213213
&root_path,
214214
None,
215+
None,
215216
)
216217
},
217218
&self.cx.shared.style_files,
@@ -259,6 +260,7 @@ crate fn print_src(
259260
context: &Context<'_>,
260261
root_path: &str,
261262
offset: Option<usize>,
263+
decoration_info: Option<highlight::DecorationInfo>,
262264
) {
263265
let lines = s.lines().count();
264266
let mut line_numbers = Buffer::empty_from(buf);
@@ -283,5 +285,6 @@ crate fn print_src(
283285
edition,
284286
Some(line_numbers),
285287
Some(highlight::ContextInfo { context, file_span, root_path }),
288+
decoration_info,
286289
);
287290
}

0 commit comments

Comments
 (0)