Compare commits

..

113 Commits

Author SHA1 Message Date
Eric Huss
5eb7d46a99 Merge pull request #1696 from ehuss/bump-version
Update to 0.4.14
2021-11-27 10:34:17 -08:00
Eric Huss
dffcedf031 Update to 0.4.14 2021-11-27 10:10:51 -08:00
Eric Huss
c9b6be8660 Merge pull request #1675 from ehuss/deprecate-ga
Deprecate google-analytics
2021-11-27 09:53:44 -08:00
Eric Huss
23af80c506 Merge pull request #1685 from igxactly/fix-ios-text-size-landscape
Prevent iOS Safari from enlarging text in landscape orientation
2021-11-20 10:01:41 -08:00
Eric Huss
857acb9759 Merge pull request #1683 from abdnh/strip-html-from-anchor-ids
Strip HTML tags from anchor IDs
2021-11-20 09:47:18 -08:00
Eric Huss
2ddcb43899 Merge pull request #1668 from joshrotenberg/update-pulldown-cmark
Update pulldown-cmark to 0.8.0 and use Smart Punctuation feature
2021-11-20 08:33:45 -08:00
josh rotenberg
1c0983b811 use pulldown cmarks curly quotes 2021-11-19 17:26:30 -08:00
josh rotenberg
1be69af553 need to actually enable it 2021-11-19 15:14:22 -08:00
josh rotenberg
c63000f365 Merge remote-tracking branch 'upstream/master' into update-pulldown-cmark 2021-11-19 15:01:04 -08:00
Ingu Kang
bbaa0ea1fa Prevent iOS Safari from enlarging text in landscape orientation
referernces:
  - https://developer.mozilla.org/en-US/docs/Web/CSS/text-size-adjust#basic_disabling_usage
  - https://trac.webkit.org/changeset/261940/webkit
2021-11-13 15:37:20 +09:00
Abdo
58bc92d380 Strip HTML tags from anchor IDs 2021-11-10 00:41:44 +03:00
Eric Huss
17d1ed3716 Merge pull request #1676 from YJDoc2/add_triagebot
Add minimal traigebot config
2021-10-27 09:31:40 -07:00
Yashodhan Joshi
8df8ce063d Add minimal traigebot config 2021-10-27 19:31:08 +05:30
Eric Huss
c3ff4a5129 Deprecate google-analytics. 2021-10-24 11:43:37 -07:00
Eric Huss
4d20fa578b Merge pull request #1642 from ehuss/stabilize-2021
Stabilize 2021 flag.
2021-10-24 10:30:55 -07:00
Eric Huss
9e47498458 Merge pull request #1662 from YJDoc2/test-book
Add a Test Book to verify style changes against
2021-10-24 10:01:22 -07:00
josh rotenberg
903469a45f update pulldown-cmark to 0.8.0 2021-10-13 20:27:00 -07:00
Yashodhan Joshi
b8ef89db62 Add rust specific codeblock examples 2021-10-05 12:11:29 +05:30
Yashodhan Joshi
c283211a37 Add Languages examples for syntax highlighting 2021-10-05 12:02:33 +05:30
Yashodhan Joshi
d5af051d0e Add individual tag examples in test_book 2021-10-04 16:17:52 +05:30
Yashodhan Joshi
68f9afe64b Setup basic structure for test book 2021-10-04 13:14:49 +05:30
Eric Huss
ffa8284743 Merge pull request #1661 from ehuss/bump-version
Update to 0.4.13
2021-10-03 15:39:23 -07:00
Eric Huss
3e91f9cd5d Update to 0.4.13 2021-10-03 15:15:21 -07:00
Eric Huss
f55028b61a Merge pull request #1657 from lclc/gitlabPages
Documentation: CI: GitLab Pages Example
2021-10-03 14:51:08 -07:00
Eric Huss
0d887505af Merge pull request #1607 from ISSOtm/preproc-order
Allow specifying preprocessor order
2021-10-03 14:22:15 -07:00
Eric Huss
6c20736a55 Merge pull request #1658 from pickfire/patch-1
Remove extra semicolon in variables.css
2021-10-03 12:24:11 -07:00
Ivan Tham
e4a46c9477 Remove extra semicolon in variables.css 2021-10-03 22:23:09 +08:00
Ivan Tham
6ae5c686d9 Remove extra semicolon in variables.css 2021-10-03 22:19:51 +08:00
Lucas Betschart
b862080006 Documentation: CI: GitLab Pages Example
Replace 'only' with 'rules'.
'only' is not actively developed anymore with GitLab (see https://docs.gitlab.com/ee/ci/yaml/#only--except)
2021-09-29 12:42:15 +02:00
ISSOtm
6b790b83ec Warn when preproc order references unknown preprocs 2021-09-28 09:25:17 +02:00
ISSOtm
d8ad68c947 Produce an error if before or after field is not a table 2021-09-27 21:28:21 +02:00
ISSOtm
6b784be616 Stabilize ties in preproc order through name sort 2021-09-27 21:28:01 +02:00
Eric Huss
f5598b2eee Merge pull request #1656 from johannst/switch-to-opener-crate
switch from open to opener
2021-09-26 12:33:23 -07:00
Johannes Stoelp
ff4b8e7a8d switch from open to opener
By default, `opener` launches the subprocess without waiting for its
completion, compared to `open` which waits for its completion.

This is helpful in case the `watch` feature is enabled and one of the
following commands `watch | serve --open` is used. If this command would
open the browser, listening for changes would be blocked by the browser.
2021-09-26 12:33:09 -07:00
ISSOtm
9c34e602bd Allow specifying preprocessor order
Fixes #1172
2021-09-26 20:55:02 +02:00
Eric Huss
a306da3ad7 Merge pull request #1604 from notriddle/notriddle/test-cli
Add CLI tests
2021-09-26 11:37:09 -07:00
Michael Howell
9bede85efa Move cli tests to their own subdir 2021-09-26 11:29:36 -07:00
Michael Howell
11b1e86187 Fix path dumps under windows 2021-09-26 11:29:36 -07:00
Michael Howell
10d30a2dc0 Add CLI tests
Part of #1568
2021-09-26 11:29:35 -07:00
Eric Huss
601ebc5499 Merge pull request #1651 from notriddle/notriddle/theme-switcher-js
Only switch to themes on buttons that have the `theme` class
2021-09-16 17:32:01 -07:00
Michael Howell
4251d7a838 Only switch to themes on buttons that have the theme class
Fixes #1649
2021-09-14 10:05:35 -07:00
Eric Huss
93008cf20b Merge pull request #1643 from apogeeoak/update-open
Update open crate to latest version.
2021-09-07 10:20:24 -07:00
apogeeoak
3f9f681b9e Update open crate to latest version. 2021-09-06 15:52:43 -04:00
Eric Huss
63680d0786 Merge pull request #1480 from pauliyobo/preprocessor_docs
Improving documentation on implementation of preprocessors.
2021-09-05 18:46:09 -07:00
Eric Huss
656a1825cc Minor fixes for preprocessor docs. 2021-09-05 18:45:37 -07:00
pauliyobo
1a2fa29209 introduced proposed suggestions related to the documentation 2021-09-04 23:44:27 +02:00
Eric Huss
6be81214b1 Stabilize 2021 flag. 2021-09-03 11:00:27 -07:00
Eric Huss
d22299d998 Merge pull request #1631 from benarmstead/master
Format better, remove unnecessary borrows, and update depends
2021-09-03 10:15:16 -07:00
Eric Huss
0af417085f Merge pull request #1634 from vv9k/master
Add information about a new backend `mdbook-man`
2021-09-03 10:11:32 -07:00
Eric Huss
9634798eb7 Merge pull request #1637 from notriddle/no-headers
Include chapters with no headers in the search index
2021-09-03 10:10:07 -07:00
Michael Howell
2a8af1c21d Include chapters with no headers in the search index 2021-08-31 12:48:21 -07:00
Wojciech Kępka
981f8695ff Add information about a new backend mdbook-man 2021-08-26 17:45:22 +02:00
Eric Huss
48b5e52f62 Merge pull request #1632 from tsoutsman/patch-1
Fix preprocessor example
2021-08-24 10:23:06 -07:00
Klim Tsoutsman
c4fec94c4c Appease Clippy 2021-08-24 23:04:32 +10:00
Ben Armstead
ab0c338c08 Update depends 2021-08-24 08:57:36 +01:00
Ben Armstead
8a82f6336a Format with cargo correctly 2021-08-24 08:48:24 +01:00
Ben Armstead
1700783594 Format better and remove unnecessary borrows 2021-08-24 08:45:06 +01:00
Eric Huss
e6629cd75b Merge pull request #1623 from ehuss/release-next
Update to 0.4.12
2021-08-02 08:40:26 -07:00
Eric Huss
5a077b9ff4 Update to 0.4.12 2021-08-02 08:19:34 -07:00
Eric Huss
8b4e488de1 Merge pull request #1621 from ehuss/revert-highlightjs11
Revert #1597 - Update to highlight.js 11.0.
2021-08-02 08:15:45 -07:00
Eric Huss
68d8ceec47 Revert #1597 - Update to highlight.js 11.0. 2021-08-02 08:02:13 -07:00
Eric Huss
db337d4a6f Merge pull request #1616 from joshrotenberg/add_cleanup_contributors
Add a couple contributors, clean up a little
2021-07-27 08:59:46 -07:00
josh rotenberg
5e277140be add a couple, clean up a little 2021-07-26 21:23:42 -07:00
Eric Huss
14add9c290 Merge pull request #1615 from ehuss/release-next
Update to 0.4.11
2021-07-26 13:47:37 -07:00
Eric Huss
87877a9dae Update to 0.4.11 2021-07-26 13:47:08 -07:00
Eric Huss
2cf00d0880 Merge pull request #1597 from ehuss/update-hightlight.js
Update to highlight.js 11.0.
2021-07-26 13:22:42 -07:00
Eric Huss
8c7af3c767 Merge pull request #1614 from joshrotenberg/code_of_conduct
Add explicit code of conduct, copying other rust-lang repos
2021-07-26 12:42:15 -07:00
Eric Huss
6dd785ea6c Update to highlight.js 11.0. 2021-07-26 12:40:28 -07:00
Eric Huss
8d131b4310 Merge pull request #1598 from ehuss/update-contributing
Some minor updates to contributing docs.
2021-07-26 12:38:06 -07:00
Eric Huss
97b38063b1 Merge pull request #1613 from FWYongxing/master
Don't highlight `inline code` blocks in headers with output.html. playpen(playgroud).editable=true
2021-07-26 12:37:39 -07:00
josh rotenberg
d23734f82e add explicit code of conduct, copying other rust-lang repos 2021-07-26 11:52:24 -07:00
Eric Huss
2ccfaadd1d Merge pull request #1609 from joshrotenberg/intro_updates
Updates to the mdbook introduction
2021-07-26 11:41:51 -07:00
Eric Huss
3d04e5c7ff Merge pull request #1612 from joshrotenberg/update_tokio_warp
Update tokio and warp to latest versions
2021-07-26 11:37:54 -07:00
FWYongxing
49ef7b6f02 Don't highlight inline code blocks in headers with output.html.playpen(playgroud).editable=true 2021-07-27 01:02:17 +08:00
josh rotenberg
da7026190c add Cargo.lock change too 2021-07-26 09:49:38 -07:00
josh rotenberg
92377013cc 1.46.0 has if, match, and loop expressions can now be used in const functions. 2021-07-25 19:33:21 -07:00
josh rotenberg
34b586ab32 tokio msrv is 1.45.2 2021-07-25 19:27:47 -07:00
josh rotenberg
a79065b0d3 update warp and tokio versions 2021-07-25 19:14:40 -07:00
josh rotenberg
b3ab93a4b3 update warp and tokio versions 2021-07-25 19:14:34 -07:00
josh rotenberg
49b75810fa fix a few typos 2021-07-24 21:17:41 -07:00
josh rotenberg
b85d5eb455 updates to the mdbook introduction 2021-07-24 18:41:53 -07:00
Eric Huss
27faa54ae8 Merge pull request #1469 from tuyen-at-work/patch-1
Support space as seperator between language and additional class in c…
2021-07-10 09:34:33 -07:00
Eric Huss
fae0759626 Split lang on tab to be consistent with rustdoc and GitHub. 2021-07-10 09:33:34 -07:00
Tuyen Pham
cc74ca2e6e Update mod.rs 2021-07-10 09:26:41 -07:00
Tuyen Pham
7cae3a058d Support space as seperator between language and additional class in code block
This PR try to resolve this issue: https://github.com/rust-lang/mdBook/issues/1384
2021-07-10 09:26:41 -07:00
Eric Huss
8fb6ac7987 Merge pull request #1599 from notriddle/notriddle/play-button-no-output
feat(playground): show "No output" on playgrounds that print nothing
2021-07-08 13:17:15 -07:00
Michael Howell
82d32ee761 feat(playground): show "No output" on playgrounds that print nothing
Fixes #1594
2021-07-07 10:44:51 -07:00
Eric Huss
fe9b534ad7 Some minor updates to contributing docs. 2021-07-06 11:42:42 -07:00
Eric Huss
56652e8fa6 Merge pull request #1559 from joshrotenberg/title_ignore_flags_init
Add --title option and --gitignore flag to mdbook init
2021-07-06 09:22:38 -07:00
Eric Huss
c3a1e41ed7 Update CLI docs for flag name change. 2021-07-06 09:22:05 -07:00
Eric Huss
3976c9d8f0 Merge pull request #1576 from kana4/master
MyPaths are not required and fail
2021-07-06 08:59:54 -07:00
Eric Huss
96b6f02834 Merge pull request #1425 from alexmaco/completions
Add ability to generate shell completions via clap
2021-07-06 08:59:06 -07:00
Eric Huss
b571511737 Merge pull request #1596 from joshrotenberg/rust_edition_2021_support
Rust Edition 2021 Support
2021-07-06 08:34:18 -07:00
josh rotenberg
ebdab38a32 remove debugging 2021-07-04 20:25:04 -07:00
josh rotenberg
c06f450e7d add edition2021 as an option 2021-07-04 16:28:52 -07:00
josh rotenberg
b87c231fc3 first pass at 2021 support 2021-07-04 14:44:23 -07:00
josh rotenberg
8024b08f93 format 2021-07-04 12:00:13 -07:00
josh rotenberg
8ec0bf6e30 ignore now takes a value 2021-07-04 11:57:46 -07:00
kana
a8926d5392 Update continuous-integration.md 2021-07-04 18:28:56 +01:00
kana
00473d8420 Add comments to edit in case of custom book path 2021-07-04 18:27:53 +01:00
josh rotenberg
86d390032b drop short flags 2021-07-04 08:15:51 -07:00
Eric Huss
b3c0b01350 Merge pull request #1586 from joshrotenberg/fix_library_path_doc
Clarify the library path documentation
2021-07-04 07:41:42 -07:00
Eric Huss
e33192753d Merge pull request #1591 from fritsstegmann/patch-1
Update renderers.md
2021-07-04 07:30:44 -07:00
Frits Stegmann
7932e13512 Update renderers.md
I was doing some work on the epub plugin repo and when trying to build an epub I came across what looked like some broken references.
2021-07-04 08:12:18 +02:00
josh rotenberg
9fd2509c0d fix link 2021-06-25 08:24:57 -07:00
josh rotenberg
5dec8508c7 clarify the library path documentation 2021-06-24 19:08:23 -07:00
Eric Huss
4d2dc6f482 Merge pull request #1581 from tuyen-at-work/patch-2
Fix inconsistent between bash version and rust version of the sample
2021-06-19 07:21:44 -07:00
Tuyen Pham
efb13d7bc1 Fix inconsistent between bash version and rust version of the sample
In the bash version, `y` have value 6, while rust version it has value 7
2021-06-19 20:08:27 +07:00
kana
27b1e05c87 MyPaths are not required and fail
Inputting paths are not necessary and will break ci, we can just leave paths out. We also changed 'master' to main since anyone reading this is probably going to be making mains
2021-06-11 20:21:28 +01:00
josh rotenberg
e5e10c681a add init flags
add init flags

update init command in guide
2021-05-31 20:47:51 -07:00
Paul
dcccd3289d Apply suggestions from code review
improved readability and cleared sections which could have caused more confusion

Co-authored-by: Eric Huss <eric@huss.org>
2021-03-08 20:39:39 +01:00
pauliyobo
5637a66459 hopefully made the documentation more clearer on what concerns preprocessor implementation with non rust languages 2021-03-05 21:11:29 +01:00
Alexandru Macovei
beec17e55d Add ability to generate shell completions via clap 2021-01-04 23:51:02 +02:00
69 changed files with 3056 additions and 770 deletions

View File

@@ -31,7 +31,7 @@ jobs:
rust: stable
- build: msrv
os: ubuntu-latest
rust: 1.45.0
rust: 1.46.0
steps:
- uses: actions/checkout@master
- name: Install Rust

1
.gitignore vendored
View File

@@ -8,6 +8,7 @@ guide/book
.vscode
tests/dummy_book/book/
test_book/book/
# Ignore Jetbrains specific files.
.idea/

View File

@@ -1,5 +1,82 @@
# Changelog
## mdBook 0.4.14
[ffa8284...c9b6be8](https://github.com/rust-lang/mdBook/compare/ffa8284...c9b6be8)
### Added
- The 2021 Rust edition option has been stabilized.
[#1642](https://github.com/rust-lang/mdBook/pull/1642)
### Changed
- Header anchors no longer include any HTML tags. Previously only a small
subset were excluded.
[#1683](https://github.com/rust-lang/mdBook/pull/1683)
- Deprecated the google-analytics option. Books using this option should place
the appropriate code in the `theme/head.hbs` file instead.
[#1675](https://github.com/rust-lang/mdBook/pull/1675)
### Fixed
- Updated the markdown parser which brings in a few small fixes and removes
the custom smart quote handling.
[#1668](https://github.com/rust-lang/mdBook/pull/1668)
- Fixed iOS Safari enlarging text when going into landscape mode.
[#1685](https://github.com/rust-lang/mdBook/pull/1685)
## mdBook 0.4.13
[e6629cd...f55028b](https://github.com/rust-lang/mdBook/compare/e6629cd...f55028b)
### Added
- Added the ability to specify the preprocessor order.
[#1607](https://github.com/rust-lang/mdBook/pull/1607)
### Fixed
- Include chapters with no headers in the search index
[#1637](https://github.com/rust-lang/mdBook/pull/1637)
- Switched to the `opener` crate for opening a web browser, which should fix
some issues with blocking.
[#1656](https://github.com/rust-lang/mdBook/pull/1656)
- Fixed clicking the border of the theme switcher breaking the theme selection.
[#1651](https://github.com/rust-lang/mdBook/pull/1651)
## mdBook 0.4.12
[14add9c...8b4e488](https://github.com/rust-lang/mdBook/compare/14add9c...8b4e488)
### Changed
- Reverted the change to update to highlight.js 11, as it broke hidden code lines.
[#1597](https://github.com/rust-lang/mdBook/pull/1621)
## mdBook 0.4.11
[e440094...2cf00d0](https://github.com/rust-lang/mdBook/compare/e440094...2cf00d0)
### Added
- Added support for Rust 2021 edition.
[#1596](https://github.com/rust-lang/mdBook/pull/1596)
- Added `mdbook completions` subcommand which provides shell completions.
[#1425](https://github.com/rust-lang/mdBook/pull/1425)
- Added `--title` and `--ignore` flags to `mdbook init` to avoid the
interactive input.
[#1559](https://github.com/rust-lang/mdBook/pull/1559)
### Changed
- If running a Rust example does not have any output, it now displays the text
"No output" instead of not showing anything.
[#1599](https://github.com/rust-lang/mdBook/pull/1599)
- Code block language tags can now be separated by space or tab (along with
commas) to match the behavior of other sites like GitHub and rustdoc.
[#1469](https://github.com/rust-lang/mdBook/pull/1469)
- Updated `warp` (the web server) to the latest version.
This also updates the minimum supported Rust version to 1.46.
[#1612](https://github.com/rust-lang/mdBook/pull/1612)
- Updated to highlight.js 11. This has various highlighting improvements.
[#1597](https://github.com/rust-lang/mdBook/pull/1597)
### Fixed
- Inline code blocks inside a header are no longer highlighted when
`output.html.playground.editable` is `true`.
[#1613](https://github.com/rust-lang/mdBook/pull/1613)
## mdBook 0.4.10
[2f7293a...dc2062a](https://github.com/rust-lang/mdBook/compare/2f7293a...dc2062a)

3
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,3 @@
# The Rust Code of Conduct
The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html).

View File

@@ -6,7 +6,6 @@ If you have come here to learn how to contribute to mdBook, we have some tips fo
First of all, don't hesitate to ask questions!
Use the [issue tracker](https://github.com/rust-lang/mdBook/issues), no question is too simple.
If we don't respond in a couple of days, ping us @Michael-F-Bryan, @budziq, @steveklabnik, @frewsxcv it might just be that we forgot. :wink:
### Issues to work on
@@ -46,7 +45,7 @@ mdBook builds on stable Rust, if you want to build mdBook from source, here are
0. Navigate into the newly created `mdBook` directory
0. Run `cargo build`
The resulting binary can be found in `mdBook/target/debug/` under the name `mdBook` or `mdBook.exe`.
The resulting binary can be found in `mdBook/target/debug/` under the name `mdbook` or `mdbook.exe`.
### Code Quality
@@ -106,3 +105,26 @@ and [rustfmt](https://github.com/rust-lang/rustfmt) on the code first.
This is not a requirement though and will never block a pull-request from being merged.
That's it, happy contributions! :tada: :tada: :tada:
## Browser compatibility and testing
Currently we don't have a strict browser compatibility matrix due to our limited resources.
We generally strive to keep mdBook compatible with a relatively recent browser on all of the most major platforms.
That is, supporting Chrome, Safari, Firefox, Edge on Windows, macOS, Linux, iOS, and Android.
If possible, do your best to avoid breaking older browser releases.
Any change to the HTML or styling is encouraged to manually check on as many browsers and platforms that you can.
Unfortunately at this time we don't have any automated UI or browser testing, so your assistance in testing is appreciated.
## Updating higlight.js
The following are instructions for updating [highlight.js](https://highlightjs.org/).
1. Clone the repository at <https://github.com/highlightjs/highlight.js>
1. Check out a tagged release (like `10.1.1`).
1. Run `npm install`
1. Run `node tools/build.js :common apache armasm coffeescript d handlebars haskell http julia nginx properties r scala x86asm yaml`
1. Compare the language list that it spits out to the one in [`syntax-highlighting.md`](https://github.com/camelid/mdBook/blob/master/guide/src/format/theme/syntax-highlighting.md). If any are missing, add them to the list and rebuild (and update these docs). If any are added to the common set, add them to `syntax-highlighting.md`.
1. Copy `build/highlight.min.js` to mdbook's directory [`highlight.js`](https://github.com/rust-lang/mdBook/blob/master/src/theme/highlight.js).
1. Be sure to check the highlight.js [CHANGES](https://github.com/highlightjs/highlight.js/blob/main/CHANGES.md) for any breaking changes. Breaking changes that would affect users will need to wait until the next major release.
1. Build mdbook with the new file and build some books with the new version and compare the output with a variety of languages to see if anything changes. (TODO: It would be nice to have a demo file in the repo to help with this.)

595
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "mdbook"
version = "0.4.10"
version = "0.4.14"
authors = [
"Mathieu David <mathieudavid@mathieudavid.org>",
"Michael-F-Bryan <michaelfbryan@gmail.com>",
@@ -24,8 +24,8 @@ handlebars = "4.0"
lazy_static = "1.0"
log = "0.4"
memchr = "2.0"
open = "1.1"
pulldown-cmark = "0.7.0"
opener = "0.5"
pulldown-cmark = "0.8.0"
regex = "1.0.0"
serde = "1.0"
serde_derive = "1.0"
@@ -33,6 +33,7 @@ serde_json = "1.0"
shlex = "1"
tempfile = "3.0"
toml = "0.5.1"
topological-sort = "0.1.0"
# Watch feature
notify = { version = "4.0", optional = true }
@@ -40,14 +41,16 @@ gitignore = { version = "1.0", optional = true }
# Serve feature
futures-util = { version = "0.3.4", optional = true }
tokio = { version = "0.2.18", features = ["macros"], optional = true }
warp = { version = "0.2.2", default-features = false, features = ["websocket"], optional = true }
tokio = { version = "1", features = ["macros", "rt-multi-thread"], optional = true }
warp = { version = "0.3.1", default-features = false, features = ["websocket"], optional = true }
# Search feature
elasticlunr-rs = { version = "2.3", optional = true, default-features = false }
ammonia = { version = "3", optional = true }
[dev-dependencies]
assert_cmd = "1"
predicates = "2"
select = "0.5"
semver = "0.11.0"
pretty_assertions = "0.6"

View File

@@ -163,6 +163,7 @@ of a book in order to validate links or run tests. Some existing renderers are:
preprocessors.
- [`linkcheck`] - a backend which will check that all links are valid
- [`epub`] - an experimental EPUB generator
- [`man`] - a backend that generates manual pages from the book
> **Note for Developers:** Feel free to send us a PR if you've developed your
> own plugin and want it mentioned here.
@@ -234,3 +235,4 @@ All the code in this repository is released under the ***Mozilla Public License
[master-docs]: http://rust-lang.github.io/mdBook/
[`linkcheck`]: https://crates.io/crates/mdbook-linkcheck
[`epub`]: https://crates.io/crates/mdbook-epub
[`man`]: https://crates.io/crates/mdbook-man

View File

@@ -37,7 +37,7 @@ fn handle_preprocessing(pre: &dyn Preprocessor) -> Result<(), Error> {
let book_version = Version::parse(&ctx.mdbook_version)?;
let version_req = VersionReq::parse(mdbook::MDBOOK_VERSION)?;
if version_req.matches(&book_version) != true {
if !version_req.matches(&book_version) {
eprintln!(
"Warning: The {} plugin was built against version {} of mdbook, \
but we're being called from version {}",
@@ -55,7 +55,7 @@ fn handle_preprocessing(pre: &dyn Preprocessor) -> Result<(), Error> {
fn handle_supports(pre: &dyn Preprocessor, sub_args: &ArgMatches) -> ! {
let renderer = sub_args.value_of("renderer").expect("Required argument");
let supported = pre.supports_renderer(&renderer);
let supported = pre.supports_renderer(renderer);
// Signal whether the renderer is supported by exiting with 1 or 0.
if supported {

View File

@@ -1,25 +1,40 @@
# mdBook
# Introduction
**mdBook** is a command line tool and Rust crate to create books using Markdown
(as by the [CommonMark](https://commonmark.org/) specification) files. It's very
similar to Gitbook but written in [Rust](http://www.rust-lang.org).
**mdBook** is a command line tool and Rust crate to create books with Markdown. The output resembles tools like Gitbook,
and is ideal for creating product or API documentation, tutorials, course materials or anything that requires a clean,
easily navigable and customizable presentation. mdBook is written in [Rust](https://www.rust-lang.org); its performance
and simplicity made it ideal for use as a tool to publish directly to hosted websites such
as [GitHub Pages](https://pages.github.com) via automation. This guide, in fact, serves as both the mdBook documentation
and a fine example of what mdBook produces.
What you are reading serves as an example of the output of mdBook and at the
same time as a high-level documentation.
mdBook includes built in support for both preprocessing your Markdown and alternative renderers for producing formats
other than HTML. These facilities also enable other functionality such as
validation. [Searching](https://crates.io/search?q=mdbook&sort=relevance) Rust's [crates.io](https://crates.io) is a
great way to discover more extensions.
mdBook is free and open source, you can find the source code on
[GitHub](https://github.com/rust-lang/mdBook). Issues and feature
requests can be posted on the [GitHub issue
tracker](https://github.com/rust-lang/mdBook/issues).
## API Documentation
## API docs
In addition to the above features, mdBook also has a Rust [API](https://docs.rs/mdbook/*/mdbook/). This allows you to
write your own preprocessor or renderer, as well as incorporate mdBook features into other applications.
The [For Developers](for_developers) section of this guide contains more information and some examples.
Alongside this book you can also read the [API
docs](https://docs.rs/mdbook/*/mdbook/) generated by Rustdoc if you would like
to use mdBook as a crate or write a new renderer and need a more low-level
overview.
## Markdown
mdBook's [parser](https://github.com/raphlinus/pulldown-cmark) adheres to the [CommonMark](https://commonmark.org/)
specification. You can take a quick [tutorial](https://commonmark.org/help/tutorial/),
or [try out](https://spec.commonmark.org/dingus/) CommonMark in real time. For a more in-depth experience, check out the
[Markdown Guide](https://www.markdownguide.org).
## Contributing
mdBook is free and open source. You can find the source code on
[GitHub](https://github.com/rust-lang/mdBook) and issues and feature requests can be posted on
the [GitHub issue tracker](https://github.com/rust-lang/mdBook/issues). mdBook relies on the community to fix bugs and
add features: if you'd like to contribute, please read
the [CONTRIBUTING](https://github.com/rust-lang/mdBook/blob/master/CONTRIBUTING.md) guide and consider opening
a [pull request](https://github.com/rust-lang/mdBook/pulls).
## License
mdBook, all the source code, is released under the [Mozilla Public License
v2.0](https://www.mozilla.org/MPL/2.0/).
The mdBook source and documentation are released under
the [Mozilla Public License v2.0](https://www.mozilla.org/MPL/2.0/).

View File

@@ -1,6 +1,6 @@
# Summary
- [mdBook](README.md)
- [Introduction](README.md)
- [Command Line Tool](cli/README.md)
- [init](cli/init.md)
- [build](cli/build.md)

View File

@@ -52,3 +52,19 @@ directory called `theme` in your source directory so that you can modify it.
The theme is selectively overwritten, this means that if you don't want to
overwrite a specific file, just delete it and the default file will be used.
#### --title
Specify a title for the book. If not supplied, an interactive prompt will ask for
a title.
```bash
mdbook init --title="my amazing book"
```
#### --ignore
Create a `.gitignore` file configured to ignore the `book` directory created when [building] a book.
If not supplied, an interactive prompt will ask whether it should be created.
[building]: build.md

View File

@@ -43,7 +43,17 @@ mdbook test path/to/book
The `--library-path` (`-L`) option allows you to add directories to the library
search path used by `rustdoc` when it builds and tests the examples. Multiple
directories can be specified with multiple options (`-L foo -L bar`) or with a
comma-delimited list (`-L foo,bar`).
comma-delimited list (`-L foo,bar`). The path should point to the Cargo
[build cache](https://doc.rust-lang.org/cargo/guide/build-cache.html) `deps` directory that
contains the build output of your project. For example, if your Rust project's book is in a directory
named `my-book`, the following command would include the crate's dependencies when running `test`:
```shell
mdbook test my-book -L target/debug/deps/
```
See the `rustdoc` command-line [documentation](https://doc.rust-lang.org/rustdoc/command-line-arguments.html#-l--library-path-where-to-look-for-dependencies)
for more information.
#### --dest-dir

View File

@@ -26,7 +26,7 @@ before_script:
- cargo install-update -a
script:
- mdbook build path/to/mybook && mdbook test path/to/mybook
- mdbook build && mdbook test # In case of custom book path: mdbook build path/to/mybook && mdbook test path/to/mybook
```
## Deploying Your Book to GitHub Pages
@@ -50,10 +50,10 @@ deploy:
provider: pages
skip-cleanup: true
github-token: $GITHUB_TOKEN
local-dir: path/to/mybook/book
local-dir: book # In case of custom book path: path/to/mybook/book
keep-history: false
on:
branch: master
branch: main
```
That's it!
@@ -77,7 +77,7 @@ before_script:
- cargo install-update -a
script:
- mdbook build path/to/mybook && mdbook test path/to/mybook
- mdbook build && mdbook test # In case of custom book path: mdbook build path/to/mybook && mdbook test path/to/mybook
deploy:
provider: pages
@@ -85,10 +85,10 @@ deploy:
edge: true
cleanup: false
github-token: $GITHUB_TOKEN
local-dir: path/to/mybook/book
local-dir: book # In case of custom book path: path/to/mybook/book
keep-history: false
on:
branch: master
branch: main
target_branch: gh-pages
```
@@ -140,15 +140,15 @@ pages:
- export PATH="$PATH:$CARGO_HOME/bin"
- mdbook --version || cargo install mdbook
script:
- mdbook build -d public
only:
- master
- mdbook build -d public
rules:
- if: '$CI_COMMIT_REF_NAME == "master"'
artifacts:
paths:
- public
paths:
- public
cache:
paths:
- $CARGO_HOME/bin
- $CARGO_HOME/bin
```
After you commit and push this new file, GitLab CI will run and your book will be available!

View File

@@ -13,6 +13,7 @@ rough example of how this is accomplished in practice.
- [mdbook-epub] - an EPUB renderer
- [mdbook-test] - a program to run the book's contents through [rust-skeptic] to
verify everything compiles and runs correctly (similar to `rustdoc --test`)
- [mdbook-man] - generate manual pages from the book
This page will step you through creating your own alternative backend in the form
of a simple word counting program. Although it will be written in Rust, there's
@@ -377,6 +378,7 @@ the source code or ask questions.
[mdbook-linkcheck]: https://github.com/Michael-F-Bryan/mdbook-linkcheck
[mdbook-epub]: https://github.com/Michael-F-Bryan/mdbook-epub
[mdbook-test]: https://github.com/Michael-F-Bryan/mdbook-test
[mdbook-man]: https://github.com/vv9k/mdbook-man
[rust-skeptic]: https://github.com/budziq/rust-skeptic
[`RenderContext`]: https://docs.rs/mdbook/*/mdbook/renderer/struct.RenderContext.html
[`RenderContext::from_json()`]: https://docs.rs/mdbook/*/mdbook/renderer/struct.RenderContext.html#method.from_json

View File

@@ -34,8 +34,15 @@ command = "python3 /path/to/foo.py"
renderer = ["html", "epub"]
```
In typical unix style, all inputs to the plugin will be written to `stdin` as
JSON and `mdbook` will read from `stdout` if it is expecting output.
Once the preprocessor has been defined and the build process starts, mdBook executes the command defined in the `preprocessor.foo.command` key twice.
The first time it runs the preprocessor to determine if it supports the given renderer.
mdBook passes two arguments to the process: the first argument is the string `supports` and the second argument is the renderer name.
The preprocessor should exit with a status code 0 if it supports the given renderer, or return a non-zero exit code if it does not.
If the preprocessor supports the renderer, then mdbook runs it a second time, passing JSON data into stdin.
The JSON consists of an array of `[context, book]` where `context` is the serialized object [`PreprocessorContext`] and `book` is a [`Book`] object containing the content of the book.
The preprocessor should return the JSON format of the [`Book`] object to stdout, with any modifications it wishes to perform.
The easiest way to get started is by creating your own implementation of the
`Preprocessor` trait (e.g. in `lib.rs`) and then creating a shell binary which
@@ -106,6 +113,33 @@ fn remove_emphasis(
For everything else, have a look [at the complete example][example].
## Implementing a preprocessor with a different language
The fact that mdBook utilizes stdin and stdout to communicate with the preprocessors makes it easy to implement them in a language other than Rust.
The following code shows how to implement a simple preprocessor in Python, which will modify the content of the first chapter.
The example below follows the configuration shown above with `preprocessor.foo.command` actually pointing to a Python script.
```python
import json
import sys
if __name__ == '__main__':
if len(sys.argv) > 1: # we check if we received any argument
if sys.argv[1] == "supports":
# then we are good to return an exit status code of 0, since the other argument will just be the renderer's name
sys.exit(0)
# load both the context and the book representations from stdin
context, book = json.load(sys.stdin)
# and now, we can just modify the content of the first chapter
book['sections'][0]['Chapter']['content'] = '# Hello'
# we are done with the book's modification, we can just print it to stdout,
print(json.dumps(book))
```
[preprocessor-docs]: https://docs.rs/mdbook/latest/mdbook/preprocess/trait.Preprocessor.html
[pc]: https://crates.io/crates/pulldown-cmark
[pctc]: https://crates.io/crates/pulldown-cmark-to-cmark
@@ -113,3 +147,5 @@ For everything else, have a look [at the complete example][example].
[an example no-op preprocessor]: https://github.com/rust-lang/mdBook/blob/master/examples/nop-preprocessor.rs
[`CmdPreprocessor::parse_input()`]: https://docs.rs/mdbook/latest/mdbook/preprocess/trait.Preprocessor.html#method.parse_input
[`Book::for_each_mut()`]: https://docs.rs/mdbook/latest/mdbook/book/struct.Book.html#method.for_each_mut
[`PreprocessorContext`]: https://docs.rs/mdbook/latest/mdbook/preprocess/struct.PreprocessorContext.html
[`Book`]: https://docs.rs/mdbook/latest/mdbook/book/struct.Book.html

View File

@@ -63,8 +63,8 @@ Options for the Rust language, relevant to running tests and playground
integration.
- **edition**: Rust edition to use by default for the code snippets. Default
is "2015". Individual code blocks can be controlled with the `edition2015`
or `edition2018` annotations, such as:
is "2015". Individual code blocks can be controlled with the `edition2015`,
`edition2018` or `edition2021` annotations, such as:
~~~text
```rust,edition2015

View File

@@ -56,3 +56,25 @@ be overridden by adding a `command` field.
[preprocessor.random]
command = "python random.py"
```
### Require A Certain Order
The order in which preprocessors are run can be controlled with the `before` and `after` fields.
For example, suppose you want your `linenos` preprocessor to process lines that may have been `{{#include}}`d; then you want it to run after the built-in `links` preprocessor, which you can require using either the `before` or `after` field:
```toml
[preprocessor.linenos]
after = [ "links" ]
```
or
```toml
[preprocessor.links]
before = [ "linenos" ]
```
It would also be possible, though redundant, to specify both of the above in the same config file.
Preprocessors having the same priority specified through `before` and `after` are sorted by name.
Any infinite loops will be detected and produce an error.

View File

@@ -21,8 +21,8 @@ The following configuration options are available:
- **mathjax-support:** Adds support for [MathJax](../mathjax.md). Defaults to
`false`.
- **copy-fonts:** Copies fonts.css and respective font files to the output directory and use them in the default theme. Defaults to `true`.
- **google-analytics:** If you use Google Analytics, this option lets you enable
it by simply specifying your ID in the configuration file.
- **google-analytics:** This field has been deprecated and will be removed in a future release.
Use the `theme/head.hbs` file to add the appropriate Google Analytics code instead.
- **additional-css:** If you need to slightly change the appearance of your book
without overwriting the whole style, you can specify a set of stylesheets that
will be loaded after the default ones where you can surgically change the
@@ -131,7 +131,6 @@ preferred-dark-theme = "navy"
curly-quotes = true
mathjax-support = false
copy-fonts = true
google-analytics = "UA-123456-7"
additional-css = ["custom.css", "custom2.css"]
additional-js = ["custom.js"]
no-section-label = false
@@ -188,13 +187,13 @@ Enable it by adding an empty table to your `book.toml` as follows:
There are no configuration options for the Markdown renderer at this time;
only whether it is enabled or disabled.
See [the preprocessors documentation](#configuring-preprocessors) for how to
See [the preprocessors documentation](preprocessors.md) for how to
specify which preprocessors should run before the Markdown renderer.
### Custom Renderers
A custom renderer can be enabled by adding a `[output.foo]` table to your
`book.toml`. Similar to [preprocessors](#configuring-preprocessors) this will
`book.toml`. Similar to [preprocessors](preprocessors.md) this will
instruct `mdbook` to pass a representation of the book to `mdbook-foo` for
rendering. See the [alternative backends] chapter for more detail.

View File

@@ -21,7 +21,7 @@ Will render as
```rust
# fn main() {
let x = 5;
let y = 7;
let y = 6;
println!("{}", x + y);
# }

View File

@@ -15,8 +15,10 @@ shout-out to them!
- [projektir](https://github.com/projektir)
- [Phaiax](https://github.com/Phaiax)
- Matt Ickstadt ([mattico](https://github.com/mattico))
- Weihang Lo ([@weihanglo](https://github.com/weihanglo))
- Avision Ho ([@avisionh](https://github.com/avisionh))
- Vivek Akupatni ([@apatniv](https://github.com/apatniv))
- Weihang Lo ([weihanglo](https://github.com/weihanglo))
- Avision Ho ([avisionh](https://github.com/avisionh))
- Vivek Akupatni ([apatniv](https://github.com/apatniv))
- Eric Huss ([ehuss](https://github.com/ehuss))
- Josh Rotenberg ([joshrotenberg](https://github.com/joshrotenberg))
If you feel you're missing from this list, feel free to add yourself in a PR.

View File

@@ -22,7 +22,7 @@ pub fn load_book<P: AsRef<Path>>(src_dir: P, cfg: &BuildConfig) -> Result<Book>
.with_context(|| format!("Summary parsing failed for file={:?}", summary_md))?;
if cfg.create_missing {
create_missing(&src_dir, &summary).with_context(|| "Unable to create missing chapters")?;
create_missing(src_dir, &summary).with_context(|| "Unable to create missing chapters")?;
}
load_book_from_disk(&summary, src_dir)
@@ -381,7 +381,7 @@ And here is some \
root.nested_items.push(second.clone().into());
root.nested_items.push(SummaryItem::Separator);
root.nested_items.push(second.clone().into());
root.nested_items.push(second.into());
(root, temp_dir)
}
@@ -454,7 +454,7 @@ And here is some \
sub_items: vec![
BookItem::Chapter(nested.clone()),
BookItem::Separator,
BookItem::Chapter(nested.clone()),
BookItem::Chapter(nested),
],
});

View File

@@ -20,6 +20,7 @@ use std::process::Command;
use std::string::ToString;
use tempfile::Builder as TempFileBuilder;
use toml::Value;
use topological_sort::TopologicalSort;
use crate::errors::*;
use crate::preprocess::{
@@ -69,6 +70,20 @@ impl MDBook {
config.update_from_env();
if config
.html_config()
.map_or(false, |html| html.google_analytics.is_some())
{
warn!(
"The output.html.google-analytics field has been deprecated; \
it will be removed in a future release.\n\
Consider placing the appropriate site tag code into the \
theme/head.hbs file instead.\n\
The tracking code may be found in the Google Analytics Admin page.\n\
"
);
}
if log_enabled!(log::Level::Trace) {
for line in format!("Config: {:#?}", config).lines() {
trace!("{}", line);
@@ -274,6 +289,9 @@ impl MDBook {
RustEdition::E2018 => {
cmd.args(&["--edition", "2018"]);
}
RustEdition::E2021 => {
cmd.args(&["--edition", "2021"]);
}
}
}
@@ -368,12 +386,7 @@ fn determine_renderers(config: &Config) -> Vec<Box<dyn Renderer>> {
renderers
}
fn default_preprocessors() -> Vec<Box<dyn Preprocessor>> {
vec![
Box::new(LinkPreprocessor::new()),
Box::new(IndexPreprocessor::new()),
]
}
const DEFAULT_PREPROCESSORS: &[&'static str] = &["links", "index"];
fn is_default_preprocessor(pre: &dyn Preprocessor) -> bool {
let name = pre.name();
@@ -382,36 +395,127 @@ fn is_default_preprocessor(pre: &dyn Preprocessor) -> bool {
/// Look at the `MDBook` and try to figure out what preprocessors to run.
fn determine_preprocessors(config: &Config) -> Result<Vec<Box<dyn Preprocessor>>> {
let mut preprocessors = Vec::new();
// Collect the names of all preprocessors intended to be run, and the order
// in which they should be run.
let mut preprocessor_names = TopologicalSort::<String>::new();
if config.build.use_default_preprocessors {
preprocessors.extend(default_preprocessors());
for name in DEFAULT_PREPROCESSORS {
preprocessor_names.insert(name.to_string());
}
}
if let Some(preprocessor_table) = config.get("preprocessor").and_then(Value::as_table) {
for key in preprocessor_table.keys() {
match key.as_ref() {
"links" => preprocessors.push(Box::new(LinkPreprocessor::new())),
"index" => preprocessors.push(Box::new(IndexPreprocessor::new())),
name => preprocessors.push(interpret_custom_preprocessor(
name,
&preprocessor_table[name],
)),
for (name, table) in preprocessor_table.iter() {
preprocessor_names.insert(name.to_string());
let exists = |name| {
(config.build.use_default_preprocessors && DEFAULT_PREPROCESSORS.contains(&name))
|| preprocessor_table.contains_key(name)
};
if let Some(before) = table.get("before") {
let before = before.as_array().ok_or_else(|| {
Error::msg(format!(
"Expected preprocessor.{}.before to be an array",
name
))
})?;
for after in before {
let after = after.as_str().ok_or_else(|| {
Error::msg(format!(
"Expected preprocessor.{}.before to contain strings",
name
))
})?;
if !exists(after) {
// Only warn so that preprocessors can be toggled on and off (e.g. for
// troubleshooting) without having to worry about order too much.
warn!(
"preprocessor.{}.after contains \"{}\", which was not found",
name, after
);
} else {
preprocessor_names.add_dependency(name, after);
}
}
}
if let Some(after) = table.get("after") {
let after = after.as_array().ok_or_else(|| {
Error::msg(format!(
"Expected preprocessor.{}.after to be an array",
name
))
})?;
for before in after {
let before = before.as_str().ok_or_else(|| {
Error::msg(format!(
"Expected preprocessor.{}.after to contain strings",
name
))
})?;
if !exists(before) {
// See equivalent warning above for rationale
warn!(
"preprocessor.{}.before contains \"{}\", which was not found",
name, before
);
} else {
preprocessor_names.add_dependency(before, name);
}
}
}
}
}
Ok(preprocessors)
// Now that all links have been established, queue preprocessors in a suitable order
let mut preprocessors = Vec::with_capacity(preprocessor_names.len());
// `pop_all()` returns an empty vector when no more items are not being depended upon
for mut names in std::iter::repeat_with(|| preprocessor_names.pop_all())
.take_while(|names| !names.is_empty())
{
// The `topological_sort` crate does not guarantee a stable order for ties, even across
// runs of the same program. Thus, we break ties manually by sorting.
// Careful: `str`'s default sorting, which we are implicitly invoking here, uses code point
// values ([1]), which may not be an alphabetical sort.
// As mentioned in [1], doing so depends on locale, which is not desirable for deciding
// preprocessor execution order.
// [1]: https://doc.rust-lang.org/stable/std/cmp/trait.Ord.html#impl-Ord-14
names.sort();
for name in names {
let preprocessor: Box<dyn Preprocessor> = match name.as_str() {
"links" => Box::new(LinkPreprocessor::new()),
"index" => Box::new(IndexPreprocessor::new()),
_ => {
// The only way to request a custom preprocessor is through the `preprocessor`
// table, so it must exist, be a table, and contain the key.
let table = &config.get("preprocessor").unwrap().as_table().unwrap()[&name];
let command = get_custom_preprocessor_cmd(&name, table);
Box::new(CmdPreprocessor::new(name, command))
}
};
preprocessors.push(preprocessor);
}
}
// "If `pop_all` returns an empty vector and `len` is not 0, there are cyclic dependencies."
// Normally, `len() == 0` is equivalent to `is_empty()`, so we'll use that.
if preprocessor_names.is_empty() {
Ok(preprocessors)
} else {
Err(Error::msg("Cyclic dependency detected in preprocessors"))
}
}
fn interpret_custom_preprocessor(key: &str, table: &Value) -> Box<CmdPreprocessor> {
let command = table
fn get_custom_preprocessor_cmd(key: &str, table: &Value) -> String {
table
.get("command")
.and_then(Value::as_str)
.map(ToString::to_string)
.unwrap_or_else(|| format!("mdbook-{}", key));
Box::new(CmdPreprocessor::new(key.to_string(), command))
.unwrap_or_else(|| format!("mdbook-{}", key))
}
fn interpret_custom_renderer(key: &str, table: &Value) -> Box<CmdRenderer> {
@@ -511,8 +615,8 @@ mod tests {
assert!(got.is_ok());
assert_eq!(got.as_ref().unwrap().len(), 2);
assert_eq!(got.as_ref().unwrap()[0].name(), "links");
assert_eq!(got.as_ref().unwrap()[1].name(), "index");
assert_eq!(got.as_ref().unwrap()[0].name(), "index");
assert_eq!(got.as_ref().unwrap()[1].name(), "links");
}
#[test]
@@ -559,9 +663,123 @@ mod tests {
// make sure the `preprocessor.random` table exists
let random = cfg.get_preprocessor("random").unwrap();
let random = interpret_custom_preprocessor("random", &Value::Table(random.clone()));
let random = get_custom_preprocessor_cmd("random", &Value::Table(random.clone()));
assert_eq!(random.cmd(), "python random.py");
assert_eq!(random, "python random.py");
}
#[test]
fn preprocessor_before_must_be_array() {
let cfg_str = r#"
[preprocessor.random]
before = 0
"#;
let cfg = Config::from_str(cfg_str).unwrap();
assert!(determine_preprocessors(&cfg).is_err());
}
#[test]
fn preprocessor_after_must_be_array() {
let cfg_str = r#"
[preprocessor.random]
after = 0
"#;
let cfg = Config::from_str(cfg_str).unwrap();
assert!(determine_preprocessors(&cfg).is_err());
}
#[test]
fn preprocessor_order_is_honored() {
let cfg_str = r#"
[preprocessor.random]
before = [ "last" ]
after = [ "index" ]
[preprocessor.last]
after = [ "links", "index" ]
"#;
let cfg = Config::from_str(cfg_str).unwrap();
let preprocessors = determine_preprocessors(&cfg).unwrap();
let index = |name| {
preprocessors
.iter()
.enumerate()
.find(|(_, preprocessor)| preprocessor.name() == name)
.unwrap()
.0
};
let assert_before = |before, after| {
if index(before) >= index(after) {
eprintln!("Preprocessor order:");
for preprocessor in &preprocessors {
eprintln!(" {}", preprocessor.name());
}
panic!("{} should come before {}", before, after);
}
};
assert_before("index", "random");
assert_before("index", "last");
assert_before("random", "last");
assert_before("links", "last");
}
#[test]
fn cyclic_dependencies_are_detected() {
let cfg_str = r#"
[preprocessor.links]
before = [ "index" ]
[preprocessor.index]
before = [ "links" ]
"#;
let cfg = Config::from_str(cfg_str).unwrap();
assert!(determine_preprocessors(&cfg).is_err());
}
#[test]
fn dependencies_dont_register_undefined_preprocessors() {
let cfg_str = r#"
[preprocessor.links]
before = [ "random" ]
"#;
let cfg = Config::from_str(cfg_str).unwrap();
let preprocessors = determine_preprocessors(&cfg).unwrap();
assert!(preprocessors
.iter()
.find(|preprocessor| preprocessor.name() == "random")
.is_none());
}
#[test]
fn dependencies_dont_register_builtin_preprocessors_if_disabled() {
let cfg_str = r#"
[preprocessor.random]
before = [ "links" ]
[build]
use-default-preprocessors = false
"#;
let cfg = Config::from_str(cfg_str).unwrap();
let preprocessors = determine_preprocessors(&cfg).unwrap();
assert!(preprocessors
.iter()
.find(|preprocessor| preprocessor.name() == "links")
.is_none());
}
#[test]

View File

@@ -382,7 +382,7 @@ impl<'a> SummaryParser<'a> {
}
Some(ev @ Event::Start(Tag::List(..))) => {
self.back(ev);
let mut bunch_of_items = self.parse_nested_numbered(&root_number)?;
let mut bunch_of_items = self.parse_nested_numbered(root_number)?;
// if we've resumed after something like a rule the root sections
// will be numbered from 1. We need to manually go back and update

View File

@@ -1,5 +1,5 @@
use crate::get_book_dir;
use clap::{App, ArgMatches, SubCommand};
use clap::{App, Arg, ArgMatches, SubCommand};
use mdbook::config;
use mdbook::errors::Result;
use mdbook::MDBook;
@@ -18,6 +18,21 @@ pub fn make_subcommand<'a, 'b>() -> App<'a, 'b> {
)
.arg_from_usage("--theme 'Copies the default theme into your source folder'")
.arg_from_usage("--force 'Skips confirmation prompts'")
.arg(
Arg::with_name("title")
.long("title")
.takes_value(true)
.help("Sets the book title")
.required(false),
)
.arg(
Arg::with_name("ignore")
.long("ignore")
.takes_value(true)
.possible_values(&["none", "git"])
.help("Creates a VCS ignore file (i.e. .gitignore)")
.required(false),
)
}
// Init command implementation
@@ -25,7 +40,6 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
let book_dir = get_book_dir(args);
let mut builder = MDBook::init(&book_dir);
let mut config = config::Config::default();
// If flag `--theme` is present, copy theme to src
if args.is_present("theme") {
let theme_dir = book_dir.join("theme");
@@ -45,13 +59,23 @@ pub fn execute(args: &ArgMatches) -> Result<()> {
}
}
println!("\nDo you want a .gitignore to be created? (y/n)");
if confirm() {
builder.create_gitignore(true);
if let Some(ignore) = args.value_of("ignore") {
match ignore {
"git" => builder.create_gitignore(true),
_ => builder.create_gitignore(false),
};
} else {
println!("\nDo you want a .gitignore to be created? (y/n)");
if confirm() {
builder.create_gitignore(true);
}
}
config.book.title = request_book_title();
config.book.title = if args.is_present("title") {
args.value_of("title").map(String::from)
} else {
request_book_title()
};
if let Some(author) = get_author_name() {
debug!("Obtained user name from gitconfig: {:?}", author);

View File

@@ -467,6 +467,9 @@ pub struct RustConfig {
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
/// Rust edition to use for the code.
pub enum RustEdition {
/// The 2021 edition of Rust
#[serde(rename = "2021")]
E2021,
/// The 2018 edition of Rust
#[serde(rename = "2018")]
E2018,
@@ -855,6 +858,26 @@ mod tests {
assert_eq!(got.rust, rust_should_be);
}
#[test]
fn edition_2021() {
let src = r#"
[book]
title = "mdBook Documentation"
description = "Create book from markdown files. Like Gitbook but implemented in Rust"
authors = ["Mathieu David"]
src = "./source"
[rust]
edition = "2021"
"#;
let rust_should_be = RustConfig {
edition: Some(RustEdition::E2021),
};
let got = Config::from_str(src).unwrap();
assert_eq!(got.rust, rust_should_be);
}
#[test]
fn load_arbitrary_output_type() {
#[derive(Debug, Deserialize, PartialEq)]

View File

@@ -3,8 +3,9 @@ extern crate clap;
#[macro_use]
extern crate log;
use anyhow::anyhow;
use chrono::Local;
use clap::{App, AppSettings, ArgMatches};
use clap::{App, AppSettings, Arg, ArgMatches, Shell, SubCommand};
use env_logger::Builder;
use log::LevelFilter;
use mdbook::utils;
@@ -20,7 +21,40 @@ const VERSION: &str = concat!("v", crate_version!());
fn main() {
init_logger();
// Create a list of valid arguments and sub-commands
let app = create_clap_app();
// Check which subcomamnd the user ran...
let res = match app.get_matches().subcommand() {
("init", Some(sub_matches)) => cmd::init::execute(sub_matches),
("build", Some(sub_matches)) => cmd::build::execute(sub_matches),
("clean", Some(sub_matches)) => cmd::clean::execute(sub_matches),
#[cfg(feature = "watch")]
("watch", Some(sub_matches)) => cmd::watch::execute(sub_matches),
#[cfg(feature = "serve")]
("serve", Some(sub_matches)) => cmd::serve::execute(sub_matches),
("test", Some(sub_matches)) => cmd::test::execute(sub_matches),
("completions", Some(sub_matches)) => (|| {
let shell: Shell = sub_matches
.value_of("shell")
.ok_or_else(|| anyhow!("Shell name missing."))?
.parse()
.map_err(|s| anyhow!("Invalid shell: {}", s))?;
create_clap_app().gen_completions_to("mdbook", shell, &mut std::io::stdout().lock());
Ok(())
})(),
(_, _) => unreachable!(),
};
if let Err(e) = res {
utils::log_backtrace(&e);
std::process::exit(101);
}
}
/// Create a list of valid arguments and sub-commands
fn create_clap_app<'a, 'b>() -> App<'a, 'b> {
let app = App::new(crate_name!())
.about(crate_description!())
.author("Mathieu David <mathieudavid@mathieudavid.org>")
@@ -35,31 +69,26 @@ fn main() {
.subcommand(cmd::init::make_subcommand())
.subcommand(cmd::build::make_subcommand())
.subcommand(cmd::test::make_subcommand())
.subcommand(cmd::clean::make_subcommand());
.subcommand(cmd::clean::make_subcommand())
.subcommand(
SubCommand::with_name("completions")
.about("Generate shell completions for your shell to stdout")
.arg(
Arg::with_name("shell")
.takes_value(true)
.possible_values(&Shell::variants())
.help("the shell to generate completions for")
.value_name("SHELL")
.required(true),
),
);
#[cfg(feature = "watch")]
let app = app.subcommand(cmd::watch::make_subcommand());
#[cfg(feature = "serve")]
let app = app.subcommand(cmd::serve::make_subcommand());
// Check which subcomamnd the user ran...
let res = match app.get_matches().subcommand() {
("init", Some(sub_matches)) => cmd::init::execute(sub_matches),
("build", Some(sub_matches)) => cmd::build::execute(sub_matches),
("clean", Some(sub_matches)) => cmd::clean::execute(sub_matches),
#[cfg(feature = "watch")]
("watch", Some(sub_matches)) => cmd::watch::execute(sub_matches),
#[cfg(feature = "serve")]
("serve", Some(sub_matches)) => cmd::serve::execute(sub_matches),
("test", Some(sub_matches)) => cmd::test::execute(sub_matches),
(_, _) => unreachable!(),
};
if let Err(e) = res {
utils::log_backtrace(&e);
std::process::exit(101);
}
app
}
fn init_logger() {
@@ -103,7 +132,8 @@ fn get_book_dir(args: &ArgMatches) -> PathBuf {
}
fn open<P: AsRef<OsStr>>(path: P) {
if let Err(e) = open::that(path) {
info!("Opening web browser");
if let Err(e) = opener::open(path) {
error!("Error opening web browser: {}", e);
}
}

View File

@@ -49,7 +49,7 @@ impl CmdPreprocessor {
fn write_input_to_child(&self, child: &mut Child, book: &Book, ctx: &PreprocessorContext) {
let stdin = child.stdin.take().expect("Child has stdin");
if let Err(e) = self.write_input(stdin, &book, &ctx) {
if let Err(e) = self.write_input(stdin, book, ctx) {
// Looks like the backend hung up before we could finish
// sending it the render context. Log the error and keep going
warn!("Error writing the RenderContext to the backend, {}", e);

View File

@@ -54,11 +54,8 @@ impl HtmlHandlebars {
let content = ch.content.clone();
let content = utils::render_markdown(&content, ctx.html_config.curly_quotes);
let fixed_content = utils::render_markdown_with_path(
&ch.content,
ctx.html_config.curly_quotes,
Some(&path),
);
let fixed_content =
utils::render_markdown_with_path(&ch.content, ctx.html_config.curly_quotes, Some(path));
if !ctx.is_index {
// Add page break between chapters
// See https://developer.mozilla.org/en-US/docs/Web/CSS/break-before and https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-before
@@ -178,7 +175,7 @@ impl HtmlHandlebars {
let rendered =
self.post_process(rendered, &html_config.playground, ctx.config.rust.edition);
let output_file = get_404_output_file(&html_config.input_404);
utils::fs::write_file(&destination, output_file, rendered.as_bytes())?;
utils::fs::write_file(destination, output_file, rendered.as_bytes())?;
debug!("Creating 404.html ✓");
Ok(())
}
@@ -223,10 +220,10 @@ impl HtmlHandlebars {
}
write_file(destination, "css/variables.css", &theme.variables_css)?;
if let Some(contents) = &theme.favicon_png {
write_file(destination, "favicon.png", &contents)?;
write_file(destination, "favicon.png", contents)?;
}
if let Some(contents) = &theme.favicon_svg {
write_file(destination, "favicon.svg", &contents)?;
write_file(destination, "favicon.svg", contents)?;
}
write_file(destination, "highlight.css", &theme.highlight_css)?;
write_file(destination, "tomorrow-night.css", &theme.tomorrow_night_css)?;
@@ -509,7 +506,7 @@ impl Renderer for HtmlHandlebars {
debug!("Register handlebars helpers");
self.register_hbs_helpers(&mut handlebars, &html_config);
let mut data = make_data(&ctx.root, &book, &ctx.config, &html_config, &theme)?;
let mut data = make_data(&ctx.root, book, &ctx.config, &html_config, &theme)?;
// Print version
let mut print_content = String::new();
@@ -552,14 +549,14 @@ impl Renderer for HtmlHandlebars {
let rendered =
self.post_process(rendered, &html_config.playground, ctx.config.rust.edition);
utils::fs::write_file(&destination, "print.html", rendered.as_bytes())?;
utils::fs::write_file(destination, "print.html", rendered.as_bytes())?;
debug!("Creating print.html ✓");
}
debug!("Copy static files");
self.copy_static_files(&destination, &theme, &html_config)
self.copy_static_files(destination, &theme, &html_config)
.with_context(|| "Unable to copy across static files")?;
self.copy_additional_css_and_js(&html_config, &ctx.root, &destination)
self.copy_additional_css_and_js(&html_config, &ctx.root, destination)
.with_context(|| "Unable to copy across additional CSS and JS")?;
// Render search index
@@ -567,7 +564,7 @@ impl Renderer for HtmlHandlebars {
{
let search = html_config.search.unwrap_or_default();
if search.enable {
super::search::create_files(&search, &destination, &book)?;
super::search::create_files(&search, destination, book)?;
}
}
@@ -575,7 +572,7 @@ impl Renderer for HtmlHandlebars {
.context("Unable to emit redirects")?;
// Copy all remaining files, avoid a recursive copy from/to the book build dir
utils::fs::copy_files_except_ext(&src_dir, &destination, true, Some(&build_dir), &["md"])?;
utils::fs::copy_files_except_ext(&src_dir, destination, true, Some(&build_dir), &["md"])?;
Ok(())
}
@@ -836,13 +833,15 @@ fn add_playground_pre(
{
let contains_e2015 = classes.contains("edition2015");
let contains_e2018 = classes.contains("edition2018");
let edition_class = if contains_e2015 || contains_e2018 {
let contains_e2021 = classes.contains("edition2021");
let edition_class = if contains_e2015 || contains_e2018 || contains_e2021 {
// the user forced edition, we should not overwrite it
""
} else {
match edition {
Some(RustEdition::E2015) => " edition2015",
Some(RustEdition::E2018) => " edition2018",
Some(RustEdition::E2021) => " edition2021",
None => "",
}
};
@@ -981,7 +980,7 @@ mod tests {
];
for (src, should_be) in inputs {
let got = build_header_links(&src);
let got = build_header_links(src);
assert_eq!(got, should_be);
}
}
@@ -1064,4 +1063,28 @@ mod tests {
assert_eq!(&*got, *should_be);
}
}
#[test]
fn add_playground_edition2021() {
let inputs = [
("<code class=\"language-rust\">x()</code>",
"<pre class=\"playground\"><code class=\"language-rust edition2021\">\n<span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>x()\n<span class=\"boring\">}\n</span></code></pre>"),
("<code class=\"language-rust\">fn main() {}</code>",
"<pre class=\"playground\"><code class=\"language-rust edition2021\">fn main() {}\n</code></pre>"),
("<code class=\"language-rust edition2015\">fn main() {}</code>",
"<pre class=\"playground\"><code class=\"language-rust edition2015\">fn main() {}\n</code></pre>"),
("<code class=\"language-rust edition2018\">fn main() {}</code>",
"<pre class=\"playground\"><code class=\"language-rust edition2018\">fn main() {}\n</code></pre>"),
];
for (src, should_be) in &inputs {
let got = add_playground_pre(
src,
&Playground {
editable: true,
..Playground::default()
},
Some(RustEdition::E2021),
);
assert_eq!(&*got, *should_be);
}
}
}

View File

@@ -91,7 +91,7 @@ fn find_chapter(
match item.get("path") {
Some(path) if !path.is_empty() => {
if let Some(previous) = previous {
if let Some(item) = target.find(&base_path, &path, &item, &previous)? {
if let Some(item) = target.find(&base_path, path, &item, &previous)? {
return Ok(Some(item));
}
}

View File

@@ -142,7 +142,7 @@ impl HelperDef for RenderToc {
// Section does not necessarily exist
if let Some(section) = item.get("section") {
out.write("<strong aria-hidden=\"true\">")?;
out.write(&section)?;
out.write(section)?;
out.write("</strong> ")?;
}
}

View File

@@ -17,10 +17,10 @@ pub fn create_files(search_config: &Search, destination: &Path, book: &Book) ->
let mut doc_urls = Vec::with_capacity(book.sections.len());
for item in book.iter() {
render_item(&mut index, &search_config, &mut doc_urls, item)?;
render_item(&mut index, search_config, &mut doc_urls, item)?;
}
let index = write_to_json(index, &search_config, doc_urls)?;
let index = write_to_json(index, search_config, doc_urls)?;
debug!("Writing search index ✓");
if index.len() > 10_000_000 {
warn!("searchindex.json is very large ({} bytes)", index.len());
@@ -85,7 +85,7 @@ fn render_item(
.with_context(|| "Could not convert HTML path to str")?;
let anchor_base = utils::fs::normalize_path(filepath);
let mut p = utils::new_cmark_parser(&chapter.content).peekable();
let mut p = utils::new_cmark_parser(&chapter.content, false).peekable();
let mut in_heading = false;
let max_section_depth = u32::from(search_config.heading_split_level);
@@ -134,7 +134,7 @@ fn render_item(
// in an HtmlBlock tag. We must collect consecutive Html events
// into a block ourselves.
while let Some(Event::Html(html)) = p.peek() {
html_block.push_str(&html);
html_block.push_str(html);
p.next();
}
@@ -165,7 +165,12 @@ fn render_item(
}
}
if !heading.is_empty() {
if !body.is_empty() || !heading.is_empty() {
if heading.is_empty() {
if let Some(chapter) = breadcrumbs.first() {
heading = chapter.clone();
}
}
// Make sure the last section is added to the index
add_doc(
index,

View File

@@ -108,9 +108,12 @@ function playground_text(playground) {
let text = playground_text(code_block);
let classes = code_block.querySelector('code').classList;
let has_2018 = classes.contains("edition2018");
let edition = has_2018 ? "2018" : "2015";
let edition = "2015";
if(classes.contains("edition2018")) {
edition = "2018";
} else if(classes.contains("edition2021")) {
edition = "2021";
}
var params = {
version: "stable",
optimize: "0",
@@ -133,7 +136,15 @@ function playground_text(playground) {
body: JSON.stringify(params)
})
.then(response => response.json())
.then(response => result_block.innerText = response.result)
.then(response => {
if (response.result.trim() === '') {
result_block.innerText = "No output";
result_block.classList.add("result-no-output");
} else {
result_block.innerText = response.result;
result_block.classList.remove("result-no-output");
}
})
.catch(error => result_block.innerText = "Playground Communication: " + error.message);
}
@@ -151,12 +162,13 @@ function playground_text(playground) {
if (window.ace) {
// language-rust class needs to be removed for editable
// blocks or highlightjs will capture events
Array
.from(document.querySelectorAll('code.editable'))
code_nodes
.filter(function (node) {return node.classList.contains("editable"); })
.forEach(function (block) { block.classList.remove('language-rust'); });
Array
.from(document.querySelectorAll('code:not(.editable)'))
code_nodes
.filter(function (node) {return !node.classList.contains("editable"); })
.forEach(function (block) { hljs.highlightBlock(block); });
} else {
code_nodes.forEach(function (block) { hljs.highlightBlock(block); });
@@ -359,7 +371,14 @@ function playground_text(playground) {
});
themePopup.addEventListener('click', function (e) {
var theme = e.target.id || e.target.parentElement.id;
var theme;
if (e.target.className === "theme") {
theme = e.target.id;
} else if (e.target.parentElement.className === "theme") {
theme = e.target.parentElement.id;
} else {
return;
}
set_theme(theme);
});

View File

@@ -12,6 +12,7 @@ html {
color: var(--fg);
background-color: var(--bg);
text-size-adjust: none;
-webkit-text-size-adjust: none;
}
body {
@@ -175,3 +176,7 @@ blockquote {
margin: 5px 0px;
font-weight: bold;
}
.result-no-output {
font-style: italic;
}

View File

@@ -67,7 +67,7 @@
--links: #2b79a2;
--inline-code-color: #c5c8c6;;
--inline-code-color: #c5c8c6;
--theme-popup-bg: #141617;
--theme-popup-border: #43484d;
@@ -147,7 +147,7 @@
--links: #2b79a2;
--inline-code-color: #c5c8c6;;
--inline-code-color: #c5c8c6;
--theme-popup-bg: #161923;
--theme-popup-border: #737480;
@@ -228,7 +228,7 @@
--links: #2b79a2;
--inline-code-color: #c5c8c6;;
--inline-code-color: #c5c8c6;
--theme-popup-bg: #141617;
--theme-popup-border: #43484d;

View File

@@ -247,7 +247,7 @@ mod tests {
}
if let Err(e) =
copy_files_except_ext(&tmp.path(), &tmp.path().join("output"), true, None, &["md"])
copy_files_except_ext(tmp.path(), &tmp.path().join("output"), true, None, &["md"])
{
panic!("Error while executing the function:\n{:?}", e);
}

View File

@@ -48,19 +48,11 @@ pub fn id_from_content(content: &str) -> String {
let mut content = content.to_string();
// Skip any tags or html-encoded stuff
const REPL_SUB: &[&str] = &[
"<em>",
"</em>",
"<code>",
"</code>",
"<strong>",
"</strong>",
"&lt;",
"&gt;",
"&amp;",
"&#39;",
"&quot;",
];
lazy_static! {
static ref HTML: Regex = Regex::new(r"(<.*?>)").unwrap();
}
content = HTML.replace_all(&content, "").into();
const REPL_SUB: &[&str] = &["&lt;", "&gt;", "&amp;", "&#39;", "&quot;"];
for sub in REPL_SUB {
content = content.replace(sub, "");
}
@@ -168,67 +160,40 @@ pub fn render_markdown(text: &str, curly_quotes: bool) -> String {
render_markdown_with_path(text, curly_quotes, None)
}
pub fn new_cmark_parser(text: &str) -> Parser<'_> {
pub fn new_cmark_parser(text: &str, curly_quotes: bool) -> Parser<'_> {
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);
if curly_quotes {
opts.insert(Options::ENABLE_SMART_PUNCTUATION);
}
Parser::new_ext(text, opts)
}
pub fn render_markdown_with_path(text: &str, curly_quotes: bool, path: Option<&Path>) -> String {
let mut s = String::with_capacity(text.len() * 3 / 2);
let p = new_cmark_parser(text);
let mut converter = EventQuoteConverter::new(curly_quotes);
let p = new_cmark_parser(text, curly_quotes);
let events = p
.map(clean_codeblock_headers)
.map(|event| adjust_links(event, path))
.map(|event| converter.convert(event));
.map(|event| adjust_links(event, path));
html::push_html(&mut s, events);
s
}
struct EventQuoteConverter {
enabled: bool,
convert_text: bool,
}
impl EventQuoteConverter {
fn new(enabled: bool) -> Self {
EventQuoteConverter {
enabled,
convert_text: true,
}
}
fn convert<'a>(&mut self, event: Event<'a>) -> Event<'a> {
if !self.enabled {
return event;
}
match event {
Event::Start(Tag::CodeBlock(_)) => {
self.convert_text = false;
event
}
Event::End(Tag::CodeBlock(_)) => {
self.convert_text = true;
event
}
Event::Text(ref text) if self.convert_text => {
Event::Text(CowStr::from(convert_quotes_to_curly(text)))
}
_ => event,
}
}
}
fn clean_codeblock_headers(event: Event<'_>) -> Event<'_> {
match event {
Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(ref info))) => {
let info: String = info.chars().filter(|ch| !ch.is_whitespace()).collect();
let info: String = info
.chars()
.map(|x| match x {
' ' | '\t' => ',',
_ => x,
})
.filter(|ch| !ch.is_whitespace())
.collect();
Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(CowStr::from(info))))
}
@@ -236,38 +201,6 @@ fn clean_codeblock_headers(event: Event<'_>) -> Event<'_> {
}
}
fn convert_quotes_to_curly(original_text: &str) -> String {
// We'll consider the start to be "whitespace".
let mut preceded_by_whitespace = true;
original_text
.chars()
.map(|original_char| {
let converted_char = match original_char {
'\'' => {
if preceded_by_whitespace {
''
} else {
''
}
}
'"' => {
if preceded_by_whitespace {
'“'
} else {
'”'
}
}
_ => original_char,
};
preceded_by_whitespace = original_char.is_whitespace();
converted_char
})
.collect()
}
/// Prints a "backtrace" of some `Error`.
pub fn log_backtrace(e: &Error) {
error!("Error: {}", e);
@@ -372,7 +305,7 @@ more text with spaces
```
"#;
let expected = r#"<pre><code class="language-rust,no_run,,,should_panic,,property_3"></code></pre>
let expected = r#"<pre><code class="language-rust,,,,,no_run,,,should_panic,,,,property_3"></code></pre>
"#;
assert_eq!(render_markdown(input, false), expected);
assert_eq!(render_markdown(input, true), expected);
@@ -410,6 +343,10 @@ more text with spaces
);
assert_eq!(id_from_content("## **Bold** title"), "bold-title");
assert_eq!(id_from_content("## `Code` title"), "code-title");
assert_eq!(
id_from_content("## title <span dir=rtl>foo</span>"),
"title-foo"
);
}
#[test]
@@ -443,23 +380,4 @@ more text with spaces
assert_eq!(normalize_id(""), "");
}
}
mod convert_quotes_to_curly {
use super::super::convert_quotes_to_curly;
#[test]
fn it_converts_single_quotes() {
assert_eq!(convert_quotes_to_curly("'one', 'two'"), "one, two");
}
#[test]
fn it_converts_double_quotes() {
assert_eq!(convert_quotes_to_curly(r#""one", "two""#), "“one”, “two”");
}
#[test]
fn it_treats_tab_as_whitespace() {
assert_eq!(convert_quotes_to_curly("\t'one'"), "\tone");
}
}
}

27
test_book/book.toml Normal file
View File

@@ -0,0 +1,27 @@
[book]
title = "mdBook test book"
description = "A demo book to test and validate changes"
authors = ["YJDoc2"]
language = "en"
[rust]
edition = "2018"
[output.html]
mathjax-support = true
[output.html.playground]
editable = true
line-numbers = true
[output.html.search]
limit-results = 20
use-boolean-and = true
boost-title = 2
boost-hierarchy = 2
boost-paragraph = 1
expand = true
heading-split-level = 2
[output.html.redirect]
"/format/config.html" = "configuration/index.html"

12
test_book/src/README.md Normal file
View File

@@ -0,0 +1,12 @@
# Demo Book
This is a simple demo book, which is intended to be used for verifying and validating style changes in mdBook.
This contains dummy examples of various markdown elements and code languages, so that one can check changes made in mdBook styles.
This rough outline is :
- individual : contains basic markdown elements such as headings, paragraphs, links etc.
- languages : contains a `hello world` in each of supported language to see changes in syntax highlighting
- rust : contains language examples specific to rust, such as play pen, runnable examples etc.
This is more for checking and fixing style, rather than verifying that correct code is generated for given markdown, that is better handled in tests.

33
test_book/src/SUMMARY.md Normal file
View File

@@ -0,0 +1,33 @@
# Summary
[Prefix Chapter](prefix.md)
---
- [Introduction](README.md)
- [Draft Chapter]()
# Actual Markdown Tag Examples
- [Markdown Individual tags](individual/README.md)
- [Heading](individual/heading.md)
- [Paragraphs](individual/paragraph.md)
- [Line Break](individual/linebreak.md)
- [Emphasis](individual/emphasis.md)
- [Blockquote](individual/blockquote.md)
- [List](individual/list.md)
- [Code](individual/code.md)
- [Image](individual/image.md)
- [Links and Horizontal Rule](individual/link_hr.md)
- [Tables](individual/table.md)
- [Tasks](individual/task.md)
- [Strikethrough](individual/strikethrough.md)
- [Mixed](individual/mixed.md)
- [Languages](languages/README.md)
- [Syntax Highlight](languages/highlight.md)
- [Rust Specific](rust/README.md)
- [Rust Codeblocks](rust/rust_codeblock.md)
---
[Suffix Chapter](suffix.md)

View File

@@ -0,0 +1,17 @@
# Individual Common mark tags
This contains following tags:
- Headings
- Paragraphs
- Line breaks
- Emphasis
- Blockquotes
- Lists
- Code blocks
- Images
- Links and Horizontal rules
- Github tables
- Github Task Lists
- Strikethrough
- Mixed

View File

@@ -0,0 +1,30 @@
# Blockquote
> This is a quoted sentence.
> This is a quoted paragraph
>
> separated lines
> here
> Nested
>
> > Quoted
> > Paragraph
> ### And now,
>
> **Let us _introduce_**
> All kinds of
>
> - tags
> - etc
> - stuff
>
> 1. In
> 2. The
> 3. blockquote
>
> > cause we can
> >
> > > Cause we can

View File

@@ -0,0 +1,31 @@
# Code
This section only does simple code blocks and inline code, detailed syntax highlight and stuff is in the languages section
---
```
This is a codeblock
```
---
This line contains `inline code`
---
````
escaping ``` in ```, fun, isn't is?
````
---
```bash,editable
This is an editable codeblock
```
---
```rust
// This links to a playpen
```

View File

@@ -0,0 +1,13 @@
# Emphasis
This has **bold text** in between normal.
This has _italic text_ in between normal.
A **line** having _both_, bold and italic text.
**A bold line _having_ italic text**
_An Italic line having **bold** text_
Now this is going **_out of hands_**.

View File

@@ -0,0 +1,15 @@
# Chapter Heading
---
# Really Big Heading
## Big Heading
### Normal-ish Heading
#### Small Heading...?
##### Really Small Heading
###### Is it even a heading anymore - heading

View File

@@ -0,0 +1,27 @@
# Images
For copyright and trademark information on these images, please check [rust-artwork repository](https://github.com/rust-lang/rust-artworkhttps://github.com/rust-lang/rust-artwork)
## A 16x16 image
![16x16 rust-lang logo](http://rust-lang.org/logos/rust-logo-16x16.png)
## A 32x32 image
![32x32 rust-lang logo](http://rust-lang.org/logos/rust-logo-32x32-blk.png)
## A 256x256 image
![256x256 rust-lang logo](http://rust-lang.org/logos/rust-logo-256x256.png)
## A 512x512 image
![512x512 rust-lang logo](http://rust-lang.org/logos/rust-logo-512x512-blk.png)
## A large image
![2018 rust-conf art](https://raw.githubusercontent.com/rust-lang/rust-artwork/master/2018-RustConf/lucy-mountain-climber.png)
## A SVG image
![2018 rust-conf art svg](https://raw.githubusercontent.com/rust-lang/rust-artwork/461afe27d8e02451cf9f46e507f2c2a71d2b276b/2018-RustConf/lucy-mountain-climber.svg)

View File

@@ -0,0 +1,8 @@
# Line breaks
This is a long
line with a couple of
line breaks in <br/>
between : both with two
spaces and return, <br/>
and with HTML tags.

View File

@@ -0,0 +1,15 @@
# Links and Horizontal Rule
This is followed by a Horizontal rule
---
And this is preceded by a horizontal rule.
[This](www.rust-lang.org) should link to rust-lang website
[So should this][rl].
**[This][rl]** is a strong link.
_[This][rl]_ is italic.
**_[This][rl]_** is both.
[rl]: www.rust-lang.org

View File

@@ -0,0 +1,35 @@
# Lists
1. A
2. Normal
3. Ordered
4. List
---
1. A
1. Nested
2. List
2. But
3. Still
4. Normal
---
- An
- Unordered
- Normal
- List
---
- Nested
- Unordered
- List
---
- This
1. Is
2. Normal
- ?!

View File

@@ -0,0 +1,61 @@
# Mixed
This contains all tags randomly mixed together, to make sure style changes in one does not affect others.
### A heading
**Quite a Strong statement , to make**
~~No, cross that~~
> Whose **quote** is this
>
> > And ~~this~~
> >
> > > - and
> > > - this
> > > - also
```
You encountered a wild codepen
```
```rust,editable
// The codepen is editable and runnable
fn main(){
println!("Hello world!");
}
```
A random image sprinkled in between
![16x16 rust-lang logo](http://rust-lang.org/logos/rust-logo-16x16.png)
---
- ~~An unordered list~~
- **Hello**
- _World_
- What
1. Should
2. be
3. `put`
4. here?
| col1 | col2 | col 3 | col 4 | col 5 | col 6 |
| ---- | ---- | ----- | ----- | ----- | ----- |
| val1 | val2 | val3 | val5 | val4 | val6 |
| col1 | col2 | col 3 | An Questionable table header | col 5 | col 6 |
| ---- | ---- | ----- | ---------------------------- | ----- | ---------------------------------------- |
| val1 | val2 | val3 | val5 | val4 | An equally Questionable long table value |
### Things to do
- [x] Add individual tags
- [ ] Add language examples
- [ ] Add rust specific examples
And another image
![2018 rust-conf art svg](https://raw.githubusercontent.com/rust-lang/rust-artwork/461afe27d8e02451cf9f46e507f2c2a71d2b276b/2018-RustConf/lucy-mountain-climber.svg)

View File

@@ -0,0 +1,25 @@
Just a simple paragraph.
Let's stress test this.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer elit lorem, eleifend eu leo sit amet, suscipit feugiat libero. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin congue lectus sit amet lacus venenatis, ac sollicitudin purus condimentum. Suspendisse pretium volutpat sapien at gravida. In tincidunt, sem non accumsan consectetur, leo libero porttitor dolor, at imperdiet erat nibh quis leo. Cras dictum erat augue, quis pharetra justo porttitor posuere. Aenean sed lacinia justo, vel suscipit nisl. Etiam eleifend id mauris at gravida. Aliquam molestie cursus lorem pulvinar sollicitudin. Nam et ex dignissim, posuere sem non, pellentesque lacus. Morbi vulputate sed lorem et convallis. Duis non turpis eget elit posuere volutpat. Donec accumsan euismod enim, id consequat ex rhoncus ac. Pellentesque ac felis nisl. Duis imperdiet vel tellus ac iaculis.
Vivamus nec tempus enim. Integer in ligula eget elit ornare vulputate id et est. Proin mi elit, sagittis nec urna et, iaculis imperdiet neque. Vestibulum placerat cursus dolor. Donec eu sodales nulla. Praesent ac tellus eros. Donec venenatis ligula id ex porttitor malesuada. Aliquam maximus, nisi in fringilla finibus, ante elit rhoncus dui, placerat semper nisl tellus quis odio. Cras luctus magna ultrices dolor pharetra volutpat. Maecenas non enim vitae ligula efficitur aliquet id quis quam. In sagittis mollis magna eu porta. Morbi at nulla et ante elementum pharetra in sed est. Nam commodo purus enim.
Ut non elit sit amet urna luctus facilisis vel et sapien. Morbi nec metus at libero imperdiet sollicitudin eget quis lacus. Donec in ipsum at enim accumsan tempor vel sed magna. Aliquam non imperdiet neque. Etiam pharetra neque sed pretium interdum. Suspendisse potenti. Phasellus varius, lectus quis dapibus faucibus, purus mauris accumsan nibh, vel tempor quam metus nec sem. Nunc sagittis suscipit lorem eu finibus. Nullam augue leo, imperdiet vel diam et, vulputate scelerisque turpis. Nullam ut volutpat diam. Praesent cursus accumsan dui a commodo. Vivamus sed libero sed turpis facilisis rutrum id sed ligula. Ut id sollicitudin dui. Nulla pulvinar commodo lectus. Cras ut quam congue, consectetur dolor ac, consequat ante.
Curabitur scelerisque sed leo eu facilisis. Nam faucibus neque eget dictum hendrerit. Duis efficitur ex sed vulputate volutpat. Praesent condimentum nisl ac sapien efficitur laoreet. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut ut nibh elit. Nunc a neque lobortis, tempus diam vitae, interdum magna. Aenean eget nisl sed justo volutpat interdum. Mauris malesuada ex nisl, a dignissim dui elementum eget. Suspendisse potenti.
Praesent congue fringilla sem sed faucibus. Vivamus malesuada eget mauris at molestie. In sed faucibus nulla. Vivamus elementum accumsan metus quis suscipit. Maecenas interdum est nulla. Cras volutpat cursus nibh quis sollicitudin. Morbi vitae massa laoreet, aliquet tellus quis, consectetur ipsum. Mauris euismod congue purus non condimentum. Etiam laoreet mi vel sem consectetur gravida. Vestibulum volutpat magna nunc, vitae ultrices risus commodo eu.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer elit lorem, eleifend eu leo sit amet, suscipit feugiat libero. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin congue lectus sit amet lacus venenatis, ac sollicitudin purus condimentum. Suspendisse pretium volutpat sapien at gravida. In tincidunt, sem non accumsan consectetur, leo libero porttitor dolor, at imperdiet erat nibh quis leo. Cras dictum erat augue, quis pharetra justo porttitor posuere. Aenean sed lacinia justo, vel suscipit nisl. Etiam eleifend id mauris at gravida. Aliquam molestie cursus lorem pulvinar sollicitudin. Nam et ex dignissim, posuere sem non, pellentesque lacus. Morbi vulputate sed lorem et convallis. Duis non turpis eget elit posuere volutpat. Donec accumsan euismod enim, id consequat ex rhoncus ac. Pellentesque ac felis nisl. Duis imperdiet vel tellus ac iaculis.
Vivamus nec tempus enim. Integer in ligula eget elit ornare vulputate id et est. Proin mi elit, sagittis nec urna et, iaculis imperdiet neque. Vestibulum placerat cursus dolor. Donec eu sodales nulla. Praesent ac tellus eros. Donec venenatis ligula id ex porttitor malesuada. Aliquam maximus, nisi in fringilla finibus, ante elit rhoncus dui, placerat semper nisl tellus quis odio. Cras luctus magna ultrices dolor pharetra volutpat. Maecenas non enim vitae ligula efficitur aliquet id quis quam. In sagittis mollis magna eu porta. Morbi at nulla et ante elementum pharetra in sed est. Nam commodo purus enim.
Ut non elit sit amet urna luctus facilisis vel et sapien. Morbi nec metus at libero imperdiet sollicitudin eget quis lacus. Donec in ipsum at enim accumsan tempor vel sed magna. Aliquam non imperdiet neque. Etiam pharetra neque sed pretium interdum. Suspendisse potenti. Phasellus varius, lectus quis dapibus faucibus, purus mauris accumsan nibh, vel tempor quam metus nec sem. Nunc sagittis suscipit lorem eu finibus. Nullam augue leo, imperdiet vel diam et, vulputate scelerisque turpis. Nullam ut volutpat diam. Praesent cursus accumsan dui a commodo. Vivamus sed libero sed turpis facilisis rutrum id sed ligula. Ut id sollicitudin dui. Nulla pulvinar commodo lectus. Cras ut quam congue, consectetur dolor ac, consequat ante.
Curabitur scelerisque sed leo eu facilisis. Nam faucibus neque eget dictum hendrerit. Duis efficitur ex sed vulputate volutpat. Praesent condimentum nisl ac sapien efficitur laoreet. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut ut nibh elit. Nunc a neque lobortis, tempus diam vitae, interdum magna. Aenean eget nisl sed justo volutpat interdum. Mauris malesuada ex nisl, a dignissim dui elementum eget. Suspendisse potenti.
Praesent congue fringilla sem sed faucibus. Vivamus malesuada eget mauris at molestie. In sed faucibus nulla. Vivamus elementum accumsan metus quis suscipit. Maecenas interdum est nulla. Cras volutpat cursus nibh quis sollicitudin. Morbi vitae massa laoreet, aliquet tellus quis, consectetur ipsum. Mauris euismod congue purus non condimentum. Etiam laoreet mi vel sem consectetur gravida. Vestibulum volutpat magna nunc, vitae ultrices risus commodo eu.
Hopefully everything above was rendered nicely, on both desktop and mobile.

View File

@@ -0,0 +1,5 @@
# Strikethrough
~~This is Striked~~
~~This is **strong**, _italic_ , **_both_** and striked~~

View File

@@ -0,0 +1,28 @@
# Tables
| col1 | col2 |
| ---- | ---- |
---
| col1 | col2 |
| ---- | ---- |
| val1 | val2 |
---
| col1 | col2 | col 3 | col 4 | col 5 | col 6 |
| ---- | ---- | ----- | ----- | ----- | ----- |
| val1 | val2 | val3 | val5 | val4 | val6 |
| val1 | val2 | val3 | val5 | val4 | val6 |
| val1 | val2 | val3 | val5 | val4 | val6 |
| val1 | val2 | val3 | val5 | val4 | val6 |
---
| col1 | col2 | col 3 | col 4 | col 5 | col 6 |
| -------------------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ----- | -------------------------------------------------------------------------------------------------------------- |
| This is a simple demo book, which is intended to be used for verifying and validating style changes in mdBook. | val2 | val3 | val5 | val4 | val6 |
| val1 | val2 | val3 | val5 | val4 | val6 |
| val1 | val2 | val3 | val5 | val4 | This is a simple demo book, which is intended to be used for verifying and validating style changes in mdBook. |
| val1 | val2 | This is a simple demo book, which is intended to be used for verifying and validating style changes in mdBook. | This is a simple demo book, which is intended to be used for verifying and validating style changes in mdBook. | val4 | val6 |

View File

@@ -0,0 +1,11 @@
# Tasks
- [ ] Task 1
- [ ] Task 2
- [x] Completed Task 1
- [x] Completed Task 2
---
- [ ] **Important Task**
- [x] _Completed Important task_

View File

@@ -0,0 +1,47 @@
# Syntax Highlighting
This Currently contains following languages
- apache
- armasm
- bash
- c
- coffeescript
- cpp
- csharp
- css
- d
- diff
- go
- handlebars
- haskell
- http
- ini
- java
- javascript
- json
- julia
- kotlin
- less
- lua
- makefile
- markdown
- nginx
- objectivec
- perl
- php
- plaintext
- properties
- python
- r
- ruby
- rust
- scala
- scss
- shell
- sql
- swift
- typescript
- x86asm
- xml
- yaml

View File

@@ -0,0 +1,927 @@
# Syntax Highlights
## apache
```apache
# rewrite`s rules for wordpress pretty url
LoadModule rewrite_module modules/mod_rewrite.so
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [NC,L]
ExpiresActive On
ExpiresByType application/x-javascript "access plus 1 days"
Order Deny,Allow
Allow from All
<Location /maps/>
RewriteMap map txt:map.txt
RewriteMap lower int:tolower
RewriteCond %{REQUEST_URI} ^/([^/.]+)\.html$ [NC]
RewriteCond ${map:${lower:%1}|NOT_FOUND} !NOT_FOUND
RewriteRule .? /index.php?q=${map:${lower:%1}} [NC,L]
</Location>
20.164.151.111 - - [20/Aug/2015:22:20:18 -0400] "GET /mywebpage/index.php HTTP/1.1" 403 772 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.220 Safari/535.1"
```
## armasm
```armasm
.data
/* Data segment: define our message string and calculate its length. */
msg:
.ascii "Hello, ARM!\n"
len = . - msg
.text
/* Our application's entry point. */
.globl _start
_start:
/* syscall write(int fd, const void *buf, size_t count) */
mov %r0, $1 /* fd := STDOUT_FILENO */
ldr %r1, =msg /* buf := msg */
ldr %r2, =len /* count := len */
mov %r7, $4 /* write is syscall #4 */
swi $0 /* invoke syscall */
/* syscall exit(int status) */
mov %r0, $0 /* status := 0 */
mov %r7, $1 /* exit is syscall #1 */
swi $0 /* invoke syscall */
```
## bash
```
#!/bin/bash
###### CONFIG
ACCEPTED_HOSTS="/root/.hag_accepted.conf"
BE_VERBOSE=false
if [ "$UID" -ne 0 ]
then
echo "Superuser rights required"
exit 2
fi
genApacheConf(){
echo -e "# Host ${HOME_DIR}$1/$2 :"
}
echo '"quoted"' | tr -d \" > text.txt
```
## c
```c
#include <stdio.h>
void main(int argc,char ** argv){
printf("Hello World!");
}
```
## coffeescript
```coffeescript
grade = (student, period=(if b? then 7 else 6)) ->
if student.excellentWork
"A+"
else if student.okayStuff
if student.triedHard then "B" else "B-"
else
"C"
class Animal extends Being
constructor: (@name) ->
move: (meters) ->
alert @name + " moved #{meters}m."
```
## cpp
```cpp
#include <iostream>
using namespace std;
int main() {
cout << "Hello, World!" << endl; // This prints Hello, World!
return 0;
}
```
## csharp
```csharp
using System;
class App
{
static void Main()
{
Console.WriteLine("Hello World!");
}
}
```
## css
```css
@font-face {
font-family: Chunkfive;
src: url('Chunkfive.otf');
}
body,
.usertext {
color: #f0f0f0;
background: #600;
font-family: Chunkfive, sans;
--heading-1: 30px/32px Helvetica, sans-serif;
}
@import url(print.css);
@media print {
a[href^='http']::after {
content: attr(href);
}
}
```
## d
```d
/* This program prints a
hello world message
to the console. */
import std.stdio;
void main()
{
writeln("Hello, World!");
}
```
## diff
```diff
Index: languages/ini.js
===================================================================
--- languages/ini.js (revision 199)
+++ languages/ini.js (revision 200)
@@ -1,8 +1,7 @@
hljs.LANGUAGES.ini =
{
case_insensitive: true,
- defaultMode:
- {
+ defaultMode: {
contains: ['comment', 'title', 'setting'],
illegal: '[^\\s]'
},
*** /path/to/original timestamp
--- /path/to/new timestamp
***************
*** 1,3 ****
--- 1,9 ----
+ This is an important
+ notice! It should
+ therefore be located at
+ the beginning of this
+ document!
! compress the size of the
! changes.
It is important to spell
```
## go
```go
package main
import "fmt"
func main() {
fmt.Println("Hello World!")
}
```
## handlebars
```handlebars
<div class='entry'>
{{! only show if author exists }}
{{#if author}}
<h1>{{firstName}} {{lastName}}</h1>
{{/if}}
</div>
```
## haskell
```haskell
main :: IO ()
main = putStrLn "Hello World!"
```
## http
```http
POST /task?id=1 HTTP/1.1
Host: example.org
Content-Type: application/json; charset=utf-8
Content-Length: 137
```
## ini
```ini
; boilerplate
[package]
name = "some_name"
authors = ["Author"]
description = "This is \
a description"
[[lib]]
name = ${NAME}
default = True
auto = no
counter = 1_000
```
## java
```java
class Main {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
```
## javascript
```javascript
function $initHighlight(block, cls) {
try {
if (cls.search(/\bno\-highlight\b/) != -1)
return process(block, true, 0x0F) +
` class="${cls}"`;
} catch (e) {
/* handle exception */
}
for (var i = 0 / 2; i < classes.length; i++) {
if (checkCondition(classes[i]) === undefined)
console.log('undefined');
}
return (
<div>
<web-component>{block}</web-component>
</div>
)
}
export $initHighlight;
```
## json
```json
[
{
"title": "apples",
"count": [12000, 20000],
"description": { "text": "...", "sensitive": false }
},
{
"title": "oranges",
"count": [17500, null],
"description": { "text": "...", "sensitive": false }
}
]
```
## julia
```julia
# function to calculate the volume of a sphere
function sphere_vol(r)
# julia allows Unicode names (in UTF-8 encoding)
# so either "pi" or the symbol π can be used
return 4/3*pi*r^3
end
# functions can also be defined more succinctly
quadratic(a, sqr_term, b) = (-b + sqr_term) / 2a
# calculates x for 0 = a*x^2+b*x+c, arguments types can be defined in function definitions
function quadratic2(a::Float64, b::Float64, c::Float64)
# unlike other languages 2a is equivalent to 2*a
# a^2 is used instead of a**2 or pow(a,2)
sqr_term = sqrt(b^2-4a*c)
r1 = quadratic(a, sqr_term, b)
r2 = quadratic(a, -sqr_term, b)
# multiple values can be returned from a function using tuples
# if the return keyword is omitted, the last term is returned
r1, r2
end
vol = sphere_vol(3)
```
## kotlin
```kotlin
package org.kotlinlang.play
fun main() {
println("Hello, World!")
}
```
## less
```less
@import 'fruits';
@rhythm: 1.5em;
@media screen and (min-resolution: 2dppx) {
body {
font-size: 125%;
}
}
section > .foo + #bar:hover [href*='less'] {
margin: @rhythm 0 0 @rhythm;
padding: calc(5% + 20px);
background: #f00ba7 url(http://placehold.alpha-centauri/42.png) no-repeat;
background-image: linear-gradient(-135deg, wheat, fuchsia) !important ;
background-blend-mode: multiply;
}
@font-face {
font-family: /* ? */ 'Omega';
src: url('../fonts/omega-webfont.woff?v=2.0.2');
}
.icon-baz::before {
display: inline-block;
font-family: 'Omega', Alpha, sans-serif;
content: '\f085';
color: rgba(98, 76 /* or 54 */, 231, 0.75);
}
```
## lua
```lua
--[[
Simple signal/slot implementation
]]
local signal_mt = {
__index = {
register = table.insert
}
}
function signal_mt.__index:emit(... --[[ Comment in params ]])
for _, slot in ipairs(self) do
slot(self, ...)
end
end
local function create_signal()
return setmetatable({}, signal_mt)
end
-- Signal test
local signal = create_signal()
signal:register(function(signal, ...)
print(...)
end)
signal:emit('Answer to Life, the Universe, and Everything:', 42)
--[==[ [=[ [[
Nested ]]
multi-line ]=]
comment ]==]
[==[ Nested
[=[ multi-line
[[ string
]] ]=] ]==]
```
## makefile
```makefile
# Makefile
BUILDDIR = _build
EXTRAS ?= $(BUILDDIR)/extras
.PHONY: main clean
main:
@echo "Building main facility..."
build_main $(BUILDDIR)
clean:
rm -rf $(BUILDDIR)/*
```
## markdown
```markdown
# hello world
you can write text [with links](http://example.com) inline or [link references][1].
- one _thing_ has *em*phasis
- two **things** are **bold**
[1]: http://example.com
---
# hello world
<this_is inline="xml"></this_is>
> markdown is so cool
so are code segments
1. one thing (yeah!)
2. two thing `i can write code`, and `more` wipee!
```
## nginx
```nginx
user www www;
worker_processes 2;
pid /var/run/nginx.pid;
error_log /var/log/nginx.error_log debug | info | notice | warn | error | crit;
events {
connections 2000;
use kqueue | rtsig | epoll | /dev/poll | select | poll;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $bytes_sent '
'"$http_referer" "$http_user_agent" '
'"$gzip_ratio"';
send_timeout 3m;
client_header_buffer_size 1k;
gzip on;
gzip_min_length 1100;
#lingering_time 30;
server {
server_name one.example.com www.one.example.com;
access_log /var/log/nginx.access_log main;
rewrite (.*) /index.php?page=$1 break;
location / {
proxy_pass http://127.0.0.1/;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
charset koi8-r;
}
location /api/ {
fastcgi_pass 127.0.0.1:9000;
}
location ~* \.(jpg|jpeg|gif)$ {
root /spool/www;
}
}
}
```
## objectivec
```objectivec
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@mylak {
NSLog(@"Hello World!");
}
return 0;
}
```
## perl
```perl
print "Hello World!\n";
```
## php
```php
<?php
echo "Hello World!";
?>
```
## plaintext
```plaintext
I think this is simply plain text?
Hello World!
```
## properties
```properties
# .properties
! Exclamation mark = comments, too
key1 = value1
key2 : value2
key3 value3
key\ spaces multiline\
value4
empty_key
! Key can contain escaped chars
\:\= = value5
```
## python
```python
@requires_authorization(roles=["ADMIN"])
def somefunc(param1='', param2=0):
r'''A docstring'''
if param1 > param2: # interesting
print 'Gre\'ater'
return (param2 - param1 + 1 + 0b10l) or None
class SomeClass:
pass
>>> message = '''interpreter
... prompt'''
```
## r
```r
require(stats)
#' Compute different averages
#'
#' @param x \code{numeric} vector of sample data
#' @param type \code{character} vector of length 1 specifying the average type
#' @return \code{centre} returns the sample average according to the chosen method.
#' @examples
#' centre(rcauchy(10), "mean")
#' @export
centre <- function(x, type) {
switch(type,
mean = mean(x),
median = median(x),
trimmed = mean(x, trim = .1))
}
x <- rcauchy(10)
centre(x, "mean")
library(ggplot2)
models <- tibble::tribble(
~model_name, ~ formula,
"length-width", Sepal.Length ~ Petal.Width + Petal.Length,
"interaction", Sepal.Length ~ Petal.Width * Petal.Length
)
iris %>%
nest_by(Species) %>%
left_join(models, by = character()) %>%
rowwise(Species, model_name) %>%
mutate(model = list(lm(formula, data = data))) %>%
summarise(broom::glance(model))
```
## ruby
```ruby
# The Greeter class
class Greeter
def initialize(name)
@name = name.capitalize
end
def salute
puts "Hello #{@name}!"
end
end
g = Greeter.new("world")
g.salute
```
## rust
```rust
fn main()->(){
println!("Hello World!");
}
```
## scala
```scala
/**
* A person has a name and an age.
*/
case class Person(name: String, age: Int)
abstract class Vertical extends CaseJeu
case class Haut(a: Int) extends Vertical
case class Bas(name: String, b: Double) extends Vertical
sealed trait Ior[+A, +B]
case class Left[A](a: A) extends Ior[A, Nothing]
case class Right[B](b: B) extends Ior[Nothing, B]
case class Both[A, B](a: A, b: B) extends Ior[A, B]
trait Functor[F[_]] {
def map[A, B](fa: F[A], f: A => B): F[B]
}
// beware Int.MinValue
def absoluteValue(n: Int): Int =
if (n < 0) -n else n
def interp(n: Int): String =
s"there are $n ${color} balloons.\n"
type ξ[A] = (A, A)
trait Hist { lhs =>
def ⊕(rhs: Hist): Hist
}
def gsum[A: Ring](as: Seq[A]): A =
as.foldLeft(Ring[A].zero)(_ + _)
val actions: List[Symbol] =
'init :: 'read :: 'write :: 'close :: Nil
```
## scss
```scss
import "compass/reset";
// variables
$colorGreen: #008000;
$colorGreenDark: darken($colorGreen, 10);
@mixin container {
max-width: 980px;
}
// mixins with parameters
@mixin button($color:green) {
@if ($color == green) {
background-color: #008000;
}
@else if ($color == red) {
background-color: #B22222;
}
}
button {
@include button(red);
}
div,
.navbar,
#header,
input[type="input"] {
font-family: "Helvetica Neue", Arial, sans-serif;
width: auto;
margin: 0 auto;
display: block;
}
.row-12 > [class*="spans"] {
border-left: 1px solid #B5C583;
}
```
## shell
```shell
$ echo $EDITOR
vim
$ git checkout main
Switched to branch 'main'
Your branch is up-to-date with 'origin/main'.
$ git push
Everything up-to-date
$ echo 'All
> done!'
All
done!
```
## sql
```sql
CREATE TABLE "topic" (
"id" integer NOT NULL PRIMARY KEY,
"forum_id" integer NOT NULL,
"subject" varchar(255) NOT NULL
);
ALTER TABLE "topic"
ADD CONSTRAINT forum_id FOREIGN KEY ("forum_id")
REFERENCES "forum" ("id");
-- Initials
insert into "topic" ("forum_id", "subject")
values (2, 'D''artagnian');
```
## swift
```swift
import Foundation
@objc class Person: Entity {
var name: String!
var age: Int!
init(name: String, age: Int) {
/* /* ... */ */
}
// Return a descriptive string for this person
func description(offset: Int = 0) -> String {
return "\(name) is \(age + offset) years old"
}
}
```
## typescript
```typescript
class MyClass {
public static myValue: string;
constructor(init: string) {
this.myValue = init;
}
}
import fs = require("fs");
module MyModule {
export interface MyInterface extends Other {
myProperty: any;
}
}
declare magicNumber number;
myArray.forEach(() => { }); // fat arrow syntax
```
## x86asm
```x86asm
section .text
extern _MessageBoxA@16
%if __NASM_VERSION_ID__ >= 0x02030000
safeseh handler ; register handler as "safe handler"
%endif
handler:
push dword 1 ; MB_OKCANCEL
push dword caption
push dword text
push dword 0
call _MessageBoxA@16
sub eax,1 ; incidentally suits as return value
; for exception handler
ret
global _main
_main: push dword handler
push dword [fs:0]
mov dword [fs:0], esp
xor eax,eax
mov eax, dword[eax] ; cause exception
pop dword [fs:0] ; disengage exception handler
add esp, 4
ret
avx2: vzeroupper
push rbx
mov rbx, rsp
sub rsp, 0h20
vmovdqa ymm0, [rcx]
vpaddb ymm0, [rdx]
leave
ret
text: db 'OK to rethrow, CANCEL to generate core dump',0
caption:db 'SEGV',0
section .drectve info
db '/defaultlib:user32.lib /defaultlib:msvcrt.lib '
```
## xml
```xml
<!DOCTYPE html>
<title>Title</title>
<style>body {width: 500px;}</style>
<script type="application/javascript">
function $init() {return true;}
</script>
<body>
<p checked class="title" id='title'>Title</p>
<!-- here goes the rest of the page -->
</body>
```
## yaml
```yaml
---
# comment
string_1: "Bar"
string_2: 'bar'
string_3: bar
inline_keys_ignored: sompath/name/file.jpg
keywords_in_yaml:
- true
- false
- TRUE
- FALSE
- 21
- 21.0
- !!str 123
"quoted_key": &foobar
bar: foo
foo:
"foo": bar
reference: *foobar
multiline_1: |
Multiline
String
multiline_2: >
Multiline
String
multiline_3: "
Multiline string
"
ansible_variables: "foo {{variable}}"
array_nested:
- a
- b: 1
c: 2
- b
- comment
```
ansible_variables: "foo {{variable}}"
array_nested:
- a
- b: 1
c: 2
- b
- comment
```

3
test_book/src/prefix.md Normal file
View File

@@ -0,0 +1,3 @@
# Prefix Chapter
This is to verify the placement and style of prefix chapter in book index.

View File

@@ -0,0 +1 @@
# Rust specific code examples

View File

@@ -0,0 +1,27 @@
## Rust codeblocks
This contains various examples of codeblocks, specific to rust
## Simple
```rust
fn main(){
println!("Hello world!");
}
```
## With Hidden lines
```rust
# fn main(){
println!("Hello world!");
# }
```
## Editable
```rust,editable
fn main(){
println!("Hello world!");
}
```

3
test_book/src/suffix.md Normal file
View File

@@ -0,0 +1,3 @@
# Suffix Chapter
This is to verify the placement and style of suffix chapter in book index.

29
tests/cli/build.rs Normal file
View File

@@ -0,0 +1,29 @@
use crate::dummy_book::DummyBook;
use assert_cmd::Command;
#[test]
fn mdbook_cli_dummy_book_generates_index_html() {
let temp = DummyBook::new().build().unwrap();
// doesn't exist before
assert!(!temp.path().join("book").exists());
let mut cmd = Command::cargo_bin("mdbook").unwrap();
cmd.arg("build").current_dir(temp.path());
cmd.assert()
.success()
.stderr(
predicates::str::is_match(r##"Stack depth exceeded in first[\\/]recursive.md."##)
.unwrap(),
)
.stderr(predicates::str::contains(
r##"[INFO] (mdbook::book): Running the html backend"##,
));
// exists afterward
assert!(temp.path().join("book").exists());
let index_file = temp.path().join("book/index.html");
assert!(index_file.exists());
}

2
tests/cli/mod.rs Normal file
View File

@@ -0,0 +1,2 @@
mod build;
mod test;

34
tests/cli/test.rs Normal file
View File

@@ -0,0 +1,34 @@
use crate::dummy_book::DummyBook;
use assert_cmd::Command;
use predicates::boolean::PredicateBooleanExt;
#[test]
fn mdbook_cli_can_correctly_test_a_passing_book() {
let temp = DummyBook::new().with_passing_test(true).build().unwrap();
let mut cmd = Command::cargo_bin("mdbook").unwrap();
cmd.arg("test").current_dir(temp.path());
cmd.assert().success()
.stderr(predicates::str::is_match(r##"Testing file: "([^"]+)[\\/]README.md""##).unwrap())
.stderr(predicates::str::is_match(r##"Testing file: "([^"]+)[\\/]intro.md""##).unwrap())
.stderr(predicates::str::is_match(r##"Testing file: "([^"]+)[\\/]first[\\/]index.md""##).unwrap())
.stderr(predicates::str::is_match(r##"Testing file: "([^"]+)[\\/]first[\\/]nested.md""##).unwrap())
.stderr(predicates::str::is_match(r##"rustdoc returned an error:\n\n"##).unwrap().not())
.stderr(predicates::str::is_match(r##"Nested_Chapter::Rustdoc_include_works_with_anchors_too \(line \d+\) ... FAILED"##).unwrap().not());
}
#[test]
fn mdbook_cli_detects_book_with_failing_tests() {
let temp = DummyBook::new().with_passing_test(false).build().unwrap();
let mut cmd = Command::cargo_bin("mdbook").unwrap();
cmd.arg("test").current_dir(temp.path());
cmd.assert().failure()
.stderr(predicates::str::is_match(r##"Testing file: "([^"]+)[\\/]README.md""##).unwrap())
.stderr(predicates::str::is_match(r##"Testing file: "([^"]+)[\\/]intro.md""##).unwrap())
.stderr(predicates::str::is_match(r##"Testing file: "([^"]+)[\\/]first[\\/]index.md""##).unwrap())
.stderr(predicates::str::is_match(r##"Testing file: "([^"]+)[\\/]first[\\/]nested.md""##).unwrap())
.stderr(predicates::str::is_match(r##"rustdoc returned an error:\n\n"##).unwrap())
.stderr(predicates::str::is_match(r##"Nested_Chapter::Rustdoc_include_works_with_anchors_too \(line \d+\) ... FAILED"##).unwrap());
}

2
tests/cli_tests.rs Normal file
View File

@@ -0,0 +1,2 @@
mod cli;
mod dummy_book;

View File

@@ -12,6 +12,7 @@
- [Recursive](first/recursive.md)
- [Markdown](first/markdown.md)
- [Unicode](first/unicode.md)
- [No Headers](first/no-headers.md)
- [Second Chapter](second.md)
- [Nested Chapter](second/nested.md)

View File

@@ -0,0 +1,3 @@
Capybara capybara capybara.
Capybara capybara capybara.

View File

@@ -34,6 +34,7 @@ const TOC_SECOND_LEVEL: &[&str] = &[
"1.3. Recursive",
"1.4. Markdown",
"1.5. Unicode",
"1.6. No Headers",
"2.1. Nested Chapter",
];
@@ -264,7 +265,7 @@ fn root_index_html() -> Result<Document> {
fn check_second_toc_level() {
let doc = root_index_html().unwrap();
let mut should_be = Vec::from(TOC_SECOND_LEVEL);
should_be.sort();
should_be.sort_unstable();
let pred = descendants!(
Class("chapter"),
@@ -288,7 +289,7 @@ fn check_first_toc_level() {
let mut should_be = Vec::from(TOC_TOP_LEVEL);
should_be.extend(TOC_SECOND_LEVEL);
should_be.sort();
should_be.sort_unstable();
let pred = descendants!(
Class("chapter"),
@@ -535,7 +536,7 @@ fn redirects_are_emitted_correctly() {
let mut redirect_file = md.build_dir_for("html");
// append everything except the bits that make it absolute
// (e.g. "/" or "C:\")
redirect_file.extend(remove_absolute_components(&original));
redirect_file.extend(remove_absolute_components(original));
let contents = fs::read_to_string(&redirect_file).unwrap();
assert!(contents.contains(redirect));
}
@@ -552,7 +553,7 @@ fn edit_url_has_default_src_dir_edit_url() {
edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}"
"#;
write_file(&temp.path(), "book.toml", book_toml.as_bytes()).unwrap();
write_file(temp.path(), "book.toml", book_toml.as_bytes()).unwrap();
let md = MDBook::load(temp.path()).unwrap();
md.build().unwrap();
@@ -560,7 +561,7 @@ fn edit_url_has_default_src_dir_edit_url() {
let index_html = temp.path().join("book").join("index.html");
assert_contains_strings(
index_html,
&vec![
&[
r#"href="https://github.com/rust-lang/mdBook/edit/master/guide/src/README.md" title="Suggest an edit""#,
],
);
@@ -578,7 +579,7 @@ fn edit_url_has_configured_src_dir_edit_url() {
edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}"
"#;
write_file(&temp.path(), "book.toml", book_toml.as_bytes()).unwrap();
write_file(temp.path(), "book.toml", book_toml.as_bytes()).unwrap();
let md = MDBook::load(temp.path()).unwrap();
md.build().unwrap();
@@ -586,7 +587,7 @@ fn edit_url_has_configured_src_dir_edit_url() {
let index_html = temp.path().join("book").join("index.html");
assert_contains_strings(
index_html,
&vec![
&[
r#"href="https://github.com/rust-lang/mdBook/edit/master/guide/src2/README.md" title="Suggest an edit""#,
],
);
@@ -611,7 +612,7 @@ mod search {
let index = fs::read_to_string(index).unwrap();
let index = index.trim_start_matches("Object.assign(window.search, ");
let index = index.trim_end_matches(");");
serde_json::from_str(&index).unwrap()
serde_json::from_str(index).unwrap()
}
#[test]
@@ -631,6 +632,7 @@ mod search {
let introduction = get_doc_ref("intro.html#introduction");
let some_section = get_doc_ref("first/index.html#some-section");
let summary = get_doc_ref("first/includes.html#summary");
let no_headers = get_doc_ref("first/no-headers.html");
let conclusion = get_doc_ref("conclusion.html#conclusion");
let bodyidx = &index["index"]["index"]["body"]["root"];
@@ -644,13 +646,21 @@ mod search {
assert_eq!(docs[&some_section]["body"], "");
assert_eq!(
docs[&summary]["body"],
"Dummy Book Introduction First Chapter Nested Chapter Includes Recursive Markdown Unicode Second Chapter Nested Chapter Conclusion"
"Dummy Book Introduction First Chapter Nested Chapter Includes Recursive Markdown Unicode No Headers Second Chapter Nested Chapter Conclusion"
);
assert_eq!(
docs[&summary]["breadcrumbs"],
"First Chapter » Includes » Summary"
);
assert_eq!(docs[&conclusion]["body"], "I put &lt;HTML&gt; in here!");
assert_eq!(
docs[&no_headers]["breadcrumbs"],
"First Chapter » No Headers"
);
assert_eq!(
docs[&no_headers]["body"],
"Capybara capybara capybara. Capybara capybara capybara."
);
}
// Setting this to `true` may cause issues with `cargo watch`,

File diff suppressed because it is too large Load Diff

12
triagebot.toml Normal file
View File

@@ -0,0 +1,12 @@
# This will allow users to self assign, and/or drop assignment
[assign]
[relabel]
allow-unauthenticated = [
# For Issue areas
"A-*",
"E-Help-Wanted",
"Bug",
"Feature-Request"
]