Skip to content

Commit 383dbb9

Browse files
traviscrossehuss
authored andcommitted
Cross link between grammar and railroad diagrams
When reviewing a production in the grammar, one often wants to quickly find the corresponding railroad diagram, and when reviewing a railroad diagram, one often wants to quickly find the corresponding production in the grammar. Let's make this easy by linking each production in the grammar to the corresponding railroad diagram, and from the name of each railroad diagram to the corresponding production in the grammar. When clicking on a production in the grammar, we'll automatically display the railroad diagrams if those are not already displayed.
1 parent 8b913ee commit 383dbb9

File tree

5 files changed

+54
-19
lines changed

5 files changed

+54
-19
lines changed

mdbook-spec/src/grammar.rs

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,18 @@ fn render_names(
308308
};
309309

310310
let markdown_link_map = updated_link_map(render_markdown::markdown_id);
311-
if let Err(e) = grammar.render_markdown(&names, &markdown_link_map, &mut output, for_summary) {
311+
// Modify the link map so that it contains the exact destination needed to
312+
// link to the railroad productions, and to accommodate the summary
313+
// chapter.
314+
let railroad_link_map = updated_link_map(render_railroad::railroad_id);
315+
316+
if let Err(e) = grammar.render_markdown(
317+
&names,
318+
&markdown_link_map,
319+
&railroad_link_map,
320+
&mut output,
321+
for_summary,
322+
) {
312323
warn_or_err!(
313324
diag,
314325
"grammar failed in chapter {:?}: {e}",
@@ -318,21 +329,23 @@ fn render_names(
318329

319330
output.push_str(
320331
"\n\
321-
<button class=\"grammar-toggle\" type=\"button\" \
322-
title=\"Toggle grammar display\" \
323-
onclick=\"toggle_grammar()\">\
332+
<button class=\"grammar-toggle-railroad\" type=\"button\" \
333+
title=\"Toggle railroad display\" \
334+
onclick=\"toggle_railroad()\">\
324335
Show Railroad\
325336
</button>\n\
326337
</div>\n\
327338
<div class=\"grammar-railroad grammar-hidden\">\n\
328339
\n",
329340
);
330341

331-
// Modify the link map so that it contains the exact destination needed to
332-
// link to the railroad productions, and to accommodate the summary
333-
// chapter.
334-
let railroad_link_map = updated_link_map(render_railroad::railroad_id);
335-
if let Err(e) = grammar.render_railroad(&names, &railroad_link_map, &mut output, for_summary) {
342+
if let Err(e) = grammar.render_railroad(
343+
&names,
344+
&railroad_link_map,
345+
&markdown_link_map,
346+
&mut output,
347+
for_summary,
348+
) {
336349
warn_or_err!(
337350
diag,
338351
"grammar failed in chapter {:?}: {e}",

mdbook-spec/src/grammar/render_markdown.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ impl Grammar {
1414
&self,
1515
names: &[&str],
1616
link_map: &HashMap<String, String>,
17+
rr_link_map: &HashMap<String, String>,
1718
output: &mut String,
1819
for_summary: bool,
1920
) -> anyhow::Result<()> {
@@ -23,7 +24,7 @@ impl Grammar {
2324
Some(p) => p,
2425
None => bail!("could not find grammar production named `{name}`"),
2526
};
26-
prod.render_markdown(link_map, output, for_summary);
27+
prod.render_markdown(link_map, rr_link_map, output, for_summary);
2728
if iter.peek().is_some() {
2829
output.push_str("\n");
2930
}
@@ -45,14 +46,23 @@ impl Production {
4546
fn render_markdown(
4647
&self,
4748
link_map: &HashMap<String, String>,
49+
rr_link_map: &HashMap<String, String>,
4850
output: &mut String,
4951
for_summary: bool,
5052
) {
53+
let dest = rr_link_map
54+
.get(&self.name)
55+
.map(|path| path.to_string())
56+
.unwrap_or_else(|| format!("missing"));
5157
write!(
5258
output,
53-
"<span class=\"grammar-text grammar-production\" id=\"{id}\">{name}</span> → ",
59+
"<span class=\"grammar-text grammar-production\" id=\"{id}\" \
60+
onclick=\"show_railroad()\"\
61+
>\
62+
[{name}]({dest})\
63+
</span> → ",
5464
id = markdown_id(&self.name, for_summary),
55-
name = self.name
65+
name = self.name,
5666
)
5767
.unwrap();
5868
self.expression.render_markdown(link_map, output);

mdbook-spec/src/grammar/render_railroad.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ impl Grammar {
1414
&self,
1515
names: &[&str],
1616
link_map: &HashMap<String, String>,
17+
md_link_map: &HashMap<String, String>,
1718
output: &mut String,
1819
for_summary: bool,
1920
) -> anyhow::Result<()> {
@@ -22,7 +23,7 @@ impl Grammar {
2223
Some(p) => p,
2324
None => bail!("could not find grammar production named `{name}`"),
2425
};
25-
prod.render_railroad(link_map, output, for_summary);
26+
prod.render_railroad(link_map, md_link_map, output, for_summary);
2627
}
2728
Ok(())
2829
}
@@ -41,16 +42,17 @@ impl Production {
4142
fn render_railroad(
4243
&self,
4344
link_map: &HashMap<String, String>,
45+
md_link_map: &HashMap<String, String>,
4446
output: &mut String,
4547
for_summary: bool,
4648
) {
47-
let mut dia = self.make_diagram(false, link_map);
49+
let mut dia = self.make_diagram(false, link_map, md_link_map);
4850
// If the diagram is very wide, try stacking it to reduce the width.
4951
// This 900 is somewhat arbitrary based on looking at productions that
5052
// looked too squished. If your diagram is still too squished,
5153
// consider adding more rules to shorten it.
5254
if dia.width() > 900 {
53-
dia = self.make_diagram(true, link_map);
55+
dia = self.make_diagram(true, link_map, md_link_map);
5456
}
5557
writeln!(
5658
output,
@@ -67,12 +69,17 @@ impl Production {
6769
&self,
6870
stack: bool,
6971
link_map: &HashMap<String, String>,
72+
md_link_map: &HashMap<String, String>,
7073
) -> Diagram<Box<dyn Node>> {
7174
let n = self.expression.render_railroad(stack, link_map, false);
75+
let dest = md_link_map
76+
.get(&self.name)
77+
.map(|path| path.to_string())
78+
.unwrap_or_else(|| format!("missing"));
7279
let seq: Sequence<Box<dyn Node>> =
7380
Sequence::new(vec![Box::new(SimpleStart), n.unwrap(), Box::new(SimpleEnd)]);
7481
let vert = VerticalGrid::<Box<dyn Node>>::new(vec![
75-
Box::new(Comment::new(self.name.clone())),
82+
Box::new(Link::new(Comment::new(self.name.clone()), dest)),
7683
Box::new(seq),
7784
]);
7885

theme/reference.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ main > .rule {
626626
}
627627

628628
/* The toggle button. */
629-
.grammar-toggle {
629+
.grammar-toggle-railroad {
630630
width: 120px;
631631
padding: 5px 0px;
632632
border-radius: 5px;

theme/reference.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,17 @@ function spec_toggle_tests(rule_id) {
2323
}
2424
}
2525

26-
function toggle_grammar() {
26+
function toggle_railroad() {
2727
const grammarRailroad = get_railroad();
2828
set_railroad(!grammarRailroad);
2929
update_railroad();
3030
}
3131

32+
function show_railroad() {
33+
set_railroad(true);
34+
update_railroad();
35+
}
36+
3237
function get_railroad() {
3338
let grammarRailroad = null;
3439
try {
@@ -58,7 +63,7 @@ function update_railroad() {
5863
element.classList.add('grammar-hidden');
5964
}
6065
});
61-
const buttons = document.querySelectorAll('.grammar-toggle');
66+
const buttons = document.querySelectorAll('.grammar-toggle-railroad');
6267
buttons.forEach(button => {
6368
if (grammarRailroad) {
6469
button.innerText = "Hide Railroad";

0 commit comments

Comments
 (0)