Skip to content

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)