From 6e7ab3e5e44a39a451a42438092c595250baa21c Mon Sep 17 00:00:00 2001
From: Eric Huss <eric@huss.org>
Date: Sun, 30 Mar 2025 06:32:53 -0700
Subject: [PATCH 1/2] Add the ability for rules to be specified in link
 definitions

This adds the ability to link to rules from a link definition. For example:

```markdown
See [this rule].

[this rule]: expr.array
```

This will convert `[this rule]` to point to the link of the file for
that rule.

This is somewhat hacky, as parsing markdown with regex is unwise (and
this is intentionally incomplete), and could maybe be done more
efficiently.
---
 mdbook-spec/src/lib.rs | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/mdbook-spec/src/lib.rs b/mdbook-spec/src/lib.rs
index 9eac0e4ed..7b06704f4 100644
--- a/mdbook-spec/src/lib.rs
+++ b/mdbook-spec/src/lib.rs
@@ -24,6 +24,10 @@ static ADMONITION_RE: Lazy<Regex> = Lazy::new(|| {
     Regex::new(r"(?m)^ *> \[!(?<admon>[^]]+)\]\n(?<blockquote>(?: *>.*\n)+)").unwrap()
 });
 
+/// A primitive regex to find link reference definitions.
+static MD_LINK_REFERENCE_DEFINITION: Lazy<Regex> =
+    Lazy::new(|| Regex::new(r"(?m)^\[(?<label>[^]]+)]: (?<dest>.*)").unwrap());
+
 pub fn handle_preprocessing() -> Result<(), Error> {
     let pre = Spec::new(None)?;
     let (ctx, book) = CmdPreprocessor::parse_input(io::stdin())?;
@@ -114,6 +118,34 @@ impl Spec {
         Ok(Spec { rust_root })
     }
 
+    /// Converts link reference definitions that point to a rule to the correct link.
+    ///
+    /// For example:
+    /// ```markdown
+    /// See [this rule].
+    ///
+    /// [this rule]: expr.array
+    /// ```
+    ///
+    /// This will convert the `[this rule]` definition to point to the actual link.
+    fn rule_link_references(&self, chapter: &Chapter, rules: &Rules) -> String {
+        let current_path = chapter.path.as_ref().unwrap().parent().unwrap();
+        MD_LINK_REFERENCE_DEFINITION
+            .replace_all(&chapter.content, |caps: &Captures<'_>| {
+                let dest = &caps["dest"];
+                if let Some((_source_path, path)) = rules.def_paths.get(dest) {
+                    let label = &caps["label"];
+                    let relative = pathdiff::diff_paths(path, current_path).unwrap();
+                    // Adjust paths for Windows.
+                    let relative = relative.display().to_string().replace('\\', "/");
+                    format!("[{label}]: {relative}#r-{dest}")
+                } else {
+                    caps.get(0).unwrap().as_str().to_string()
+                }
+            })
+            .to_string()
+    }
+
     /// Generates link references to all rules on all pages, so you can easily
     /// refer to rules anywhere in the book.
     fn auto_link_references(&self, chapter: &Chapter, rules: &Rules) -> String {
@@ -255,6 +287,7 @@ impl Preprocessor for Spec {
                 return;
             }
             ch.content = self.admonitions(&ch, &mut diag);
+            ch.content = self.rule_link_references(&ch, &rules);
             ch.content = self.auto_link_references(&ch, &rules);
             ch.content = self.render_rule_definitions(&ch.content, &tests, &git_ref);
             if ch.name == "Test summary" {

From f37173def5b7fe4eef828f0fb7589c80d40e7609 Mon Sep 17 00:00:00 2001
From: Travis Cross <tc@traviscross.com>
Date: Tue, 1 Apr 2025 22:25:40 +0000
Subject: [PATCH 2/2] Add example use of rule in link definition

On the introduction page, where we talk about rule identifiers, let's
add an example use of putting the rule identifier in the link
definition so that we have an easy way of verifying that this
mechanism works as intended.
---
 src/introduction.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/introduction.md b/src/introduction.md
index 88666a236..d80b8e9f5 100644
--- a/src/introduction.md
+++ b/src/introduction.md
@@ -116,7 +116,7 @@ These conventions are documented here.
   See [Notation] for more detail.
 
 r[example.rule.label]
-* Rule identifiers appear before each language rule enclosed in square brackets. These identifiers provide a way to refer to a specific rule in the language. The rule identifier uses periods to separate sections from most general to most specific ([destructors.scope.nesting.function-body] for example). On narrow screens, the rule name will collapse to display `[*]`.
+* Rule identifiers appear before each language rule enclosed in square brackets. These identifiers provide a way to refer to and link to a specific rule in the language ([e.g.][example rule]). The rule identifier uses periods to separate sections from most general to most specific ([destructors.scope.nesting.function-body] for example). On narrow screens, the rule name will collapse to display `[*]`.
 
   The rule name can be clicked to link to that rule.
 
@@ -144,6 +144,7 @@ We also want the reference to be as normative as possible, so if you see anythin
 [_Expression_]: expressions.md
 [cargo book]: ../cargo/index.html
 [cargo reference]: ../cargo/reference/index.html
+[example rule]: example.rule.label
 [expressions chapter]: expressions.html
 [file an issue]: https://github.com/rust-lang/reference/issues
 [lifetime of temporaries]: expressions.html#temporaries