Skip to content

Commit 29c359e

Browse files
authored
Merge pull request #1812 from ehuss/example-admonition
Add an example admonition
2 parents 3873926 + 83b0d7f commit 29c359e

File tree

5 files changed

+134
-77
lines changed

5 files changed

+134
-77
lines changed

docs/authoring.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,15 @@ Admonitions use a style similar to GitHub-flavored markdown, where the style nam
180180

181181
> [!NOTE]
182182
> This is a note.
183+
184+
> [!EDITION-2024]
185+
> This is an edition-specific difference.
186+
187+
> [!EXAMPLE]
188+
> This is an example.
183189
```
184190

185-
The color and styling is defined in [`theme/reference.css`](https://github.com/rust-lang/reference/blob/master/theme/reference.css) and the transformation and icons are in [`mdbook-spec/src/lib.rs`](https://github.com/rust-lang/reference/blob/HEAD/mdbook-spec/src/lib.rs).
191+
The color and styling is defined in [`theme/reference.css`](https://github.com/rust-lang/reference/blob/master/theme/reference.css) and the transformation and icons are in [`mdbook-spec/src/admonitions.rs`](https://github.com/rust-lang/reference/blob/HEAD/mdbook-spec/src/admonitions.rs).
186192

187193
## Style
188194

mdbook-spec/src/admonitions.rs

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
//! Support for admonitions using markdown blockquotes.
2+
//!
3+
//! To add support for a new admonition:
4+
//!
5+
//! 1. Modify the [`admonitions`] function below to include an icon.
6+
//! 2. Modify `theme/reference.css` to set the color for the different themes.
7+
//! Look at one of the other admonitions as a guide.
8+
//! 3. Update `src/introduction.md` and describe what this new block is for
9+
//! with an example.
10+
//! 4. Update `docs/authoring.md` to show an example of your new admonition.
11+
12+
use crate::{Diagnostics, warn_or_err};
13+
use mdbook::book::Chapter;
14+
use regex::{Captures, Regex};
15+
use std::sync::LazyLock;
16+
17+
/// The Regex for the syntax for blockquotes that have a specific CSS class,
18+
/// like `> [!WARNING]`.
19+
static ADMONITION_RE: LazyLock<Regex> = LazyLock::new(|| {
20+
Regex::new(r"(?m)^ *> \[!(?<admon>[^]]+)\]\n(?<blockquote>(?: *>.*\n)+)").unwrap()
21+
});
22+
23+
// This icon is from GitHub, MIT License, see https://github.com/primer/octicons
24+
const ICON_NOTE: &str = r#"<path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path>"#;
25+
26+
// This icon is from GitHub, MIT License, see https://github.com/primer/octicons
27+
const ICON_WARNING: &str = r#"<path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>"#;
28+
29+
// This icon is from GitHub, MIT License, see https://github.com/primer/octicons
30+
const ICON_EXAMPLE: &str = r#"<path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"></path>"#;
31+
32+
/// Converts blockquotes with special headers into admonitions.
33+
///
34+
/// The blockquote should look something like:
35+
///
36+
/// ```markdown
37+
/// > [!WARNING]
38+
/// > ...
39+
/// ```
40+
///
41+
/// This will add a `<div class="alert alert-warning">` around the
42+
/// blockquote so that it can be styled differently, and injects an icon.
43+
/// The actual styling needs to be added in the `reference.css` CSS file.
44+
pub fn admonitions(chapter: &Chapter, diag: &mut Diagnostics) -> String {
45+
ADMONITION_RE
46+
.replace_all(&chapter.content, |caps: &Captures<'_>| {
47+
let lower = caps["admon"].to_lowercase();
48+
let term = to_initial_case(&caps["admon"]);
49+
let blockquote = &caps["blockquote"];
50+
let initial_spaces = blockquote.chars().position(|ch| ch != ' ').unwrap_or(0);
51+
let space = &blockquote[..initial_spaces];
52+
53+
let format_div = |class, content| {
54+
format!(
55+
"{space}<div class=\"alert alert-{class}\">\n\
56+
\n\
57+
{space}> <p class=\"alert-title\">\
58+
{content}</p>\n\
59+
{space} >\n\
60+
{blockquote}\n\
61+
\n\
62+
{space}</div>\n",
63+
)
64+
};
65+
66+
if lower.starts_with("edition-") {
67+
let edition = &lower[8..];
68+
return format_div(
69+
"edition",
70+
format!(
71+
"<span class=\"alert-title-edition\">{edition}</span> Edition differences"
72+
),
73+
);
74+
}
75+
76+
let svg = match lower.as_str() {
77+
"note" => ICON_NOTE,
78+
"warning" => ICON_WARNING,
79+
"example" => ICON_EXAMPLE,
80+
_ => {
81+
warn_or_err!(
82+
diag,
83+
"admonition `{lower}` in {:?} is incorrect or not yet supported",
84+
chapter.path.as_ref().unwrap()
85+
);
86+
""
87+
}
88+
};
89+
format_div(
90+
&lower,
91+
format!(
92+
"<svg viewBox=\"0 0 16 16\" width=\"18\" height=\"18\">\
93+
{svg}\
94+
</svg>{term}"
95+
),
96+
)
97+
})
98+
.to_string()
99+
}
100+
101+
fn to_initial_case(s: &str) -> String {
102+
let mut chars = s.chars();
103+
let first = chars.next().expect("not empty").to_uppercase();
104+
let rest = chars.as_str().to_lowercase();
105+
format!("{first}{rest}")
106+
}

mdbook-spec/src/lib.rs

+2-75
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,12 @@ use std::io;
1414
use std::ops::Range;
1515
use std::path::PathBuf;
1616

17+
mod admonitions;
1718
pub mod grammar;
1819
mod rules;
1920
mod std_links;
2021
mod test_links;
2122

22-
/// The Regex for the syntax for blockquotes that have a specific CSS class,
23-
/// like `> [!WARNING]`.
24-
static ADMONITION_RE: Lazy<Regex> = Lazy::new(|| {
25-
Regex::new(r"(?m)^ *> \[!(?<admon>[^]]+)\]\n(?<blockquote>(?: *>.*\n)+)").unwrap()
26-
});
27-
2823
/// A primitive regex to find link reference definitions.
2924
static MD_LINK_REFERENCE_DEFINITION: Lazy<Regex> =
3025
Lazy::new(|| Regex::new(r"(?m)^\[(?<label>[^]]+)]: +(?<dest>.*)").unwrap());
@@ -167,74 +162,6 @@ impl Spec {
167162
chapter.content
168163
)
169164
}
170-
171-
/// Converts blockquotes with special headers into admonitions.
172-
///
173-
/// The blockquote should look something like:
174-
///
175-
/// ```markdown
176-
/// > [!WARNING]
177-
/// > ...
178-
/// ```
179-
///
180-
/// This will add a `<div class="alert alert-warning">` around the
181-
/// blockquote so that it can be styled differently, and injects an icon.
182-
/// The actual styling needs to be added in the `reference.css` CSS file.
183-
fn admonitions(&self, chapter: &Chapter, diag: &mut Diagnostics) -> String {
184-
ADMONITION_RE
185-
.replace_all(&chapter.content, |caps: &Captures<'_>| {
186-
let lower = caps["admon"].to_lowercase();
187-
let term = to_initial_case(&caps["admon"]);
188-
let blockquote = &caps["blockquote"];
189-
let initial_spaces = blockquote.chars().position(|ch| ch != ' ').unwrap_or(0);
190-
let space = &blockquote[..initial_spaces];
191-
if lower.starts_with("edition-") {
192-
let edition = &lower[8..];
193-
return format!("{space}<div class=\"alert alert-edition\">\n\
194-
\n\
195-
{space}> <p class=\"alert-title\">\
196-
<span class=\"alert-title-edition\">{edition}</span> Edition differences</p>\n\
197-
{space} >\n\
198-
{blockquote}\n\
199-
\n\
200-
{space}</div>\n");
201-
}
202-
203-
// These icons are from GitHub, MIT License, see https://github.com/primer/octicons
204-
let svg = match lower.as_str() {
205-
"note" => "<path d=\"M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"></path>",
206-
"warning" => "<path d=\"M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\"></path>",
207-
_ => {
208-
warn_or_err!(
209-
diag,
210-
"admonition `{lower}` in {:?} is incorrect or not yet supported",
211-
chapter.path.as_ref().unwrap()
212-
);
213-
""
214-
}
215-
};
216-
format!(
217-
"{space}<div class=\"alert alert-{lower}\">\n\
218-
\n\
219-
{space}> <p class=\"alert-title\">\
220-
<svg viewBox=\"0 0 16 16\" width=\"18\" height=\"18\">\
221-
{svg}\
222-
</svg>{term}</p>\n\
223-
{space} >\n\
224-
{blockquote}\n\
225-
\n\
226-
{space}</div>\n",
227-
)
228-
})
229-
.to_string()
230-
}
231-
}
232-
233-
fn to_initial_case(s: &str) -> String {
234-
let mut chars = s.chars();
235-
let first = chars.next().expect("not empty").to_uppercase();
236-
let rest = chars.as_str().to_lowercase();
237-
format!("{first}{rest}")
238165
}
239166

240167
/// Determines the git ref used for linking to a particular branch/tag in GitHub.
@@ -288,7 +215,7 @@ impl Preprocessor for Spec {
288215
if ch.is_draft_chapter() {
289216
return;
290217
}
291-
ch.content = self.admonitions(&ch, &mut diag);
218+
ch.content = admonitions::admonitions(&ch, &mut diag);
292219
ch.content = self.rule_link_references(&ch, &rules);
293220
ch.content = self.auto_link_references(&ch, &rules);
294221
ch.content = self.render_rule_definitions(&ch.content, &tests, &git_ref);

src/introduction.md

+8
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ These conventions are documented here.
7878
> [!NOTE]
7979
> This is an example note.
8080
81+
* Example blocks show an example that demonstrates some rule or points out some interesting aspect. Some examples may have hidden lines which can be viewed by clicking the eye icon that appears when hovering or tapping the example.
82+
83+
> [!EXAMPLE]
84+
> This is a code example.
85+
> ```rust
86+
> println!("hello world");
87+
> ```
88+
8189
* Warnings that show unsound behavior in the language or possibly confusing interactions of language features are in a special warning box.
8290
8391
> [!WARNING]

theme/reference.css

+11-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Admonitions are defined with blockquotes:
1616
> [!WARNING]
1717
> This is bad!
1818
19-
See mdbook-spec/src/lib.rs.
19+
See mdbook-spec/src/admonitions.rs.
2020
*/
2121
.alert blockquote {
2222
/* Add some padding to make the vertical bar a little taller than the text.*/
@@ -62,22 +62,26 @@ See mdbook-spec/src/lib.rs.
6262
--alert-note-color: #0969da;
6363
--alert-warning-color: #9a6700;
6464
--alert-edition-color: #1a7f37;
65+
--alert-example-color: #8250df;
6566
}
6667
.ayu .alert {
6768
--alert-note-color: #74b9ff;
6869
--alert-warning-color: #f0b72f;
6970
--alert-edition-color: #2bd853;
71+
--alert-example-color: #d3abff;
7072
}
7173
.rust .alert {
7274
--alert-note-color: #023b95;
7375
--alert-warning-color: #603700;
7476
--alert-edition-color: #008200;
77+
--alert-example-color: #8250df;
7578
}
7679
.coal .alert,
7780
.navy .alert {
7881
--alert-note-color: #4493f8;
7982
--alert-warning-color: #d29922;
8083
--alert-edition-color: #3fb950;
84+
--alert-example-color: #ab7df8;
8185
}
8286
.alert-note blockquote {
8387
border-inline-start-color: var(--alert-note-color);
@@ -88,6 +92,9 @@ See mdbook-spec/src/lib.rs.
8892
.alert-edition blockquote {
8993
border-inline-start-color: var(--alert-edition-color);
9094
}
95+
.alert-example blockquote {
96+
border-inline-start-color: var(--alert-example-color);
97+
}
9198
.alert-note .alert-title {
9299
color: var(--alert-note-color);
93100
}
@@ -106,6 +113,9 @@ See mdbook-spec/src/lib.rs.
106113
font-weight: bold;
107114
color: var(--alert-edition-color);
108115
}
116+
.alert-example .alert-title {
117+
color: var(--alert-example-color);
118+
}
109119

110120
/* <kbd> tags can be used to highlight specific character elements. */
111121
kbd {

0 commit comments

Comments
 (0)