Compare commits

...

50 Commits

Author SHA1 Message Date
Tom Milligan
ea6c0a687a Merge pull request #229 from tommilligan/prep-1.20.0
chore: prep v1.20.0 release
2025-06-06 14:53:02 +01:00
Tom Milligan
50b9187ee5 chore: prep v1.20.0 release 2025-06-06 14:35:51 +01:00
Tom Milligan
fbd07ecaf0 Merge pull request #228 from tommilligan/print-collapsed-details
feat: show collapsed content in print view
2025-06-06 14:21:49 +01:00
Tom Milligan
37d03b872e feat: show collapsed content in print view 2025-06-06 12:58:11 +01:00
Tom Milligan
fa91fe3778 Merge pull request #227 from tommilligan/update-deps-2025-06
chore: update dependencies
2025-06-06 12:37:47 +01:00
Tom Milligan
d59dce3533 chore: update dependencies 2025-06-06 12:34:38 +01:00
Tom Milligan
21d86546ff Merge pull request #224 from tommilligan/fix-nested-list-content-indent
fix: do not double indent content in lists
2025-02-17 15:55:07 +00:00
Tom Milligan
79ee592f56 chore: prep v1.19.0 release 2025-02-17 15:42:27 +00:00
Tom Milligan
0d3eb11a2c fix: do not double indent content in lists 2025-02-17 15:38:33 +00:00
CoralPink
43579f902b Hide extra markers displayed in Safari. (#185)
* Hide extra markers displayed in `Safari`.

* [review] move webkit fix

---------

Co-authored-by: Tom Milligan <tom.milligan@uipath.com>
2024-08-17 06:19:01 +01:00
Tom Milligan
7ee3643dc3 Merge pull request #209 from tommilligan/deps-upgrade-2
chore: relax action constraints
2024-08-16 08:54:37 +01:00
Tom Milligan
c4bf2608e7 chore: relax action constraints 2024-08-16 08:50:25 +01:00
Tom Milligan
d4667a5f55 Merge pull request #208 from tommilligan/deps-upgrade
chore: upgrade rust, deps and actions
2024-08-16 08:39:23 +01:00
Tom Milligan
8751b3cd24 chore: upgrade github actions 2024-08-16 07:56:14 +01:00
Tom Milligan
516bbc6e7e chore: update rust lockfile 2024-08-16 07:48:11 +01:00
Tom Milligan
fc5c5eae39 chore: bump msrvc to 1.76.0 2024-08-16 07:46:50 +01:00
Tom Milligan
147a9bdb98 Merge pull request #199 from tommilligan/prep-1.18.0
chore: prep 1.18.0 release
2024-06-20 15:26:16 +01:00
Tom Milligan
540ea8e5e8 chore: prep 1.18.0 release 2024-06-20 14:43:12 +01:00
Tom Milligan
ae4bac21ba Merge pull request #198 from tommilligan/upgrade-deps
chore: upgrade deps
2024-06-20 10:53:11 +01:00
Tom Milligan
f0c4d3764e chore: upgrade deps 2024-06-20 10:42:01 +01:00
Tom Milligan
a942a8094e Merge pull request #197 from tommilligan/toml-serialize-error
fix: use up to date toml for serialization
2024-06-20 10:40:44 +01:00
Tom Milligan
d10a427b0f fix: use up to date toml for serialization 2024-06-20 10:05:48 +01:00
Tom Milligan
41a3b05094 Merge pull request #195 from tommilligan/aria-labelledby
feat: add aria attributes to block
2024-06-15 19:51:45 +01:00
Tom Milligan
d0f73baa3f Merge pull request #194 from tommilligan/readme-examples
readme: add more example projects
2024-06-15 13:46:16 +01:00
Tom Milligan
4c40418090 feat: add aria attributes to block 2024-06-15 13:43:58 +01:00
toastal
30c9ad2269 internal: refactor html rendering 2024-06-15 13:42:38 +01:00
Tom Milligan
f7482415a7 readme: add more example projects 2024-06-15 10:20:37 +01:00
Tom Milligan
08397fbffa Merge pull request #192 from tommilligan/prep-v1.17.1
chore: prep v1.17.1 release
2024-06-11 14:51:18 +01:00
Tom Milligan
b78fc39031 chore: prep v1.17.1 release 2024-06-11 14:14:48 +01:00
Tom Milligan
fe7b475753 Merge pull request #186 from tommilligan/tommilligan-patch-1
fix: remove stray debug statement
2024-05-29 19:54:24 +01:00
Tom Milligan
a56976d085 fix: remove stray debug statement 2024-05-29 19:48:54 +01:00
Tom Milligan
08967d550d ci: fix deploy.yml 2024-05-24 15:06:58 +01:00
Tom Milligan
8fa1411095 Merge pull request #184 from tommilligan/fix-cargo-target-dir-release
ci: be more limited with CARGO_TARGET_DIR
2024-05-24 15:05:19 +01:00
Tom Milligan
a1e5cfa48d ci: be more limited with CARGO_TARGET_DIR 2024-05-24 14:59:46 +01:00
Tom Milligan
33fd522d68 Revert "ci: fix deploy artefacts"
This reverts commit c681ff922d.
2024-05-24 14:50:55 +01:00
Tom Milligan
4805398359 Merge pull request #183 from tommilligan/fix-deploy-artefacts
ci: fix deploy artefacts
2024-05-24 14:47:06 +01:00
Tom Milligan
c681ff922d ci: fix deploy artefacts 2024-05-24 14:46:52 +01:00
Tom Milligan
cd0726aaf2 Merge pull request #182 from tommilligan/prep-1.17.0
chore: bump version to 1.17.0, update ci
2024-05-24 13:37:20 +01:00
Tom Milligan
a08967e073 chore: remove deprecated set-output command 2024-05-24 13:27:29 +01:00
Tom Milligan
f7e6970fa3 chore: upgrade github actions 2024-05-24 13:22:51 +01:00
Tom Milligan
5d2124b319 chore: bump version to 1.17.0 2024-05-24 13:17:20 +01:00
Tom Milligan
d79ebb4fad chore: update v2 wishlist 2024-05-24 13:16:12 +01:00
Tom Milligan
9df896cd77 Merge pull request #181 from tommilligan/fix-breaking-on-equals-in-value
fix: support toml values with equal sign
2024-05-24 13:04:16 +01:00
Tom Milligan
ffb819c315 fix: support toml values with equal sign 2024-05-24 12:59:59 +01:00
Tom Milligan
f278374c88 Merge pull request #180 from tommilligan/better-errors-for-html
fix: better errors on toml parsing failures
2024-05-24 11:01:48 +01:00
Tom Milligan
2c18292401 fix: better errors on toml parsing failures 2024-05-24 10:38:39 +01:00
Tom Milligan
294af2478c Merge pull request #177 from tommilligan/cache-ci-more
ci: cache more cargo target dir
2024-05-19 17:38:27 +01:00
Tom Milligan
278d17792b Merge pull request #178 from tommilligan/fix-changelog
chore: fix changelog
2024-05-19 17:33:39 +01:00
Tom Milligan
9f6c73091a chore: fix changelog 2024-05-19 17:33:24 +01:00
Tom Milligan
9f221abc12 ci: cache more cargo target dir 2024-05-19 17:30:47 +01:00
32 changed files with 2830 additions and 891 deletions

View File

@@ -6,12 +6,12 @@ jobs:
# Fast test before we kick off all the other jobs
fast-test:
name: Fast test
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- name: Checkout sources
uses: actions/checkout@v4
- name: Cache build files
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
@@ -19,10 +19,9 @@ jobs:
target
key: fast-test-${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml') }}
- name: Install toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
override: true
- name: Install more toolchain
run: rustup component add rustfmt clippy
- name: Run tests
@@ -32,34 +31,36 @@ jobs:
detailed-test:
needs: fast-test
name: Test main target
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- name: Checkout sources
uses: actions/checkout@v4
- name: Cache build files
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
~/.cargo/bin
cargo_target
/tmp/cargo-install-target-dir
key: detailed-test-${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml') }}
- name: Install toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
override: true
components: rustfmt, clippy
- name: Enable Corepack
run: corepack enable
- name: Install node toolchain
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "yarn"
cache-dependency-path: compile_assets/yarn.lock
- name: Install additional test dependencies
env:
CARGO_TARGET_DIR: cargo_target
CARGO_TARGET_DIR: "/tmp/cargo-install-target-dir"
run: ./scripts/install
- name: Run check script
run: ./scripts/check
@@ -71,16 +72,16 @@ jobs:
strategy:
matrix:
os:
- ubuntu-20.04
- ubuntu-24.04
rust:
- stable
- beta
- 1.74.0
- 1.82.0
experimental:
- false
include:
# Run a canary test on nightly that's allowed to fail
- os: ubuntu-20.04
- os: ubuntu-24.04
rust: nightly
experimental: true
# Test only stable on Windows, presume we'd get same result on other
@@ -90,7 +91,7 @@ jobs:
experimental: false
exclude:
# Don't bother retesting stable linux, we did it in the comprehensive test
- os: ubuntu-20.04
- os: ubuntu-24.04
rust: stable
experimental: false
runs-on: ${{ matrix.os }}
@@ -105,7 +106,7 @@ jobs:
- name: Checkout sources
uses: actions/checkout@v4
- name: Cache build files
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
@@ -113,9 +114,8 @@ jobs:
target
key: test-${{ matrix.os }}-${{ matrix.rust }}-cargo-${{ hashFiles('**/Cargo.toml') }}
- name: Install toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
override: true
- name: Run tests
run: cargo test

View File

@@ -25,7 +25,7 @@ jobs:
# binaries, so we use the same glibc version
#
# ref: https://github.com/rust-lang/mdBook/pull/1955
os: ubuntu-20.04
os: ubuntu-24.04
name: x86_64-unknown-linux-gnu.tar.gz
- target: x86_64-unknown-linux-musl
os: ubuntu-latest
@@ -43,19 +43,19 @@ jobs:
# Cache files between builds
- name: Setup | Cache Cargo
uses: actions/cache@v3
uses: actions/cache@v4
with:
# Note that we don't cache the `target` directory here
# so we do a completely clean rebuild for artefacts
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Setup | Rust
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
override: true
profile: minimal
target: ${{ matrix.target }}
- name: Setup | cross
@@ -74,7 +74,7 @@ jobs:
- name: Post Setup | Extract tag name
shell: bash
run: echo "##[set-output name=tag;]$(echo ${GITHUB_REF#refs/tags/})"
run: echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
id: extract_tag
- name: Post Setup | Prepare artifacts [Windows]
@@ -92,10 +92,12 @@ jobs:
tar czvf ../../stage/${{ env.CRATE_NAME }}-${{ steps.extract_tag.outputs.tag }}-${{ matrix.name }} ${{ env.CRATE_NAME }}
cd -
- name: Post Setup | Upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ env.CRATE_NAME }}-${{ steps.extract_tag.outputs.tag }}-${{ matrix.name }}
path: target/stage/*
# Idempotency: overwrite artefact by name if we're rerunning the deployment
overwrite: true
# Create GitHub release with Rust build targets and release notes
github_release:
@@ -109,18 +111,18 @@ jobs:
fetch-depth: 0
- name: Setup | Artifacts
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
- name: Setup | Extract version
shell: bash
run: echo "##[set-output name=version;]$(echo ${GITHUB_REF#refs/tags/v})"
run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
id: extract_version
- name: Setup | Release notes
run: |
cat CHANGELOG.md | sed -n '/^## ${{ steps.extract_version.outputs.version }}$/,/^## /p' | sed '$d' > RELEASE.md
- name: Build | Publish
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
files: ${{ env.CRATE_NAME }}-*/${{ env.CRATE_NAME }}-*
body_path: RELEASE.md
@@ -131,24 +133,22 @@ jobs:
publish:
name: Publish to crates.io
needs: github_release
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
~/.cargo/bin
cargo_target
# We reuse the cache from our detailed test environment, if available
key: detailed-test-${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml') }}
- name: Install toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
override: true
- name: Publish crate
env:
CARGO_LOGIN_TOKEN: ${{ secrets.CARGO_LOGIN_TOKEN }}

View File

@@ -11,32 +11,35 @@ permissions:
jobs:
publish:
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
~/.cargo/bin
cargo_target
/tmp/cargo-install-target-dir
# We reuse the cache from our detailed test environment, if available
key: detailed-test-${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml') }}
- name: Install toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
override: true
- name: Install mdbook
env:
CARGO_TARGET_DIR: "/tmp/cargo-install-target-dir"
run: ./scripts/install-mdbook
- name: Install mdbook extras
env:
CARGO_TARGET_DIR: "/tmp/cargo-install-target-dir"
run: ./book/scripts/install-mdbook-extras
- name: Build book
run: ./scripts/build-book
- name: Push docs
uses: JamesIves/github-pages-deploy-action@v4.4.3
uses: JamesIves/github-pages-deploy-action@v4
with:
branch: gh-pages
folder: book/book

View File

@@ -1,6 +1,50 @@
# Changelog
## Unreleased
## v1.20.0
### Changed
- MSRV (minimum supported rust version) is now 1.82.0 ([#227](https://github.com/tommilligan/mdbook-admonish/pull/227))
- Collapsible blocks now show content in print view ([#228](https://github.com/tommilligan/mdbook-admonish/pull/228)). Thanks to [@igor-petruk](https://github.com/igor-petruk) for raising this issue.
## v1.19.0
### Changed
- MSRV (minimum supported rust version) is now 1.76.0 ([#208](https://github.com/tommilligan/mdbook-admonish/pull/208))
### Fixed
- Fixed blocks not rendering correctly in indented list items. Thanks to [@JorelAli](https://github.com/JorelAli) for the bug report! ([#224](https://github.com/tommilligan/mdbook-admonish/pull/224))
## v1.18.0
### Changed
- Add ARIA attributes to generated blocks. Thanks to [@toastal](https://github.com/toastal) for suggesting this feature! ([#195](https://github.com/tommilligan/mdbook-admonish/pull/195))
- Note: This subtly alters the emitted HTML, and could cause additional styles applied to blocks to break. Native `mdbook-admonish` styles are not affected.
### Fixed
- Fixed some valid configurations producing TOML serialization errors. Thanks to [@DianaNites](https://github.com/DianaNites) for reporting this! ([#197](https://github.com/tommilligan/mdbook-admonish/pull/197))
## v1.17.1
### Fixed
- Removed a stray debug statement ([#186](https://github.com/tommilligan/mdbook-admonish/pull/186))
## v1.17.0
### Changed
- Blocks should have key-value options separated by commas. Existing syntax remains is supported for back-compatibility. See [the documentation on Additional Options](https://tommilligan.github.io/mdbook-admonish/#additional-options) for more details ([#181](https://github.com/tommilligan/mdbook-admonish/pull/181))
### Fixed
- Titles contining `=` will now render correctly. Thanks to [@s00500](https://github.com/s00500) for the bug report! ([#181](https://github.com/tommilligan/mdbook-admonish/pull/181))
## v1.16.0
### Changed

1523
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
[package]
name = "mdbook-admonish"
version = "1.16.0"
version = "1.20.0"
edition = "2021"
rust-version = "1.74.0"
rust-version = "1.76.0"
authors = ["Tom Milligan <code@tommilligan.net>"]
description = "A preprocessor for mdbook to add Material Design admonishments."
@@ -26,27 +26,23 @@ name = "mdbook_admonish"
path = "src/lib.rs"
[dependencies]
anyhow = "1.0.75"
anyhow = "1.0.86"
# 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 }
clap = { version = "4.5", default-features = false, features = ["std", "derive"], optional = true }
env_logger = { version = "0.11", default-features = false, optional = true }
log = "0.4.20"
mdbook = "0.4.35"
once_cell = "1.18.0"
log = "0.4.21"
mdbook = "0.4.40"
once_cell = "1.19.0"
path-slash = "0.2.1"
pulldown-cmark = "0.11"
regex = "1.9.6"
semver = "1.0.19"
serde = { version = "1.0.188", features = ["derive"] }
serde_json = "1.0.107"
# Peer dependency of mdbook
# 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.22.13", optional = true }
pulldown-cmark = "0.13"
regex = "1.10.5"
semver = "1.0.23"
serde = { version = "1.0.203", features = ["derive"] }
serde_json = "1.0.117"
toml = "0.8.14"
toml_edit = { version = "0.22.14", optional = true }
hex_color = { version = "3.0.0", features = ["serde"] }
[dev-dependencies]

View File

@@ -21,9 +21,11 @@ into this:
Read the documentation [here](https://tommilligan.github.io/mdbook-admonish/), to see the actual examples in action. You can see the source in the [`./book`](./book) subdirectory.
Other projects using mdbook-admonish:
Projects using mdbook-admonish include:
- [The Rhai Book](https://rhai.rs/book/)
- [The Rhai Book](https://rhai.rs/book/) ([source](https://github.com/rhaiscript/book))
- [The Trunk Guide](https://trunkrs.dev/guide/) ([source](https://github.com/trunk-rs/trunk))
- [PRQL language book](https://prql-lang.org/book/) ([source](https://github.com/PRQL/prql))
## Usage

View File

@@ -10,7 +10,7 @@ git-repository-url = "https://github.com/tommilligan/mdbook-admonish"
[preprocessor.admonish]
command = "mdbook-admonish"
assets_version = "3.0.2" # do not edit: managed by `mdbook-admonish install`
assets_version = "3.1.0" # do not edit: managed by `mdbook-admonish install`
[preprocessor.admonish.directive.custom]
expensive = { icon = "./money-bag.svg", color = "#24ab38" }

View File

@@ -2,3 +2,4 @@
- [Overview](./overview.md)
- [Reference](./reference.md)
- [Examples](./examples.md)

15
book/src/examples.md Normal file
View File

@@ -0,0 +1,15 @@
# Examples
## Combining multiple custom properties
Note that the comma `,` is used to seperate custom options.
````
```admonish quote collapsible=true, title='A title that really <span style="color: #e70073">pops</span>'
To really <b><span style="color: #e70073">grab</span></b> your reader's attention.
```
````
```admonish quote collapsible=true, title='A title that really <span style="color: #e70073">pops</span>'
To really <b><span style="color: #e70073">grab</span></b> your reader's attention.
```

View File

@@ -78,14 +78,19 @@ You can also configure the build to fail loudly, by setting `on_failure = "bail"
### Additional Options
You can pass additional options to each block. The options are structured as TOML key-value pairs.
You can pass additional options to each block. Options are given like a [TOML Inline Table](https://toml.io/en/v1.0.0#inline-table), as key-value pairs separated by commas.
`mdbook-admonish` parses options by wrapping your options in an inline table before parsing them, so please consult [The TOML Reference](https://toml.io) if you run into any syntax errors. Be aware that:
- Key-value pairs must be separated with a comma `,`
- TOML escapes must be escaped again - for instance, write `\"` as `\\"`.
- For complex strings such as HTML, you may want to use a [literal string](https://toml.io/en/v1.0.0#string) to avoid complex escape sequences
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.
Note that TOML escapes must be escaped again - for instance, write `\"` as `\\"`.
A custom title can be provided:
````
```admonish warning title="Data loss"
@@ -114,13 +119,13 @@ This will take a while, go and grab a drink of water.
Markdown and HTML can be used in the inner content, as you'd expect:
````
```admonish tip title="_Referencing_ and <i>dereferencing</i>"
```admonish tip title='_Referencing_ and <i>dereferencing</i>'
The opposite of *referencing* by using `&` is *dereferencing*, which is
accomplished with the <span style="color: hotpink">dereference operator</span>, `*`.
```
````
```admonish tip title="_Referencing_ and <i>dereferencing</i>"
```admonish tip title='_Referencing_ and <i>dereferencing</i>'
The opposite of *referencing* by using `&` is *dereferencing*, which is
accomplished with the <span style="color: hotpink">dereference operator</span>, `*`.
```
@@ -148,7 +153,7 @@ print "Hello, world!"
If you want to provide custom styling to a specific admonition, you can attach one or more custom classnames:
````
```admonish note class="custom-0 custom-1"
```admonish note title="Stylish", class="custom-0 custom-1"
Styled with my custom CSS class.
```
````
@@ -173,7 +178,7 @@ with an appended number if multiple blocks would have the same id.
Setting the `id` field will _ignore_ all other ids and the duplicate counter.
````
```admonish info title="My Info" id="my-special-info"
```admonish info title="My Info", id="my-special-info"
Link to this block with `#my-special-info` instead of the default `#admonition-my-info`.
```
````
@@ -183,14 +188,14 @@ Link to this block with `#my-special-info` instead of the default `#admonition-m
For a block to be initially collapsible, and then be openable, set `collapsible=true`:
````
```admonish collapsible=true
```admonish title="Sneaky", collapsible=true
Content will be hidden initially.
```
````
Will yield something like the following HTML, which you can then apply styles to:
```admonish collapsible=true
```admonish title="Sneaky", collapsible=true
Content will be hidden initially.
```

View File

@@ -1,3 +1,5 @@
/node_modules
*.css
*.css.map
.yarn/
.pnp*

View File

@@ -15,5 +15,6 @@
},
"devDependencies": {
"prettier": "^3.0.3"
}
},
"packageManager": "yarn@4.8.1+sha512.bc946f2a022d7a1a38adfc15b36a66a3807a67629789496c3714dd1703d2e6c6b1c69ff9ec3b43141ac7a1dd853b7685638eb0074300386a59c18df351ef8ff6"
}

View File

@@ -92,8 +92,9 @@ $admonitions: (
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
// During print mode in browser, pdf renderer
@media print {
// Omit shadow as it may lead to rendering errors
box-shadow: none;
}
@@ -200,6 +201,14 @@ a.admonition-anchor-link {
}
}
// During print mode in browser, pdf renderer
@media print {
// Expand collapsed sections to show details
details.admonition::details-content {
display: contents;
}
}
summary.admonition-title {
details.admonition > &::after {
position: absolute;
@@ -222,6 +231,12 @@ summary.admonition-title {
details[open].admonition > &::after {
transform: rotate(90deg);
}
// Hide details marker for Safari that loves to show it anyway
// ref: https://github.com/tommilligan/mdbook-admonish/pull/185
&::-webkit-details-marker {
display: none;
}
}
:root {

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@ title = "mdbook-admonish-integration"
[preprocessor.admonish]
command = "mdbook-admonish"
assets_version = "3.0.2" # do not edit: managed by `mdbook-admonish install`
assets_version = "3.1.0" # do not edit: managed by `mdbook-admonish install`
after = ["links"]
[[preprocessor.admonish.custom]]

View File

@@ -9,7 +9,7 @@ title = "mdbook-admonish-integration"
[preprocessor.admonish]
command = "mdbook-admonish"
assets_version = "3.0.2" # do not edit: managed by `mdbook-admonish install`
assets_version = "3.1.0" # do not edit: managed by `mdbook-admonish install`
after = ["links"]
[[preprocessor.admonish.custom]]

View File

@@ -1,90 +1,108 @@
<h1 id="chapter-1"><a class="header" href="#chapter-1">Chapter 1</a></h1>
<div id="admonition-what-is-this" class="admonition admonish-abstract">
<div id="admonition-what-is-this" class="admonition admonish-abstract" role="note" aria-labelledby="admonition-what-is-this-title">
<div class="admonition-title">
<div id="admonition-what-is-this-title">
<p>What <i>is</i> this?</p>
<p><a class="admonition-anchor-link" href="#admonition-what-is-this"></a></p>
</div>
<a class="admonition-anchor-link" href="#admonition-what-is-this"></a>
</div>
<div>
<p>This book acts as an integration test for <code>mdbook-admonish</code>.</p>
<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 admonish-note">
<div id="admonition-note" class="admonition admonish-note" role="note" aria-labelledby="admonition-note-title">
<div class="admonition-title">
<div id="admonition-note-title">
<p>Note</p>
<p><a class="admonition-anchor-link" href="#admonition-note"></a></p>
</div>
<a class="admonition-anchor-link" href="#admonition-note"></a>
</div>
<div>
<p>Simples</p>
</div>
</div>
<div id="admonition-frog" class="admonition admonish-frog">
<div id="admonition-frog" class="admonition admonish-frog" role="note" aria-labelledby="admonition-frog-title">
<div class="admonition-title">
<div id="admonition-frog-title">
<p>Frog</p>
<p><a class="admonition-anchor-link" href="#admonition-frog"></a></p>
</div>
<a class="admonition-anchor-link" href="#admonition-frog"></a>
</div>
<div>
<p>Custom frog directive</p>
</div>
</div>
<div id="admonition-default" class="admonition admonish-warning">
<div id="admonition-default" class="admonition admonish-warning" role="note">
<div>
<p>No title, only body</p>
</div>
</div>
<div id="admonition-error-rendering-admonishment" class="admonition admonish-bug">
<div id="admonition-error-rendering-admonishment" class="admonition admonish-bug" role="note" aria-labelledby="admonition-error-rendering-admonishment-title">
<div class="admonition-title">
<div id="admonition-error-rendering-admonishment-title">
<p>Error rendering admonishment</p>
<p><a class="admonition-anchor-link" href="#admonition-error-rendering-admonishment"></a></p>
</div>
<a class="admonition-anchor-link" href="#admonition-error-rendering-admonishment"></a>
</div>
<div>
<p>Failed with:</p>
<pre><code class="language-log">TOML parsing error: TOML parse error at line 1, column 8
<pre><code class="language-log">'title="' is not a valid directive or TOML key-value pair.
TOML parsing error: TOML parse error at line 1, column 21
|
1 | title=&quot;
| ^
1 | config = { title=" }
| ^
invalid basic string
</code></pre>
<p>Original markdown input:</p>
<pre><code class="language-markdown">```admonish title=&quot;
<pre><code class="language-markdown">```admonish title="
No title, only body
```
</code></pre>
</div>
</div>
<details id="admonition-note-1" class="admonition admonish-note">
<details id="admonition-note-1" class="admonition admonish-note" role="note" aria-labelledby="admonition-note-1-title">
<summary class="admonition-title">
<div id="admonition-note-1-title">
<p>Note</p>
<p><a class="admonition-anchor-link" href="#admonition-note-1"></a></p>
</div>
<a class="admonition-anchor-link" href="#admonition-note-1"></a>
</summary>
<div>
<p>Hidden on load</p>
</div>
</details>
<div id="admonition-warning" class="admonition admonish-warning">
<div id="admonition-warning" class="admonition admonish-warning" role="note" aria-labelledby="admonition-warning-title">
<div class="admonition-title">
<div id="admonition-warning-title">
<p>Warning</p>
<p><a class="admonition-anchor-link" href="#admonition-warning"></a></p>
</div>
<a class="admonition-anchor-link" href="#admonition-warning"></a>
</div>
<div>
<p>This is a commonly shared warning!</p>
</div>
</div>
<div id="admonition-note-2" class="admonition admonish-note">
<div id="admonition-note-2" class="admonition admonish-note" role="note" aria-labelledby="admonition-note-2-title">
<div class="admonition-title">
<div id="admonition-note-2-title">
<p>Note</p>
<p><a class="admonition-anchor-link" href="#admonition-note-2"></a></p>
</div>
<a class="admonition-anchor-link" href="#admonition-note-2"></a>
</div>
<div>
<pre><code class="language-bash">Nested code block
</code></pre>
</div>
</div>
<div id="admonition-note-3" class="admonition admonish-note">
<div id="admonition-note-3" class="admonition admonish-note" role="note" aria-labelledby="admonition-note-3-title">
<div class="admonition-title">
<div id="admonition-note-3-title">
<p>Note</p>
<p><a class="admonition-anchor-link" href="#admonition-note-3"></a></p>
</div>
<a class="admonition-anchor-link" href="#admonition-note-3"></a>
</div>
<div>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
@@ -108,15 +126,34 @@ let x = 20;
</li>
<li>
<p>Thing two</p>
<div id="admonition-note-4" class="admonition admonish-note">
<div id="admonition-note-4" class="admonition admonish-note" role="note" aria-labelledby="admonition-note-4-title">
<div class="admonition-title">
<div id="admonition-note-4-title">
<p>Note</p>
<p><a class="admonition-anchor-link" href="#admonition-note-4"></a></p>
</div>
<a class="admonition-anchor-link" href="#admonition-note-4"></a>
</div>
<div>
<p>Thing two</p>
</div>
</div>
<ol>
<li>
<p>Thing two nested</p>
<div id="admonition-note-5" class="admonition admonish-note" role="note" aria-labelledby="admonition-note-5-title">
<div class="admonition-title">
<div id="admonition-note-5-title">
<p>Note</p>
</div>
<a class="admonition-anchor-link" href="#admonition-note-5"></a>
</div>
<div>
<p>Thing two nested (should not be a code block)
Regression tests for https://github.com/tommilligan/mdbook-admonish/issues/223</p>
</div>
</div>
</li>
</ol>
</li>
<li>
<p>Thing three</p>

View File

@@ -60,6 +60,13 @@ In a list:
Thing two
```
1. Thing two nested
```admonish
Thing two nested (should not be a code block)
Regression tests for https://github.com/tommilligan/mdbook-admonish/issues/223
```
1. Thing three
```sh

View File

@@ -8,7 +8,7 @@ function eprintln() {
>&2 echo "$1"
}
VERSION="0.4.35"
VERSION="0.4.51"
eprintln "Checking if mdbook $VERSION is installed"
if [[ "$(mdbook --version)" != "mdbook v$VERSION" ]]; then

View File

@@ -1 +1 @@
3.0.2
3.1.0

View File

@@ -86,6 +86,11 @@ html :is(.admonition-title, summary.admonition-title):last-child {
display: initial;
}
@media print {
details.admonition::details-content {
display: contents;
}
}
details.admonition > summary.admonition-title::after {
position: absolute;
top: 0.625em;
@@ -106,6 +111,9 @@ details.admonition > summary.admonition-title::after {
details[open].admonition > summary.admonition-title::after {
transform: rotate(90deg);
}
summary.admonition-title::-webkit-details-marker {
display: none;
}
:root {
--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>");

View File

@@ -125,7 +125,7 @@ struct Preprocessors {
/// Load the plugin specific config as a toml string, for private deserialization.
fn admonish_config_string(config: &Config) -> Result<String> {
Ok(toml_mdbook::to_string(
Ok(toml::to_string(
&config
.preprocessor
.admonish

View File

@@ -11,11 +11,12 @@ use crate::types::{AdmonitionDefaults, BuiltinDirective, BuiltinDirectiveConfig}
/// 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(
let table: String = toml::to_string(
ctx.config
.get_preprocessor("admonish")
.context("No configuration for mdbook-admonish in book.toml")?,
)?;
)
.context("Could not serialize mdbook-admonish config. This is a bug in the toml library.")?;
admonish_config_from_str(&table)
}

View File

@@ -1,11 +1,13 @@
mod toml_wrangling;
mod v1;
mod v2;
mod v3;
/// Configuration as described by the instance of an admonition in markdown.
///
/// This structure represents the configuration the user must provide in each
/// instance.
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Default)]
pub(crate) struct InstanceConfig {
pub(crate) directive: String,
pub(crate) title: Option<String>,
@@ -35,20 +37,29 @@ impl InstanceConfig {
/// - `Some(InstanceConfig)` if this is an `admonish` block
pub fn from_info_string(info_string: &str) -> Option<Result<Self, String>> {
let config_string = admonition_config_string(info_string)?;
Some(Self::from_admonish_config_string(config_string))
}
// If we succeed at parsing v2, return that. Otherwise hold onto the error
let config_v2_error = match v2::from_config_string(config_string) {
Ok(config) => return Some(Ok(config)),
Err(config) => config,
/// Parse an info string that is known to be for `admonish`.
fn from_admonish_config_string(config_string: &str) -> Result<Self, String> {
// If we succeed at parsing v3, return that. Otherwise hold onto the error
let config_v3_error = match v3::from_config_string(config_string) {
Ok(config) => return Ok(config),
Err(error) => error,
};
Some(if let Ok(config) = v1::from_config_string(config_string) {
// If we succeed at parsing v1, return that.
Ok(config)
} else {
// Otherwise return our v2 error.
Err(config_v2_error)
})
// If we succeed at parsing v2, return that
if let Ok(config) = v2::from_config_string(config_string) {
return Ok(config);
};
// If we succeed at parsing v1, return that.
if let Ok(config) = v1::from_config_string(config_string) {
return Ok(config);
}
// Otherwise return our v3 error.
Err(config_v3_error)
}
}
@@ -90,5 +101,20 @@ mod test {
collapsible: None,
}
);
// v3 syntax is supported
assert_eq!(
InstanceConfig::from_info_string(
r#"admonish title="Custom Title", type="question", id="my-id""#
)
.unwrap()
.unwrap(),
InstanceConfig {
directive: "question".to_owned(),
title: Some("Custom Title".to_owned()),
id: Some("my-id".to_owned()),
additional_classnames: Vec::new(),
collapsible: None,
}
);
}
}

View File

@@ -0,0 +1,44 @@
use once_cell::sync::Lazy;
use regex::Regex;
use serde::Deserialize;
use std::fmt::Display;
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub(crate) struct UserInput {
#[serde(default)]
pub r#type: Option<String>,
#[serde(default)]
pub title: Option<String>,
#[serde(default)]
pub id: Option<String>,
#[serde(default)]
pub class: Option<String>,
#[serde(default)]
pub collapsible: Option<bool>,
}
impl UserInput {
pub fn classnames(&self) -> Vec<String> {
self.class
.as_ref()
.map(|class| {
class
.split(' ')
.filter(|classname| !classname.is_empty())
.map(|classname| classname.to_owned())
.collect()
})
.unwrap_or_default()
}
}
pub(crate) static RX_DIRECTIVE: Lazy<Regex> =
Lazy::new(|| Regex::new(r#"^[A-Za-z0-9_-]+$"#).expect("directive regex"));
pub(crate) fn format_toml_parsing_error(error: impl Display) -> String {
format!("TOML parsing error: {error}")
}
pub(crate) fn format_invalid_directive(directive: &str, original_error: impl Display) -> String {
format!("'{directive}' is not a valid directive or TOML key-value pair.\n\n{original_error}")
}

View File

@@ -1,30 +1,16 @@
use super::toml_wrangling::{
format_invalid_directive, format_toml_parsing_error, UserInput, RX_DIRECTIVE,
};
use super::InstanceConfig;
use once_cell::sync::Lazy;
use regex::Regex;
use serde::Deserialize;
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
struct UserInput {
#[serde(default)]
r#type: Option<String>,
#[serde(default)]
title: Option<String>,
#[serde(default)]
id: Option<String>,
#[serde(default)]
class: Option<String>,
#[serde(default)]
collapsible: Option<bool>,
}
/// Transform our config string into valid toml
fn bare_key_value_pairs_to_toml(pairs: &str) -> String {
use regex::Captures;
static RX_BARE_KEY_ASSIGNMENT: Lazy<Regex> = Lazy::new(|| {
let bare_key = r#"[A-Za-z0-9_-]+"#;
Regex::new(&format!("(?:{bare_key}) *=")).expect("bare key assignment regex")
});
static RX_BARE_KEY_ASSIGNMENT: Lazy<Regex> =
Lazy::new(|| Regex::new(r#"(?:[A-Za-z0-9_-]+) *="#).expect("bare key assignment regex"));
fn prefix_with_newline(captures: &Captures) -> String {
format!(
@@ -41,10 +27,18 @@ fn bare_key_value_pairs_to_toml(pairs: &str) -> String {
.into_owned()
}
fn user_input_from_config_toml(config_toml: &str) -> Result<UserInput, String> {
toml::from_str(config_toml).map_err(format_toml_parsing_error)
}
/// Parse and return the config assuming v2 format.
///
/// Note that if an error occurs, a parsed struct that can be returned to
/// show the error message will be returned.
///
/// The basic idea here is to accept space separated key-value pairs, break them
/// onto separate lines, and then parse them as a TOML document.
/// This breaks when values contain a literal '=' sign, for which v3 syntax should be used.
pub(crate) fn from_config_string(config_string: &str) -> Result<InstanceConfig, String> {
let config_toml = bare_key_value_pairs_to_toml(config_string);
let config_toml = config_toml.trim();
@@ -52,7 +46,7 @@ pub(crate) fn from_config_string(config_string: &str) -> Result<InstanceConfig,
let config: UserInput = match toml::from_str(config_toml) {
Ok(config) => config,
Err(error) => {
let original_error = Err(format!("TOML parsing error: {error}"));
let original_error = format_toml_parsing_error(error);
// For ergonomic reasons, we allow users to specify the directive without
// a key. So if parsing fails initially, take the first line,
@@ -62,17 +56,11 @@ pub(crate) fn from_config_string(config_string: &str) -> Result<InstanceConfig,
None => (config_toml, ""),
};
static RX_DIRECTIVE: Lazy<Regex> =
Lazy::new(|| Regex::new(r#"^[A-Za-z0-9_-]+$"#).expect("directive regex"));
if !RX_DIRECTIVE.is_match(directive) {
return original_error;
return Err(format_invalid_directive(directive, original_error));
}
let mut config: UserInput = match toml::from_str(config_toml) {
Ok(config) => config,
Err(_) => return original_error,
};
let mut config = user_input_from_config_toml(config_toml)?;
config.r#type = Some(directive.to_owned());
config
}
@@ -102,97 +90,120 @@ mod test {
use pretty_assertions::assert_eq;
#[test]
fn test_from_config_string_v2() {
assert_eq!(
from_config_string("").unwrap(),
fn test_from_config_string_v2() -> Result<(), ()> {
fn check(config_string: &str, expected: InstanceConfig) -> Result<(), ()> {
let actual = match from_config_string(config_string) {
Ok(config) => config,
Err(error) => panic!("Expected config to be valid, got error:\n\n{}", error),
};
assert_eq!(actual, expected);
Ok(())
}
check(
"",
InstanceConfig {
directive: "".to_owned(),
title: None,
id: None,
additional_classnames: Vec::new(),
collapsible: None,
}
);
assert_eq!(
from_config_string(" ").unwrap(),
},
)?;
check(
" ",
InstanceConfig {
directive: "".to_owned(),
title: None,
id: None,
additional_classnames: Vec::new(),
collapsible: None,
}
);
assert_eq!(
from_config_string(
r#"type="note" class="additional classname" title="Никита" collapsible=true"#
)
.unwrap(),
},
)?;
check(
r#"type="note" class="additional classname" title="Никита" collapsible=true"#,
InstanceConfig {
directive: "note".to_owned(),
title: Some("Никита".to_owned()),
id: None,
additional_classnames: vec!["additional".to_owned(), "classname".to_owned()],
collapsible: Some(true),
}
);
},
)?;
// Specifying unknown keys is okay, as long as they're valid
assert_eq!(
from_config_string(r#"unkonwn="but valid toml""#).unwrap(),
check(
r#"unkonwn="but valid toml""#,
InstanceConfig {
directive: "".to_owned(),
title: None,
id: None,
additional_classnames: Vec::new(),
collapsible: None,
}
);
},
)?;
// Just directive is fine
assert_eq!(
from_config_string(r#"info"#).unwrap(),
check(
r#"info"#,
InstanceConfig {
directive: "info".to_owned(),
title: None,
id: None,
additional_classnames: Vec::new(),
collapsible: None,
}
);
},
)?;
// Directive plus toml config
assert_eq!(
from_config_string(r#"info title="Information" collapsible=false"#).unwrap(),
check(
r#"info title="Information" collapsible=false"#,
InstanceConfig {
directive: "info".to_owned(),
title: Some("Information".to_owned()),
id: None,
additional_classnames: Vec::new(),
collapsible: Some(false),
}
);
},
)?;
// Test custom id
assert_eq!(
from_config_string(r#"info title="My Info" id="my-info-custom-id""#).unwrap(),
check(
r#"info title="My Info" id="my-info-custom-id""#,
InstanceConfig {
directive: "info".to_owned(),
title: Some("My Info".to_owned()),
id: Some("my-info-custom-id".to_owned()),
additional_classnames: Vec::new(),
collapsible: None,
}
);
},
)?;
// Directive after toml config is an error
assert!(from_config_string(r#"title="Information" info"#).is_err());
Ok(())
}
#[test]
fn test_from_config_string_invalid_directive() {
assert_eq!(
from_config_string(r#"oh!wow titlel=""#).unwrap_err(),
r#"'oh!wow' is not a valid directive or TOML key-value pair.
TOML parsing error: TOML parse error at line 1, column 3
|
1 | oh!wow
| ^
expected `.`, `=`
"#
);
}
#[test]
fn test_from_config_string_invalid_toml_value() {
assert_eq!(
from_config_string(r#"note titlel=""#).unwrap_err(),
r#"TOML parsing error: TOML parse error at line 1, column 6
r#"TOML parsing error: TOML parse error at line 1, column 9
|
1 | note
| ^
expected `.`, `=`
1 | titlel="
| ^
invalid basic string
"#
);
}

202
src/config/v3.rs Normal file
View File

@@ -0,0 +1,202 @@
use super::toml_wrangling::{
format_invalid_directive, format_toml_parsing_error, UserInput, RX_DIRECTIVE,
};
use super::InstanceConfig;
use serde::Deserialize;
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
struct Wrapper<T> {
config: T,
}
/// Transform our config string into valid toml
fn bare_inline_table_to_toml(pairs: &str) -> String {
format!("config = {{ {pairs} }}")
}
fn user_input_from_config_string(config_string: &str) -> Result<UserInput, String> {
match toml::from_str::<Wrapper<_>>(&bare_inline_table_to_toml(config_string)) {
Ok(wrapper) => Ok(wrapper.config),
Err(error) => Err(format_toml_parsing_error(error)),
}
}
/// Parse and return the config assuming v3 format.
///
/// Note that if an error occurs, a parsed struct that can be returned to
/// show the error message will be returned.
///
/// The basic idea here is to accept the inside of an inline table, wrap it,
/// parse it, and then use the toml values.
pub(crate) fn from_config_string(config_string: &str) -> Result<InstanceConfig, String> {
let config_string = config_string.trim();
let config = match user_input_from_config_string(config_string) {
Ok(config) => config,
Err(error) => {
// For ergonomic reasons, we allow users to specify the directive without
// a key. So if parsing fails initially, take the first word,
// use that as the directive, and reparse.
let (directive, config_string) = match config_string.split_once(' ') {
Some((directive, config_string)) => (directive.trim(), config_string.trim()),
None => (config_string, ""),
};
if !RX_DIRECTIVE.is_match(directive) {
return Err(format_invalid_directive(directive, error));
}
let mut config = user_input_from_config_string(config_string)?;
config.r#type = Some(directive.to_owned());
config
}
};
let additional_classnames = config.classnames();
Ok(InstanceConfig {
directive: config.r#type.unwrap_or_default(),
title: config.title,
id: config.id,
additional_classnames,
collapsible: config.collapsible,
})
}
#[cfg(test)]
mod test {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_from_config_string_v3() -> Result<(), ()> {
fn check(config_string: &str, expected: InstanceConfig) -> Result<(), ()> {
let actual = match from_config_string(config_string) {
Ok(config) => config,
Err(error) => {
panic!("Expected config '{config_string}' to be valid, got error:\n\n{error}")
}
};
assert_eq!(actual, expected);
Ok(())
}
check(
"",
InstanceConfig {
directive: "".to_owned(),
title: None,
id: None,
additional_classnames: Vec::new(),
collapsible: None,
},
)?;
check(
" ",
InstanceConfig {
directive: "".to_owned(),
title: None,
id: None,
additional_classnames: Vec::new(),
collapsible: None,
},
)?;
check(
r#"type="note", class="additional classname", title="Никита", collapsible=true"#,
InstanceConfig {
directive: "note".to_owned(),
title: Some("Никита".to_owned()),
id: None,
additional_classnames: vec!["additional".to_owned(), "classname".to_owned()],
collapsible: Some(true),
},
)?;
// Specifying unknown keys is okay, as long as they're valid
check(
r#"unkonwn="but valid toml""#,
InstanceConfig {
directive: "".to_owned(),
title: None,
id: None,
additional_classnames: Vec::new(),
collapsible: None,
},
)?;
// Just directive is fine
check(
r#"info"#,
InstanceConfig {
directive: "info".to_owned(),
title: None,
id: None,
additional_classnames: Vec::new(),
collapsible: None,
},
)?;
// Directive plus toml config
check(
r#"info title="Information", collapsible=false"#,
InstanceConfig {
directive: "info".to_owned(),
title: Some("Information".to_owned()),
id: None,
additional_classnames: Vec::new(),
collapsible: Some(false),
},
)?;
// Test custom id
check(
r#"info title="My Info", id="my-info-custom-id""#,
InstanceConfig {
directive: "info".to_owned(),
title: Some("My Info".to_owned()),
id: Some("my-info-custom-id".to_owned()),
additional_classnames: Vec::new(),
collapsible: None,
},
)?;
// Directive after toml config is an error
assert!(from_config_string(r#"title="Information" info"#).is_err());
// HTML with quotes inside content
// Note that we use toml literal (single quoted) strings here
check(
r#"info title='My <span class="emphasis">Title</span>'"#,
InstanceConfig {
directive: "info".to_owned(),
title: Some(r#"My <span class="emphasis">Title</span>"#.to_owned()),
id: None,
additional_classnames: Vec::new(),
collapsible: None,
},
)?;
Ok(())
}
#[test]
fn test_from_config_string_invalid_directive() {
assert_eq!(
from_config_string(r#"oh!wow titlel=""#).unwrap_err(),
r#"'oh!wow' is not a valid directive or TOML key-value pair.
TOML parsing error: TOML parse error at line 1, column 14
|
1 | config = { oh!wow titlel=" }
| ^
expected `.`, `=`
"#
);
}
#[test]
fn test_from_config_string_invalid_toml_value() {
assert_eq!(
from_config_string(r#"note titlel=""#).unwrap_err(),
r#"TOML parsing error: TOML parse error at line 1, column 22
|
1 | config = { titlel=" }
| ^
invalid basic string
"#
);
}
}

View File

@@ -155,11 +155,13 @@ Text
let expected = r##"# Chapter
<div id="admonition-note" class="admonition admonish-note">
<div id="admonition-note" class="admonition admonish-note" role="note" aria-labelledby="admonition-note-title">
<div class="admonition-title">
<div id="admonition-note-title">
Note
</div>
<a class="admonition-anchor-link" href="#admonition-note"></a>
</div>
<div>
@@ -187,11 +189,13 @@ Text
let expected = r##"# Chapter
<div id="admonition-note" class="admonition admonish-note">
<div id="admonition-note" class="admonition admonish-note" role="note" aria-labelledby="admonition-note-title">
<div class="admonition-title">
<div id="admonition-note-title">
Note
</div>
<a class="admonition-anchor-link" href="#admonition-note"></a>
</div>
<div>
@@ -219,11 +223,13 @@ Text
let expected = r##"# Chapter
<div id="admonition-warning" class="admonition admonish-warning">
<div id="admonition-warning" class="admonition admonish-warning" role="note" aria-labelledby="admonition-warning-title">
<div class="admonition-title">
<div id="admonition-warning-title">
Warning
</div>
<a class="admonition-anchor-link" href="#admonition-warning"></a>
</div>
<div>
@@ -249,11 +255,13 @@ Text
let expected = r##"# Chapter
<div id="admonition-caution" class="admonition admonish-warning">
<div id="admonition-caution" class="admonition admonish-warning" role="note" aria-labelledby="admonition-caution-title">
<div class="admonition-title">
<div id="admonition-caution-title">
Caution
</div>
<a class="admonition-anchor-link" href="#admonition-caution"></a>
</div>
<div>
@@ -279,11 +287,13 @@ Text
let expected = r##"# Chapter
<div id="admonition-read-this" class="admonition admonish-warning">
<div id="admonition-read-this" class="admonition admonish-warning" role="note" aria-labelledby="admonition-read-this-title">
<div class="admonition-title">
<div id="admonition-read-this-title">
Read **this**!
</div>
<a class="admonition-anchor-link" href="#admonition-read-this"></a>
</div>
<div>
@@ -373,11 +383,13 @@ hello
let expected = r##"
<div id="admonition-and-in-the-title" class="admonition admonish-note">
<div id="admonition-and-in-the-title" class="admonition admonish-note" role="note" aria-labelledby="admonition-and-in-the-title-title">
<div class="admonition-title">
<div id="admonition-and-in-the-title-title">
And "<i>in</i>" the title
</div>
<a class="admonition-anchor-link" href="#admonition-and-in-the-title"></a>
</div>
<div>
@@ -403,11 +415,13 @@ hello
let expected = r##"
<div id="admonition-trademark" class="admonition admonish-warning">
<div id="admonition-trademark" class="admonition admonish-warning" role="note" aria-labelledby="admonition-trademark-title">
<div class="admonition-title">
<div id="admonition-trademark-title">
Trademark™
</div>
<a class="admonition-anchor-link" href="#admonition-trademark"></a>
</div>
<div>
@@ -432,11 +446,13 @@ Will have bonus classnames
let expected = r##"
<div id="admonition-tip" class="admonition admonish-tip my-style other-style">
<div id="admonition-tip" class="admonition admonish-tip my-style other-style" role="note" aria-labelledby="admonition-tip-title">
<div class="admonition-title">
<div id="admonition-tip-title">
Tip
</div>
<a class="admonition-anchor-link" href="#admonition-tip"></a>
</div>
<div>
@@ -460,11 +476,13 @@ 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 id="admonition-developers-dont-want-you-to-know-this-one-weird-tip" class="admonition admonish-tip my-style other-style" role="note" aria-labelledby="admonition-developers-dont-want-you-to-know-this-one-weird-tip-title">
<div class="admonition-title">
<div id="admonition-developers-dont-want-you-to-know-this-one-weird-tip-title">
Developers don't want you to know this one weird tip!
</div>
<a class="admonition-anchor-link" href="#admonition-developers-dont-want-you-to-know-this-one-weird-tip"></a>
</div>
<div>
@@ -487,7 +505,7 @@ Will have bonus classnames
let expected = r#"
<div id="admonition-default" class="admonition admonish-note">
<div id="admonition-default" class="admonition admonish-note" role="note">
<div>
@@ -513,11 +531,13 @@ Content one.
let expected = r##"
<div id="admonition-my-note" class="admonition admonish-note">
<div id="admonition-my-note" class="admonition admonish-note" role="note" aria-labelledby="admonition-my-note-title">
<div class="admonition-title">
<div id="admonition-my-note-title">
My Note
</div>
<a class="admonition-anchor-link" href="#admonition-my-note"></a>
</div>
<div>
@@ -528,11 +548,13 @@ Content zero.
</div>
<div id="admonition-my-note-1" class="admonition admonish-note">
<div id="admonition-my-note-1" class="admonition admonish-note" role="note" aria-labelledby="admonition-my-note-1-title">
<div class="admonition-title">
<div id="admonition-my-note-1-title">
My Note
</div>
<a class="admonition-anchor-link" href="#admonition-my-note-1"></a>
</div>
<div>
@@ -556,11 +578,13 @@ Bonus content!
let expected = r##"
<div id="admonition-article-heading" class="admonition admonish-tip my other-style">
<div id="admonition-article-heading" class="admonition admonish-tip my other-style" role="note" aria-labelledby="admonition-article-heading-title">
<div class="admonition-title">
<div id="admonition-article-heading-title">
Article Heading
</div>
<a class="admonition-anchor-link" href="#admonition-article-heading"></a>
</div>
<div>
@@ -584,11 +608,13 @@ Bonus content!
let expected = r##"
<div id="admonition-error-rendering-admonishment" class="admonition admonish-bug">
<div id="admonition-error-rendering-admonishment" class="admonition admonish-bug" role="note" aria-labelledby="admonition-error-rendering-admonishment-title">
<div class="admonition-title">
<div id="admonition-error-rendering-admonishment-title">
Error rendering admonishment
</div>
<a class="admonition-anchor-link" href="#admonition-error-rendering-admonishment"></a>
</div>
<div>
@@ -596,10 +622,12 @@ Error rendering admonishment
Failed with:
```log
TOML parsing error: TOML parse error at line 1, column 8
'title="' is not a valid directive or TOML key-value pair.
TOML parsing error: TOML parse error at line 1, column 21
|
1 | title="
| ^
1 | config = { title=" }
| ^
invalid basic string
```
@@ -684,11 +712,13 @@ Hidden
let expected = r##"
<details id="admonition-note" class="admonition admonish-note">
<details id="admonition-note" class="admonition admonish-note" role="note" aria-labelledby="admonition-note-title">
<summary class="admonition-title">
<div id="admonition-note-title">
Note
</div>
<a class="admonition-anchor-link" href="#admonition-note"></a>
</summary>
<div>
@@ -713,11 +743,13 @@ Text
let expected = r##"# Chapter
<div id="admonition-admonish" class="admonition admonish-note">
<div id="admonition-admonish" class="admonition admonish-note" role="note" aria-labelledby="admonition-admonish-title">
<div class="admonition-title">
<div id="admonition-admonish-title">
Admonish
</div>
<a class="admonition-anchor-link" href="#admonition-admonish"></a>
</div>
<div>
@@ -757,7 +789,7 @@ Text
let expected = r#"# Chapter
<div id="admonition-default" class="admonition admonish-note">
<div id="admonition-default" class="admonition admonish-note" role="note">
<div>
A simple admonition.
@@ -795,7 +827,7 @@ Text
let expected = r#"# Chapter
<div id="admonition-default" class="admonition admonish-note">
<div id="admonition-default" class="admonition admonish-note" role="note">
<div>
A simple admonition.
@@ -819,11 +851,13 @@ Text
let expected = r##"# Chapter
<div id="yay-custom-id" class="admonition admonish-success">
<div id="yay-custom-id" class="admonition admonish-success" role="note" aria-labelledby="yay-custom-id-title">
<div class="admonition-title">
<div id="yay-custom-id-title">
Check
</div>
<a class="admonition-anchor-link" href="#yay-custom-id"></a>
</div>
<div>
@@ -849,11 +883,13 @@ Text
let expected = r##"# Chapter
<div id="admonition-check" class="admonition admonish-success">
<div id="admonition-check" class="admonition admonish-success" role="note" aria-labelledby="admonition-check-title">
<div class="admonition-title">
<div id="admonition-check-title">
Check
</div>
<a class="admonition-anchor-link" href="#admonition-check"></a>
</div>
<div>
@@ -879,17 +915,54 @@ Text
let expected = r##"# Chapter
<div id="admonition-check-mark" class="admonition admonish-success">
<div id="admonition-check-mark" class="admonition admonish-success" role="note" aria-labelledby="admonition-check-mark-title">
<div class="admonition-title">
<div id="admonition-check-mark-title">
Check Mark
</div>
<a class="admonition-anchor-link" href="#admonition-check-mark"></a>
</div>
<div>
A simple admonition.
</div>
</div>
Text
"##;
assert_eq!(expected, prep(content));
}
#[test]
fn title_and_content_with_html() {
// Note that we use toml literal (single quoted) strings here
// and the fact we have an equals sign in the value does not cause
// us to break (because we're using v3 syntax, not v2)
let content = r#"# Chapter
```admonish success title='Check <span class="emphasis">Mark</span>'
A <span class="emphasis">simple</span> admonition.
```
Text
"#;
let expected = r##"# Chapter
<div id="admonition-check-mark" class="admonition admonish-success" role="note" aria-labelledby="admonition-check-mark-title">
<div class="admonition-title">
<div id="admonition-check-mark-title">
Check <span class="emphasis">Mark</span>
</div>
<a class="admonition-anchor-link" href="#admonition-check-mark"></a>
</div>
<div>
A <span class="emphasis">simple</span> admonition.
</div>
</div>
Text
@@ -909,11 +982,13 @@ Text
let expected = r##"# Chapter
<div id="info" class="admonition admonish-info">
<div id="info" class="admonition admonish-info" role="note" aria-labelledby="info-title">
<div class="admonition-title">
<div id="info-title">
Info
</div>
<a class="admonition-anchor-link" href="#info"></a>
</div>
<div>
@@ -953,11 +1028,13 @@ Text
let expected = r##"# Chapter
<div id="prefix-my-title" class="admonition admonish-info">
<div id="prefix-my-title" class="admonition admonish-info" role="note" aria-labelledby="prefix-my-title-title">
<div class="admonition-title">
<div id="prefix-my-title-title">
My Title
</div>
<a class="admonition-anchor-link" href="#prefix-my-title"></a>
</div>
<div>
@@ -997,11 +1074,13 @@ Text
let expected = r##"# Chapter
<div id="my-section-id" class="admonition admonish-info">
<div id="my-section-id" class="admonition admonish-info" role="note" aria-labelledby="my-section-id-title">
<div class="admonition-title">
<div id="my-section-id-title">
My Title
</div>
<a class="admonition-anchor-link" href="#my-section-id"></a>
</div>
<div>
@@ -1064,17 +1143,19 @@ Text
1. Thing two
<div id="admonition-note" class="admonition admonish-note">
<div id="admonition-note" class="admonition admonish-note" role="note" aria-labelledby="admonition-note-title">
<div class="admonition-title">
<div id="admonition-note-title">
Note
</div>
<a class="admonition-anchor-link" href="#admonition-note"></a>
</div>
<div>
Thing two
Thing two
</div>
</div>
@@ -1087,4 +1168,97 @@ Text
assert_eq!(expected, prep(content));
}
// Regression test for https://github.com/tommilligan/mdbook-admonish/issues/223
#[test]
fn nested_list_should_not_render_indented_code_block() {
let content = r#"# Chapter
- Level one
```admonish
Thing one
line two
```
- Level two
```admonish
Thing two
line two
```
- Level three
```admonish
Thing three
line two
```
"#;
let expected = r##"# Chapter
- Level one
<div id="admonition-note" class="admonition admonish-note" role="note" aria-labelledby="admonition-note-title">
<div class="admonition-title">
<div id="admonition-note-title">
Note
</div>
<a class="admonition-anchor-link" href="#admonition-note"></a>
</div>
<div>
Thing one
line two
</div>
</div>
- Level two
<div id="admonition-note-1" class="admonition admonish-note" role="note" aria-labelledby="admonition-note-1-title">
<div class="admonition-title">
<div id="admonition-note-1-title">
Note
</div>
<a class="admonition-anchor-link" href="#admonition-note-1"></a>
</div>
<div>
Thing two
line two
</div>
</div>
- Level three
<div id="admonition-note-2" class="admonition admonish-note" role="note" aria-labelledby="admonition-note-2-title">
<div class="admonition-title">
<div id="admonition-note-2-title">
Note
</div>
<a class="admonition-anchor-link" href="#admonition-note-2"></a>
</div>
<div>
Thing three
line two
</div>
</div>
"##;
assert_eq!(expected, prep(content));
}
}

View File

@@ -178,11 +178,13 @@ x = 20;
"#;
let expected_content = r##"
<div id="admonition-title" class="admonition admonish-note">
<div id="admonition-title" class="admonition admonish-note" role="note" aria-labelledby="admonition-title-title">
<div class="admonition-title">
<div id="admonition-title-title">
Title
</div>
<a class="admonition-anchor-link" href="#admonition-title"></a>
</div>
<div>

View File

@@ -35,9 +35,9 @@ impl<'a> Admonition<'a> {
}
}
pub(crate) fn html(&self, id_counter: &mut HashMap<String, usize>) -> String {
pub(crate) fn html(self, id_counter: &mut HashMap<String, usize>) -> String {
let anchor_id = match &self.css_id {
CssId::Verbatim(id) => Cow::Borrowed(id),
CssId::Verbatim(id) => Cow::Borrowed(id.as_str()),
CssId::Prefix(prefix) => {
let id = unique_id_from_content(
if !self.title.is_empty() {
@@ -56,53 +56,79 @@ impl<'a> Admonition<'a> {
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">
let (titlebar_html, title_id) = if !title.is_empty() {
let titlebar_element = if self.collapsible { "summary" } else { "div" };
let title_id = format!("{anchor_id}-title");
let titlebar_html = Cow::Owned(format!(
r##"{indent}<{titlebar_element} class="admonition-title">
{indent}<div id="{title_id}">
{indent}
{indent}{title}
{indent}
{indent}</div>
{indent}<a class="admonition-anchor-link" href="#{anchor_id}"></a>
{indent}</{title_block}>
{indent}</{titlebar_element}>
"##
))
));
(titlebar_html, Some(title_id))
} else {
Cow::Borrowed("")
(Cow::Borrowed(""), None)
};
let mut additional_class = format!("admonish-{}", self.directive);
if !self.additional_classnames.is_empty() {
for additional_classname in &self.additional_classnames {
additional_class.push(' ');
additional_class.push_str(additional_classname);
}
}
let mut classes = vec![
"admonition".to_owned(),
format!("admonish-{}", self.directive),
];
classes.extend(self.additional_classnames);
let classes = classes.join(" ");
let admonition_block = if self.collapsible { "details" } else { "div" };
let mut attributes = vec![
("id", anchor_id),
("class", Cow::Owned(classes)),
("role", Cow::Borrowed("note")),
];
if let Some(title_id) = title_id {
attributes.push(("aria-labelledby", Cow::Owned(title_id)));
}
let attributes = join_attributes(&attributes);
let admonition_element = 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.
// - We should not indent the inner content, as it retains the indent
// it is written with.
format!(
r#"
{indent}<{admonition_block} id="{anchor_id}" class="admonition {additional_class}">
{title_html}{indent}<div>
{indent}
{indent}{content}
{indent}
{indent}<{admonition_element} {attributes}>
{titlebar_html}{indent}<div>
{content}
{indent}</div>
{indent}</{admonition_block}>"#,
{indent}</{admonition_element}>"#,
)
}
/// Strips all admonish syntax, leaving the plain content of the block.
pub(crate) fn strip(&self) -> String {
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)
}
}
fn join_attributes(attributes: &[(impl AsRef<str>, impl AsRef<str>)]) -> String {
let mut buffer = String::new();
for (key, value) in attributes {
buffer.push_str(key.as_ref());
buffer.push_str(r#"=""#);
buffer.push_str(value.as_ref());
buffer.push_str(r#"" "#);
}
buffer.pop();
buffer
}
const ANCHOR_ID_DEFAULT: &str = "default";

1
v2.md
View File

@@ -3,6 +3,7 @@
## Compatibility to drop
- v1 config loading system from block info strings
- v2 config loading system from block info strings
- Support for `custom` configuration, moved to `directive.custom`
- `css-id-prefix` kebab case