Skip to content

Commit 662dd5d

Browse files
committed
Refactor rendering with RenderCtx
We thread certain contextual information through to most of our rendering functions. Currently this information includes whether the rendering is for the summary page and the two link maps. Adding new information to thread through, or changing something that was only using a subset to use more of the context, requires touching a lot of places. Let's improve this by refactoring these details into a `RenderCtx` that we'll thread through everywhere instead. In this way, new details can simply be added to this struct, and callees can use whatever details are needed.
1 parent eefaabb commit 662dd5d

File tree

3 files changed

+71
-110
lines changed

3 files changed

+71
-110
lines changed

mdbook-spec/src/grammar.rs

+15-20
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ enum Characters {
8888
Range(char, char),
8989
}
9090

91+
#[derive(Debug)]
92+
pub struct RenderCtx {
93+
md_link_map: HashMap<String, String>,
94+
rr_link_map: HashMap<String, String>,
95+
for_summary: bool,
96+
}
97+
9198
impl Grammar {
9299
fn visit_nt(&self, callback: &mut dyn FnMut(&str)) {
93100
for p in self.productions.values() {
@@ -301,7 +308,7 @@ fn render_names(
301308
output.push_str("<br>\n");
302309

303310
// Convert the link map to add the id.
304-
let updated_link_map = |get_id: fn(&str, bool) -> String| -> HashMap<String, String> {
311+
let update_link_map = |get_id: fn(&str, bool) -> String| -> HashMap<String, String> {
305312
link_map
306313
.iter()
307314
.map(|(name, path)| {
@@ -316,19 +323,13 @@ fn render_names(
316323
.collect()
317324
};
318325

319-
let markdown_link_map = updated_link_map(render_markdown::markdown_id);
320-
// Modify the link map so that it contains the exact destination needed to
321-
// link to the railroad productions, and to accommodate the summary
322-
// chapter.
323-
let railroad_link_map = updated_link_map(render_railroad::railroad_id);
324-
325-
if let Err(e) = grammar.render_markdown(
326-
&names,
327-
&markdown_link_map,
328-
&railroad_link_map,
329-
&mut output,
326+
let render_ctx = RenderCtx {
327+
md_link_map: update_link_map(render_markdown::markdown_id),
328+
rr_link_map: update_link_map(render_railroad::railroad_id),
330329
for_summary,
331-
) {
330+
};
331+
332+
if let Err(e) = grammar.render_markdown(&render_ctx, &names, &mut output) {
332333
warn_or_err!(
333334
diag,
334335
"grammar failed in chapter {:?}: {e}",
@@ -348,13 +349,7 @@ fn render_names(
348349
\n",
349350
);
350351

351-
if let Err(e) = grammar.render_railroad(
352-
&names,
353-
&railroad_link_map,
354-
&markdown_link_map,
355-
&mut output,
356-
for_summary,
357-
) {
352+
if let Err(e) = grammar.render_railroad(&render_ctx, &names, &mut output) {
358353
warn_or_err!(
359354
diag,
360355
"grammar failed in chapter {:?}: {e}",

mdbook-spec/src/grammar/render_markdown.rs

+26-44
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,26 @@
11
//! Renders the grammar to markdown.
22
3-
use super::{Characters, Expression, ExpressionKind, Production};
3+
use super::{Characters, Expression, ExpressionKind, Production, RenderCtx};
44
use crate::grammar::Grammar;
55
use anyhow::bail;
66
use regex::Regex;
77
use std::borrow::Cow;
8-
use std::collections::HashMap;
98
use std::fmt::Write;
109
use std::sync::LazyLock;
1110

1211
impl Grammar {
1312
pub fn render_markdown(
1413
&self,
14+
cx: &RenderCtx,
1515
names: &[&str],
16-
link_map: &HashMap<String, String>,
17-
rr_link_map: &HashMap<String, String>,
1816
output: &mut String,
19-
for_summary: bool,
2017
) -> anyhow::Result<()> {
2118
let mut iter = names.into_iter().peekable();
2219
while let Some(name) = iter.next() {
2320
let Some(prod) = self.productions.get(*name) else {
2421
bail!("could not find grammar production named `{name}`");
2522
};
26-
prod.render_markdown(link_map, rr_link_map, output, for_summary);
23+
prod.render_markdown(cx, output);
2724
if iter.peek().is_some() {
2825
output.push_str("\n");
2926
}
@@ -42,14 +39,9 @@ pub fn markdown_id(name: &str, for_summary: bool) -> String {
4239
}
4340

4441
impl Production {
45-
fn render_markdown(
46-
&self,
47-
link_map: &HashMap<String, String>,
48-
rr_link_map: &HashMap<String, String>,
49-
output: &mut String,
50-
for_summary: bool,
51-
) {
52-
let dest = rr_link_map
42+
fn render_markdown(&self, cx: &RenderCtx, output: &mut String) {
43+
let dest = cx
44+
.rr_link_map
5345
.get(&self.name)
5446
.map(|path| path.to_string())
5547
.unwrap_or_else(|| format!("missing"));
@@ -60,12 +52,11 @@ impl Production {
6052
>\
6153
[{name}]({dest})\
6254
</span> → ",
63-
id = markdown_id(&self.name, for_summary),
55+
id = markdown_id(&self.name, cx.for_summary),
6456
name = self.name,
6557
)
6658
.unwrap();
67-
self.expression
68-
.render_markdown(link_map, output, for_summary);
59+
self.expression.render_markdown(cx, output);
6960
output.push('\n');
7061
}
7162
}
@@ -92,16 +83,11 @@ impl Expression {
9283
}
9384
}
9485

95-
fn render_markdown(
96-
&self,
97-
link_map: &HashMap<String, String>,
98-
output: &mut String,
99-
for_summary: bool,
100-
) {
86+
fn render_markdown(&self, cx: &RenderCtx, output: &mut String) {
10187
match &self.kind {
10288
ExpressionKind::Grouped(e) => {
10389
output.push_str("( ");
104-
e.render_markdown(link_map, output, for_summary);
90+
e.render_markdown(cx, output);
10591
if !matches!(e.last(), ExpressionKind::Break(_)) {
10692
output.push(' ');
10793
}
@@ -110,7 +96,7 @@ impl Expression {
11096
ExpressionKind::Alt(es) => {
11197
let mut iter = es.iter().peekable();
11298
while let Some(e) = iter.next() {
113-
e.render_markdown(link_map, output, for_summary);
99+
e.render_markdown(cx, output);
114100
if iter.peek().is_some() {
115101
if !matches!(e.last(), ExpressionKind::Break(_)) {
116102
output.push(' ');
@@ -122,34 +108,34 @@ impl Expression {
122108
ExpressionKind::Sequence(es) => {
123109
let mut iter = es.iter().peekable();
124110
while let Some(e) = iter.next() {
125-
e.render_markdown(link_map, output, for_summary);
111+
e.render_markdown(cx, output);
126112
if iter.peek().is_some() && !matches!(e.last(), ExpressionKind::Break(_)) {
127113
output.push(' ');
128114
}
129115
}
130116
}
131117
ExpressionKind::Optional(e) => {
132-
e.render_markdown(link_map, output, for_summary);
118+
e.render_markdown(cx, output);
133119
output.push_str("<sup>?</sup>");
134120
}
135121
ExpressionKind::Repeat(e) => {
136-
e.render_markdown(link_map, output, for_summary);
122+
e.render_markdown(cx, output);
137123
output.push_str("<sup>\\*</sup>");
138124
}
139125
ExpressionKind::RepeatNonGreedy(e) => {
140-
e.render_markdown(link_map, output, for_summary);
126+
e.render_markdown(cx, output);
141127
output.push_str("<sup>\\* (non-greedy)</sup>");
142128
}
143129
ExpressionKind::RepeatPlus(e) => {
144-
e.render_markdown(link_map, output, for_summary);
130+
e.render_markdown(cx, output);
145131
output.push_str("<sup>+</sup>");
146132
}
147133
ExpressionKind::RepeatPlusNonGreedy(e) => {
148-
e.render_markdown(link_map, output, for_summary);
134+
e.render_markdown(cx, output);
149135
output.push_str("<sup>+ (non-greedy)</sup>");
150136
}
151137
ExpressionKind::RepeatRange(e, a, b) => {
152-
e.render_markdown(link_map, output, for_summary);
138+
e.render_markdown(cx, output);
153139
write!(
154140
output,
155141
"<sup>{}..{}</sup>",
@@ -159,7 +145,7 @@ impl Expression {
159145
.unwrap();
160146
}
161147
ExpressionKind::Nt(nt) => {
162-
let dest = link_map.get(nt).map_or("missing", |d| d.as_str());
148+
let dest = cx.md_link_map.get(nt).map_or("missing", |d| d.as_str());
163149
write!(output, "<span class=\"grammar-text\">[{nt}]({dest})</span>").unwrap();
164150
}
165151
ExpressionKind::Terminal(t) => {
@@ -177,10 +163,10 @@ impl Expression {
177163
output.push_str("\\\n");
178164
output.push_str(&"&nbsp;".repeat(*indent));
179165
}
180-
ExpressionKind::Charset(set) => charset_render_markdown(set, link_map, output),
166+
ExpressionKind::Charset(set) => charset_render_markdown(cx, set, output),
181167
ExpressionKind::NegExpression(e) => {
182168
output.push('~');
183-
e.render_markdown(link_map, output, for_summary);
169+
e.render_markdown(cx, output);
184170
}
185171
ExpressionKind::Unicode(s) => {
186172
output.push_str("U+");
@@ -190,7 +176,7 @@ impl Expression {
190176
if let Some(suffix) = &self.suffix {
191177
write!(output, "<sub class=\"grammar-text\">{suffix}</sub>").unwrap();
192178
}
193-
if !for_summary {
179+
if !cx.for_summary {
194180
if let Some(footnote) = &self.footnote {
195181
// The `ZeroWidthSpace` is to avoid conflicts with markdown link
196182
// references.
@@ -200,15 +186,11 @@ impl Expression {
200186
}
201187
}
202188

203-
fn charset_render_markdown(
204-
set: &[Characters],
205-
link_map: &HashMap<String, String>,
206-
output: &mut String,
207-
) {
189+
fn charset_render_markdown(cx: &RenderCtx, set: &[Characters], output: &mut String) {
208190
output.push_str("\\[");
209191
let mut iter = set.iter().peekable();
210192
while let Some(chars) = iter.next() {
211-
chars.render_markdown(link_map, output);
193+
chars.render_markdown(cx, output);
212194
if iter.peek().is_some() {
213195
output.push(' ');
214196
}
@@ -217,10 +199,10 @@ fn charset_render_markdown(
217199
}
218200

219201
impl Characters {
220-
fn render_markdown(&self, link_map: &HashMap<String, String>, output: &mut String) {
202+
fn render_markdown(&self, cx: &RenderCtx, output: &mut String) {
221203
match self {
222204
Characters::Named(s) => {
223-
let dest = link_map.get(s).map_or("missing", |d| d.as_str());
205+
let dest = cx.md_link_map.get(s).map_or("missing", |d| d.as_str());
224206
write!(output, "[{s}]({dest})").unwrap();
225207
}
226208
Characters::Terminal(s) => write!(

0 commit comments

Comments
 (0)