Compare commits

...

51 Commits

Author SHA1 Message Date
Tom Milligan
0304995dbb ci: add msrv to cargo toml, update mdbook version 2023-10-15 13:11:01 +01:00
Tom Milligan
197d9cd059 Merge pull request #140 from tommilligan/prep-1.13.0
chore: prep v0.13.0 release
2023-10-06 15:21:27 +01:00
Tom Milligan
04ff932f1f chore: prep v0.13.0 release 2023-10-06 14:52:13 +01:00
Tom Milligan
1526a5d814 Merge pull request #139 from tommilligan/pr-137
prefix directive class names with 'admonish-'
2023-10-06 14:16:45 +01:00
phoenixr-codes
eb21495797 prefix directive class names with 'admonish-'
This change exists to prevent conflicts with the newly added 'warning' class by mdBook. See also: https://github.com/rust-lang/mdBook/blob/master/CHANGELOG.md#added
2023-10-06 13:43:54 +01:00
Tom Milligan
ebe6f7815c Merge pull request #138 from tommilligan/ci-prettier
ci: lint styles with prettier
2023-10-06 13:32:48 +01:00
Tom Milligan
c0c953c865 ci: lint styles with prettier 2023-10-06 13:15:45 +01:00
Tom Milligan
0fa34a66a0 Merge pull request #136 from tommilligan/deps-20231001
chore: deps 20231001
2023-10-02 08:50:53 +01:00
dependabot[bot]
dfc12c3652 chore(deps): bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-02 08:37:26 +01:00
dependabot[bot]
108edfffc5 chore(deps): bump JamesIves/github-pages-deploy-action
Bumps [JamesIves/github-pages-deploy-action](https://github.com/jamesives/github-pages-deploy-action) from 4.4.1 to 4.4.3.
- [Release notes](https://github.com/jamesives/github-pages-deploy-action/releases)
- [Commits](https://github.com/jamesives/github-pages-deploy-action/compare/v4.4.1...v4.4.3)

---
updated-dependencies:
- dependency-name: JamesIves/github-pages-deploy-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-02 08:37:26 +01:00
Tom Milligan
38f1933e8f chore: bump rust deps 2023-10-02 08:37:26 +01:00
Tom Milligan
a839038263 Merge pull request #128 from tommilligan/bug-127
markdown: fix panic when searching for indent
2023-09-19 13:02:43 +01:00
Tom Milligan
8501c812d9 chore: prep v1.12.1 2023-09-19 12:21:42 +01:00
Tom Milligan
933432afb2 markdown: fix panic when searching for indent 2023-09-19 12:21:42 +01:00
Tom Milligan
496e8f7c6d Merge pull request #126 from tommilligan/prep-1.12.0
chore: prep v1.12.0 release
2023-09-16 11:22:08 +01:00
Tom Milligan
1877fd1731 chore: upgrade internal dependencies 2023-09-16 10:58:17 +01:00
Tom Milligan
20286b3fae chore: prep v1.12.0 release 2023-09-16 10:55:53 +01:00
Tom Milligan
8e68cf919f Merge pull request #124 from tommilligan/bug-123
feat: support admonitions inside list items
2023-09-10 00:02:58 +01:00
Tom Milligan
02640dab1f feat: support admonitions inside list items 2023-09-09 23:44:58 +01:00
Tom Milligan
771e9c9fd8 Merge pull request #125 from tommilligan/prep-v1.11.1
chore: prepare v1.11.1 release
2023-09-09 23:43:48 +01:00
Tom Milligan
cce9343c47 chore: prepare v1.11.1 release 2023-09-09 23:32:10 +01:00
Tom Milligan
20b158966b Revert "chore: bump lockfile"
This reverts commit 39edc4d92a.
2023-09-09 23:24:26 +01:00
Tom Milligan
491f9cf341 Merge pull request #122 from tommilligan/fix-docs
docs: fix mdbook-toc build failure
2023-09-09 09:12:27 +01:00
Tom Milligan
6deaf1ea2b docs: fix mdbook-toc build failure 2023-09-09 09:12:07 +01:00
Tom Milligan
041e5a566f Merge pull request #121 from tommilligan/update-deps
chore: prep v1.11.0 release
2023-09-09 09:06:44 +01:00
Tom Milligan
99b5a235cf chore: prep v1.11.0 release 2023-09-09 09:05:49 +01:00
Tom Milligan
39edc4d92a chore: bump lockfile 2023-09-09 08:58:32 +01:00
Tom Milligan
7773213093 Merge pull request #118 from eitsupi/eitsupi-patch-1
ci(deploy): Use Ubuntu 20.04 for building linux gnu target binaries
2023-09-09 08:49:24 +01:00
eitsupi
e888fcd021 Use Ubuntu 20.04 for building linux gnu target binaries 2023-09-09 08:48:24 +01:00
Tom Milligan
95dc7582ad Merge pull request #119 from eitsupi/add-aarch64-musl
ci(deploy): deploy aarch64-unknown-linux-musl
2023-09-09 08:43:59 +01:00
eitsupi
b658eb6049 strip via Cargo 2023-09-09 08:31:25 +01:00
eitsupi
623291625a deploy aarch64-unknown-linux-musl 2023-09-09 08:31:25 +01:00
Tom Milligan
4dad5a86c8 chore: fix clippy 2023-09-09 08:28:21 +01:00
Tom Milligan
7e774f4655 chore: prep v1.10.2 release (#116) 2023-08-07 18:58:32 +01:00
Tom Milligan
823cefbcbc fix: unlink mdbook internal toml dependency from mdbook-admonish (#115)
* chore: bump dependencies

* fix: unlink mdbook internal toml dependency from mdbook-admonish
2023-08-07 18:55:02 +01:00
Tom Milligan
a6a2941821 chore: prep v1.10.1 release (#113) 2023-07-28 18:49:46 +01:00
Uriel
faf99a1b76 only modify summary with the admonition-title class (#112)
* only modify `summary` with the `admonition-title` class

* regenerate assets, update snapshot test, fix themes

---------

Co-authored-by: Tom Milligan <tom.milligan@uipath.com>
2023-07-28 18:44:05 +01:00
Jonas
afdc2b03d0 Improvement of links (#111) 2023-07-26 12:19:01 +01:00
Tom Milligan
e55df3e60b Merge pull request #110 from tommilligan/prep-1.10.0
chore: prep v1.10.0 release
2023-07-23 19:51:53 +01:00
Tom Milligan
f3d49b93de chore: prep v1.10.0 release 2023-07-23 19:47:37 +01:00
Tom Milligan
92caf95b34 chore: add notes for future v2 release 2023-07-23 19:38:24 +01:00
Tom Milligan
0742c6c1e8 Merge pull request #109 from tommilligan/run-doctests
feat: add support for test renderer, running doctests
2023-07-23 19:33:20 +01:00
Tom Milligan
60706be3e0 ci: bump MSRV to 1.66.0 2023-07-23 19:21:18 +01:00
Tom Milligan
f5a6b9ef0f ci: build docs on each merge 2023-07-23 19:13:10 +01:00
Tom Milligan
9361b7e7fa book: add configuration docs 2023-07-23 19:10:24 +01:00
Tom Milligan
24bef47b15 internal: restructure book.toml config parsing 2023-07-23 19:10:24 +01:00
Tom Milligan
0eb5fd35c3 internal: restructure configuration parsing 2023-07-23 17:53:19 +01:00
Tom Milligan
de539cd0fd internal: split up lib.rs 2023-07-22 11:32:40 +01:00
Tom Milligan
4842daea1c feat: add support for test renderer, running doctests 2023-07-22 10:44:11 +01:00
Tom Milligan
76212fccfb internal: refactor how whitespace is added 2023-07-22 10:43:47 +01:00
Tom Milligan
681c991a9a Merge pull request #98 from tommilligan/ci-publish-auto
ci: fix automatic crates.io publication
2023-05-05 09:52:54 +01:00
35 changed files with 2923 additions and 2175 deletions

View File

@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Checkout sources
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Cache build files
uses: actions/cache@v3
with:
@@ -35,7 +35,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Checkout sources
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Cache build files
uses: actions/cache@v3
with:
@@ -51,6 +51,12 @@ jobs:
with:
toolchain: stable
override: true
- name: Install node toolchain
uses: actions/setup-node@v3
with:
node-version: "20"
cache: "yarn"
cache-dependency-path: compile_assets/yarn.lock
- name: Install additional test dependencies
env:
CARGO_TARGET_DIR: cargo_target
@@ -70,7 +76,7 @@ jobs:
rust:
- stable
- beta
- 1.64.0
- 1.66.0
experimental:
- false
# Run a canary test on nightly that's allowed to fail
@@ -87,7 +93,7 @@ jobs:
continue-on-error: ${{ matrix.experimental }}
steps:
- name: Checkout sources
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Cache build files
uses: actions/cache@v3
with:

View File

@@ -16,14 +16,16 @@ jobs:
strategy:
fail-fast: false
matrix:
target:
- x86_64-unknown-linux-gnu
- x86_64-unknown-linux-musl
- x86_64-apple-darwin
- x86_64-pc-windows-msvc
include:
- target: x86_64-unknown-linux-gnu
- target: aarch64-unknown-linux-musl
os: ubuntu-latest
name: aarch64-unknown-linux-musl.tar.gz
- target: x86_64-unknown-linux-gnu
# Deliberately pinned to the same version `mdbook` uses to build
# binaries, so we use the same glibc version
#
# ref: https://github.com/rust-lang/mdBook/pull/1955
os: ubuntu-20.04
name: x86_64-unknown-linux-gnu.tar.gz
- target: x86_64-unknown-linux-musl
os: ubuntu-latest
@@ -37,7 +39,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Setup | Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
# Cache files between builds
- name: Setup | Cache Cargo
@@ -56,17 +58,19 @@ jobs:
profile: minimal
target: ${{ matrix.target }}
- name: Setup | musl tools
if: matrix.target == 'x86_64-unknown-linux-musl'
run: sudo apt install -y musl-tools
- name: Setup | cross
if: endsWith(matrix.target, '-unknown-linux-musl')
uses: taiki-e/install-action@v2
with:
tool: cross
- name: Build | Build
if: matrix.target != 'x86_64-unknown-linux-musl'
if: ${{ !endsWith(matrix.target, '-unknown-linux-musl') }}
run: cargo build --release --target ${{ matrix.target }}
- name: Build | Build (musl)
if: matrix.target == 'x86_64-unknown-linux-musl'
run: cargo build --release --target ${{ matrix.target }}
if: endsWith(matrix.target, '-unknown-linux-musl')
run: cross build --release --target ${{ matrix.target }}
- name: Post Setup | Extract tag name
shell: bash
@@ -78,7 +82,6 @@ jobs:
run: |
mkdir target/stage
cd target/${{ matrix.target }}/release
strip ${{ env.CRATE_NAME }}.exe
7z a ../../stage/${{ env.CRATE_NAME }}-${{ steps.extract_tag.outputs.tag }}-${{ matrix.name }} ${{ env.CRATE_NAME }}.exe
cd -
- name: Post Setup | Prepare artifacts [-nix]
@@ -86,7 +89,6 @@ jobs:
run: |
mkdir target/stage
cd target/${{ matrix.target }}/release
strip ${{ env.CRATE_NAME }}
tar czvf ../../stage/${{ env.CRATE_NAME }}-${{ steps.extract_tag.outputs.tag }}-${{ matrix.name }} ${{ env.CRATE_NAME }}
cd -
- name: Post Setup | Upload artifacts
@@ -102,7 +104,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Setup | Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -131,7 +133,7 @@ jobs:
needs: github_release
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/cache@v3
with:
path: |

View File

@@ -1,8 +1,9 @@
name: docs
on:
release:
types: [published]
push:
branches:
- "main"
workflow_dispatch:
permissions:
@@ -12,7 +13,7 @@ jobs:
publish:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/cache@v3
with:
path: |
@@ -30,10 +31,12 @@ jobs:
override: true
- name: Install mdbook
run: ./scripts/install-mdbook
- name: Install mdbook extras
run: ./book/scripts/install-mdbook-extras
- name: Build book
run: ./scripts/build-book
- name: Push docs
uses: JamesIves/github-pages-deploy-action@v4.4.1
uses: JamesIves/github-pages-deploy-action@v4.4.3
with:
branch: gh-pages
folder: book/book

View File

@@ -2,6 +2,76 @@
## Unreleased
## 1.13.0
### Changed
- Required styles version is now `^3.0.0` (release `1.13.0`). Run `mdbook-admonish install` to update.
- Internal CSS classnames for directives are now prefixed with `admonish-`, so `warning` is now `admonish-warning`. This avoids a conflict with upstream classnames introduced in `mdbook 0.4.35`. Thanks to [@phoenixr-codes](https://github.com/phoenixr-codes) for the report and fix! ([#139](https://github.com/tommilligan/mdbook-admonish/pull/139))
### Fixed
- Some minor inconsistencies in SCSS (and downstream CSS) styles were fixed by adopting Prettier linting ([#138](https://github.com/tommilligan/mdbook-admonish/pull/138))
## 1.12.1
### Fixed
- Panic when searching for an indent in non-ASCII content. Thanks to [@CoralPink](https://github.com/CoralPink) for the report! ([#128](https://github.com/tommilligan/mdbook-admonish/pull/128))
## 1.12.0
### Added
- Admonitions are now supported when indented inside other elements, such as a list. Thanks to [@mattburgess](https://github.com/mattburgess) for the report! ([#124](https://github.com/tommilligan/mdbook-admonish/pull/124))
## 1.11.1
### Fixed
- Reverted internal dependency upgrades that unintentionally increased MSRV from 1.66.0 in 1.11.0
## 1.11.0 (yanked)
**Note:** This release has been yanked.
It unintentionally increased the MSRV from 1.66.0
### Changed
- `gnu` prebuilt binaries are now built on `ubuntu-20.04` to match `mdbook` binaries. Thanks to [@eitsupi](https://github.com/eitsupi) for the fix! ([#118](https://github.com/tommilligan/mdbook-admonish/pull/118))
### Added
- `aarch64-unknown-linux-musl` prebuilt binary now available ([#119](https://github.com/tommilligan/mdbook-admonish/pull/119))
## 1.10.2
### Fixed
- Fixed `cargo install mdbook-admonish` failing due to an internal dependency mismatch with `mdbook` ([#115](https://github.com/tommilligan/mdbook-admonish/pull/115))
## 1.10.1
### Fixed
- Only restyle `summary` elements generated by `mdbook-admonish`. Thanks to [@ImUrX](https://github.com/ImUrX) for the report and fix! ([#112](https://github.com/tommilligan/mdbook-admonish/pull/112))
## 1.10.0
### Changed
- MSRV (minimum supported rust version) is now 1.66.0 for mdbook v0.4.32 ([#109](https://github.com/tommilligan/mdbook-admonish/pull/109))
### Added
- Support `mdbook test` running doctests inside admonish blocks. Opt-in to this by setting `renderer.test.action_mode = "strip"` ([#109](https://github.com/tommilligan/mdbook-admonish/pull/109))
- Log a warning when an invalid admonish block is encountered ([#109](https://github.com/tommilligan/mdbook-admonish/pull/109))
### Fixed
- Document all `book.toml` configuration options [in the reference](https://tommilligan.github.io/mdbook-admonish/reference.html), some of which were previously undocumened ([#109](https://github.com/tommilligan/mdbook-admonish/pull/109))
## 1.9.0
### Changed
@@ -109,7 +179,11 @@ This behaviour is [documented in the readme here](https://github.com/tommilligan
- Flattened indentation of generated HTML, otherwise it's styled as a markdown code block
- Fixed edge cases where the info string changes length when parsed, causing title/body to be incorrectly split
## 1.3.0
## 1.3.0 (yanked)
**Note:** This release has been yanked.
It unintentionally introduced a serious parsing bug.
### Added

991
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,8 @@
[package]
name = "mdbook-admonish"
version = "1.9.0"
version = "1.13.0"
edition = "2021"
rust-version = "1.66.0"
authors = ["Tom Milligan <code@tommilligan.net>"]
description = "A preprocessor for mdbook to add Material Design admonishments."
@@ -17,32 +18,41 @@ name = "mdbook-admonish"
path = "src/bin/mdbook-admonish.rs"
required-features = ["cli"]
[profile.release]
strip = true
[lib]
name = "mdbook_admonish"
path = "src/lib.rs"
[dependencies]
anyhow = "1.0.65"
clap = { version = "4", default_features = false, features = ["std", "derive"], optional = true }
anyhow = "1.0.75"
# Note: clap 4.4 increases MSRV to 1.70.0 (2023-06-01)
# To use MSRV supported dependencies, install using the lockfile with
# `cargo install mdbook-admonish --locked`
clap = { version = "4.3", default_features = false, features = ["std", "derive"], optional = true }
env_logger = { version = "0.10", default_features = false, optional = true }
log = { version = "0.4.17", optional = true }
mdbook = "0.4.21"
once_cell = "1.15.0"
pulldown-cmark = "0.9.2"
regex = "1.6.0"
semver = "1.0.14"
serde = { version = "1.0.145", features = ["derive"] }
serde_json = "1.0.85"
toml = "0.7.3"
toml_edit = { version = "0.19.8", optional = true }
log = "0.4.20"
mdbook = "0.4.35"
once_cell = "1.18.0"
pulldown-cmark = "0.9.3"
regex = "1.9.6"
semver = "1.0.19"
serde = { version = "1.0.188", features = ["derive"] }
serde_json = "1.0.107"
# The version of toml that mdbook uses internally (and uses in it's public api)
# Only used for compatilibilty with the mdbook public api
toml_mdbook = { package = "toml", version = "0.5.11" }
toml = "0.8.1"
toml_edit = { version = "0.20.1", optional = true }
[dev-dependencies]
pretty_assertions = "1.3.0"
pretty_assertions = "1.4.0"
[features]
default = ["cli", "cli-install"]
# Enable the command line binary
cli = ["clap", "env_logger", "log"]
cli = ["clap", "env_logger"]
# Enable installation of files and configuration
cli-install = ["toml_edit"]

View File

@@ -70,6 +70,9 @@ Install the tool:
```bash
cargo install mdbook-admonish
# If you get compilation/installation errors, try a locked installation
cargo install mdbook-admonish --locked
```
Then let `mdbook-admonish` add the required files and configuration:
@@ -101,6 +104,21 @@ Then, build your book as usual:
mdbook path/to/book
```
### Reproducible builds
For a reproducible build suitable for use in CI or scripts, please:
- Pin to a specific version
- Install with lockfile dependencies
- Always install the latest CSS assets
```bash
cargo install mdbook-admonish --vers "1.5.0" --locked
mdbook-admonish install path/to/your/book
```
The Minimum Supported Rust Version (MSRV) is documented in `Cargo.toml`, and noted in the `CHANGELOG.md`. We aims to support around six months of stable Rust.
### Updates
**Please note**, when updating your version of `mdbook-admonish`, updated styles will not be applied unless you rerun `mdbook-admonish install` to update the additional CSS files in your book.
@@ -117,25 +135,6 @@ ERROR:
If you want to update across minor versions without breakage, you should always run `mdbook-admonish install`.
Alternatively, pin to a specific version for a reproducible installation:
```bash
cargo install mdbook-admonish --vers "1.5.0" --locked
```
### Bail on error
By default, if an adomnition is incorrectly configured, an error will be shown in the book.
You can force it to break the build instead, with the following configuration:
```toml
[preprocessor.admonish]
on_failure = "bail"
```
This may be useful for non-interative workflows.
### Process included files
You can ensure that content inlined with `{{#include}}` is also processed by [setting the `after` option](https://rust-lang.github.io/mdBook/format/configuration/preprocessors.html#require-a-certain-order):
@@ -187,8 +186,6 @@ You must make the next `mdbook-admonish` crate version at least a **minor** vers
Github workflows are setup such that pushing a `vX.Y.Z` tag will trigger a release to be cut.
Once the release is created, copy and paste the relevant section of `CHANGELOG.md` manually to update the description.
## Thanks
This utility is heavily drawn from and inspired by other projects, namely:

View File

@@ -9,7 +9,11 @@ title = "The mdbook-admonish book"
[preprocessor.admonish]
command = "mdbook-admonish"
assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install`
assets_version = "3.0.0" # do not edit: managed by `mdbook-admonish install`
[preprocessor.toc]
command = "mdbook-toc"
renderer = ["html"]
[output]

View File

@@ -0,0 +1,9 @@
#!/bin/bash
set -exuo pipefail
cd "$(dirname "$0")"/../..
if ! mdbook-toc --version; then
cargo install mdbook-toc --version 0.14.1 --force
fi

View File

@@ -1,9 +1,13 @@
# mdbook-admonish
<!-- toc -->
## Intoduction
[![Latest version](https://img.shields.io/crates/v/mdbook-admonish.svg)](https://crates.io/crates/mdbook-admonish)
[![docs.rs](https://img.shields.io/docsrs/mdbook-admonish)](https://docs.rs/mdbook-admonish)
A preprocessor for [mdbook](https://github.com/rust-lang-nursery/mdBook) to add [Material Design](https://material.io/design) admonishments, based on the [mkdocs-material](https://squidfunk.github.io/mkdocs-material/reference/admonitions/) implementation.
A preprocessor for [mdbook](https://github.com/rust-lang/mdBook) to add [Material Design](https://material.io/design) admonishments, based on the [mkdocs-material](https://squidfunk.github.io/mkdocs-material/reference/admonitions/) implementation.
It turns this:
@@ -21,6 +25,8 @@ A beautifully styled message.
## Usage
### A basic `admonish` block
Use any [fenced code-block](https://spec.commonmark.org/0.30/#fenced-code-blocks) as you normally would, but annotate it with `admonish <admonition type>`:
````
@@ -33,7 +39,7 @@ My example is the best!
My example is the best!
```
See the [mkdocs-material docs](https://squidfunk.github.io/mkdocs-material/reference/admonitions/#supported-types) for a list of supported admonitions. You'll find:
See the [list of directives](./reference.md#directives) for a full list of supported admonitions. You'll find:
- `info`
- `warning`
@@ -54,8 +60,28 @@ A plain note.
A plain note.
```
### Invalid blocks
By default, if an `admonish` block cannot be parsed, an error will be rendered in the output:
````
```admonish title="\j"
This block will error
```
````
```admonish title="\j"
This block will error
```
You can also configure the build to fail loudly, by setting `on_failure = "bail"` in `book.toml`. See the [configuration reference](./reference.md#booktoml-configuration) for more details.
### Additional Options
You can pass additional options to each block. The options are structured as TOML key-value pairs.
Note that some options can be passed globally, through the `default` section in `book.toml`. See the [configuration reference](./reference.md#booktoml-configuration) for more details.
#### Custom title
A custom title can be provided, contained in a double quoted TOML string.
@@ -150,17 +176,3 @@ Will yield something like the following HTML, which you can then apply styles to
```admonish collapsible=true
Content will be hidden initially.
```
#### Invalid blocks
If a rendering error occurs, an error will be rendered in the output:
````
```admonish title="\j"
This block will error
```
````
```admonish title="\j"
This block will error
```

View File

@@ -1,5 +1,93 @@
# Reference
<!-- toc -->
## `book.toml` configuration
See below for all configuration options available to add in `book.toml`.
The options should all be nested under `preprocessor.admonish`; for example:
```toml
[preprocessor.admonish]
on_failure = "bail"
[preprocessor.admonish.default]
collapsible = true
[preprocessor.admonish.renderer.test]
render_mode = "strip"
```
### `on_failure`
Optional. Default value: `continue`.
The action to take when an invalid `admonish` block is encountered:
- `continue` (default): Continue processing future blocks, do not fail the build. If rendering to HTML, an error message will be displayed in the book output.
- `bail`: Abort the build.
### `default`
Optional.
Default values to use, when not provided in an `admonish` block explicitly.
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`.
### `renderer`
````admonish tip
It is recommended that you set:
```toml
[preprocessor.admonish.renderer.test]
render_mode = "strip"
```
This allows `mdbook test` to find and test rust examples within `admonish` blocks.
This will be the default behaviour in the next `mdbook-admonish` major version.
````
Optional.
Additional settings to apply, depending on the renderer that is running.
The most common renderers used are:
- `html`: Used by `mdbook build` to build the final book output.
- `test`: Used by `mdbook test` to find and run doctests.
Subfields:
- `renderer.<renderer_name>.render_mode` (optional): The action `mdbook-admonish` should take when running with this renderer.
- Valid values:
- `html`: Convert `admonish` blocks into HTML output.
- `preserve`: Do nothing. Leave the book untouched.
- `strip`: Strip `admonish`-specific syntax, leaving the inner content untouched.
- Default values:
- For the `html` renderer, the default value is `html`.
- For all other renderers, the default value is `preserve`.
### `command`
Required.
Used by `mdbook` to know how to call the `mdbook-admonish` plugin.
Running this command with the `--version` flag from your shell should work, for the plugin to function.
### `assets_version`
Optional.
This is automatically updated by `mdbook-admonish install` and should not be edited.
## Directives
All supported directives are listed below.

6
book/v2.md Normal file
View File

@@ -0,0 +1,6 @@
# Notes for a v2 release
## Default behaviour changes
- `on_failure` default changed from `continue` to `bail`
- `preprocessor.admonish.renderer.test.render_mode` default changed from `preserve` to `strip`

View File

@@ -4,9 +4,14 @@
"main": "index.js",
"license": "MIT",
"scripts": {
"build": "sass --no-source-map scss/mdbook-admonish.scss ../src/bin/assets/mdbook-admonish.css"
"build": "sass --no-source-map scss/mdbook-admonish.scss ../src/bin/assets/mdbook-admonish.css",
"lint": "prettier --check .",
"fix": "prettier --write ."
},
"dependencies": {
"sass": "^1.49.7"
},
"devDependencies": {
"prettier": "^3.0.3"
}
}

View File

@@ -30,29 +30,41 @@
/// Admonition flavours
$admonitions: (
// pencil
note: $clr-blue-a200 "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z'/></svg>",
admonish-note: $clr-blue-a200
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z'/></svg>",
// clipboard-text
abstract summary tldr: $clr-light-blue-a400 "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17 9H7V7h10m0 6H7v-2h10m-3 6H7v-2h7M12 3a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z'/></svg>",
admonish-abstract admonish-summary admonish-tldr: $clr-light-blue-a400
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17 9H7V7h10m0 6H7v-2h10m-3 6H7v-2h7M12 3a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z'/></svg>",
// information
info todo: $clr-cyan-a700 "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2z'/></svg>",
admonish-info admonish-todo: $clr-cyan-a700
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2z'/></svg>",
// fire
tip hint important: $clr-teal-a700 "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17.66 11.2c-.23-.3-.51-.56-.77-.82-.67-.6-1.43-1.03-2.07-1.66C13.33 7.26 13 4.85 13.95 3c-.95.23-1.78.75-2.49 1.32-2.59 2.08-3.61 5.75-2.39 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.23.1-.47.04-.66-.12a.58.58 0 0 1-.14-.17c-1.13-1.43-1.31-3.48-.55-5.12C5.78 10 4.87 12.3 5 14.47c.06.5.12 1 .29 1.5.14.6.41 1.2.71 1.73 1.08 1.73 2.95 2.97 4.96 3.22 2.14.27 4.43-.12 6.07-1.6 1.83-1.66 2.47-4.32 1.53-6.6l-.13-.26c-.21-.46-.77-1.26-.77-1.26m-3.16 6.3c-.28.24-.74.5-1.1.6-1.12.4-2.24-.16-2.9-.82 1.19-.28 1.9-1.16 2.11-2.05.17-.8-.15-1.46-.28-2.23-.12-.74-.1-1.37.17-2.06.19.38.39.76.63 1.06.77 1 1.98 1.44 2.24 2.8.04.14.06.28.06.43.03.82-.33 1.72-.93 2.27z'/></svg>",
admonish-tip admonish-hint admonish-important: $clr-teal-a700
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17.66 11.2c-.23-.3-.51-.56-.77-.82-.67-.6-1.43-1.03-2.07-1.66C13.33 7.26 13 4.85 13.95 3c-.95.23-1.78.75-2.49 1.32-2.59 2.08-3.61 5.75-2.39 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.23.1-.47.04-.66-.12a.58.58 0 0 1-.14-.17c-1.13-1.43-1.31-3.48-.55-5.12C5.78 10 4.87 12.3 5 14.47c.06.5.12 1 .29 1.5.14.6.41 1.2.71 1.73 1.08 1.73 2.95 2.97 4.96 3.22 2.14.27 4.43-.12 6.07-1.6 1.83-1.66 2.47-4.32 1.53-6.6l-.13-.26c-.21-.46-.77-1.26-.77-1.26m-3.16 6.3c-.28.24-.74.5-1.1.6-1.12.4-2.24-.16-2.9-.82 1.19-.28 1.9-1.16 2.11-2.05.17-.8-.15-1.46-.28-2.23-.12-.74-.1-1.37.17-2.06.19.38.39.76.63 1.06.77 1 1.98 1.44 2.24 2.8.04.14.06.28.06.43.03.82-.33 1.72-.93 2.27z'/></svg>",
// check-bold
success check done: $clr-green-a700 "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m9 20.42-6.21-6.21 2.83-2.83L9 14.77l9.88-9.89 2.83 2.83L9 20.42z'/></svg>",
admonish-success admonish-check admonish-done: $clr-green-a700
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m9 20.42-6.21-6.21 2.83-2.83L9 14.77l9.88-9.89 2.83 2.83L9 20.42z'/></svg>",
// help-circle
question help faq: $clr-light-green-a700 "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m15.07 11.25-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8a4 4 0 0 1 4-4 4 4 0 0 1 4 4 3.2 3.2 0 0 1-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10c0-5.53-4.5-10-10-10z'/></svg>",
admonish-question admonish-help admonish-faq: $clr-light-green-a700
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m15.07 11.25-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8a4 4 0 0 1 4-4 4 4 0 0 1 4 4 3.2 3.2 0 0 1-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10c0-5.53-4.5-10-10-10z'/></svg>",
// alert
warning caution attention: $clr-orange-a400 "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 14h-2V9h2m0 9h-2v-2h2M1 21h22L12 2 1 21z'/></svg>",
admonish-warning admonish-caution admonish-attention: $clr-orange-a400
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 14h-2V9h2m0 9h-2v-2h2M1 21h22L12 2 1 21z'/></svg>",
// close-thick
failure fail missing: $clr-red-a200 "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20 6.91 17.09 4 12 9.09 6.91 4 4 6.91 9.09 12 4 17.09 6.91 20 12 14.91 17.09 20 20 17.09 14.91 12 20 6.91z'/></svg>",
admonish-failure admonish-fail admonish-missing: $clr-red-a200
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20 6.91 17.09 4 12 9.09 6.91 4 4 6.91 9.09 12 4 17.09 6.91 20 12 14.91 17.09 20 20 17.09 14.91 12 20 6.91z'/></svg>",
// lighting-bold
danger error: $clr-red-a400 "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 15H6l7-14v8h5l-7 14v-8z'/></svg>",
admonish-danger admonish-error: $clr-red-a400
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 15H6l7-14v8h5l-7 14v-8z'/></svg>",
// bug
bug: $clr-pink-a400 "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 0 0-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 0 0-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z'/></svg>",
admonish-bug: $clr-pink-a400
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 0 0-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 0 0-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z'/></svg>",
// format-list-numbered
example: $clr-deep-purple-a200 "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 0 1 .75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z'/></svg>",
admonish-example: $clr-deep-purple-a200
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 0 1 .75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z'/></svg>",
// format-quote-close
quote cite: $clr-grey "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z'/></svg>",
admonish-quote admonish-cite: $clr-grey
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z'/></svg>"
) !default;
// ----------------------------------------------------------------------------
@@ -62,16 +74,13 @@ $admonitions: (
// Admonition variables
:root {
@each $names, $props in $admonitions {
--md-admonition-icon--#{nth($names, 1)}:
url("data:image/svg+xml;charset=utf-8,#{nth($props, 2)}");
--md-admonition-icon--#{nth($names, 1)}: url("data:image/svg+xml;charset=utf-8,#{nth($props, 2)}");
}
--md-details-icon:
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M8.59 16.58 13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42Z'/></svg>");
--md-details-icon: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M8.59 16.58 13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42Z'/></svg>");
}
// ----------------------------------------------------------------------------
// Admonition
:is(.admonition) {
display: flow-root;
@@ -86,7 +95,9 @@ $admonitions: (
border: 0 solid black;
border-inline-start-width: 0.4rem;
border-radius: 0.2rem;
box-shadow: 0 0.2rem 1rem rgba(0, 0, 0, 0.05), 0 0 0.1rem rgba(0, 0, 0, 0.1);;
box-shadow:
0 0.2rem 1rem rgba(0, 0, 0, 0.05),
0 0 0.1rem rgba(0, 0, 0, 0.1);
// [print]: Omit shadow as it may lead to rendering errors
@media print {
@@ -125,25 +136,27 @@ a.admonition-anchor-link {
position: absolute;
left: -1.2rem;
// Ensure we have enough padding, so that we can move the mouse to click on it
padding-right: 1.0rem;
padding-right: 1rem;
&:link, &:visited {
&:link,
&:visited {
// Don't make links colored (override to standard text color)
// variable provided downstream by mdbook
color: var(--fg);
}
&:link:hover, &:visited:hover {
&:link:hover,
&:visited:hover {
// No underline on hover
text-decoration: none;
}
&::before {
content: '§';
content: "§";
}
}
// Admonition title
:is(.admonition-title, summary) {
:is(.admonition-title, summary.admonition-title) {
position: relative;
min-height: 4rem;
margin-block: 0;
@@ -182,17 +195,16 @@ a.admonition-anchor-link {
content: "";
}
// Show anchor link on hover over title
&:hover a.admonition-anchor-link {
display: initial
display: initial;
}
}
summary.admonition-title {
details.admonition > &::after {
position: absolute;
top: .625em;
top: 0.625em;
inset-inline-end: 1.6rem;
height: 2rem;
width: 2rem;
@@ -205,7 +217,7 @@ summary.admonition-title {
-webkit-mask-size: contain;
content: "";
transform: rotate(0deg);
transition: transform .25s;
transition: transform 0.25s;
}
details[open].admonition > &::after {
@@ -223,6 +235,7 @@ summary.admonition-title {
// Admonition flavour selectors
$flavours: ();
@each $name in $names {
$flavours: list.join($flavours, ".#{$name}", $separator: comma);
}
@@ -233,7 +246,7 @@ summary.admonition-title {
}
// Admonition flavour title
:is(#{$flavours}) > :is(.admonition-title, summary) {
:is(#{$flavours}) > :is(.admonition-title, summary.admonition-title) {
background-color: color.adjust($tint, $alpha: -0.9);
// Admonition icon
@@ -261,7 +274,8 @@ summary.admonition-title {
}
}
.ayu, .coal {
.ayu,
.coal {
& :is(.admonition) {
background-color: var(--theme-hover);
}
@@ -272,8 +286,10 @@ summary.admonition-title {
background-color: var(--sidebar-bg);
color: var(--sidebar-fg);
}
& .admonition-anchor-link {
&:link, &:visited {
&:link,
&:visited {
color: var(--sidebar-fg);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -95,6 +95,11 @@ picomatch@^2.0.4, picomatch@^2.2.1:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
prettier@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.3.tgz#432a51f7ba422d1469096c0fdc28e235db8f9643"
integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"

View File

@@ -9,9 +9,12 @@ title = "mdbook-admonish-integration"
[preprocessor.admonish]
command = "mdbook-admonish"
assets_version = "2.0.1" # do not edit: managed by `mdbook-admonish install`
assets_version = "3.0.0" # do not edit: managed by `mdbook-admonish install`
after = ["links"]
[preprocessor.admonish.renderer.test]
render_mode = "strip"
[output]
[output.html]

View File

@@ -9,9 +9,12 @@ title = "mdbook-admonish-integration"
[preprocessor.admonish]
command = "mdbook-admonish"
assets_version = "2.0.1" # do not edit: managed by `mdbook-admonish install`
assets_version = "3.0.0" # do not edit: managed by `mdbook-admonish install`
after = ["links"]
[preprocessor.admonish.renderer.test]
render_mode = "strip"
[output]
[output.html]

View File

@@ -1,5 +1,5 @@
<h1 id="chapter-1"><a class="header" href="#chapter-1">Chapter 1</a></h1>
<div id="admonition-what-is-this" class="admonition abstract">
<div id="admonition-what-is-this" class="admonition admonish-abstract">
<div class="admonition-title">
<p>What <i>is</i> this?</p>
<p><a class="admonition-anchor-link" href="#admonition-what-is-this"></a></p>
@@ -9,7 +9,7 @@
<p>It verifies that <code>mdbook</code> post-processes our generated HTML in the way we expect.</p>
</div>
</div>
<div id="admonition-note" class="admonition note">
<div id="admonition-note" class="admonition admonish-note">
<div class="admonition-title">
<p>Note</p>
<p><a class="admonition-anchor-link" href="#admonition-note"></a></p>
@@ -18,19 +18,19 @@
<p>Simples</p>
</div>
</div>
<div id="admonition-default" class="admonition warning">
<div id="admonition-default" class="admonition admonish-warning">
<div>
<p>No title, only body</p>
</div>
</div>
<div id="admonition-error-rendering-admonishment" class="admonition bug">
<div id="admonition-error-rendering-admonishment" class="admonition admonish-bug">
<div class="admonition-title">
<p>Error rendering admonishment</p>
<p><a class="admonition-anchor-link" href="#admonition-error-rendering-admonishment"></a></p>
</div>
<div>
<p>Failed with:</p>
<pre><code>TOML parsing error: TOML parse error at line 1, column 8
<pre><code class="language-log">TOML parsing error: TOML parse error at line 1, column 8
|
1 | title=&quot;
| ^
@@ -38,13 +38,13 @@ invalid basic string
</code></pre>
<p>Original markdown input:</p>
<pre><code>```admonish title=&quot;
<pre><code class="language-markdown">```admonish title=&quot;
No title, only body
```
</code></pre>
</div>
</div>
<details id="admonition-note-1" class="admonition note">
<details id="admonition-note-1" class="admonition admonish-note">
<summary class="admonition-title">
<p>Note</p>
<p><a class="admonition-anchor-link" href="#admonition-note-1"></a></p>
@@ -53,7 +53,7 @@ No title, only body
<p>Hidden on load</p>
</div>
</details>
<div id="admonition-warning" class="admonition warning">
<div id="admonition-warning" class="admonition admonish-warning">
<div class="admonition-title">
<p>Warning</p>
<p><a class="admonition-anchor-link" href="#admonition-warning"></a></p>
@@ -62,7 +62,7 @@ No title, only body
<p>This is a commonly shared warning!</p>
</div>
</div>
<div id="admonition-note-2" class="admonition note">
<div id="admonition-note-2" class="admonition admonish-note">
<div class="admonition-title">
<p>Note</p>
<p><a class="admonition-anchor-link" href="#admonition-note-2"></a></p>
@@ -72,4 +72,47 @@ No title, only body
</code></pre>
</div>
</div>
<div id="admonition-note-3" class="admonition admonish-note">
<div class="admonition-title">
<p>Note</p>
<p><a class="admonition-anchor-link" href="#admonition-note-3"></a></p>
</div>
<div>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let x = 10;
x = 20;
<span class="boring">}</span></code></pre></pre>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let x = 10;
let x = 20;
<span class="boring">}</span></code></pre></pre>
</div>
</div>
<p>In a list:</p>
<ol>
<li>
<p>Thing one</p>
<pre><code class="language-sh">Thing one
</code></pre>
</li>
<li>
<p>Thing two</p>
<div id="admonition-note-4" class="admonition admonish-note">
<div class="admonition-title">
<p>Note</p>
<p><a class="admonition-anchor-link" href="#admonition-note-4"></a></p>
</div>
<div>
<p>Thing two</p>
</div>
</div>
</li>
<li>
<p>Thing three</p>
<pre><code class="language-sh">Thing three
</code></pre>
</li>
</ol>

View File

@@ -51,7 +51,21 @@ if [ "$DIFF_RESULT" != 0 ]; then
eprintln "error: generated html was different than expected"
eprintln ""
eprintln "error: If you expected the output to change, run:"
eprintln "./integration/update-snapshot"
eprintln "./integration/scripts/update-snapshot"
eprintln "and commit the result"
exit 1
fi
eprintln "Verifying mdbook test runs doctests"
set +e
TEST_RESULT="$(mdbook test 2>&1 | grep "1 passed; 1 failed")"
set -e
if [[ "$TEST_RESULT" != "test result: FAILED. 1 passed; 1 failed;"* ]]; then
eprintln ""
eprintln "error: mdbook test did not complete as expected"
eprintln ""
eprintln "Full output:"
mdbook test
exit 1
fi

View File

@@ -29,3 +29,35 @@ Hidden on load
Nested code block
```
````
````admonish
```rust
let x = 10;
x = 20;
```
```rust
let x = 10;
let x = 20;
```
````
In a list:
1. Thing one
```sh
Thing one
```
1. Thing two
```admonish
Thing two
```
1. Thing three
```sh
Thing three
```

View File

@@ -8,6 +8,21 @@ function eprintln() {
>&2 echo "$1"
}
# Node things
pushd compile_assets > /dev/null
eprintln "Linting style sources"
yarn run lint
eprintln "Checking compiled styles up to date"
COMITTED_ASSETS="$(cat ../src/bin/assets/mdbook-admonish.css)"
yarn run build
RECOMPILED_ASSETS="$(cat ../src/bin/assets/mdbook-admonish.css)"
diff -u <(printf "%s" "$COMITTED_ASSETS") <(printf "%s" "$RECOMPILED_ASSETS")
popd > /dev/null
# Rust things
eprintln "Formatting sources"
cargo fmt -- --check
@@ -24,5 +39,6 @@ cargo test --no-default-features --features cli
eprintln "Building documentation"
cargo doc --no-deps --lib
# Integration test
eprintln "Running mdbook integration test"
./integration/scripts/check

View File

@@ -4,10 +4,21 @@
#
# Does not include offline node development stack (i.e. yarn)
set -exuo pipefail
set -euo pipefail
function eprintln() {
>&2 echo "$1"
}
cd "$(dirname "$0")"/..
eprintln "Installing additional Rust components"
rustup component add rustfmt clippy
eprintln "Installing mdbook"
./scripts/install-mdbook
eprintln "Installing node dependencies"
pushd compile_assets > /dev/null
yarn install --frozen-lockfile
popd > /dev/null

View File

@@ -1,9 +1,18 @@
#!/bin/bash
set -exuo pipefail
set -euo pipefail
cd "$(dirname "$0")"/..
if ! mdbook --version; then
cargo install mdbook --force
function eprintln() {
>&2 echo "$1"
}
VERSION="0.4.35"
eprintln "Checking if mdbook $VERSION is installed"
if [[ "$(mdbook --version)" != "mdbook v$VERSION" ]]; then
eprintln "Installing mdbook $VERSION"
cargo install mdbook --version "$VERSION" --force
fi
eprintln "mdbook $VERSION is installed"

View File

@@ -1 +1 @@
^2.0.0
^3.0.0

View File

@@ -1 +1 @@
2.0.1
3.0.0

View File

@@ -1,31 +1,18 @@
@charset "UTF-8";
:root {
--md-admonition-icon--note:
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z'/></svg>");
--md-admonition-icon--abstract:
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17 9H7V7h10m0 6H7v-2h10m-3 6H7v-2h7M12 3a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z'/></svg>");
--md-admonition-icon--info:
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2z'/></svg>");
--md-admonition-icon--tip:
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17.66 11.2c-.23-.3-.51-.56-.77-.82-.67-.6-1.43-1.03-2.07-1.66C13.33 7.26 13 4.85 13.95 3c-.95.23-1.78.75-2.49 1.32-2.59 2.08-3.61 5.75-2.39 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.23.1-.47.04-.66-.12a.58.58 0 0 1-.14-.17c-1.13-1.43-1.31-3.48-.55-5.12C5.78 10 4.87 12.3 5 14.47c.06.5.12 1 .29 1.5.14.6.41 1.2.71 1.73 1.08 1.73 2.95 2.97 4.96 3.22 2.14.27 4.43-.12 6.07-1.6 1.83-1.66 2.47-4.32 1.53-6.6l-.13-.26c-.21-.46-.77-1.26-.77-1.26m-3.16 6.3c-.28.24-.74.5-1.1.6-1.12.4-2.24-.16-2.9-.82 1.19-.28 1.9-1.16 2.11-2.05.17-.8-.15-1.46-.28-2.23-.12-.74-.1-1.37.17-2.06.19.38.39.76.63 1.06.77 1 1.98 1.44 2.24 2.8.04.14.06.28.06.43.03.82-.33 1.72-.93 2.27z'/></svg>");
--md-admonition-icon--success:
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m9 20.42-6.21-6.21 2.83-2.83L9 14.77l9.88-9.89 2.83 2.83L9 20.42z'/></svg>");
--md-admonition-icon--question:
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m15.07 11.25-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8a4 4 0 0 1 4-4 4 4 0 0 1 4 4 3.2 3.2 0 0 1-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10c0-5.53-4.5-10-10-10z'/></svg>");
--md-admonition-icon--warning:
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 14h-2V9h2m0 9h-2v-2h2M1 21h22L12 2 1 21z'/></svg>");
--md-admonition-icon--failure:
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20 6.91 17.09 4 12 9.09 6.91 4 4 6.91 9.09 12 4 17.09 6.91 20 12 14.91 17.09 20 20 17.09 14.91 12 20 6.91z'/></svg>");
--md-admonition-icon--danger:
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 15H6l7-14v8h5l-7 14v-8z'/></svg>");
--md-admonition-icon--bug:
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 0 0-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 0 0-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z'/></svg>");
--md-admonition-icon--example:
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 0 1 .75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z'/></svg>");
--md-admonition-icon--quote:
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z'/></svg>");
--md-details-icon:
url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M8.59 16.58 13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42Z'/></svg>");
--md-admonition-icon--admonish-note: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z'/></svg>");
--md-admonition-icon--admonish-abstract: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17 9H7V7h10m0 6H7v-2h10m-3 6H7v-2h7M12 3a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1m7 0h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z'/></svg>");
--md-admonition-icon--admonish-info: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 9h-2V7h2m0 10h-2v-6h2m-1-9A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2z'/></svg>");
--md-admonition-icon--admonish-tip: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M17.66 11.2c-.23-.3-.51-.56-.77-.82-.67-.6-1.43-1.03-2.07-1.66C13.33 7.26 13 4.85 13.95 3c-.95.23-1.78.75-2.49 1.32-2.59 2.08-3.61 5.75-2.39 8.9.04.1.08.2.08.33 0 .22-.15.42-.35.5-.23.1-.47.04-.66-.12a.58.58 0 0 1-.14-.17c-1.13-1.43-1.31-3.48-.55-5.12C5.78 10 4.87 12.3 5 14.47c.06.5.12 1 .29 1.5.14.6.41 1.2.71 1.73 1.08 1.73 2.95 2.97 4.96 3.22 2.14.27 4.43-.12 6.07-1.6 1.83-1.66 2.47-4.32 1.53-6.6l-.13-.26c-.21-.46-.77-1.26-.77-1.26m-3.16 6.3c-.28.24-.74.5-1.1.6-1.12.4-2.24-.16-2.9-.82 1.19-.28 1.9-1.16 2.11-2.05.17-.8-.15-1.46-.28-2.23-.12-.74-.1-1.37.17-2.06.19.38.39.76.63 1.06.77 1 1.98 1.44 2.24 2.8.04.14.06.28.06.43.03.82-.33 1.72-.93 2.27z'/></svg>");
--md-admonition-icon--admonish-success: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m9 20.42-6.21-6.21 2.83-2.83L9 14.77l9.88-9.89 2.83 2.83L9 20.42z'/></svg>");
--md-admonition-icon--admonish-question: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='m15.07 11.25-.9.92C13.45 12.89 13 13.5 13 15h-2v-.5c0-1.11.45-2.11 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41a2 2 0 0 0-2-2 2 2 0 0 0-2 2H8a4 4 0 0 1 4-4 4 4 0 0 1 4 4 3.2 3.2 0 0 1-.93 2.25M13 19h-2v-2h2M12 2A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10c0-5.53-4.5-10-10-10z'/></svg>");
--md-admonition-icon--admonish-warning: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M13 14h-2V9h2m0 9h-2v-2h2M1 21h22L12 2 1 21z'/></svg>");
--md-admonition-icon--admonish-failure: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M20 6.91 17.09 4 12 9.09 6.91 4 4 6.91 9.09 12 4 17.09 6.91 20 12 14.91 17.09 20 20 17.09 14.91 12 20 6.91z'/></svg>");
--md-admonition-icon--admonish-danger: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M11 15H6l7-14v8h5l-7 14v-8z'/></svg>");
--md-admonition-icon--admonish-bug: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 12h-4v-2h4m0 6h-4v-2h4m6-6h-2.81a5.985 5.985 0 0 0-1.82-1.96L17 4.41 15.59 3l-2.17 2.17a6.002 6.002 0 0 0-2.83 0L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8z'/></svg>");
--md-admonition-icon--admonish-example: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M7 13v-2h14v2H7m0 6v-2h14v2H7M7 7V5h14v2H7M3 8V5H2V4h2v4H3m-1 9v-1h3v4H2v-1h2v-.5H3v-1h1V17H2m2.25-7a.75.75 0 0 1 .75.75c0 .2-.08.39-.21.52L3.12 13H5v1H2v-.92L4 11H2v-1h2.25z'/></svg>");
--md-admonition-icon--admonish-quote: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M14 17h3l2-4V7h-6v6h3M6 17h3l2-4V7H5v6h3l-2 4z'/></svg>");
--md-details-icon: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M8.59 16.58 13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.42Z'/></svg>");
}
:is(.admonition) {
@@ -75,7 +62,7 @@ a.admonition-anchor-link::before {
content: "§";
}
:is(.admonition-title, summary) {
:is(.admonition-title, summary.admonition-title) {
position: relative;
min-height: 4rem;
margin-block: 0;
@@ -86,13 +73,13 @@ a.admonition-anchor-link::before {
background-color: rgba(68, 138, 255, 0.1);
display: flex;
}
:is(.admonition-title, summary) p {
:is(.admonition-title, summary.admonition-title) p {
margin: 0;
}
html :is(.admonition-title, summary):last-child {
html :is(.admonition-title, summary.admonition-title):last-child {
margin-bottom: 0;
}
:is(.admonition-title, summary)::before {
:is(.admonition-title, summary.admonition-title)::before {
position: absolute;
top: 0.625em;
inset-inline-start: 1.6rem;
@@ -107,7 +94,7 @@ html :is(.admonition-title, summary):last-child {
-webkit-mask-size: contain;
content: "";
}
:is(.admonition-title, summary):hover a.admonition-anchor-link {
:is(.admonition-title, summary.admonition-title):hover a.admonition-anchor-link {
display: initial;
}
@@ -132,204 +119,204 @@ details[open].admonition > summary.admonition-title::after {
transform: rotate(90deg);
}
:is(.admonition):is(.note) {
:is(.admonition):is(.admonish-note) {
border-color: #448aff;
}
:is(.note) > :is(.admonition-title, summary) {
:is(.admonish-note) > :is(.admonition-title, summary.admonition-title) {
background-color: rgba(68, 138, 255, 0.1);
}
:is(.note) > :is(.admonition-title, summary)::before {
:is(.admonish-note) > :is(.admonition-title, summary.admonition-title)::before {
background-color: #448aff;
mask-image: var(--md-admonition-icon--note);
-webkit-mask-image: var(--md-admonition-icon--note);
mask-image: var(--md-admonition-icon--admonish-note);
-webkit-mask-image: var(--md-admonition-icon--admonish-note);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}
:is(.admonition):is(.abstract, .summary, .tldr) {
:is(.admonition):is(.admonish-abstract, .admonish-summary, .admonish-tldr) {
border-color: #00b0ff;
}
:is(.abstract, .summary, .tldr) > :is(.admonition-title, summary) {
:is(.admonish-abstract, .admonish-summary, .admonish-tldr) > :is(.admonition-title, summary.admonition-title) {
background-color: rgba(0, 176, 255, 0.1);
}
:is(.abstract, .summary, .tldr) > :is(.admonition-title, summary)::before {
:is(.admonish-abstract, .admonish-summary, .admonish-tldr) > :is(.admonition-title, summary.admonition-title)::before {
background-color: #00b0ff;
mask-image: var(--md-admonition-icon--abstract);
-webkit-mask-image: var(--md-admonition-icon--abstract);
mask-image: var(--md-admonition-icon--admonish-abstract);
-webkit-mask-image: var(--md-admonition-icon--admonish-abstract);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}
:is(.admonition):is(.info, .todo) {
:is(.admonition):is(.admonish-info, .admonish-todo) {
border-color: #00b8d4;
}
:is(.info, .todo) > :is(.admonition-title, summary) {
:is(.admonish-info, .admonish-todo) > :is(.admonition-title, summary.admonition-title) {
background-color: rgba(0, 184, 212, 0.1);
}
:is(.info, .todo) > :is(.admonition-title, summary)::before {
:is(.admonish-info, .admonish-todo) > :is(.admonition-title, summary.admonition-title)::before {
background-color: #00b8d4;
mask-image: var(--md-admonition-icon--info);
-webkit-mask-image: var(--md-admonition-icon--info);
mask-image: var(--md-admonition-icon--admonish-info);
-webkit-mask-image: var(--md-admonition-icon--admonish-info);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}
:is(.admonition):is(.tip, .hint, .important) {
:is(.admonition):is(.admonish-tip, .admonish-hint, .admonish-important) {
border-color: #00bfa5;
}
:is(.tip, .hint, .important) > :is(.admonition-title, summary) {
:is(.admonish-tip, .admonish-hint, .admonish-important) > :is(.admonition-title, summary.admonition-title) {
background-color: rgba(0, 191, 165, 0.1);
}
:is(.tip, .hint, .important) > :is(.admonition-title, summary)::before {
:is(.admonish-tip, .admonish-hint, .admonish-important) > :is(.admonition-title, summary.admonition-title)::before {
background-color: #00bfa5;
mask-image: var(--md-admonition-icon--tip);
-webkit-mask-image: var(--md-admonition-icon--tip);
mask-image: var(--md-admonition-icon--admonish-tip);
-webkit-mask-image: var(--md-admonition-icon--admonish-tip);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}
:is(.admonition):is(.success, .check, .done) {
:is(.admonition):is(.admonish-success, .admonish-check, .admonish-done) {
border-color: #00c853;
}
:is(.success, .check, .done) > :is(.admonition-title, summary) {
:is(.admonish-success, .admonish-check, .admonish-done) > :is(.admonition-title, summary.admonition-title) {
background-color: rgba(0, 200, 83, 0.1);
}
:is(.success, .check, .done) > :is(.admonition-title, summary)::before {
:is(.admonish-success, .admonish-check, .admonish-done) > :is(.admonition-title, summary.admonition-title)::before {
background-color: #00c853;
mask-image: var(--md-admonition-icon--success);
-webkit-mask-image: var(--md-admonition-icon--success);
mask-image: var(--md-admonition-icon--admonish-success);
-webkit-mask-image: var(--md-admonition-icon--admonish-success);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}
:is(.admonition):is(.question, .help, .faq) {
:is(.admonition):is(.admonish-question, .admonish-help, .admonish-faq) {
border-color: #64dd17;
}
:is(.question, .help, .faq) > :is(.admonition-title, summary) {
:is(.admonish-question, .admonish-help, .admonish-faq) > :is(.admonition-title, summary.admonition-title) {
background-color: rgba(100, 221, 23, 0.1);
}
:is(.question, .help, .faq) > :is(.admonition-title, summary)::before {
:is(.admonish-question, .admonish-help, .admonish-faq) > :is(.admonition-title, summary.admonition-title)::before {
background-color: #64dd17;
mask-image: var(--md-admonition-icon--question);
-webkit-mask-image: var(--md-admonition-icon--question);
mask-image: var(--md-admonition-icon--admonish-question);
-webkit-mask-image: var(--md-admonition-icon--admonish-question);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}
:is(.admonition):is(.warning, .caution, .attention) {
:is(.admonition):is(.admonish-warning, .admonish-caution, .admonish-attention) {
border-color: #ff9100;
}
:is(.warning, .caution, .attention) > :is(.admonition-title, summary) {
:is(.admonish-warning, .admonish-caution, .admonish-attention) > :is(.admonition-title, summary.admonition-title) {
background-color: rgba(255, 145, 0, 0.1);
}
:is(.warning, .caution, .attention) > :is(.admonition-title, summary)::before {
:is(.admonish-warning, .admonish-caution, .admonish-attention) > :is(.admonition-title, summary.admonition-title)::before {
background-color: #ff9100;
mask-image: var(--md-admonition-icon--warning);
-webkit-mask-image: var(--md-admonition-icon--warning);
mask-image: var(--md-admonition-icon--admonish-warning);
-webkit-mask-image: var(--md-admonition-icon--admonish-warning);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}
:is(.admonition):is(.failure, .fail, .missing) {
:is(.admonition):is(.admonish-failure, .admonish-fail, .admonish-missing) {
border-color: #ff5252;
}
:is(.failure, .fail, .missing) > :is(.admonition-title, summary) {
:is(.admonish-failure, .admonish-fail, .admonish-missing) > :is(.admonition-title, summary.admonition-title) {
background-color: rgba(255, 82, 82, 0.1);
}
:is(.failure, .fail, .missing) > :is(.admonition-title, summary)::before {
:is(.admonish-failure, .admonish-fail, .admonish-missing) > :is(.admonition-title, summary.admonition-title)::before {
background-color: #ff5252;
mask-image: var(--md-admonition-icon--failure);
-webkit-mask-image: var(--md-admonition-icon--failure);
mask-image: var(--md-admonition-icon--admonish-failure);
-webkit-mask-image: var(--md-admonition-icon--admonish-failure);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}
:is(.admonition):is(.danger, .error) {
:is(.admonition):is(.admonish-danger, .admonish-error) {
border-color: #ff1744;
}
:is(.danger, .error) > :is(.admonition-title, summary) {
:is(.admonish-danger, .admonish-error) > :is(.admonition-title, summary.admonition-title) {
background-color: rgba(255, 23, 68, 0.1);
}
:is(.danger, .error) > :is(.admonition-title, summary)::before {
:is(.admonish-danger, .admonish-error) > :is(.admonition-title, summary.admonition-title)::before {
background-color: #ff1744;
mask-image: var(--md-admonition-icon--danger);
-webkit-mask-image: var(--md-admonition-icon--danger);
mask-image: var(--md-admonition-icon--admonish-danger);
-webkit-mask-image: var(--md-admonition-icon--admonish-danger);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}
:is(.admonition):is(.bug) {
:is(.admonition):is(.admonish-bug) {
border-color: #f50057;
}
:is(.bug) > :is(.admonition-title, summary) {
:is(.admonish-bug) > :is(.admonition-title, summary.admonition-title) {
background-color: rgba(245, 0, 87, 0.1);
}
:is(.bug) > :is(.admonition-title, summary)::before {
:is(.admonish-bug) > :is(.admonition-title, summary.admonition-title)::before {
background-color: #f50057;
mask-image: var(--md-admonition-icon--bug);
-webkit-mask-image: var(--md-admonition-icon--bug);
mask-image: var(--md-admonition-icon--admonish-bug);
-webkit-mask-image: var(--md-admonition-icon--admonish-bug);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}
:is(.admonition):is(.example) {
:is(.admonition):is(.admonish-example) {
border-color: #7c4dff;
}
:is(.example) > :is(.admonition-title, summary) {
:is(.admonish-example) > :is(.admonition-title, summary.admonition-title) {
background-color: rgba(124, 77, 255, 0.1);
}
:is(.example) > :is(.admonition-title, summary)::before {
:is(.admonish-example) > :is(.admonition-title, summary.admonition-title)::before {
background-color: #7c4dff;
mask-image: var(--md-admonition-icon--example);
-webkit-mask-image: var(--md-admonition-icon--example);
mask-image: var(--md-admonition-icon--admonish-example);
-webkit-mask-image: var(--md-admonition-icon--admonish-example);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
}
:is(.admonition):is(.quote, .cite) {
:is(.admonition):is(.admonish-quote, .admonish-cite) {
border-color: #9e9e9e;
}
:is(.quote, .cite) > :is(.admonition-title, summary) {
:is(.admonish-quote, .admonish-cite) > :is(.admonition-title, summary.admonition-title) {
background-color: rgba(158, 158, 158, 0.1);
}
:is(.quote, .cite) > :is(.admonition-title, summary)::before {
:is(.admonish-quote, .admonish-cite) > :is(.admonition-title, summary.admonition-title)::before {
background-color: #9e9e9e;
mask-image: var(--md-admonition-icon--quote);
-webkit-mask-image: var(--md-admonition-icon--quote);
mask-image: var(--md-admonition-icon--admonish-quote);
-webkit-mask-image: var(--md-admonition-icon--admonish-quote);
mask-repeat: no-repeat;
-webkit-mask-repeat: no-repeat;
mask-size: contain;
@@ -340,7 +327,8 @@ details[open].admonition > summary.admonition-title::after {
background-color: var(--sidebar-bg);
}
.ayu :is(.admonition), .coal :is(.admonition) {
.ayu :is(.admonition),
.coal :is(.admonition) {
background-color: var(--theme-hover);
}

60
src/book_config.rs Normal file
View File

@@ -0,0 +1,60 @@
use anyhow::{Context, Result};
use mdbook::preprocess::PreprocessorContext;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::types::AdmonitionDefaults;
/// Loads the plugin configuration from mdbook internals.
///
/// Roundtrips config to string, to avoid linking the plugin's internal version of toml
/// to the one publically exposed by the mdbook library.
pub(crate) fn admonish_config_from_context(ctx: &PreprocessorContext) -> Result<Config> {
let table: String = toml_mdbook::to_string(
ctx.config
.get_preprocessor("admonish")
.context("No configuration for mdbook-admonish in book.toml")?,
)?;
toml::from_str(&table).context("Invalid mdbook-admonish configuration in book.toml")
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub(crate) struct Config {
#[serde(default)]
pub on_failure: OnFailure,
#[serde(default)]
pub default: AdmonitionDefaults,
#[serde(default)]
pub renderer: HashMap<String, RendererConfig>,
#[serde(default)]
pub assets_version: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub(crate) struct RendererConfig {
pub render_mode: Option<RenderMode>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub(crate) enum RenderMode {
Preserve,
Strip,
Html,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub(crate) enum OnFailure {
Bail,
Continue,
}
impl Default for OnFailure {
fn default() -> Self {
Self::Continue
}
}

1009
src/lib.rs

File diff suppressed because it is too large Load Diff

858
src/markdown.rs Normal file
View File

@@ -0,0 +1,858 @@
use mdbook::errors::Result as MdbookResult;
use pulldown_cmark::{CodeBlockKind::*, Event, Options, Parser, Tag};
pub use crate::preprocessor::Admonish;
use crate::{
book_config::OnFailure,
parse::parse_admonition,
types::{AdmonitionDefaults, RenderTextMode},
};
pub(crate) fn preprocess(
content: &str,
on_failure: OnFailure,
admonition_defaults: &AdmonitionDefaults,
render_text_mode: RenderTextMode,
) -> MdbookResult<String> {
let mut id_counter = Default::default();
let mut opts = Options::empty();
opts.insert(Options::ENABLE_TABLES);
opts.insert(Options::ENABLE_FOOTNOTES);
opts.insert(Options::ENABLE_STRIKETHROUGH);
opts.insert(Options::ENABLE_TASKLISTS);
let mut admonish_blocks = vec![];
let events = Parser::new_ext(content, opts);
for (event, span) in events.into_offset_iter() {
if let Event::Start(Tag::CodeBlock(Fenced(info_string))) = event.clone() {
let span_content = &content[span.start..span.end];
const INDENT_SCAN_MAX: usize = 1024;
let indent = indent_of(content, span.start, INDENT_SCAN_MAX);
let admonition = match parse_admonition(
info_string.as_ref(),
admonition_defaults,
span_content,
on_failure,
indent,
) {
Some(admonition) => admonition,
None => continue,
};
let admonition = admonition?;
// 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::Strip => admonition.strip(),
};
admonish_blocks.push((span, new_content));
}
}
let mut content = content.to_string();
for (span, block) in admonish_blocks.iter().rev() {
let pre_content = &content[..span.start];
let post_content = &content[span.end..];
content = format!("{}{}{}", pre_content, block, post_content);
}
Ok(content)
}
/// Returns the indent of the given position.
///
/// Defined as the number of characters between the given `position` (where
/// position is a valid char boundary byte-index in `content`),
/// and the previous newline character `\n`.
///
/// `max` is the maximum number of characters to scan before assuming there is
/// no indent (will return zero if exceeded).
///
/// ## Panics
///
/// Will panic if `position` is not a valid utf-8 char boundary index of `content`.
fn indent_of(content: &str, position: usize, max: usize) -> usize {
// Scan for a line start before this span.
content[..position]
.chars()
.rev()
// For safety, only scan up to a fixed limit of the text
.take(max)
.position(|c| c == '\n')
// If we can't find a newline, assume no indent
.unwrap_or_default()
}
#[cfg(test)]
mod test {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn indent_of_samples() {
for (content, position, max, expected) in [
// Empty case
("", 0, 10, 0),
("no newline", 4, 10, 0),
// Newline at position 5, difference from 8 is 3
("with\nnewline", 8, 10, 3),
// If no newline in safety limit, return 0
("with\nnewline", 8, 2, 0),
// Safety limit is characters, not bytes
// Regression test for FIXME LINK
(
"例えばこれは",
// Position is generated by mdbook internals, should be valid char limit
// This mimics the second character starting the span
"".len(),
// Any arbitrary safetly limit should be valid
1,
// Should not panic
0,
),
(
"例え\n れは",
// Position is generated by mdbook internals, should be valid char limit
// This mimics the second character starting the span
"例え\n ".len(),
// Any arbitrary safetly limit should be valid
4,
// Should not panic
2,
),
] {
let actual = indent_of(content, position, max);
assert_eq!(actual, expected);
}
}
fn prep(content: &str) -> String {
preprocess(
content,
OnFailure::Continue,
&AdmonitionDefaults::default(),
RenderTextMode::Html,
)
.unwrap()
}
#[test]
fn adds_admonish() {
let content = r#"# Chapter
```admonish
A simple admonition.
```
Text
"#;
let expected = r##"# Chapter
<div id="admonition-note" class="admonition admonish-note">
<div class="admonition-title">
Note
<a class="admonition-anchor-link" href="#admonition-note"></a>
</div>
<div>
A simple admonition.
</div>
</div>
Text
"##;
assert_eq!(expected, prep(content));
}
#[test]
fn adds_admonish_longer_code_fence() {
let content = r#"# Chapter
````admonish
```json
{}
```
````
Text
"#;
let expected = r##"# Chapter
<div id="admonition-note" class="admonition admonish-note">
<div class="admonition-title">
Note
<a class="admonition-anchor-link" href="#admonition-note"></a>
</div>
<div>
```json
{}
```
</div>
</div>
Text
"##;
assert_eq!(expected, prep(content));
}
#[test]
fn adds_admonish_directive() {
let content = r#"# Chapter
```admonish warning
A simple admonition.
```
Text
"#;
let expected = r##"# Chapter
<div id="admonition-warning" class="admonition admonish-warning">
<div class="admonition-title">
Warning
<a class="admonition-anchor-link" href="#admonition-warning"></a>
</div>
<div>
A simple admonition.
</div>
</div>
Text
"##;
assert_eq!(expected, prep(content));
}
#[test]
fn adds_admonish_directive_alternate() {
let content = r#"# Chapter
```admonish caution
A warning with alternate title.
```
Text
"#;
let expected = r##"# Chapter
<div id="admonition-caution" class="admonition admonish-warning">
<div class="admonition-title">
Caution
<a class="admonition-anchor-link" href="#admonition-caution"></a>
</div>
<div>
A warning with alternate title.
</div>
</div>
Text
"##;
assert_eq!(expected, prep(content));
}
#[test]
fn adds_admonish_directive_title() {
let content = r#"# Chapter
```admonish warning "Read **this**!"
A simple admonition.
```
Text
"#;
let expected = r##"# Chapter
<div id="admonition-read-this" class="admonition admonish-warning">
<div class="admonition-title">
Read **this**!
<a class="admonition-anchor-link" href="#admonition-read-this"></a>
</div>
<div>
A simple admonition.
</div>
</div>
Text
"##;
assert_eq!(expected, prep(content));
}
#[test]
fn leaves_tables_untouched() {
// Regression test.
// Previously we forgot to enable the same markdwon extensions as mdbook itself.
let content = r#"# Heading
| Head 1 | Head 2 |
|--------|--------|
| Row 1 | Row 2 |
"#;
let expected = r#"# Heading
| Head 1 | Head 2 |
|--------|--------|
| Row 1 | Row 2 |
"#;
assert_eq!(expected, prep(content));
}
#[test]
fn leaves_html_untouched() {
// Regression test.
// Don't remove important newlines for syntax nested inside HTML
let content = r#"# Heading
<del>
*foo*
</del>
"#;
let expected = r#"# Heading
<del>
*foo*
</del>
"#;
assert_eq!(expected, prep(content));
}
#[test]
fn html_in_list() {
// Regression test.
// Don't remove important newlines for syntax nested inside HTML
let content = r#"# Heading
1. paragraph 1
```
code 1
```
2. paragraph 2
"#;
let expected = r#"# Heading
1. paragraph 1
```
code 1
```
2. paragraph 2
"#;
assert_eq!(expected, prep(content));
}
#[test]
fn info_string_that_changes_length_when_parsed() {
let content = r#"
```admonish note "And \\"<i>in</i>\\" the title"
With <b>html</b> styling.
```
hello
"#;
let expected = r##"
<div id="admonition-and-in-the-title" class="admonition admonish-note">
<div class="admonition-title">
And "<i>in</i>" the title
<a class="admonition-anchor-link" href="#admonition-and-in-the-title"></a>
</div>
<div>
With <b>html</b> styling.
</div>
</div>
hello
"##;
assert_eq!(expected, prep(content));
}
#[test]
fn info_string_ending_in_symbol() {
let content = r#"
```admonish warning "Trademark™"
Should be respected
```
hello
"#;
let expected = r##"
<div id="admonition-trademark" class="admonition admonish-warning">
<div class="admonition-title">
Trademark™
<a class="admonition-anchor-link" href="#admonition-trademark"></a>
</div>
<div>
Should be respected
</div>
</div>
hello
"##;
assert_eq!(expected, prep(content));
}
#[test]
fn block_with_additional_classname() {
let content = r#"
```admonish tip.my-style.other-style
Will have bonus classnames
```
"#;
let expected = r##"
<div id="admonition-tip" class="admonition admonish-tip my-style other-style">
<div class="admonition-title">
Tip
<a class="admonition-anchor-link" href="#admonition-tip"></a>
</div>
<div>
Will have bonus classnames
</div>
</div>
"##;
assert_eq!(expected, prep(content));
}
#[test]
fn block_with_additional_classname_and_title() {
let content = r#"
```admonish tip.my-style.other-style "Developers don't want you to know this one weird tip!"
Will have bonus classnames
```
"#;
let expected = r##"
<div id="admonition-developers-dont-want-you-to-know-this-one-weird-tip" class="admonition admonish-tip my-style other-style">
<div class="admonition-title">
Developers don't want you to know this one weird tip!
<a class="admonition-anchor-link" href="#admonition-developers-dont-want-you-to-know-this-one-weird-tip"></a>
</div>
<div>
Will have bonus classnames
</div>
</div>
"##;
assert_eq!(expected, prep(content));
}
#[test]
fn block_with_empty_additional_classnames_title_content() {
let content = r#"
```admonish .... ""
```
"#;
let expected = r#"
<div id="admonition-default" class="admonition admonish-note">
<div>
</div>
</div>
"#;
assert_eq!(expected, prep(content));
}
#[test]
fn unique_ids_same_title() {
let content = r#"
```admonish note "My Note"
Content zero.
```
```admonish note "My Note"
Content one.
```
"#;
let expected = r##"
<div id="admonition-my-note" class="admonition admonish-note">
<div class="admonition-title">
My Note
<a class="admonition-anchor-link" href="#admonition-my-note"></a>
</div>
<div>
Content zero.
</div>
</div>
<div id="admonition-my-note-1" class="admonition admonish-note">
<div class="admonition-title">
My Note
<a class="admonition-anchor-link" href="#admonition-my-note-1"></a>
</div>
<div>
Content one.
</div>
</div>
"##;
assert_eq!(expected, prep(content));
}
#[test]
fn v2_config_works() {
let content = r#"
```admonish tip class="my other-style" title="Article Heading"
Bonus content!
```
"#;
let expected = r##"
<div id="admonition-article-heading" class="admonition admonish-tip my other-style">
<div class="admonition-title">
Article Heading
<a class="admonition-anchor-link" href="#admonition-article-heading"></a>
</div>
<div>
Bonus content!
</div>
</div>
"##;
assert_eq!(expected, prep(content));
}
#[test]
fn continue_on_error_output() {
let content = r#"
```admonish title="
Bonus content!
```
"#;
let expected = r##"
<div id="admonition-error-rendering-admonishment" class="admonition admonish-bug">
<div class="admonition-title">
Error rendering admonishment
<a class="admonition-anchor-link" href="#admonition-error-rendering-admonishment"></a>
</div>
<div>
Failed with:
```log
TOML parsing error: TOML parse error at line 1, column 8
|
1 | title="
| ^
invalid basic string
```
Original markdown input:
````markdown
```admonish title="
Bonus content!
```
````
</div>
</div>
"##;
assert_eq!(expected, prep(content));
}
#[test]
fn bail_on_error_output() {
let content = r#"
```admonish title="
Bonus content!
```
"#;
assert_eq!(
preprocess(
content,
OnFailure::Bail,
&AdmonitionDefaults::default(),
RenderTextMode::Html
)
.unwrap_err()
.to_string(),
r#"Error processing admonition, bailing:
```admonish title="
Bonus content!
```"#
.to_owned()
)
}
#[test]
fn test_renderer_strip_explicit() {
let content = r#"
````admonish title="Title"
```rust
let x = 10;
x = 20;
```
````
"#;
assert_eq!(
preprocess(
content,
OnFailure::Bail,
&AdmonitionDefaults::default(),
RenderTextMode::Strip
)
.unwrap(),
r#"
```rust
let x = 10;
x = 20;
```
"#
.to_owned()
)
}
#[test]
fn block_collapsible() {
let content = r#"
```admonish collapsible=true
Hidden
```
"#;
let expected = r##"
<details id="admonition-note" class="admonition admonish-note">
<summary class="admonition-title">
Note
<a class="admonition-anchor-link" href="#admonition-note"></a>
</summary>
<div>
Hidden
</div>
</details>
"##;
assert_eq!(expected, prep(content));
}
#[test]
fn default_toml_title() {
let content = r#"# Chapter
```admonish
A simple admonition.
```
Text
"#;
let expected = r##"# Chapter
<div id="admonition-admonish" class="admonition admonish-note">
<div class="admonition-title">
Admonish
<a class="admonition-anchor-link" href="#admonition-admonish"></a>
</div>
<div>
A simple admonition.
</div>
</div>
Text
"##;
let preprocess_result = preprocess(
content,
OnFailure::Continue,
&AdmonitionDefaults {
title: Some("Admonish".to_owned()),
collapsible: false,
},
RenderTextMode::Html,
)
.unwrap();
assert_eq!(expected, preprocess_result);
}
#[test]
fn empty_explicit_title_with_default() {
let content = r#"# Chapter
```admonish title=""
A simple admonition.
```
Text
"#;
let expected = r#"# Chapter
<div id="admonition-default" class="admonition admonish-note">
<div>
A simple admonition.
</div>
</div>
Text
"#;
let preprocess_result = preprocess(
content,
OnFailure::Continue,
&AdmonitionDefaults {
title: Some("Admonish".to_owned()),
collapsible: false,
},
RenderTextMode::Html,
)
.unwrap();
assert_eq!(expected, preprocess_result);
}
#[test]
fn empty_explicit_title() {
let content = r#"# Chapter
```admonish title=""
A simple admonition.
```
Text
"#;
let expected = r#"# Chapter
<div id="admonition-default" class="admonition admonish-note">
<div>
A simple admonition.
</div>
</div>
Text
"#;
assert_eq!(expected, prep(content));
}
#[test]
fn list_embed() {
let content = r#"# Chapter
1. Thing one
```sh
Thing one
```
1. Thing two
```admonish
Thing two
```
1. Thing three
```sh
Thing three
```
"#;
let expected = r##"# Chapter
1. Thing one
```sh
Thing one
```
1. Thing two
<div id="admonition-note" class="admonition admonish-note">
<div class="admonition-title">
Note
<a class="admonition-anchor-link" href="#admonition-note"></a>
</div>
<div>
Thing two
</div>
</div>
1. Thing three
```sh
Thing three
```
"##;
assert_eq!(expected, prep(content));
}
}

View File

@@ -1,3 +1,95 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub use crate::preprocessor::Admonish;
use crate::{
book_config::OnFailure,
render::Admonition,
resolve::AdmonitionMeta,
types::{AdmonitionDefaults, Directive},
};
/// Given the content in the span of the code block, and the info string,
/// return `Some(Admonition)` if the code block is an admonition.
///
/// If there is an error parsing the admonition, either:
///
/// - Display a UI error message output in the book.
/// - If configured, break the build.
///
/// If the code block is not an admonition, return `None`.
pub(crate) fn parse_admonition<'a>(
info_string: &'a str,
admonition_defaults: &'a AdmonitionDefaults,
content: &'a str,
on_failure: OnFailure,
indent: usize,
) -> Option<Result<Admonition<'a>>> {
// We need to know fence details anyway for error messages
let extracted = extract_admonish_body(content);
let info = AdmonitionMeta::from_info_string(info_string, admonition_defaults)?;
let info = match info {
Ok(info) => info,
Err(message) => {
// Construct a fence capable of enclosing whatever we wrote for the
// actual input block
let fence = extracted.fence;
let enclosing_fence: String = std::iter::repeat(fence.character)
.take(fence.length + 1)
.collect();
return Some(match on_failure {
OnFailure::Continue => {
log::warn!(
r#"Error processing admonition. To fail the build instead of continuing, set 'on_failure = "bail"'"#
);
Ok(Admonition {
directive: Directive::Bug,
title: "Error rendering admonishment".to_owned(),
additional_classnames: Vec::new(),
collapsible: false,
content: Cow::Owned(format!(
r#"Failed with:
```log
{message}
```
Original markdown input:
{enclosing_fence}markdown
{content}
{enclosing_fence}
"#
)),
indent,
})
}
OnFailure::Bail => Err(anyhow!("Error processing admonition, bailing:\n{content}")),
});
}
};
Some(Ok(Admonition::new(
info,
extracted.body,
// Note that this is a bit hacky - the fence information comes from the start
// of the block, and includes the whole line.
//
// This is more likely to be what we want, as ending indentation is unrelated
// according to the commonmark spec (ref https://spec.commonmark.org/0.12/#example-85)
//
// The main case we're worried about here is indenting enough to be inside list items,
// and in this case the starting code fence must be indented enough to be considered
// part of the list item.
//
// The hacky thing is that we're considering line indent in the document as a whole,
// not relative to the context of some containing item. But I think that's what we
// want for now, anyway.
indent,
)))
}
/// We can't trust the info string length to find the start of the body
/// it may change length if it contains HTML or character escapes.
///
@@ -25,7 +117,7 @@ fn extract_admonish_body_start_index(content: &str) -> usize {
}
fn extract_admonish_body_end_index(content: &str) -> (usize, Fence) {
let fence_character = content.chars().rev().next().unwrap_or('`');
let fence_character = content.chars().next_back().unwrap_or('`');
let number_fence_characters = content
.chars()
.rev()
@@ -38,21 +130,21 @@ fn extract_admonish_body_end_index(content: &str) -> (usize, Fence) {
}
#[derive(Debug, PartialEq)]
pub(crate) struct Fence {
pub(crate) character: char,
pub(crate) length: usize,
struct Fence {
character: char,
length: usize,
}
impl Fence {
pub fn new(character: char, length: usize) -> Self {
fn new(character: char, length: usize) -> Self {
Self { character, length }
}
}
#[derive(Debug, PartialEq)]
pub(crate) struct Extracted<'a> {
pub(crate) body: &'a str,
pub(crate) fence: Fence,
struct Extracted<'a> {
body: &'a str,
fence: Fence,
}
/// Given the whole text content of the code fence, extract the body.
@@ -61,7 +153,7 @@ pub(crate) struct Extracted<'a> {
/// but it's not really clear a good way of doing that.
///
/// ref: https://spec.commonmark.org/0.30/#fenced-code-blocks
pub(crate) fn extract_admonish_body(content: &str) -> Extracted<'_> {
fn extract_admonish_body(content: &str) -> Extracted<'_> {
let start_index = extract_admonish_body_start_index(content);
let (end_index, fence) = extract_admonish_body_end_index(content);

259
src/preprocessor.rs Normal file
View File

@@ -0,0 +1,259 @@
use anyhow::{anyhow, Result};
use mdbook::{
book::{Book, BookItem},
errors::Result as MdbookResult,
preprocess::{Preprocessor, PreprocessorContext},
};
use crate::{
book_config::{admonish_config_from_context, Config, RenderMode},
markdown::preprocess,
types::RenderTextMode,
};
pub struct Admonish;
impl Preprocessor for Admonish {
fn name(&self) -> &str {
"admonish"
}
fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> MdbookResult<Book> {
let config = admonish_config_from_context(ctx)?;
ensure_compatible_assets_version(&config)?;
let on_failure = config.on_failure;
let admonition_defaults = config.default;
// Load what rendering we should do from config, falling back to a default
let render_mode = config
.renderer
.get(&ctx.renderer)
.and_then(|renderer| renderer.render_mode)
.unwrap_or_else(|| {
// By default only render html for the html renderer
// For everything else, do nothing
if &ctx.renderer == "html" {
RenderMode::Html
} else {
RenderMode::Preserve
}
});
let render_text_mode = match render_mode {
RenderMode::Preserve => return Ok(book),
RenderMode::Html => RenderTextMode::Html,
RenderMode::Strip => RenderTextMode::Strip,
};
let mut res = None;
book.for_each_mut(|item: &mut BookItem| {
if let Some(Err(_)) = res {
return;
}
if let BookItem::Chapter(ref mut chapter) = *item {
res = Some(
preprocess(
&chapter.content,
on_failure,
&admonition_defaults,
render_text_mode,
)
.map(|md| {
chapter.content = md;
}),
);
}
});
res.unwrap_or(Ok(())).map(|_| book)
}
fn supports_renderer(&self, _renderer: &str) -> bool {
// We support all renderers, but will only actually take action
// if configured to do so - or, if it's the html renderer
true
}
}
fn ensure_compatible_assets_version(config: &Config) -> Result<()> {
use semver::{Version, VersionReq};
const REQUIRES_ASSETS_VERSION: &str = std::include_str!("./REQUIRED_ASSETS_VERSION");
let requirement = VersionReq::parse(REQUIRES_ASSETS_VERSION.trim()).unwrap();
const USER_ACTION: &str = "Please run `mdbook-admonish install` to update installed assets.";
const DOCS_REFERENCE: &str = "For more information, see: https://github.com/tommilligan/mdbook-admonish#semantic-versioning";
let version = match &config.assets_version {
Some(version) => version,
None => {
return Err(anyhow!(
r#"ERROR:
Incompatible assets installed: required mdbook-admonish assets version '{requirement}', but did not find a version.
{USER_ACTION}
{DOCS_REFERENCE}"#
))
}
};
let version = Version::parse(version).unwrap();
if !requirement.matches(&version) {
return Err(anyhow!(
r#"ERROR:
Incompatible assets installed: required mdbook-admonish assets version '{requirement}', but found '{version}'.
{USER_ACTION}
{DOCS_REFERENCE}"#
));
};
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
use pretty_assertions::assert_eq;
use serde_json::{json, Value};
fn mock_book(content: &str) -> Book {
serde_json::from_value(json!({
"sections": [
{
"Chapter": {
"name": "Chapter 1",
"content": content,
"number": [1],
"sub_items": [],
"path": "chapter_1.md",
"source_path": "chapter_1.md",
"parent_names": []
}
}
],
"__non_exhaustive": null
}))
.unwrap()
}
fn mock_context(admonish: &Value, renderer: &str) -> PreprocessorContext {
let value = json!({
"root": "/path/to/book",
"config": {
"book": {
"authors": ["AUTHOR"],
"language": "en",
"multilingual": false,
"src": "src",
"title": "TITLE"
},
"preprocessor": {
"admonish": admonish,
}
},
"renderer": renderer,
"mdbook_version": "0.4.21"
});
serde_json::from_value(value).unwrap()
}
#[test]
fn run_html() {
let content = r#"
````admonish title="Title"
```rust
let x = 10;
x = 20;
```
````
"#;
let expected_content = r##"
<div id="admonition-title" class="admonition admonish-note">
<div class="admonition-title">
Title
<a class="admonition-anchor-link" href="#admonition-title"></a>
</div>
<div>
```rust
let x = 10;
x = 20;
```
</div>
</div>
"##;
let ctx = mock_context(
&json!({
"assets_version": "3.0.0"
}),
"html",
);
let book = mock_book(content);
let expected_book = mock_book(expected_content);
assert_eq!(Admonish.run(&ctx, book).unwrap(), expected_book)
}
#[test]
fn run_test_preserves_by_default() {
let content = r#"
````admonish title="Title"
```rust
let x = 10;
x = 20;
```
````
"#;
let ctx = mock_context(
&json!({
"assets_version": "3.0.0"
}),
"test",
);
let book = mock_book(content);
let expected_book = book.clone();
assert_eq!(Admonish.run(&ctx, book).unwrap(), expected_book)
}
#[test]
fn run_test_can_strip() {
let content = r#"
````admonish title="Title"
```rust
let x = 10;
x = 20;
```
````
"#;
let expected_content = r#"
```rust
let x = 10;
x = 20;
```
"#;
let ctx = mock_context(
&json!({
"assets_version": "3.0.0",
"renderer": {
"test": {
"render_mode": "strip",
},
},
}),
"test",
);
let book = mock_book(content);
let expected_book = mock_book(expected_content);
assert_eq!(Admonish.run(&ctx, book).unwrap(), expected_book)
}
}

125
src/render.rs Normal file
View File

@@ -0,0 +1,125 @@
use mdbook::utils::unique_id_from_content;
use std::borrow::Cow;
use std::collections::HashMap;
pub use crate::preprocessor::Admonish;
use crate::{resolve::AdmonitionMeta, types::Directive};
impl Directive {
fn classname(&self) -> &'static str {
match self {
Directive::Note => "admonish-note",
Directive::Abstract => "admonish-abstract",
Directive::Info => "admonish-info",
Directive::Tip => "admonish-tip",
Directive::Success => "admonish-success",
Directive::Question => "admonish-question",
Directive::Warning => "admonish-warning",
Directive::Failure => "admonish-failure",
Directive::Danger => "admonish-danger",
Directive::Bug => "admonish-bug",
Directive::Example => "admonish-example",
Directive::Quote => "admonish-quote",
}
}
}
#[derive(Debug, PartialEq)]
pub(crate) struct Admonition<'a> {
pub(crate) directive: Directive,
pub(crate) title: String,
pub(crate) content: Cow<'a, str>,
pub(crate) additional_classnames: Vec<String>,
pub(crate) collapsible: bool,
pub(crate) indent: usize,
}
impl<'a> Admonition<'a> {
pub(crate) fn new(info: AdmonitionMeta, content: &'a str, indent: usize) -> Self {
let AdmonitionMeta {
directive,
title,
additional_classnames,
collapsible,
} = info;
Self {
directive,
title,
content: Cow::Borrowed(content),
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)
}
fn html(&self, anchor_id: &str) -> String {
let mut additional_class = Cow::Borrowed(self.directive.classname());
let title = &self.title;
let content = &self.content;
let indent = " ".repeat(self.indent);
let title_block = if self.collapsible { "summary" } else { "div" };
let title_html = if !title.is_empty() {
Cow::Owned(format!(
r##"{indent}<{title_block} class="admonition-title">
{indent}
{indent}{title}
{indent}
{indent}<a class="admonition-anchor-link" href="#{ANCHOR_ID_PREFIX}-{anchor_id}"></a>
{indent}</{title_block}>
"##
))
} else {
Cow::Borrowed("")
};
if !self.additional_classnames.is_empty() {
let mut buffer = additional_class.into_owned();
for additional_classname in &self.additional_classnames {
buffer.push(' ');
buffer.push_str(additional_classname);
}
additional_class = Cow::Owned(buffer);
}
let admonition_block = if self.collapsible { "details" } else { "div" };
// Notes on the HTML template:
// - the additional whitespace around the content are deliberate
// In line with the commonmark spec, this allows the inner content to be
// rendered as markdown paragraphs.
format!(
r#"
{indent}<{admonition_block} id="{ANCHOR_ID_PREFIX}-{anchor_id}" class="admonition {additional_class}">
{title_html}{indent}<div>
{indent}
{indent}{content}
{indent}
{indent}</div>
{indent}</{admonition_block}>"#,
)
}
/// Strips all admonish syntax, leaving the plain content of the block.
pub(crate) fn strip(&self) -> String {
// Add in newlines to preserve line numbering for test output
// These replace the code fences we stripped out
format!("\n{}\n", self.content)
}
}
const ANCHOR_ID_PREFIX: &str = "admonition";
const ANCHOR_ID_DEFAULT: &str = "default";

View File

@@ -34,7 +34,7 @@ impl AdmonitionMeta {
// Use values from block, else load default value
let title = title.or_else(|| defaults.title.clone());
let collapsible = collapsible.or(defaults.collapsible).unwrap_or_default();
let collapsible = collapsible.unwrap_or(defaults.collapsible);
// Load the directive (and title, if one still not given)
let (directive, title) = match (Directive::from_str(&raw_directive), title) {
@@ -89,4 +89,28 @@ mod test {
}
);
}
#[test]
fn test_admonition_info_from_raw_with_defaults() {
assert_eq!(
AdmonitionMeta::resolve(
InstanceConfig {
directive: " ".to_owned(),
title: None,
additional_classnames: Vec::new(),
collapsible: None,
},
&AdmonitionDefaults {
title: Some("Important!!!".to_owned()),
collapsible: true,
},
),
AdmonitionMeta {
directive: Directive::Note,
title: "Important!!!".to_owned(),
additional_classnames: Vec::new(),
collapsible: true,
}
);
}
}

View File

@@ -2,13 +2,13 @@ use serde::{Deserialize, Serialize};
use std::str::FromStr;
/// Book wide defaults that may be provided by the user.
#[derive(Deserialize, Serialize, Debug, Default)]
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Default)]
pub(crate) struct AdmonitionDefaults {
#[serde(default)]
pub(crate) title: Option<String>,
#[serde(default)]
pub(crate) collapsible: Option<bool>,
pub(crate) collapsible: bool,
}
#[derive(Debug, PartialEq)]
@@ -48,3 +48,9 @@ impl FromStr for Directive {
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum RenderTextMode {
Strip,
Html,
}