Skip to content

Commit f7e2b38

Browse files
committed
Resolve grammar rules in link reference definitions
Currently, in our Markdown, we support `[text][RULE_NAME]` and `[text][grammar-RULE_NAME]` for linking to grammar rules, but we don't support this syntax within link reference definitions, i.e., `[text]: grammar-RULE_NAME`, even though we do support linking to (non-grammar) rule identifiers within link reference definitions. That's an inconsistency that continually surprises us. Let's fix that. In this commit, we add `grammar_link_references`, which scans link reference definitions for destinations that match a grammar rule name -- either with a `grammar-` prefix or not. When a match is found, the destination is replaced with the resolved path and anchor, just as `rule_link_references` does for rules. Unrecognized destinations pass through unchanged, falling through to `std_links` for rustdoc resolution -- the same behavior as unresolved `[text][NAME]` reference links. We also update the dev-guide to document the new feature in both `links.md` and `grammar.md`.
1 parent caa4205 commit f7e2b38

4 files changed

Lines changed: 60 additions & 0 deletions

File tree

dev-guide/src/grammar.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,5 +154,19 @@ The [`mdbook-spec`] plugin automatically adds Markdown link definitions for all
154154

155155
In some cases, there might be name collisions with the automatic linking of rule names. In that case, disambiguate with the `grammar-` prefix, such as `[Type][grammar-Type]`. The prefix can also be used when explicitness would aid clarity.
156156

157+
Production names can also be used in link reference definitions to provide custom link text, both with and without the `grammar-` prefix.
158+
159+
```markdown
160+
We accept any [type].
161+
162+
[type]: grammar-Type
163+
```
164+
165+
```markdown
166+
We accept any [type].
167+
168+
[type]: Type
169+
```
170+
157171
[`mdbook-spec`]: tooling/mdbook-spec.md
158172
[Notation]: https://doc.rust-lang.org/nightly/reference/notation.html

dev-guide/src/links.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ Link definitions are automatically generated for all grammar production names. S
7474
This attribute uses the [MetaWord] syntax.
7575

7676
Explicit grammar links can have the `grammar-` prefix like [Type][grammar-Type].
77+
78+
Grammar links can also appear in link reference definitions, e.g. [type].
79+
80+
[type]: grammar-Type
7781
```
7882

7983
## Outside book links

tools/mdbook-spec/src/grammar.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,47 @@ pub fn insert_grammar(grammar: &Grammar, chapter: &Chapter, diag: &mut Diagnosti
7575
content
7676
}
7777

78+
/// Converts link reference definitions that point to a grammar rule
79+
/// to the correct link.
80+
///
81+
/// For example:
82+
///
83+
/// ```markdown
84+
/// We accept any [token].
85+
///
86+
/// [token]: grammar-Token
87+
/// ```
88+
///
89+
/// This will convert the `[token]` definition to point
90+
/// to the actual link.
91+
///
92+
/// This supports both a `grammar-` prefixed form (e.g.
93+
/// `grammar-Token`) and a bare rule name (e.g. `Token`).
94+
pub fn grammar_link_references(chapter: &Chapter, grammar: &Grammar) -> String {
95+
let current_path = chapter.path.as_ref().unwrap().parent().unwrap();
96+
let for_summary = is_summary(chapter);
97+
crate::MD_LINK_REFERENCE_DEFINITION
98+
.replace_all(&chapter.content, |caps: &Captures<'_>| {
99+
let dest = &caps["dest"];
100+
let name = dest.strip_prefix("grammar-").unwrap_or(dest);
101+
if let Some(production) = grammar.productions.get(name) {
102+
let label = &caps["label"];
103+
let relative = pathdiff::diff_paths(&production.path, current_path).unwrap();
104+
// Adjust paths for Windows.
105+
let relative = relative.display().to_string().replace('\\', "/");
106+
let id = render_markdown::markdown_id(name, for_summary);
107+
if for_summary {
108+
format!("[{label}]: #{id}")
109+
} else {
110+
format!("[{label}]: {relative}#{id}")
111+
}
112+
} else {
113+
caps.get(0).unwrap().as_str().to_string()
114+
}
115+
})
116+
.to_string()
117+
}
118+
78119
/// Creates a map of production name -> relative link path.
79120
fn make_relative_link_map(grammar: &Grammar, chapter: &Chapter) -> HashMap<String, String> {
80121
let current_path = chapter.path.as_ref().unwrap().parent().unwrap();

tools/mdbook-spec/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ impl Preprocessor for Spec {
168168
}
169169
ch.content = admonitions::admonitions(&ch, &mut diag);
170170
ch.content = self.rule_link_references(&ch, &rules);
171+
ch.content = grammar::grammar_link_references(&ch, &grammar);
171172
ch.content = self.auto_link_references(&ch, &rules);
172173
ch.content = self.render_rule_definitions(&ch.content, &tests, &git_ref);
173174
if ch.name == "Test summary" {

0 commit comments

Comments
 (0)