mirror of
https://github.com/tommilligan/mdbook-admonish.git
synced 2025-12-28 14:58:48 -05:00
Compare commits
10 Commits
rust-versi
...
v1.14.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc219f755d | ||
|
|
a2524f890b | ||
|
|
c3207e4d16 | ||
|
|
ab63c90231 | ||
|
|
d5bdde1f5c | ||
|
|
52ca8fc831 | ||
|
|
e1ea411e9a | ||
|
|
31d5a27a6d | ||
|
|
0f0e02702c | ||
|
|
7235d5f349 |
28
CHANGELOG.md
28
CHANGELOG.md
@@ -2,6 +2,32 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 1.14.0
|
||||
|
||||
### Changed
|
||||
|
||||
- Styles version updated to `3.0.1`. Run `mdbook-admonish install` to update.
|
||||
|
||||
### Added
|
||||
|
||||
- You can now set custom CSS ids for admonition blocks with the `id` field. Thanks to [@Sky9x](https://github.com/Sky9x) for contributing this feature! ([#144](https://github.com/tommilligan/mdbook-admonish/pull/144))
|
||||
- You can also now customize the CSS id prefix with the config option `default.css_id_prefix`
|
||||
|
||||
### Fixed
|
||||
|
||||
- Improve rendering of blocks in print/PDF view. Thanks to [@csk111165](https://github.com/csk111165) for the report ([#152](https://github.com/tommilligan/mdbook-admonish/issues/152))
|
||||
- Fix the default titles for `tldr` and `faq` directives looking bad. They now render as `TL;DR` and `FAQ` by default. Thanks [@joshka](https://github.com/joshka) for fixing this! ([#154](https://github.com/tommilligan/mdbook-admonish/pull/154))
|
||||
|
||||
## 1.13.1
|
||||
|
||||
### Changed
|
||||
|
||||
- Bumped internal `mdbook` version to `0.4.35` ([#142](https://github.com/tommilligan/mdbook-admonish/pull/142))
|
||||
|
||||
### Fixed
|
||||
|
||||
- Relaxed `clap` dependency to fix compilation error when using other `mdbook-*` plugins. Thanks to [@joshka](https://github.com/joshka) for the [report](https://github.com/tommilligan/mdbook-admonish/pull/141)! ([#142](https://github.com/tommilligan/mdbook-admonish/pull/142))
|
||||
|
||||
## 1.13.0
|
||||
|
||||
### Changed
|
||||
@@ -76,7 +102,7 @@ It unintentionally increased the MSRV from 1.66.0
|
||||
|
||||
### Changed
|
||||
|
||||
- Styles updated to `^2.0.1`. Run `mdbook-admonish install` to update.
|
||||
- Styles version updated to `2.0.1`. Run `mdbook-admonish install` to update.
|
||||
- MSRV (minimum supported rust version) is now 1.64.0 for clap v4 ([#79](https://github.com/tommilligan/mdbook-admonish/pull/79))
|
||||
- More verbose error messages for invalid TOML configurations ([#79](https://github.com/tommilligan/mdbook-admonish/pull/79))
|
||||
|
||||
|
||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -952,7 +952,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mdbook-admonish"
|
||||
version = "1.13.0"
|
||||
version = "1.14.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mdbook-admonish"
|
||||
version = "1.13.0"
|
||||
version = "1.14.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.66.0"
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ title = "The mdbook-admonish book"
|
||||
|
||||
[preprocessor.admonish]
|
||||
command = "mdbook-admonish"
|
||||
assets_version = "3.0.0" # do not edit: managed by `mdbook-admonish install`
|
||||
assets_version = "3.0.1" # do not edit: managed by `mdbook-admonish install`
|
||||
|
||||
[preprocessor.toc]
|
||||
command = "mdbook-toc"
|
||||
|
||||
@@ -161,6 +161,23 @@ Will yield something like the following HTML, which you can then apply styles to
|
||||
</div>
|
||||
```
|
||||
|
||||
#### Custom CSS ID
|
||||
|
||||
If you want to customize the CSS `id` field, set `id="custom-id"`.
|
||||
This will ignore [`default.css_id_prefix`](reference.md#default).
|
||||
|
||||
The default id is a normalized version of the admonishment's title,
|
||||
prefixed with the `default.css_id_prefix`,
|
||||
with an appended number if multiple blocks would have the same id.
|
||||
|
||||
Setting the `id` field will *ignore* all other ids and the duplicate counter.
|
||||
|
||||
````
|
||||
```admonish info title="My Info" id="my-special-info"
|
||||
Link to this block with `#my-special-info` instead of the default `#admonition-my-info`.
|
||||
```
|
||||
````
|
||||
|
||||
#### Collapsible
|
||||
|
||||
For a block to be initially collapsible, and then be openable, set `collapsible=true`:
|
||||
@@ -176,3 +193,4 @@ Will yield something like the following HTML, which you can then apply styles to
|
||||
```admonish collapsible=true
|
||||
Content will be hidden initially.
|
||||
```
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ Subfields:
|
||||
|
||||
- `default.title` (optional): Title to use for blocks. Defaults to the directive used in titlecase.
|
||||
- `default.collapsible` (optional, default: `false`): Make blocks collapsible by default when set to `true`.
|
||||
- `default.css_id_prefix` (optional, default: `"admonition-"`): The default css id prefix to add to the id of all blocks. Ignored on blocks with an `id` field.
|
||||
|
||||
### `renderer`
|
||||
|
||||
|
||||
@@ -165,6 +165,9 @@ a.admonition-anchor-link {
|
||||
padding-inline: 4.4rem 1.2rem;
|
||||
font-weight: 700;
|
||||
background-color: color.adjust($clr-blue-a200, $alpha: -0.9);
|
||||
// Always print title bar tint
|
||||
print-color-adjust: exact;
|
||||
-webkit-print-color-adjust: exact;
|
||||
// Compatilility with rendering markdown inside the content
|
||||
display: flex;
|
||||
|
||||
@@ -186,6 +189,9 @@ a.admonition-anchor-link {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
background-color: $clr-blue-a200;
|
||||
// Always print icon
|
||||
print-color-adjust: exact;
|
||||
-webkit-print-color-adjust: exact;
|
||||
mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>');
|
||||
-webkit-mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>');
|
||||
mask-repeat: no-repeat;
|
||||
|
||||
@@ -9,7 +9,7 @@ title = "mdbook-admonish-integration"
|
||||
|
||||
[preprocessor.admonish]
|
||||
command = "mdbook-admonish"
|
||||
assets_version = "3.0.0" # do not edit: managed by `mdbook-admonish install`
|
||||
assets_version = "3.0.1" # do not edit: managed by `mdbook-admonish install`
|
||||
after = ["links"]
|
||||
|
||||
[preprocessor.admonish.renderer.test]
|
||||
|
||||
@@ -9,7 +9,7 @@ title = "mdbook-admonish-integration"
|
||||
|
||||
[preprocessor.admonish]
|
||||
command = "mdbook-admonish"
|
||||
assets_version = "3.0.0" # do not edit: managed by `mdbook-admonish install`
|
||||
assets_version = "3.0.1" # do not edit: managed by `mdbook-admonish install`
|
||||
after = ["links"]
|
||||
|
||||
[preprocessor.admonish.renderer.test]
|
||||
|
||||
@@ -1 +1 @@
|
||||
3.0.0
|
||||
3.0.1
|
||||
|
||||
@@ -71,6 +71,8 @@ a.admonition-anchor-link::before {
|
||||
padding-inline: 4.4rem 1.2rem;
|
||||
font-weight: 700;
|
||||
background-color: rgba(68, 138, 255, 0.1);
|
||||
print-color-adjust: exact;
|
||||
-webkit-print-color-adjust: exact;
|
||||
display: flex;
|
||||
}
|
||||
:is(.admonition-title, summary.admonition-title) p {
|
||||
@@ -86,6 +88,8 @@ html :is(.admonition-title, summary.admonition-title):last-child {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
background-color: #448aff;
|
||||
print-color-adjust: exact;
|
||||
-webkit-print-color-adjust: exact;
|
||||
mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>');
|
||||
-webkit-mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"></svg>');
|
||||
mask-repeat: no-repeat;
|
||||
|
||||
@@ -9,6 +9,7 @@ mod v2;
|
||||
pub(crate) struct InstanceConfig {
|
||||
pub(crate) directive: String,
|
||||
pub(crate) title: Option<String>,
|
||||
pub(crate) id: Option<String>,
|
||||
pub(crate) additional_classnames: Vec<String>,
|
||||
pub(crate) collapsible: Option<bool>,
|
||||
}
|
||||
@@ -69,18 +70,22 @@ mod test {
|
||||
InstanceConfig {
|
||||
directive: "note".to_owned(),
|
||||
title: None,
|
||||
id: None,
|
||||
additional_classnames: vec!["additional-classname".to_owned()],
|
||||
collapsible: None,
|
||||
}
|
||||
);
|
||||
// v2 syntax is supported
|
||||
assert_eq!(
|
||||
InstanceConfig::from_info_string(r#"admonish title="Custom Title" type="question""#)
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
InstanceConfig::from_info_string(
|
||||
r#"admonish title="Custom Title" type="question" id="my-id""#
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
InstanceConfig {
|
||||
directive: "question".to_owned(),
|
||||
title: Some("Custom Title".to_owned()),
|
||||
id: Some("my-id".to_owned()),
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: None,
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ pub(crate) fn from_config_string(config_string: &str) -> Result<InstanceConfig,
|
||||
Ok(InstanceConfig {
|
||||
directive: directive.to_owned(),
|
||||
title,
|
||||
id: None,
|
||||
additional_classnames,
|
||||
collapsible: None,
|
||||
})
|
||||
@@ -69,6 +70,7 @@ mod test {
|
||||
InstanceConfig {
|
||||
directive: "".to_owned(),
|
||||
title: None,
|
||||
id: None,
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: None,
|
||||
}
|
||||
@@ -78,6 +80,7 @@ mod test {
|
||||
InstanceConfig {
|
||||
directive: "".to_owned(),
|
||||
title: None,
|
||||
id: None,
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: None,
|
||||
}
|
||||
@@ -87,6 +90,7 @@ mod test {
|
||||
InstanceConfig {
|
||||
directive: "unknown".to_owned(),
|
||||
title: None,
|
||||
id: None,
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: None,
|
||||
}
|
||||
@@ -96,6 +100,7 @@ mod test {
|
||||
InstanceConfig {
|
||||
directive: "note".to_owned(),
|
||||
title: None,
|
||||
id: None,
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: None,
|
||||
}
|
||||
@@ -105,6 +110,7 @@ mod test {
|
||||
InstanceConfig {
|
||||
directive: "note".to_owned(),
|
||||
title: None,
|
||||
id: None,
|
||||
additional_classnames: vec!["additional-classname".to_owned()],
|
||||
collapsible: None,
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ struct UserInput {
|
||||
#[serde(default)]
|
||||
title: Option<String>,
|
||||
#[serde(default)]
|
||||
id: Option<String>,
|
||||
#[serde(default)]
|
||||
class: Option<String>,
|
||||
#[serde(default)]
|
||||
collapsible: Option<bool>,
|
||||
@@ -88,6 +90,7 @@ pub(crate) fn from_config_string(config_string: &str) -> Result<InstanceConfig,
|
||||
Ok(InstanceConfig {
|
||||
directive: config.r#type.unwrap_or_default(),
|
||||
title: config.title,
|
||||
id: config.id,
|
||||
additional_classnames,
|
||||
collapsible: config.collapsible,
|
||||
})
|
||||
@@ -105,6 +108,7 @@ mod test {
|
||||
InstanceConfig {
|
||||
directive: "".to_owned(),
|
||||
title: None,
|
||||
id: None,
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: None,
|
||||
}
|
||||
@@ -114,6 +118,7 @@ mod test {
|
||||
InstanceConfig {
|
||||
directive: "".to_owned(),
|
||||
title: None,
|
||||
id: None,
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: None,
|
||||
}
|
||||
@@ -126,6 +131,7 @@ mod test {
|
||||
InstanceConfig {
|
||||
directive: "note".to_owned(),
|
||||
title: Some("Никита".to_owned()),
|
||||
id: None,
|
||||
additional_classnames: vec!["additional".to_owned(), "classname".to_owned()],
|
||||
collapsible: Some(true),
|
||||
}
|
||||
@@ -136,6 +142,7 @@ mod test {
|
||||
InstanceConfig {
|
||||
directive: "".to_owned(),
|
||||
title: None,
|
||||
id: None,
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: None,
|
||||
}
|
||||
@@ -146,6 +153,7 @@ mod test {
|
||||
InstanceConfig {
|
||||
directive: "info".to_owned(),
|
||||
title: None,
|
||||
id: None,
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: None,
|
||||
}
|
||||
@@ -156,10 +164,22 @@ mod test {
|
||||
InstanceConfig {
|
||||
directive: "info".to_owned(),
|
||||
title: Some("Information".to_owned()),
|
||||
id: None,
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: Some(false),
|
||||
}
|
||||
);
|
||||
// Test custom id
|
||||
assert_eq!(
|
||||
from_config_string(r#"info title="My Info" id="my-info-custom-id""#).unwrap(),
|
||||
InstanceConfig {
|
||||
directive: "info".to_owned(),
|
||||
title: Some("My Info".to_owned()),
|
||||
id: Some("my-info-custom-id".to_owned()),
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: None,
|
||||
}
|
||||
);
|
||||
// Directive after toml config is an error
|
||||
assert!(from_config_string(r#"title="Information" info"#).is_err());
|
||||
}
|
||||
|
||||
217
src/markdown.rs
217
src/markdown.rs
@@ -47,7 +47,7 @@ pub(crate) fn preprocess(
|
||||
// Once we've identitified admonition blocks, handle them differently
|
||||
// depending on our render mode
|
||||
let new_content = match render_text_mode {
|
||||
RenderTextMode::Html => admonition.html_with_unique_ids(&mut id_counter),
|
||||
RenderTextMode::Html => admonition.html(&mut id_counter),
|
||||
RenderTextMode::Strip => admonition.strip(),
|
||||
};
|
||||
|
||||
@@ -732,6 +732,7 @@ Text
|
||||
OnFailure::Continue,
|
||||
&AdmonitionDefaults {
|
||||
title: Some("Admonish".to_owned()),
|
||||
css_id_prefix: None,
|
||||
collapsible: false,
|
||||
},
|
||||
RenderTextMode::Html,
|
||||
@@ -766,6 +767,7 @@ Text
|
||||
OnFailure::Continue,
|
||||
&AdmonitionDefaults {
|
||||
title: Some("Admonish".to_owned()),
|
||||
css_id_prefix: None,
|
||||
collapsible: false,
|
||||
},
|
||||
RenderTextMode::Html,
|
||||
@@ -798,6 +800,219 @@ Text
|
||||
assert_eq!(expected, prep(content));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn standard_custom_id() {
|
||||
let content = r#"# Chapter
|
||||
```admonish check id="yay-custom-id"
|
||||
A simple admonition.
|
||||
```
|
||||
Text
|
||||
"#;
|
||||
|
||||
let expected = r##"# Chapter
|
||||
|
||||
<div id="yay-custom-id" class="admonition admonish-success">
|
||||
<div class="admonition-title">
|
||||
|
||||
Check
|
||||
|
||||
<a class="admonition-anchor-link" href="#yay-custom-id"></a>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
A simple admonition.
|
||||
|
||||
</div>
|
||||
</div>
|
||||
Text
|
||||
"##;
|
||||
|
||||
assert_eq!(expected, prep(content));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_custom_id_default_prefix() {
|
||||
let content = r#"# Chapter
|
||||
```admonish check
|
||||
A simple admonition.
|
||||
```
|
||||
Text
|
||||
"#;
|
||||
|
||||
let expected = r##"# Chapter
|
||||
|
||||
<div id="admonition-check" class="admonition admonish-success">
|
||||
<div class="admonition-title">
|
||||
|
||||
Check
|
||||
|
||||
<a class="admonition-anchor-link" href="#admonition-check"></a>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
A simple admonition.
|
||||
|
||||
</div>
|
||||
</div>
|
||||
Text
|
||||
"##;
|
||||
|
||||
assert_eq!(expected, prep(content));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_custom_id_default_prefix_custom_title() {
|
||||
let content = r#"# Chapter
|
||||
```admonish check title="Check Mark"
|
||||
A simple admonition.
|
||||
```
|
||||
Text
|
||||
"#;
|
||||
|
||||
let expected = r##"# Chapter
|
||||
|
||||
<div id="admonition-check-mark" class="admonition admonish-success">
|
||||
<div class="admonition-title">
|
||||
|
||||
Check Mark
|
||||
|
||||
<a class="admonition-anchor-link" href="#admonition-check-mark"></a>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
A simple admonition.
|
||||
|
||||
</div>
|
||||
</div>
|
||||
Text
|
||||
"##;
|
||||
|
||||
assert_eq!(expected, prep(content));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_default_id_prefix() {
|
||||
let content = r#"# Chapter
|
||||
```admonish info
|
||||
A simple admonition.
|
||||
```
|
||||
Text
|
||||
"#;
|
||||
|
||||
let expected = r##"# Chapter
|
||||
|
||||
<div id="info" class="admonition admonish-info">
|
||||
<div class="admonition-title">
|
||||
|
||||
Info
|
||||
|
||||
<a class="admonition-anchor-link" href="#info"></a>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
A simple admonition.
|
||||
|
||||
</div>
|
||||
</div>
|
||||
Text
|
||||
"##;
|
||||
|
||||
let preprocess_result = preprocess(
|
||||
content,
|
||||
OnFailure::Continue,
|
||||
&AdmonitionDefaults {
|
||||
title: Some("Info".to_owned()),
|
||||
css_id_prefix: Some("".to_owned()),
|
||||
collapsible: false,
|
||||
},
|
||||
RenderTextMode::Html,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(expected, preprocess_result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_id_prefix_custom_title() {
|
||||
let content = r#"# Chapter
|
||||
```admonish info title="My Title"
|
||||
A simple admonition.
|
||||
```
|
||||
Text
|
||||
"#;
|
||||
|
||||
let expected = r##"# Chapter
|
||||
|
||||
<div id="prefix-my-title" class="admonition admonish-info">
|
||||
<div class="admonition-title">
|
||||
|
||||
My Title
|
||||
|
||||
<a class="admonition-anchor-link" href="#prefix-my-title"></a>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
A simple admonition.
|
||||
|
||||
</div>
|
||||
</div>
|
||||
Text
|
||||
"##;
|
||||
|
||||
let preprocess_result = preprocess(
|
||||
content,
|
||||
OnFailure::Continue,
|
||||
&AdmonitionDefaults {
|
||||
title: Some("Info".to_owned()),
|
||||
css_id_prefix: Some("prefix-".to_owned()),
|
||||
collapsible: false,
|
||||
},
|
||||
RenderTextMode::Html,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(expected, preprocess_result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_id_custom_title() {
|
||||
let content = r#"# Chapter
|
||||
```admonish info title="My Title" id="my-section-id"
|
||||
A simple admonition.
|
||||
```
|
||||
Text
|
||||
"#;
|
||||
|
||||
let expected = r##"# Chapter
|
||||
|
||||
<div id="my-section-id" class="admonition admonish-info">
|
||||
<div class="admonition-title">
|
||||
|
||||
My Title
|
||||
|
||||
<a class="admonition-anchor-link" href="#my-section-id"></a>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
A simple admonition.
|
||||
|
||||
</div>
|
||||
</div>
|
||||
Text
|
||||
"##;
|
||||
|
||||
let preprocess_result = preprocess(
|
||||
content,
|
||||
OnFailure::Continue,
|
||||
&AdmonitionDefaults {
|
||||
title: Some("Info".to_owned()),
|
||||
css_id_prefix: Some("ignored-prefix-".to_owned()),
|
||||
collapsible: false,
|
||||
},
|
||||
RenderTextMode::Html,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(expected, preprocess_result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_embed() {
|
||||
let content = r#"# Chapter
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
book_config::OnFailure,
|
||||
render::Admonition,
|
||||
resolve::AdmonitionMeta,
|
||||
types::{AdmonitionDefaults, Directive},
|
||||
types::{AdmonitionDefaults, CssId, Directive},
|
||||
};
|
||||
|
||||
/// Given the content in the span of the code block, and the info string,
|
||||
@@ -46,6 +46,7 @@ pub(crate) fn parse_admonition<'a>(
|
||||
Ok(Admonition {
|
||||
directive: Directive::Bug,
|
||||
title: "Error rendering admonishment".to_owned(),
|
||||
css_id: CssId::Prefix("admonition-".to_owned()),
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: false,
|
||||
content: Cow::Owned(format!(
|
||||
|
||||
@@ -3,7 +3,10 @@ use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub use crate::preprocessor::Admonish;
|
||||
use crate::{resolve::AdmonitionMeta, types::Directive};
|
||||
use crate::{
|
||||
resolve::AdmonitionMeta,
|
||||
types::{CssId, Directive},
|
||||
};
|
||||
|
||||
impl Directive {
|
||||
fn classname(&self) -> &'static str {
|
||||
@@ -29,6 +32,7 @@ pub(crate) struct Admonition<'a> {
|
||||
pub(crate) directive: Directive,
|
||||
pub(crate) title: String,
|
||||
pub(crate) content: Cow<'a, str>,
|
||||
pub(crate) css_id: CssId,
|
||||
pub(crate) additional_classnames: Vec<String>,
|
||||
pub(crate) collapsible: bool,
|
||||
pub(crate) indent: usize,
|
||||
@@ -39,6 +43,7 @@ impl<'a> Admonition<'a> {
|
||||
let AdmonitionMeta {
|
||||
directive,
|
||||
title,
|
||||
css_id,
|
||||
additional_classnames,
|
||||
collapsible,
|
||||
} = info;
|
||||
@@ -46,25 +51,30 @@ impl<'a> Admonition<'a> {
|
||||
directive,
|
||||
title,
|
||||
content: Cow::Borrowed(content),
|
||||
css_id,
|
||||
additional_classnames,
|
||||
collapsible,
|
||||
indent,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn html_with_unique_ids(&self, id_counter: &mut HashMap<String, usize>) -> String {
|
||||
let anchor_id = unique_id_from_content(
|
||||
if !self.title.is_empty() {
|
||||
&self.title
|
||||
} else {
|
||||
ANCHOR_ID_DEFAULT
|
||||
},
|
||||
id_counter,
|
||||
);
|
||||
self.html(&anchor_id)
|
||||
}
|
||||
pub(crate) fn html(&self, id_counter: &mut HashMap<String, usize>) -> String {
|
||||
let anchor_id = match &self.css_id {
|
||||
CssId::Verbatim(id) => Cow::Borrowed(id),
|
||||
CssId::Prefix(prefix) => {
|
||||
let id = unique_id_from_content(
|
||||
if !self.title.is_empty() {
|
||||
&self.title
|
||||
} else {
|
||||
ANCHOR_ID_DEFAULT
|
||||
},
|
||||
id_counter,
|
||||
);
|
||||
|
||||
Cow::Owned(format!("{}{}", prefix, id))
|
||||
}
|
||||
};
|
||||
|
||||
fn html(&self, anchor_id: &str) -> String {
|
||||
let mut additional_class = Cow::Borrowed(self.directive.classname());
|
||||
let title = &self.title;
|
||||
let content = &self.content;
|
||||
@@ -78,7 +88,7 @@ impl<'a> Admonition<'a> {
|
||||
{indent}
|
||||
{indent}{title}
|
||||
{indent}
|
||||
{indent}<a class="admonition-anchor-link" href="#{ANCHOR_ID_PREFIX}-{anchor_id}"></a>
|
||||
{indent}<a class="admonition-anchor-link" href="#{anchor_id}"></a>
|
||||
{indent}</{title_block}>
|
||||
"##
|
||||
))
|
||||
@@ -103,7 +113,7 @@ impl<'a> Admonition<'a> {
|
||||
// rendered as markdown paragraphs.
|
||||
format!(
|
||||
r#"
|
||||
{indent}<{admonition_block} id="{ANCHOR_ID_PREFIX}-{anchor_id}" class="admonition {additional_class}">
|
||||
{indent}<{admonition_block} id="{anchor_id}" class="admonition {additional_class}">
|
||||
{title_html}{indent}<div>
|
||||
{indent}
|
||||
{indent}{content}
|
||||
@@ -121,5 +131,4 @@ impl<'a> Admonition<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
const ANCHOR_ID_PREFIX: &str = "admonition";
|
||||
const ANCHOR_ID_DEFAULT: &str = "default";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::config::InstanceConfig;
|
||||
use crate::types::{AdmonitionDefaults, Directive};
|
||||
use crate::types::{AdmonitionDefaults, CssId, Directive};
|
||||
use std::str::FromStr;
|
||||
|
||||
/// All information required to render an admonition.
|
||||
@@ -9,6 +9,7 @@ use std::str::FromStr;
|
||||
pub(crate) struct AdmonitionMeta {
|
||||
pub directive: Directive,
|
||||
pub title: String,
|
||||
pub css_id: CssId,
|
||||
pub additional_classnames: Vec<String>,
|
||||
pub collapsible: bool,
|
||||
}
|
||||
@@ -28,6 +29,7 @@ impl AdmonitionMeta {
|
||||
let InstanceConfig {
|
||||
directive: raw_directive,
|
||||
title,
|
||||
id,
|
||||
additional_classnames,
|
||||
collapsible,
|
||||
} = raw;
|
||||
@@ -38,25 +40,49 @@ impl AdmonitionMeta {
|
||||
|
||||
// Load the directive (and title, if one still not given)
|
||||
let (directive, title) = match (Directive::from_str(&raw_directive), title) {
|
||||
(Ok(directive), None) => (directive, ucfirst(&raw_directive)),
|
||||
(Ok(directive), None) => (directive, format_directive_title(&raw_directive)),
|
||||
(Err(_), None) => (Directive::Note, "Note".to_owned()),
|
||||
(Ok(directive), Some(title)) => (directive, title),
|
||||
(Err(_), Some(title)) => (Directive::Note, title),
|
||||
};
|
||||
|
||||
let css_id = if let Some(verbatim) = id {
|
||||
CssId::Verbatim(verbatim)
|
||||
} else {
|
||||
const DEFAULT_CSS_ID_PREFIX: &str = "admonition-";
|
||||
CssId::Prefix(
|
||||
defaults
|
||||
.css_id_prefix
|
||||
.clone()
|
||||
.unwrap_or_else(|| DEFAULT_CSS_ID_PREFIX.to_owned()),
|
||||
)
|
||||
};
|
||||
|
||||
Self {
|
||||
directive,
|
||||
title,
|
||||
css_id,
|
||||
additional_classnames,
|
||||
collapsible,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Make the first letter of `input` upppercase.
|
||||
/// Format the title of an admonition directive
|
||||
///
|
||||
/// We special case a few words to make them look nicer (e.g. "tldr" -> "TL;DR" and "faq" -> "FAQ").
|
||||
fn format_directive_title(input: &str) -> String {
|
||||
match input {
|
||||
"tldr" => "TL;DR".to_owned(),
|
||||
"faq" => "FAQ".to_owned(),
|
||||
_ => uppercase_first(input),
|
||||
}
|
||||
}
|
||||
|
||||
/// Make the first letter of `input` uppercase.
|
||||
///
|
||||
/// source: https://stackoverflow.com/a/38406885
|
||||
fn ucfirst(input: &str) -> String {
|
||||
fn uppercase_first(input: &str) -> String {
|
||||
let mut chars = input.chars();
|
||||
match chars.next() {
|
||||
None => String::new(),
|
||||
@@ -69,6 +95,18 @@ mod test {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn test_format_directive_title() {
|
||||
assert_eq!(format_directive_title(""), "");
|
||||
assert_eq!(format_directive_title("a"), "A");
|
||||
assert_eq!(format_directive_title("tldr"), "TL;DR");
|
||||
assert_eq!(format_directive_title("faq"), "FAQ");
|
||||
assert_eq!(format_directive_title("note"), "Note");
|
||||
assert_eq!(format_directive_title("abstract"), "Abstract");
|
||||
// Unicode
|
||||
assert_eq!(format_directive_title("🦀"), "🦀");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_admonition_info_from_raw() {
|
||||
assert_eq!(
|
||||
@@ -76,6 +114,7 @@ mod test {
|
||||
InstanceConfig {
|
||||
directive: " ".to_owned(),
|
||||
title: None,
|
||||
id: None,
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: None,
|
||||
},
|
||||
@@ -84,6 +123,7 @@ mod test {
|
||||
AdmonitionMeta {
|
||||
directive: Directive::Note,
|
||||
title: "Note".to_owned(),
|
||||
css_id: CssId::Prefix("admonition-".to_owned()),
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: false,
|
||||
}
|
||||
@@ -97,17 +137,47 @@ mod test {
|
||||
InstanceConfig {
|
||||
directive: " ".to_owned(),
|
||||
title: None,
|
||||
id: None,
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: None,
|
||||
},
|
||||
&AdmonitionDefaults {
|
||||
title: Some("Important!!!".to_owned()),
|
||||
css_id_prefix: Some("custom-prefix-".to_owned()),
|
||||
collapsible: true,
|
||||
},
|
||||
),
|
||||
AdmonitionMeta {
|
||||
directive: Directive::Note,
|
||||
title: "Important!!!".to_owned(),
|
||||
css_id: CssId::Prefix("custom-prefix-".to_owned()),
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_admonition_info_from_raw_with_defaults_and_custom_id() {
|
||||
assert_eq!(
|
||||
AdmonitionMeta::resolve(
|
||||
InstanceConfig {
|
||||
directive: " ".to_owned(),
|
||||
title: None,
|
||||
id: Some("my-custom-id".to_owned()),
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: None,
|
||||
},
|
||||
&AdmonitionDefaults {
|
||||
title: Some("Important!!!".to_owned()),
|
||||
css_id_prefix: Some("ignored-custom-prefix-".to_owned()),
|
||||
collapsible: true,
|
||||
},
|
||||
),
|
||||
AdmonitionMeta {
|
||||
directive: Directive::Note,
|
||||
title: "Important!!!".to_owned(),
|
||||
css_id: CssId::Verbatim("my-custom-id".to_owned()),
|
||||
additional_classnames: Vec::new(),
|
||||
collapsible: true,
|
||||
}
|
||||
|
||||
16
src/types.rs
16
src/types.rs
@@ -3,12 +3,16 @@ use std::str::FromStr;
|
||||
|
||||
/// Book wide defaults that may be provided by the user.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Default)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub(crate) struct AdmonitionDefaults {
|
||||
#[serde(default)]
|
||||
pub(crate) title: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub(crate) collapsible: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub(crate) css_id_prefix: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -54,3 +58,15 @@ pub(crate) enum RenderTextMode {
|
||||
Strip,
|
||||
Html,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) enum CssId {
|
||||
/// id="my-id" in the admonishment
|
||||
///
|
||||
/// used directly for the id field
|
||||
Verbatim(String),
|
||||
/// the prefix from default.css_id_prefix (or "admonish-" if not specified)
|
||||
///
|
||||
/// will generate the rest of the id based on the title
|
||||
Prefix(String),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user